diff --git a/Assets/3DUI/scenes/VR-parkour-template-scene.unity b/Assets/3DUI/scenes/VR-parkour-template-scene.unity
index 6de5d2c12edcb25e733d8200b65c0b79bd1fe498..35c9720c2314435ebe209434bd431c512a01b058 100644
--- a/Assets/3DUI/scenes/VR-parkour-template-scene.unity
+++ b/Assets/3DUI/scenes/VR-parkour-template-scene.unity
@@ -2528,7 +2528,7 @@ RectTransform:
   m_GameObject: {fileID: 104091534}
   m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
   m_LocalPosition: {x: 0, y: 0, z: -2}
-  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_LocalScale: {x: 1.0177, y: 1.0177, z: 1.0177}
   m_ConstrainProportionsScale: 0
   m_Children: []
   m_Father: {fileID: 1995106194}
@@ -2581,7 +2581,7 @@ MonoBehaviour:
   m_spriteAsset: {fileID: 0}
   m_tintAllSprites: 0
   m_StyleSheet: {fileID: 0}
-  m_TextStyleHashCode: -1183493901
+  m_TextStyleHashCode: 2426
   m_overrideHtmlColors: 0
   m_faceColor:
     serializedVersion: 2
@@ -2593,7 +2593,7 @@ MonoBehaviour:
   m_fontSizeMin: 18
   m_fontSizeMax: 72
   m_fontStyle: 0
-  m_HorizontalAlignment: 1
+  m_HorizontalAlignment: 2
   m_VerticalAlignment: 256
   m_textAlignment: 65535
   m_characterSpacing: 0
@@ -2622,7 +2622,7 @@ MonoBehaviour:
   m_VertexBufferAutoSizeReduction: 0
   m_useMaxVisibleDescender: 1
   m_pageToDisplay: 1
-  m_margin: {x: 0, y: 0, z: 0, w: 0}
+  m_margin: {x: -151.20546, y: 0, z: -146.92606, w: 0}
   m_isUsingLegacyAnimationComponent: 0
   m_isVolumetricText: 0
   m_hasFontAssetChanged: 0
@@ -6683,7 +6683,7 @@ RectTransform:
   m_GameObject: {fileID: 419526859}
   m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
   m_LocalPosition: {x: 0, y: 0, z: -2}
-  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_LocalScale: {x: 0.98674, y: 1, z: 1}
   m_ConstrainProportionsScale: 0
   m_Children: []
   m_Father: {fileID: 93541477}
@@ -6777,7 +6777,7 @@ MonoBehaviour:
   m_VertexBufferAutoSizeReduction: 0
   m_useMaxVisibleDescender: 1
   m_pageToDisplay: 1
-  m_margin: {x: 0, y: 0, z: 0, w: 0}
+  m_margin: {x: 0, y: 0, z: -288.71198, w: 0}
   m_isUsingLegacyAnimationComponent: 0
   m_isVolumetricText: 0
   m_hasFontAssetChanged: 0
diff --git a/Assets/3DUI/scripts/EndParkourDetection.cs b/Assets/3DUI/scripts/EndParkourDetection.cs
index fa793906802d1b8b13e5fd4120eecf0ae14cc261..fb528ce7ed1bb63390cfc2f71dccd249336f6c8c 100644
--- a/Assets/3DUI/scripts/EndParkourDetection.cs
+++ b/Assets/3DUI/scripts/EndParkourDetection.cs
@@ -7,10 +7,17 @@ public class EndParkourDetection : MonoBehaviour
     private void OnTriggerEnter(Collider other)
     {
         Debug.Log("Something touched The End Barrier:" + other.name);
-        if(other.CompareTag("VRUserBodyPart"))
+        if (other.CompareTag("VRUserBodyPart"))
         {
             Debug.Log("Player finished the race!!");
             gameObject.SetActive(false);
+
+            HandRun handRunScript = FindObjectOfType<HandRun>();
+            if (handRunScript != null)
+            {
+                handRunScript.canMove = false; // Stop the player from moving
+            }
+
             FindObjectOfType<GameTimerScript>().EndRace(true);
         }
     }
diff --git a/Assets/3DUI/scripts/Mamad_Scripts/HandRun.cs b/Assets/3DUI/scripts/Mamad_Scripts/HandRun.cs
index 2e6bbed415c3036ee8e6acbeed46bc176a45a58c..143d8b954ea29480dd637db3afa8f8254d61e590 100644
--- a/Assets/3DUI/scripts/Mamad_Scripts/HandRun.cs
+++ b/Assets/3DUI/scripts/Mamad_Scripts/HandRun.cs
@@ -17,6 +17,8 @@ public class HandRun : MonoBehaviour
     private GameObject rightHandController;
     private bool buttonIsPressed;
 
+    public bool canMove = true;
+
     void Start()
     { 
         controllerInputManager = GameObject.FindObjectOfType<ControllerInputManager>();
@@ -33,7 +35,7 @@ public class HandRun : MonoBehaviour
 
     void Update()
     {
-        if (rightHandController != null)
+        if (rightHandController != null && canMove)
         {
             // Check if the primary button on the right controller is pressed
             if (controllerInputManager.rightController.isValid &&
diff --git a/Assets/Oculus/Avatar2.meta b/Assets/Oculus/Avatar2.meta
new file mode 100644
index 0000000000000000000000000000000000000000..8868e8845437b1a7bca993665e809f3ebe867943
--- /dev/null
+++ b/Assets/Oculus/Avatar2.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 3c2deeb483a580840846dcfc895ed4a6
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Editor.meta b/Assets/Oculus/Avatar2/Editor.meta
new file mode 100644
index 0000000000000000000000000000000000000000..1d6c3f5f4b285bab6193b1d8d62e74c9a51ac198
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Editor.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 40d4be79ed5789b4fb929f657f2bf79d
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Editor/Scripts.meta b/Assets/Oculus/Avatar2/Editor/Scripts.meta
new file mode 100644
index 0000000000000000000000000000000000000000..c080556506c9cc2180a6d6748b4c5f50bd549442
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Editor/Scripts.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: fb27487e9b8b71a4bbe3bb98be4e0adf
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Editor/Scripts/AvatarResourcesWindow.cs b/Assets/Oculus/Avatar2/Editor/Scripts/AvatarResourcesWindow.cs
new file mode 100644
index 0000000000000000000000000000000000000000..0bbf74be2b577e57caed6a51f483aca0ddc480b3
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Editor/Scripts/AvatarResourcesWindow.cs
@@ -0,0 +1,255 @@
+using System.Linq;
+using System.Reflection;
+
+using UnityEditor;
+
+using UnityEngine;
+using UnityEngine.Profiling;
+using UnityEngine.Rendering;
+
+namespace Oculus.Avatar2
+{
+    public class AvatarResourcesWindow : EditorWindow
+    {
+        public enum ResourceTabId : int
+        {
+            Loaders = 0,
+            GpuSkinning = 1,
+
+            Default = Loaders,
+        }
+
+        private static GUIStyle _titleStyle;
+        Vector2 _scrollPosition;
+        private OvrAvatarPrimitive _primitive;
+
+        private string[] _tabs = new string[] { "Resource Loaders", "GPU Skinning" };
+        private ResourceTabId _selectedTab = ResourceTabId.Default;
+
+        private static readonly string[] _inputPrefixes = new string[] { "neutral", "morphSrc", "morphCombined", "indirection", "joints" };
+        private static readonly string[] _outputPrefixes = new string[] { "morphJointSkinnerOutput", "jointSkinnerOutput", "morphSkinnerOutput" };
+
+        private long _totalTextureMemoryUsed = 0;
+        private long _totalMeshMemoryUsed = 0;
+
+        // Add menu named "My Window" to the Window menu
+        [MenuItem("AvatarSDK2/Resources Window")]
+        static void Init()
+        {
+            AvatarResourcesWindow window = (AvatarResourcesWindow)EditorWindow.GetWindow(typeof(AvatarResourcesWindow));
+            window.Show();
+
+        }
+
+        void OnGUI()
+        {
+            if (EditorApplication.isPlaying)
+            {
+                _selectedTab = (ResourceTabId)GUILayout.Toolbar((int)_selectedTab, _tabs);
+                _scrollPosition = EditorGUILayout.BeginScrollView(_scrollPosition);
+
+                switch (_selectedTab)
+                {
+                    case ResourceTabId.Loaders:
+                        {
+                            if (_primitive != null)
+                            {
+                                RenderPrimitiveData();
+                            }
+                            else
+                            {
+                                RenderResourceLoaders();
+                            }
+                        }
+                        break;
+                    case ResourceTabId.GpuSkinning:
+                        RenderGPUSkinningData();
+                        break;
+                }
+                EditorGUILayout.EndScrollView();
+            }
+            else
+            {
+                EditorGUILayout.LabelField("Game must be running to view resources");
+            }
+        }
+
+        void RenderPrimitiveData()
+        {
+            _titleStyle = new GUIStyle(EditorStyles.helpBox);
+            _titleStyle.fontSize = 24;
+            if (GUILayout.Button("Back"))
+            {
+                _primitive = null;
+            }
+
+            if (_primitive == null)
+            {
+                EditorGUILayout.LabelField("Data has no value");
+                return;
+            }
+
+            var fields = _primitive.data.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
+            EditorGUILayout.LabelField(_primitive.name, _titleStyle);
+            int i = 0;
+            foreach (var field in fields)
+            {
+                GUI.backgroundColor = i % 2 == 0 ? Color.white : Color.gray;
+
+                EditorGUILayout.BeginHorizontal(EditorStyles.helpBox);
+                EditorGUILayout.LabelField(field.Name);
+                EditorGUILayout.LabelField(field.GetValue(_primitive.data).ToString());
+                EditorGUILayout.EndHorizontal();
+                i++;
+            }
+
+            long memoryUsed = Profiler.GetRuntimeMemorySizeLong(_primitive.mesh);
+            GUI.backgroundColor = i % 2 == 0 ? Color.white : Color.gray;
+            EditorGUILayout.BeginHorizontal(EditorStyles.helpBox);
+            EditorGUILayout.LabelField("Mesh Memory");
+            EditorGUILayout.LabelField((memoryUsed / 1000000f).ToString() + "mb");
+            EditorGUILayout.EndHorizontal();
+        }
+
+        void RenderResourceLoaders()
+        {
+            if (OvrAvatarManager.Instance != null)
+            {
+                var _resourcesByID = OvrAvatarManager.Instance.GetResourceID();
+                EditorGUILayout.LabelField("Resource Loaders");
+                EditorGUILayout.BeginHorizontal();
+                EditorGUILayout.LabelField("Texture Memory: " + (_totalTextureMemoryUsed / 1000000f) + "mb");
+                EditorGUILayout.LabelField("Mesh Memory: " + (_totalMeshMemoryUsed / 1000000f) + "mb");
+                EditorGUILayout.EndHorizontal();
+                _totalTextureMemoryUsed = 0;
+                _totalMeshMemoryUsed = 0;
+
+                int i = 0;
+                foreach (var kvp in _resourcesByID)
+                {
+                    long totalMemoryPerLoader = 0;
+
+                    var primitives = kvp.Value.Primitives;
+                    var images = kvp.Value.Images;
+                    GUI.backgroundColor = i % 2 == 0 ? Color.white : Color.gray;
+                    EditorGUILayout.BeginHorizontal(EditorStyles.helpBox);
+                    EditorGUILayout.LabelField(kvp.Key.ToString(), GUILayout.MaxWidth(50));
+                    EditorGUILayout.BeginVertical();
+                    foreach (var p in primitives)
+                    {
+                        long memoryUsed = Profiler.GetRuntimeMemorySizeLong(p.mesh);
+                        totalMemoryPerLoader += memoryUsed;
+                        if (GUILayout.Button(p.name))
+                        {
+                            _primitive = p;
+                        }
+                        _totalMeshMemoryUsed += memoryUsed;
+                    }
+                    EditorGUILayout.EndVertical();
+
+                    EditorGUILayout.BeginVertical();
+                    foreach (var im in images)
+                    {
+                        long memoryUsed = Profiler.GetRuntimeMemorySizeLong(im.texture);
+                        _totalTextureMemoryUsed += memoryUsed;
+                        totalMemoryPerLoader += memoryUsed;
+                        EditorGUILayout.BeginVertical();
+                        var rect = GUILayoutUtility.GetRect(128, 128);
+                        EditorGUI.DrawPreviewTexture(rect, im.texture);
+                        EditorGUILayout.LabelField((memoryUsed / 1000000f).ToString() + "mb");
+                        EditorGUILayout.EndVertical();
+                    }
+                    EditorGUILayout.EndVertical();
+                    EditorGUILayout.BeginVertical(EditorStyles.helpBox, GUILayout.MaxWidth(82));
+                    EditorGUILayout.LabelField("Total Memory:", GUILayout.MaxWidth(82));
+                    EditorGUILayout.LabelField((totalMemoryPerLoader / 1000000f).ToString() + "mb", GUILayout.MaxWidth(82));
+                    EditorGUILayout.EndVertical();
+                    EditorGUILayout.EndHorizontal();
+                    i++;
+                }
+            }
+        }
+
+        void RenderGPUSkinningData()
+        {
+            var textures = Resources.FindObjectsOfTypeAll<Texture>();
+
+
+            var inputs = textures.Where(t =>
+            {
+                foreach (var p in _inputPrefixes)
+                {
+                    if (t.name.StartsWith(p))
+                    {
+                        return true;
+                    }
+                }
+                return false;
+            });
+
+            var outputs = textures.Where(t =>
+            {
+                foreach (var p in _outputPrefixes)
+                {
+                    if (t.name.StartsWith(p))
+                    {
+                        return true;
+                    }
+                }
+                return false;
+            });
+
+            EditorGUILayout.LabelField("Inputs");
+            foreach (var t in inputs)
+            {
+                EditorGUILayout.BeginHorizontal();
+                EditorGUILayout.ObjectField(t, typeof(Texture), true);
+
+                textureLabelField(t);
+                EditorGUILayout.LabelField(t.filterMode.ToString(), GUILayout.MaxWidth(50));
+
+                EditorGUILayout.EndHorizontal();
+            }
+
+            EditorGUILayout.LabelField("Outputs");
+            foreach (var t in outputs)
+            {
+                EditorGUILayout.BeginHorizontal();
+                EditorGUILayout.ObjectField(t, typeof(Texture), true);
+
+                textureLabelField(t);
+                EditorGUILayout.LabelField($"{t.graphicsFormat}");
+                EditorGUILayout.LabelField(t.filterMode.ToString(), GUILayout.MaxWidth(50));
+
+                EditorGUILayout.EndHorizontal();
+
+            }
+        }
+
+        private static void textureLabelField(Texture t)
+        {
+            switch (t.dimension)
+            {
+                case TextureDimension.Tex3D:
+                case TextureDimension.Tex2DArray:
+                    EditorGUILayout.LabelField($"{t.width}x{t.height}^{getTextureSliceCount(t)}");
+                    break;
+                default:
+                    EditorGUILayout.LabelField($"{t.width}x{t.height}");
+                    break;
+            }
+        }
+
+        private static int getTextureSliceCount(Texture t)
+        {
+            if (t == null) { return 0; }
+            if (t is RenderTexture rT) { return rT.volumeDepth; }
+            if (t is Texture2DArray aT) { return aT.depth; }
+
+            OvrAvatarLog.LogError("Unrecognized texture type", LOG_SCOPE, t);
+            return 1;
+        }
+
+        private const string LOG_SCOPE = "AvatarResourcesWindow";
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Editor/Scripts/AvatarResourcesWindow.cs.meta b/Assets/Oculus/Avatar2/Editor/Scripts/AvatarResourcesWindow.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..d6494ccc32010885dcda9527c9cd7cd116898ac3
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Editor/Scripts/AvatarResourcesWindow.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: c4b9eddc59e855a499f6421f5a41d9d7
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Editor/Scripts/EditorBuildScenes.cs b/Assets/Oculus/Avatar2/Editor/Scripts/EditorBuildScenes.cs
new file mode 100644
index 0000000000000000000000000000000000000000..76ccef4610da7ec33a51581aaefbf4b08934f753
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Editor/Scripts/EditorBuildScenes.cs
@@ -0,0 +1,30 @@
+using System.Collections.Generic;
+using UnityEditor;
+
+namespace Oculus.Avatar2
+{
+    public static class EditorBuildScenes
+    {
+        public static List<string> OverrideScenes { get; } = new List<string>();
+
+        public static string[] GetBuildScenes()
+        {
+            if (OverrideScenes.Count > 0)
+            {
+                return OverrideScenes.ToArray();
+            }
+
+            // Fall back to scenes from build settings
+            var buildScenes = new List<string>();
+            foreach (var scene in EditorBuildSettings.scenes)
+            {
+                if (scene.enabled)
+                {
+                    buildScenes.Add(scene.path);
+                }
+            }
+
+            return buildScenes.ToArray();
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Editor/Scripts/EditorBuildScenes.cs.meta b/Assets/Oculus/Avatar2/Editor/Scripts/EditorBuildScenes.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..59b4b2fc85253944a8d76ddf22434d475454f788
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Editor/Scripts/EditorBuildScenes.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 266920991cd24479a8d7e176c878f1a8
+timeCreated: 1662755875
\ No newline at end of file
diff --git a/Assets/Oculus/Avatar2/Editor/Scripts/EnumMaskDrawer.cs b/Assets/Oculus/Avatar2/Editor/Scripts/EnumMaskDrawer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..df14237425f446245efe794957042f0fc250b7e9
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Editor/Scripts/EnumMaskDrawer.cs
@@ -0,0 +1,75 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Reflection;
+using UnityEditor;
+using UnityEngine;
+
+namespace Oculus.Avatar2
+{
+#if !UNITY_2019_3_OR_NEWER
+    [CustomPropertyDrawer(typeof(EnumMaskAttribute))]
+    public class EnumMaskDrawer : PropertyDrawer
+    {
+        public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
+        {
+            if (property.propertyType != SerializedPropertyType.Enum)
+            {
+                EditorGUI.PropertyField(position, property, label, true);
+                return;
+            }
+
+            label = EditorGUI.BeginProperty(position, label, property);
+
+            Enum oldValue = (Enum)Convert.ChangeType(GetValue(property), typeof(Enum));
+            Enum newValue = EditorGUI.EnumFlagsField(position, label, oldValue);
+            
+            if (!oldValue.Equals(newValue))
+            {
+                SetValue(property, newValue);
+                EditorUtility.SetDirty(property.serializedObject.targetObject);
+            }
+
+            EditorGUI.EndProperty();
+        }
+        
+        // It's 2020 and Unity still doesn't have a way to get the value as a generic object from a SerializedProperty
+        // So we'll do it ourselves
+        private object GetValue(SerializedProperty property)
+        {
+            object valueAsObject = property.serializedObject.targetObject;
+            FieldInfo field = null;
+            foreach(var path in property.propertyPath.Split('.'))
+            {
+                var type = valueAsObject.GetType();
+                field = type.GetField(path, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
+                valueAsObject = field.GetValue(valueAsObject);
+            }
+            return valueAsObject;
+        }
+        
+        private void SetValue(SerializedProperty property, object val)
+        {
+            object obj = property.serializedObject.targetObject;
+            List<KeyValuePair<FieldInfo, object>> list = new List<KeyValuePair<FieldInfo, object>>();
+ 
+            FieldInfo field = null;
+            foreach(var path in property.propertyPath.Split('.'))
+            {
+                var type = obj.GetType();
+                field = type.GetField(path, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
+                list.Add(new KeyValuePair<FieldInfo, object>(field, obj));
+                obj = field.GetValue(obj);
+            }
+ 
+            // Now set values of all objects, from child to parent
+            for(int i = list.Count - 1; i >= 0; --i)
+            {
+                list[i].Key.SetValue(list[i].Value, val);
+                // New 'val' object will be parent of current 'val' object
+                val = list[i].Value;
+            }
+        }
+    }
+#endif
+}
diff --git a/Assets/Oculus/Avatar2/Editor/Scripts/EnumMaskDrawer.cs.meta b/Assets/Oculus/Avatar2/Editor/Scripts/EnumMaskDrawer.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..8f3a064edac923b5eed58b460994bda7a45f6c32
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Editor/Scripts/EnumMaskDrawer.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 1500943592e3ea3428e4584bbd9f2db9
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Editor/Scripts/HideIfKeywordDrawer.cs b/Assets/Oculus/Avatar2/Editor/Scripts/HideIfKeywordDrawer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..1411598bd67554ba1dc368fc3a53b051b406d572
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Editor/Scripts/HideIfKeywordDrawer.cs
@@ -0,0 +1,266 @@
+/*  Original code[1] Copyright (c) 2015 jistyles[2]
+    Released as "public domain, no restrictions, no requirements, no support"
+    Modified code[3] Copyright (c) 2018 Shane Celis[4]
+    Licensed under the MIT License[5]
+
+    This comment generated by code-cite[6].
+
+    [1]: https://forum.unity.com/threads/sharing-is-caring-hiding-optional-material-parameters.349952/
+    [2]: https://forum.unity.com/members/jistyles.458393/
+    [3]: https://gitlab.com/snippets/1828392
+    [4]: http://twitter.com/shanecelis
+    [5]: https://opensource.org/licenses/MIT
+    [6]: https://github.com/shanecelis/code-cite
+*/
+//$ cite -u https://forum.unity.com/threads/sharing-is-caring-hiding-optional-material-parameters.349952/ \
+// -X jistyles -Y 2015 -Z https://forum.unity.com/members/jistyles.458393/ \
+// -x shane -l mit -m -U https://gitlab.com/snippets/1828392
+
+using UnityEngine;
+using UnityEditor;
+
+/**
+   Hide or show properties in a material based on whether a given keyword is
+   enabled or not.
+
+   For instance, suppose we have a shader that has a toggle `RENDER_LINES` and
+   there are properties that are irrelevant when it is disabled.
+
+```
+   [Toggle(RENDER_LINES)]
+   _RenderLines ("Render Lines?", Float) = 0
+   _LineDistance("Major Line Distance", Range(0, 2)) = 1
+```
+
+   We can add the following attribute to hide _LineDistance if RENDER_LINES is
+   enabled. It reads, "hide if RENDER_LINES is false."
+
+```
+   [HideIfKeyword(RENDER_LINES, false)]
+   _LineDistance("Major Line Distance", Range(0, 2)) = 1
+```
+
+   Or we can hide it if RENDER_LINES is disabled.  It reads, "hide if RENDER_LINES is true."
+
+```
+   [HideIfKeyword(RENDER_LINES, true)]
+   _LineDistance("Major Line Distance", Range(0, 2)) = 1
+```
+
+   If no "true" or "false" argument is provided, it will default to true, so the
+   following will hide if RENDER_LINES is enabled.  It reads, "hide if RENDER_LINES is true."
+
+```
+   [HideIfKeyword(RENDER_LINES)]
+   _LineDistance("Major Line Distance", Range(0, 2)) = 1
+```
+
+  ShowIf
+  ------
+
+  The drawer `ShowIf` performs the same function but instead reads differently.
+
+  [ShowIf(X, true)]
+
+  ```
+  [Toggle(RENDER_LINES)]
+  _RenderLines ("Render Lines?", Float) = 0
+  _LineDistance("Major Line Distance", Range(0, 2)) = 1
+  ```
+
+  We can add the following attribute to show _LineDistance if RENDER_LINES is
+  enabled. It reads, "show if RENDER_LINES is false."
+
+  ```
+  [ShowIf(RENDER_LINES, false)]
+  _LineDistance("Major Line Distance", Range(0, 2)) = 1
+  ```
+
+  Or we can show it if RENDER_LINES is disabled.  It reads, "show if RENDER_LINES is true."
+
+  ```
+  [ShowIf(RENDER_LINES, true)]
+  _LineDistance("Major Line Distance", Range(0, 2)) = 1
+  ```
+
+  If no "true" or "false" argument is provided, it will default to true, so the
+  following will show if RENDER_LINES is enabled.  It reads, "show if RENDER_LINES is true."
+
+  ```
+  [ShowIf(RENDER_LINES)]
+  _LineDistance("Major Line Distance", Range(0, 2)) = 1
+  ```
+
+  Limitations
+  -----------
+
+  * Cannot work with other attributes, e.g., IntRange.
+
+  See this [tweet thread](https://twitter.com/shanecelis/status/1098714764962877440) to see it in action.
+*/
+public class HideIfKeywordDrawer : MaterialPropertyDrawer {
+  protected string[] _argValue;
+  private bool[] _enabledQueries;
+
+  //constructor permutations -- params doesn't seem to work for property drawer inputs :( -----------
+  public HideIfKeywordDrawer(string name1) {
+    _argValue = new[] {name1};
+    _enabledQueries = new[] {true};
+  }
+
+  // Constructor for (name1 || name2) or (name1 is (name2 as bool))
+  public HideIfKeywordDrawer(string name1, string name2) {
+    if (bool.TryParse(name2, out bool e)) {
+      _argValue = new[] {name1};
+      _enabledQueries = new[] {e};
+    } else {
+      _argValue = new[] {name1, name2};
+      _enabledQueries = new[] {true, true};
+    }
+  }
+
+  // The booleans apply to every keyword before them.
+  // Single boolean at name3 or
+  // single boolean at name2
+  public HideIfKeywordDrawer(string name1, string name2, string name3) {
+    if (bool.TryParse(name3, out bool e)) {
+      _argValue = new[] {name1, name2};
+      _enabledQueries = new[] {e, e};
+    } else if (bool.TryParse(name2, out e)) {
+      _argValue = new[] {name1, name3};
+      _enabledQueries = new[] {e, true};
+    } else {
+      _argValue = new[] {name1, name2, name3};
+      _enabledQueries = new[] {true, true, true};
+    }
+  }
+
+  // The booleans apply to every keyword before them.
+  // Booleans can be at name2 and name4 or
+  // single boolean at name4 or
+  // single boolean at name3 or
+  // single boolean at name2
+  public HideIfKeywordDrawer(string name1, string name2, string name3, string name4) {
+    if (bool.TryParse(name4, out bool e)) {
+      // Can be either 2 or 3 component depending on if name2 is also a bool
+      if (bool.TryParse(name2, out bool e2)) {
+        _argValue = new[] {name1, name3};
+        _enabledQueries = new[] {e2, e};
+      } else {
+        _argValue = new[] {name1, name2, name3};
+        _enabledQueries = new[] {e, e, e};
+      }
+    } else if (bool.TryParse(name3, out e)) {
+      _argValue = new[] {name1, name2, name4};
+      _enabledQueries = new[] {e, e, true};
+    } else if (bool.TryParse(name2, out e)) {
+      _argValue = new[] {name1, name3, name4};
+      _enabledQueries = new[] {e, true, true};
+    } else {
+      _argValue = new[] {name1, name2, name3, name4};
+      _enabledQueries = new[] {true, true, true, true};
+    }
+  }
+
+  //-------------------------------------------------------------------------------------------------
+
+  public override void OnGUI(Rect position, MaterialProperty prop, string label, MaterialEditor editor) {
+    bool elementMatches = KeywordsOr(editor.targets);
+
+    if (ShowElement(elementMatches))
+      editor.DefaultShaderProperty(prop, label);
+  }
+
+  public bool KeywordsOr(Object[] targets) {
+    bool elementMatches = false;
+    for (int i = 0; i < targets.Length; i++) {
+      //material object that we're targetting...
+      Material mat = targets[i] as Material;
+      if (mat != null) {
+        //check for the dependencies:
+        for (int j = 0; j < _argValue.Length; j++) {
+          elementMatches |= mat.IsKeywordEnabled(_argValue[j]) == _enabledQueries[j];
+        }
+      }
+    }
+
+    return elementMatches;
+  }
+
+  protected virtual bool ShowElement(bool matched) => !matched;
+
+  //We need to override the height so it's not adding any extra (unfortunately
+  //texture drawers will still add an extra bit of padding regardless):
+  public override float GetPropertyHeight(MaterialProperty prop, string label, MaterialEditor editor) {
+    return -EditorGUIUtility.standardVerticalSpacing;
+  }
+}
+
+
+public class ShowIfKeywordDrawer : HideIfKeywordDrawer {
+
+  public ShowIfKeywordDrawer(string name1) : base(name1) {
+  }
+  public ShowIfKeywordDrawer(string name1, string name2) : base(name1, name2) {
+  }
+  public ShowIfKeywordDrawer(string name1, string name2, string name3) : base(name1, name2, name3) {
+  }
+
+  public ShowIfKeywordDrawer(string name1, string name2, string name3, string name4) :
+    base(name1, name2, name3, name4)
+  {
+
+  }
+
+  protected override bool ShowElement(bool matched) => matched;
+}
+
+// Booleans required for both sides of the "and".
+public class HideIfKeywordAndDrawer : MaterialPropertyDrawer {
+
+  private HideIfKeywordDrawer leftSide_;
+  private HideIfKeywordDrawer rightSide_;
+
+  public HideIfKeywordAndDrawer(string name1, string name2, string name3, string name4, string name5)
+  {
+    // Find boolean locations
+    // Will either be (A, B, [bool], C, [bool])
+    // or (A, [bool], B, C, [bool])
+    if (bool.TryParse(name3, out bool e)) {
+      // (A, B, [bool], C, [bool])
+      leftSide_ = new HideIfKeywordDrawer(name1, name2, name3);
+      rightSide_ = new HideIfKeywordDrawer(name4, name5);
+    } else {
+      // (A, [bool], B, C, [bool])
+      leftSide_ = new HideIfKeywordDrawer(name1, name2);
+      rightSide_ = new HideIfKeywordDrawer(name3, name4, name5);
+    }
+  }
+
+
+  public override void OnGUI(Rect position, MaterialProperty prop, string label, MaterialEditor editor) {
+    bool elementMatches = leftSide_.KeywordsOr(editor.targets);
+    elementMatches &= rightSide_.KeywordsOr(editor.targets);
+
+    if (ShowElement(elementMatches))
+      editor.DefaultShaderProperty(prop, label);
+  }
+
+  protected virtual bool ShowElement(bool matched) => !matched;
+
+  //We need to override the height so it's not adding any extra (unfortunately
+  //texture drawers will still add an extra bit of padding regardless):
+  public override float GetPropertyHeight(MaterialProperty prop, string label, MaterialEditor editor) {
+    return -EditorGUIUtility.standardVerticalSpacing;
+  }
+}
+
+public class ShowIfKeywordAndDrawer : HideIfKeywordAndDrawer {
+  public ShowIfKeywordAndDrawer(string name1, string name2, string name3, string name4, string name5) :
+    base(name1, name2, name3, name4, name5)
+  {
+
+  }
+
+  protected override bool ShowElement(bool matched) => matched;
+}
diff --git a/Assets/Oculus/Avatar2/Editor/Scripts/HideIfKeywordDrawer.cs.meta b/Assets/Oculus/Avatar2/Editor/Scripts/HideIfKeywordDrawer.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..49f979a31e6330da3b374d0712a97cd58a817354
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Editor/Scripts/HideIfKeywordDrawer.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: f6550435c3cd0134d93645ae1db8f7ec
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Editor/Scripts/Oculus.AvatarSDK2.Editor.asmdef b/Assets/Oculus/Avatar2/Editor/Scripts/Oculus.AvatarSDK2.Editor.asmdef
new file mode 100644
index 0000000000000000000000000000000000000000..79653ea793b1f7ef4429a2f9cd6ff6ee1a07ab11
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Editor/Scripts/Oculus.AvatarSDK2.Editor.asmdef
@@ -0,0 +1,32 @@
+{
+    "name": "Oculus.AvatarSDK2.Editor",
+    "rootNamespace": "",
+    "references": [
+        "Oculus.AvatarSDK2",
+        "Oculus.AvatarSDK2.Example",
+        "Unity.XR.Management",
+        "Unity.XR.Management.Editor"
+    ],
+    "includePlatforms": [
+        "Editor"
+    ],
+    "excludePlatforms": [],
+    "allowUnsafeCode": false,
+    "overrideReferences": false,
+    "precompiledReferences": [],
+    "autoReferenced": true,
+    "defineConstraints": [],
+    "versionDefines": [
+        {
+            "name": "com.unity.xr.management",
+            "expression": "",
+            "define": "USING_XR_MANAGEMENT"
+        },
+        {
+            "name": "com.unity.xr.oculus",
+            "expression": "",
+            "define": "USING_XR_SDK_OCULUS"
+        }
+    ],
+    "noEngineReferences": false
+}
diff --git a/Assets/Oculus/Avatar2/Editor/Scripts/Oculus.AvatarSDK2.Editor.asmdef.meta b/Assets/Oculus/Avatar2/Editor/Scripts/Oculus.AvatarSDK2.Editor.asmdef.meta
new file mode 100644
index 0000000000000000000000000000000000000000..857fa9d56540581b2cb26bcf61933b4918ed9a25
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Editor/Scripts/Oculus.AvatarSDK2.Editor.asmdef.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: f9720f193d03b2c409063902e12d3e82
+AssemblyDefinitionImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Editor/Scripts/OvrAvatarLogSettings.cs b/Assets/Oculus/Avatar2/Editor/Scripts/OvrAvatarLogSettings.cs
new file mode 100644
index 0000000000000000000000000000000000000000..f12f69b381fe3d0d2c00f8e893899d95448e446d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Editor/Scripts/OvrAvatarLogSettings.cs
@@ -0,0 +1,150 @@
+using System;
+
+using UnityEditor;
+
+namespace Oculus.Avatar2
+{
+    public static class OvrLogSettings
+    {
+        private const string OVRAVATAR_LOG_ENABLE_CONDITIONAL = "OVRAVATAR_LOG_ENABLE_CONDITIONAL";
+        private const string OVRAVATAR_ASSERT_ENABLE_CONDITIONAL = "OVRAVATAR_ASSERT_ENABLE_CONDITIONAL";
+
+        [MenuItem("AvatarSDK2/Debug/Enable Logs")]
+        private static void EnableLogs()
+        {
+            SetLogsEnabled(true);
+        }
+        [MenuItem("AvatarSDK2/Debug/Enable Logs", true)]
+        private static bool CheckIfEnableLogsIsValid()
+        {
+            return AreAnyLogsDisabled();
+        }
+
+        [MenuItem("AvatarSDK2/Debug/Disable Logs")]
+        private static void DisableLogs()
+        {
+            SetLogsEnabled(false);
+        }
+        [MenuItem("AvatarSDK2/Debug/Disable Logs", true)]
+        private static bool CheckIfDisableLogsIsValid()
+        {
+            return AreAnyLogsEnabled();
+        }
+
+        [MenuItem("AvatarSDK2/Debug/Enable Asserts")]
+        private static void EnableAsserts()
+        {
+            SetAssertsEnabled(true);
+        }
+        [MenuItem("AvatarSDK2/Debug/Enable Asserts", true)]
+        private static bool CheckIfEnableAssertsIsValid()
+        {
+            return AreAnyAssertsDisabled();
+        }
+
+        [MenuItem("AvatarSDK2/Debug/Disable Asserts")]
+        private static void DisableAsserts()
+        {
+            SetAssertsEnabled(false);
+        }
+        [MenuItem("AvatarSDK2/Debug/Disable Asserts", true)]
+        private static bool CheckIfDisableAssertsIsValid()
+        {
+            return AreAnyAssertsEnabled();
+        }
+
+        private static void SetLogsEnabled(bool enableLogs)
+        {
+            ConfigureDefines(OVRAVATAR_LOG_ENABLE_CONDITIONAL, OvrAvatarLog.OVRAVATAR_LOG_FORCE_ENABLE, enableLogs);
+        }
+        private static void SetAssertsEnabled(bool enableAsserts)
+        {
+            ConfigureDefines(OVRAVATAR_ASSERT_ENABLE_CONDITIONAL, OvrAvatarLog.OVRAVATAR_ASSERT_FORCE_ENABLE, enableAsserts);
+        }
+
+        private static void ConfigureDefines(string enableConditional, string forceEnableDefine, bool enableLogs)
+        {
+            var logChange = enableLogs ? "enabling" : "disabling";
+            var conditionalDefineWithSemicolon = enableConditional + ';';
+            var forceEnableDefineWithSemicolon = forceEnableDefine + ';';
+            foreach (BuildTargetGroup target in Enum.GetValues(typeof(BuildTargetGroup)))
+            {
+                if (!IsAvatarTarget(target)) { continue; }
+
+                bool definesDidChange = false;
+
+                var defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(target);
+                if (!defines.Contains(enableConditional))
+                {
+                    UnityEngine.Debug.LogWarning($"Enabling conditional logging for {Enum.GetName(typeof(BuildTargetGroup), target)}");
+
+                    defines = conditionalDefineWithSemicolon + defines;
+                    definesDidChange = true;
+                }
+
+                if (defines.Contains(forceEnableDefine) != enableLogs)
+                {
+                    UnityEngine.Debug.Log($"Updating log settings for {Enum.GetName(typeof(BuildTargetGroup), target)} - {logChange}");
+
+                    if (enableLogs)
+                    {
+                        defines = forceEnableDefineWithSemicolon + defines;
+                    }
+                    else
+                    {
+                        defines = defines.Replace(forceEnableDefineWithSemicolon, string.Empty);
+                        defines = defines.Replace(forceEnableDefine, string.Empty);
+                    }
+                    definesDidChange = true;
+                }
+
+                if (definesDidChange)
+                {
+                    PlayerSettings.SetScriptingDefineSymbolsForGroup(target, defines);
+                }
+            }
+        }
+
+        private static bool AreAnyLogsEnabled()
+            => AreSymbolsEnabled(OVRAVATAR_LOG_ENABLE_CONDITIONAL, OvrAvatarLog.OVRAVATAR_LOG_FORCE_ENABLE);
+        private static bool AreAnyLogsDisabled()
+            => AreSymbolsDisabled(OVRAVATAR_LOG_ENABLE_CONDITIONAL, OvrAvatarLog.OVRAVATAR_LOG_FORCE_ENABLE);
+
+        private static bool AreAnyAssertsEnabled()
+            => AreSymbolsEnabled(OVRAVATAR_ASSERT_ENABLE_CONDITIONAL, OvrAvatarLog.OVRAVATAR_ASSERT_FORCE_ENABLE);
+        private static bool AreAnyAssertsDisabled()
+            => AreSymbolsDisabled(OVRAVATAR_ASSERT_ENABLE_CONDITIONAL, OvrAvatarLog.OVRAVATAR_ASSERT_FORCE_ENABLE);
+
+
+        private static bool AreSymbolsEnabled(string enableConditional, string forceEnable)
+            => !IsDefineSetOnAnyPlatform(enableConditional) || IsDefineSetOnAllPlatforms(forceEnable);
+
+        private static bool AreSymbolsDisabled(string enableConditional, string forceEnable)
+            => IsDefineSetOnAnyPlatform(enableConditional) && !IsDefineSetOnAllPlatforms(forceEnable);
+
+        private static readonly BuildTargetGroup[] SupportedPlatforms =
+        {
+                BuildTargetGroup.Standalone,
+                BuildTargetGroup.Android,
+                BuildTargetGroup.iOS,
+        };
+        private static bool IsAvatarTarget(BuildTargetGroup target) => SupportedPlatforms.Contains(target);
+
+        private static bool IsDefineSetOnAnyPlatform(string define) => IsDefineSet(define, true);
+        private static bool IsDefineSetOnAllPlatforms(string define) => IsDefineSet(define, false);
+        private static bool IsDefineSet(string define, bool matchAnyPlatform)
+        {
+            foreach (BuildTargetGroup target in Enum.GetValues(typeof(BuildTargetGroup)))
+            {
+                if (!IsAvatarTarget(target)) { continue; }
+
+                var defines = PlayerSettings.GetScriptingDefineSymbolsForGroup(target);
+                if (defines.Contains(define) == matchAnyPlatform)
+                {
+                    return matchAnyPlatform;
+                }
+            }
+            return !matchAnyPlatform;
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Editor/Scripts/OvrAvatarLogSettings.cs.meta b/Assets/Oculus/Avatar2/Editor/Scripts/OvrAvatarLogSettings.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..299ff20712966637c65ddee275685107c0fba563
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Editor/Scripts/OvrAvatarLogSettings.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: e02e6c60f8d1be4468d18e98018d1243
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Editor/Scripts/StreamingAssetsPlatformIncluder.cs b/Assets/Oculus/Avatar2/Editor/Scripts/StreamingAssetsPlatformIncluder.cs
new file mode 100644
index 0000000000000000000000000000000000000000..f3accfcdadfd86f71605bbd88de8b8e5df4c24c5
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Editor/Scripts/StreamingAssetsPlatformIncluder.cs
@@ -0,0 +1,241 @@
+#if USING_XR_MANAGEMENT && USING_XR_SDK_OCULUS && !OVRPLUGIN_UNSUPPORTED_PLATFORM
+#define USING_XR_SDK
+#endif
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using UnityEditor;
+using UnityEditor.Build;
+using UnityEditor.Build.Reporting;
+using UnityEngine;
+
+namespace Oculus.Avatar2
+{
+    /// <summary>
+    /// This editor script adds a preprocess build step that copies the Avatars SDK streaming assets that are required
+    /// on the current target platform to the project's StreamingAssets folder. The copied assets are cleaned up after
+    /// the build finishes.
+    /// Run this manually from the AvatarSDK2 > Streaming Assets menu.
+    /// </summary>
+    public class StreamingAssetsPlatformIncluder : IPreprocessBuildWithReport, IPostprocessBuildWithReport
+    {
+        private static char s = Path.DirectorySeparatorChar;
+
+        private static readonly string[] UniversalPaths =
+        {
+            $"Oculus{s}OvrAvatar2Assets.zip",
+            $"SampleAssets{s}PresetAvatars_Fastload.zip",
+        };
+
+        private static readonly string[] RiftPaths =
+        {
+            $"SampleAssets{s}PresetAvatars_Rift.zip",
+        };
+
+        private static readonly string[] QuestPaths =
+        {
+            $"SampleAssets{s}PresetAvatars_Quest.zip",
+        };
+
+
+        private static readonly List<string> AssetsToCopy = new List<string>();
+
+        public int callbackOrder => default;
+
+        public void OnPreprocessBuild(BuildReport report)
+        {
+            Application.logMessageReceived += OnBuildError; // Start listening for errors
+            CopyStreamingAssets();
+        }
+
+        private void OnBuildError(string condition, string stacktrace, LogType type)
+        {
+            if (type == LogType.Error || type == LogType.Exception)
+            {
+                Application.logMessageReceived -= OnBuildError; // Stop listening for errors
+                EditorApplication.update += CleanUpStreamingAssets; // Clean up after build stops
+            }
+        }
+
+        public void OnPostprocessBuild(BuildReport report)
+        {
+            Application.logMessageReceived -= OnBuildError; // Stop listening for errors
+            CleanUpStreamingAssets();
+        }
+
+        [MenuItem("AvatarSDK2/Streaming Assets/Copy assets for current platform")]
+        private static void CopyStreamingAssets()
+        {
+            AssetsToCopy.Clear();
+            AssetsToCopy.AddRange(UniversalPaths);
+
+#if USING_XR_SDK
+            bool isBuildingXR = true;
+#else
+            bool isBuildingXR = false;
+#endif
+
+            if (EditorUserBuildSettings.activeBuildTarget == BuildTarget.Android)
+            {
+                AssetsToCopy.AddRange(QuestPaths);
+            }
+            else
+            {
+                AssetsToCopy.AddRange(RiftPaths);
+            }
+
+            OvrAvatarLog.LogInfo("Copying required Avatar2 assets to StreamingAssets",
+                nameof(StreamingAssetsPlatformIncluder));
+
+            CopyFiles(AssetsToCopy);
+            AssetDatabase.Refresh();
+        }
+
+        private void CleanUpStreamingAssets()
+        {
+            EditorApplication.update -= CleanUpStreamingAssets;
+            OvrAvatarLog.LogInfo("Cleaning up Avatar2 streaming assets", nameof(StreamingAssetsPlatformIncluder));
+
+            // Clean up the files that were copied
+            DeleteFiles(AssetsToCopy);
+
+            AssetDatabase.Refresh();
+        }
+
+        private static void CopyFiles(List<string> paths)
+        {
+            foreach (var path in paths)
+            {
+                var source = GetSourcePath(path);
+                var destination = GetDestinationPath(path);
+
+                if (!File.Exists(source))
+                {
+                    OvrAvatarLog.LogWarning("Trying to copy an asset that doesn't exist: " + source,
+                        nameof(StreamingAssetsPlatformIncluder));
+                    continue;
+                }
+
+                if (File.Exists(destination))
+                {
+                    OvrAvatarLog.LogWarning(
+                        $"Asset at path {destination} already exists and will be overwritten. Fix this using AvatarSDK2 > Streaming Assets > Clean up",
+                        nameof(StreamingAssetsPlatformIncluder));
+                }
+
+                try
+                {
+                    var destinationDirectory = Path.GetDirectoryName(destination);
+                    Directory.CreateDirectory(destinationDirectory ?? throw new InvalidOperationException("Bad destination file path"));
+                    File.Copy(source, destination, true);
+                }
+                catch (IOException e)
+                {
+                    OvrAvatarLog.LogException("Copy StreamingAssets", e, nameof(StreamingAssetsPlatformIncluder));
+                }
+            }
+        }
+
+        private static void DeleteFiles(List<string> paths)
+        {
+            var directories = new HashSet<string>();
+
+            foreach (var path in paths)
+            {
+                try
+                {
+                    var destinationPath = GetDestinationPath(path);
+                    directories.Add(Path.GetDirectoryName(destinationPath));
+
+                    File.Delete(destinationPath);
+                    File.Delete(destinationPath + ".meta");
+                }
+                catch (IOException e)
+                {
+                    OvrAvatarLog.LogException("Clean up StreamingAssets", e, nameof(StreamingAssetsPlatformIncluder));
+                }
+            }
+
+            // Clean up empty directories too
+            foreach (var directory in directories)
+            {
+                try
+                {
+                    bool isDirectoryEmpty;
+                    using (var enumerator = Directory.EnumerateFileSystemEntries(directory).GetEnumerator())
+                    {
+                        isDirectoryEmpty = !enumerator.MoveNext();
+                    }
+
+                    if (isDirectoryEmpty)
+                    {
+                        Directory.Delete(directory);
+                        File.Delete(directory + ".meta");
+                    }
+                }
+                catch (IOException e)
+                {
+                    OvrAvatarLog.LogException("Clean up StreamingAssets", e, nameof(StreamingAssetsPlatformIncluder));
+                }
+            }
+        }
+
+        private static string GetSourcePath(string file)
+        {
+            return Path.Combine(Application.dataPath, "Oculus", "Avatar2", "StreamingAssets", file);
+        }
+
+        private static string GetDestinationPath(string file)
+        {
+            return Path.Combine(Application.streamingAssetsPath, file);
+        }
+
+        [InitializeOnLoad]
+        private static class UpgradeCheck
+        {
+            static UpgradeCheck()
+            {
+                if (!SessionState.GetBool("AvatarStreamingAssetsUpgradeCheckRanOnce", false))
+                {
+                    EditorApplication.update += RunOnce;
+                }
+            }
+
+            [MenuItem("AvatarSDK2/Streaming Assets/Clean up")]
+            private static void RunOnce()
+            {
+                EditorApplication.update -= RunOnce;
+                if (DoesProjectNeedUpgrade())
+                {
+                    SessionState.SetBool("AvatarStreamingAssetsUpgradeCheckRanOnce", true);
+                    if (EditorUtility.DisplayDialog("Avatars SDK Upgrade",
+                            "A previous version of Avatars SDK copied assets to this project's StreamingAssets folder during setup. The current version of Avatars SDK no longer needs these extra assets.\nWould you like to clean up this project's StreamingAssets folder?\n\nYou can also do this later using AvatarSDK2 > Streaming Assets > Clean up.",
+                            "Clean up", "Ignore"))
+                    {
+                        CleanUpOldStreamingAssets();
+                    }
+                }
+            }
+
+            private static bool DoesProjectNeedUpgrade()
+            {
+                var oldFilePath = Path.Combine(Application.streamingAssetsPath, "Oculus", "OvrAvatar2Assets.zip");
+                return File.Exists(oldFilePath);
+            }
+
+            private static void CleanUpOldStreamingAssets()
+            {
+                var allStreamingAssets = new List<string>();
+                allStreamingAssets.AddRange(UniversalPaths);
+                allStreamingAssets.AddRange(RiftPaths);
+                allStreamingAssets.AddRange(QuestPaths);
+
+                DeleteFiles(allStreamingAssets);
+
+                AssetDatabase.Refresh();
+            }
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Editor/Scripts/StreamingAssetsPlatformIncluder.cs.meta b/Assets/Oculus/Avatar2/Editor/Scripts/StreamingAssetsPlatformIncluder.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..b7c20e91e5e7ef91d28d1c547c77ee4eb977ceec
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Editor/Scripts/StreamingAssetsPlatformIncluder.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 4cc1dd1882869534b93b66ec759e1dd6
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Editor/Scripts/XCodeXCFrameworkHelper.cs b/Assets/Oculus/Avatar2/Editor/Scripts/XCodeXCFrameworkHelper.cs
new file mode 100644
index 0000000000000000000000000000000000000000..be913ff0e3d3dbd415ba2b117bf68bd2def9beea
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Editor/Scripts/XCodeXCFrameworkHelper.cs
@@ -0,0 +1,69 @@
+// (c) Meta Platforms, Inc. and affiliates. Confidential and proprietary.
+
+// This script adds .dylib library files to the xcode project
+// generated by Unity.
+
+#if UNITY_IOS
+
+using System;
+using System.IO;
+using UnityEditor.iOS.Xcode;
+using UnityEngine;
+using System.Collections.Generic;
+
+namespace Oculus.Avatar2
+{
+    public class XcodeXCFrameworkHelper
+    {
+        // Create and add a "Copy Files" build phase to add the XCFrameworks to the Xcode project
+        public static void AddXCFrameworksToXcodeProject(string pathToBuiltProject, string[] xcFrameworksPaths, string buildPhaseName)
+        {
+            string projectPath = PBXProject.GetPBXProjectPath(pathToBuiltProject);
+
+            PBXProject project = new PBXProject();
+            project.ReadFromString(File.ReadAllText(projectPath));
+
+            var unityMainTarget = project.GetUnityMainTargetGuid();
+            var unityFrameworkTarget = project.GetUnityFrameworkTargetGuid();
+
+            var frameworksBuildPhaseUnityFrameworkTarget = project.GetFrameworksBuildPhaseByTarget(unityFrameworkTarget);
+
+            // Add each XCFramework to the build phase
+            foreach (var xcFrameworksPath in xcFrameworksPaths)
+            {
+                string pluginDir = Path.Combine(Application.dataPath, xcFrameworksPath);
+                if (Directory.Exists(pluginDir))
+                {
+                    DirectoryInfo dirInfo = new DirectoryInfo(pluginDir);
+                    foreach (var xcFramework in dirInfo.GetDirectories("*.xcframework"))
+                    {
+                        var whereToAdd = "Frameworks/" + xcFramework.Name;
+                        var xcFrameworkGuid = project.AddFile(xcFramework.ToString(), whereToAdd, PBXSourceTree.Sdk);
+                        UnityEditor.iOS.Xcode.Extensions.PBXProjectExtensions.AddFileToEmbedFrameworks(project, unityMainTarget, xcFrameworkGuid);
+                        project.AddFileToBuildSection(unityFrameworkTarget, frameworksBuildPhaseUnityFrameworkTarget, xcFrameworkGuid);
+                        OvrAvatarLog.LogInfo($"Added {xcFramework.Name} to the Xcode project.");
+
+                        // Remove any frameworks automatically added by Unity
+                        foreach (var framework in xcFramework.GetDirectories("*.framework", SearchOption.AllDirectories))
+                        {
+                            string frameworkRelativePath = framework.FullName.Substring(xcFramework.FullName.Length + 1);
+                            string frameworkProjectPath = $"Frameworks/{xcFrameworksPath}{xcFramework.Name}/{frameworkRelativePath}";
+                            string frameworkGuid = project.FindFileGuidByProjectPath(frameworkProjectPath);
+
+                            if (frameworkGuid != null) {
+                                OvrAvatarLog.LogInfo($"Duplicate framework {frameworkRelativePath} for {xcFramework.Name} found, removing from Xcode project.");
+                                project.RemoveFileFromBuild(unityFrameworkTarget, frameworkGuid);
+                                project.RemoveFile(frameworkGuid);
+                            }
+                        }
+                    }
+                }
+            }
+
+            string projectSettings = project.WriteToString();
+            File.WriteAllText(projectPath, projectSettings);
+        }
+    }
+}
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Editor/Scripts/XCodeXCFrameworkHelper.cs.meta b/Assets/Oculus/Avatar2/Editor/Scripts/XCodeXCFrameworkHelper.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..6456ca013e0ddfcdf12e4adddf08c14101873975
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Editor/Scripts/XCodeXCFrameworkHelper.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 6244cc1d2cbda4c8aaf2d07c51070495
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Editor/Scripts/iOSPostProcess.cs b/Assets/Oculus/Avatar2/Editor/Scripts/iOSPostProcess.cs
new file mode 100644
index 0000000000000000000000000000000000000000..1f0c4e0241ec46885e2f02ea598f1a5d1b337b4d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Editor/Scripts/iOSPostProcess.cs
@@ -0,0 +1,43 @@
+#if UNITY_IOS
+
+using System.IO;
+using UnityEditor;
+using UnityEditor.iOS.Xcode;
+using UnityEditor.Callbacks;
+
+namespace Oculus.Avatar2
+{
+    public class AvatarSDK_iOS_PostProcess
+    {
+
+
+        private static readonly string[] xcFrameworksPaths =
+        {
+            "Oculus/Avatar2/Plugins/iOS/",
+            "Internal/Plugins/iOS/"
+        };
+
+        private const string BuildPhaseName = "iOS Post Process";
+
+        [PostProcessBuild]
+        public static void OnPostprocessBuild(BuildTarget buildTarget, string pathToBuiltProject)
+        {
+            var projectPath = PBXProject.GetPBXProjectPath(pathToBuiltProject);
+            var project = new PBXProject();
+            project.ReadFromString(File.ReadAllText(projectPath));
+
+            // Disable bitcode for Unity Framework
+            var frameworkTarget = project.GetUnityFrameworkTargetGuid();
+            project.SetBuildProperty(frameworkTarget, "ENABLE_BITCODE", "NO");
+
+            var projectSettings = project.WriteToString();
+            File.WriteAllText(projectPath, projectSettings);
+
+
+
+            XcodeXCFrameworkHelper.AddXCFrameworksToXcodeProject(pathToBuiltProject, xcFrameworksPaths, BuildPhaseName);
+        }
+    }
+}
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Editor/Scripts/iOSPostProcess.cs.meta b/Assets/Oculus/Avatar2/Editor/Scripts/iOSPostProcess.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..37ed438f925c53257873125b3df7cdcf94ef1470
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Editor/Scripts/iOSPostProcess.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a9ab8f84bdc6f441f885e01b8a81e70f
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example.meta b/Assets/Oculus/Avatar2/Example.meta
new file mode 100644
index 0000000000000000000000000000000000000000..ebcbd6166a8859149a5712701d9161b414750804
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 90d48ff6cedf7734f9912498b1fc93e9
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common.meta b/Assets/Oculus/Avatar2/Example/Common.meta
new file mode 100644
index 0000000000000000000000000000000000000000..d1bee81769f2e0d35786634a46d68876e5124023
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: ff46bca976072224c93b4f40861820b8
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Materials.meta b/Assets/Oculus/Avatar2/Example/Common/Materials.meta
new file mode 100644
index 0000000000000000000000000000000000000000..f7223b4963a4c8db0b06747be0a995f17e6050d4
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Materials.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 687e79064f791764db90d56ad65e888e
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Materials/BasicDark.mat b/Assets/Oculus/Avatar2/Example/Common/Materials/BasicDark.mat
new file mode 100644
index 0000000000000000000000000000000000000000..9f57b8ad18714c343e1017084e51af8499cb32e1
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Materials/BasicDark.mat
@@ -0,0 +1,77 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: BasicDark
+  m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
+  m_ShaderKeywords: 
+  m_LightmapFlags: 4
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 4, y: 5}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 4, y: 5}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 0.054467782, g: 0.0754717, b: 0.05607464, a: 1}
+    - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Materials/BasicDark.mat.meta b/Assets/Oculus/Avatar2/Example/Common/Materials/BasicDark.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..aced01fb4f666da8f488b7991fb727d42543e21d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Materials/BasicDark.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 6500e5e25ff64254aa97291b203a9902
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Materials/BasicEmissive.mat b/Assets/Oculus/Avatar2/Example/Common/Materials/BasicEmissive.mat
new file mode 100644
index 0000000000000000000000000000000000000000..7eadab3bd4063ebe4902ad40df6e92320dfef938
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Materials/BasicEmissive.mat
@@ -0,0 +1,77 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: BasicEmissive
+  m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
+  m_ShaderKeywords: _EMISSION
+  m_LightmapFlags: 2
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 0.7176471, g: 0.7176471, b: 0.7176471, a: 1}
+    - _EmissionColor: {r: 1.4470105, g: 1.4470105, b: 1.4470105, a: 1}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Materials/BasicEmissive.mat.meta b/Assets/Oculus/Avatar2/Example/Common/Materials/BasicEmissive.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..2bfbd3514f1aa05e9b88009066a734c96f37d33f
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Materials/BasicEmissive.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 13c4a0c48b475e1449df97949f0ce046
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 2100000
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Materials/BasicGreen.mat b/Assets/Oculus/Avatar2/Example/Common/Materials/BasicGreen.mat
new file mode 100644
index 0000000000000000000000000000000000000000..7d07c3e000430ef5342a668153f75bff577901c3
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Materials/BasicGreen.mat
@@ -0,0 +1,77 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: BasicGreen
+  m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
+  m_ShaderKeywords: 
+  m_LightmapFlags: 4
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 0, g: 0.7169812, b: 0.05348798, a: 1}
+    - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Materials/BasicGreen.mat.meta b/Assets/Oculus/Avatar2/Example/Common/Materials/BasicGreen.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..12c7114869913815f819ee5e70d074d00bbc1072
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Materials/BasicGreen.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: ad7ef5ae8bf013549ad371a0250d222d
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 2100000
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Materials/BasicLight.mat b/Assets/Oculus/Avatar2/Example/Common/Materials/BasicLight.mat
new file mode 100644
index 0000000000000000000000000000000000000000..fc258e1df151d1ca3b974eedbe9c379a7265833b
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Materials/BasicLight.mat
@@ -0,0 +1,77 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: BasicLight
+  m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
+  m_ShaderKeywords: 
+  m_LightmapFlags: 4
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 4, y: 5}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 4, y: 5}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 0.31572625, g: 0.4056604, b: 0.31572625, a: 1}
+    - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Materials/BasicLight.mat.meta b/Assets/Oculus/Avatar2/Example/Common/Materials/BasicLight.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..614db5237a80b0b676787dfe85519e272b53cbd7
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Materials/BasicLight.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: d84b507c82775dc4bad5d119e5f87ddf
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Materials/BasicRed.mat b/Assets/Oculus/Avatar2/Example/Common/Materials/BasicRed.mat
new file mode 100644
index 0000000000000000000000000000000000000000..c11dcba9f3fbb00b0fedbedfc2192f747a167193
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Materials/BasicRed.mat
@@ -0,0 +1,77 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: BasicRed
+  m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
+  m_ShaderKeywords: 
+  m_LightmapFlags: 4
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 0.7176471, g: 0, b: 0, a: 1}
+    - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Materials/BasicRed.mat.meta b/Assets/Oculus/Avatar2/Example/Common/Materials/BasicRed.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..dea11516a96a303c7a2c29734407e7f8f761d493
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Materials/BasicRed.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 2f5e2978552a8e048b99c94c1b5628a3
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 2100000
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Materials/BasicWhite.mat b/Assets/Oculus/Avatar2/Example/Common/Materials/BasicWhite.mat
new file mode 100644
index 0000000000000000000000000000000000000000..0f528f74b87779c53897bfc782745dfafa8cc857
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Materials/BasicWhite.mat
@@ -0,0 +1,77 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: BasicWhite
+  m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
+  m_ShaderKeywords: 
+  m_LightmapFlags: 4
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 0.7176471, g: 0.7176471, b: 0.7176471, a: 1}
+    - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Materials/BasicWhite.mat.meta b/Assets/Oculus/Avatar2/Example/Common/Materials/BasicWhite.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..f9dad33c95e3270414d1a5633962837617996cca
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Materials/BasicWhite.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 92e10dd57eef28845a928f3843d68704
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 2100000
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Materials/HumanShaderSampleBlack.mat b/Assets/Oculus/Avatar2/Example/Common/Materials/HumanShaderSampleBlack.mat
new file mode 100644
index 0000000000000000000000000000000000000000..ea8b5790203aa0629165052dfbb0859706891943
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Materials/HumanShaderSampleBlack.mat
@@ -0,0 +1,128 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: HumanShaderSampleBlack
+  m_Shader: {fileID: 4800000, guid: 48f513db188320345a43a70bbb34b7fe, type: 3}
+  m_ShaderKeywords: BRDF_LUT_MODE_ON DEBUG_NONE
+  m_LightmapFlags: 4
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ColorRamp0:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ColorRamp1:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_AttributeTexture:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_BaseColorSampler:
+        m_Texture: {fileID: 2800000, guid: 8126abc91ceccd84696395fc6d1e2ffc, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_EmissiveSampler:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_MetallicRoughnessSampler:
+        m_Texture: {fileID: 2800000, guid: ed8192c8839b473429aff481e4c2467b, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_NormalSampler:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_OcclusionSampler:
+        m_Texture: {fileID: 2800000, guid: ed8192c8839b473429aff481e4c2467b, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - BRDF_LUT_Mode: 0
+    - Debug: 0
+    - ToneMap: 0
+    - _BumpScale: 1
+    - _Cull: 2
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    - u_BaseColorUVSet: 1
+    - u_DiffuseSmoothingFactor: 1
+    - u_EmissiveSet: 0
+    - u_Exposure: 1
+    - u_EyeGlintFactor: 10
+    - u_F0Factor: 1
+    - u_MetallicFactor: 1
+    - u_MetallicRoughnessUVSet: 0
+    - u_NormalScale: 1
+    - u_NormalUVSet: 0
+    - u_OcclusionSet: 0
+    - u_OcclusionStrength: 1
+    - u_RoughnessFactor: 1
+    m_Colors:
+    - _Color: {r: 1, g: 1, b: 1, a: 1}
+    - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
+    - u_BaseColorFactor: {r: 1, g: 1, b: 1, a: 1}
+    - u_EmissiveFactor: {r: 1, g: 1, b: 1, a: 1}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Materials/HumanShaderSampleBlack.mat.meta b/Assets/Oculus/Avatar2/Example/Common/Materials/HumanShaderSampleBlack.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..30dd9983a49d4304cdf9754c633d1601c062c7f2
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Materials/HumanShaderSampleBlack.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: bf6bfd4ed28761045ac7d5bb051beb7c
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Materials/HumanShaderSampleReflective.mat b/Assets/Oculus/Avatar2/Example/Common/Materials/HumanShaderSampleReflective.mat
new file mode 100644
index 0000000000000000000000000000000000000000..e8cf6474d87c42afece2a50f9f018a37282c3d2d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Materials/HumanShaderSampleReflective.mat
@@ -0,0 +1,128 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: HumanShaderSampleReflective
+  m_Shader: {fileID: 4800000, guid: 48f513db188320345a43a70bbb34b7fe, type: 3}
+  m_ShaderKeywords: BRDF_LUT_MODE_ON DEBUG_NONE
+  m_LightmapFlags: 4
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ColorRamp0:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ColorRamp1:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_AttributeTexture:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_BaseColorSampler:
+        m_Texture: {fileID: 2800000, guid: 1303047a4a2d85649aebc891948037d0, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_EmissiveSampler:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_MetallicRoughnessSampler:
+        m_Texture: {fileID: 2800000, guid: 325a054c45020654fb52d97d93ab3680, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_NormalSampler:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_OcclusionSampler:
+        m_Texture: {fileID: 2800000, guid: 325a054c45020654fb52d97d93ab3680, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - BRDF_LUT_Mode: 0
+    - Debug: 0
+    - ToneMap: 0
+    - _BumpScale: 1
+    - _Cull: 2
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    - u_BaseColorUVSet: 1
+    - u_DiffuseSmoothingFactor: 1
+    - u_EmissiveSet: 0
+    - u_Exposure: 1
+    - u_EyeGlintFactor: 10
+    - u_F0Factor: 1
+    - u_MetallicFactor: 1
+    - u_MetallicRoughnessUVSet: 0
+    - u_NormalScale: 1
+    - u_NormalUVSet: 0
+    - u_OcclusionSet: 0
+    - u_OcclusionStrength: 1
+    - u_RoughnessFactor: 0
+    m_Colors:
+    - _Color: {r: 1, g: 1, b: 1, a: 1}
+    - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
+    - u_BaseColorFactor: {r: 1, g: 1, b: 1, a: 1}
+    - u_EmissiveFactor: {r: 1, g: 1, b: 1, a: 1}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Materials/HumanShaderSampleReflective.mat.meta b/Assets/Oculus/Avatar2/Example/Common/Materials/HumanShaderSampleReflective.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..7436f67fde719db1a008f514fd4b99723a5476d0
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Materials/HumanShaderSampleReflective.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 3ba89b7e2c4552642b5da8d4ad51fd21
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Materials/HumanShaderSampleWhite.mat b/Assets/Oculus/Avatar2/Example/Common/Materials/HumanShaderSampleWhite.mat
new file mode 100644
index 0000000000000000000000000000000000000000..fafe443e231b04cd556f725da4c286d21b1bba79
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Materials/HumanShaderSampleWhite.mat
@@ -0,0 +1,128 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: HumanShaderSampleWhite
+  m_Shader: {fileID: 4800000, guid: 48f513db188320345a43a70bbb34b7fe, type: 3}
+  m_ShaderKeywords: BRDF_LUT_MODE_ON DEBUG_NONE
+  m_LightmapFlags: 4
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ColorRamp0:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ColorRamp1:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_AttributeTexture:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_BaseColorSampler:
+        m_Texture: {fileID: 2800000, guid: 325a054c45020654fb52d97d93ab3680, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_EmissiveSampler:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_MetallicRoughnessSampler:
+        m_Texture: {fileID: 2800000, guid: ed8192c8839b473429aff481e4c2467b, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_NormalSampler:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_OcclusionSampler:
+        m_Texture: {fileID: 2800000, guid: ed8192c8839b473429aff481e4c2467b, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - BRDF_LUT_Mode: 0
+    - Debug: 0
+    - ToneMap: 0
+    - _BumpScale: 1
+    - _Cull: 2
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    - u_BaseColorUVSet: 1
+    - u_DiffuseSmoothingFactor: 1
+    - u_EmissiveSet: 0
+    - u_Exposure: 1
+    - u_EyeGlintFactor: 10
+    - u_F0Factor: 1
+    - u_MetallicFactor: 1
+    - u_MetallicRoughnessUVSet: 0
+    - u_NormalScale: 1
+    - u_NormalUVSet: 0
+    - u_OcclusionSet: 0
+    - u_OcclusionStrength: 1
+    - u_RoughnessFactor: 1
+    m_Colors:
+    - _Color: {r: 1, g: 1, b: 1, a: 1}
+    - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
+    - u_BaseColorFactor: {r: 1, g: 1, b: 1, a: 1}
+    - u_EmissiveFactor: {r: 1, g: 1, b: 1, a: 1}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Materials/HumanShaderSampleWhite.mat.meta b/Assets/Oculus/Avatar2/Example/Common/Materials/HumanShaderSampleWhite.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..dc5176c880e90d80af1cab848ec28f5a7a882e43
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Materials/HumanShaderSampleWhite.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 1080b5c292ddf2b41b069305b9c8ff60
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Materials/KhronosShaderSampleBlack.mat b/Assets/Oculus/Avatar2/Example/Common/Materials/KhronosShaderSampleBlack.mat
new file mode 100644
index 0000000000000000000000000000000000000000..b36e5a7803e1b95a4e86d474e3af8814bcd4584b
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Materials/KhronosShaderSampleBlack.mat
@@ -0,0 +1,128 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: KhronosShaderSampleBlack
+  m_Shader: {fileID: 4800000, guid: 2a6c569e922c5e34e9c05f146aaf9c78, type: 3}
+  m_ShaderKeywords: DEBUG_NONE
+  m_LightmapFlags: 4
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ColorRamp0:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ColorRamp1:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_AttributeTexture:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_BaseColorSampler:
+        m_Texture: {fileID: 2800000, guid: 8126abc91ceccd84696395fc6d1e2ffc, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_EmissiveSampler:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_MetallicRoughnessSampler:
+        m_Texture: {fileID: 2800000, guid: ed8192c8839b473429aff481e4c2467b, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_NormalSampler:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_OcclusionSampler:
+        m_Texture: {fileID: 2800000, guid: ed8192c8839b473429aff481e4c2467b, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - BRDF_LUT_Mode: 0
+    - Debug: 0
+    - ToneMap: 0
+    - _BumpScale: 1
+    - _Cull: 2
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    - u_BaseColorUVSet: 1
+    - u_DiffuseSmoothingFactor: 1
+    - u_EmissiveSet: 0
+    - u_Exposure: 1
+    - u_EyeGlintFactor: 10
+    - u_F0Factor: 1
+    - u_MetallicFactor: 1
+    - u_MetallicRoughnessUVSet: 0
+    - u_NormalScale: 1
+    - u_NormalUVSet: 0
+    - u_OcclusionSet: 0
+    - u_OcclusionStrength: 1
+    - u_RoughnessFactor: 1
+    m_Colors:
+    - _Color: {r: 1, g: 1, b: 1, a: 1}
+    - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
+    - u_BaseColorFactor: {r: 1, g: 1, b: 1, a: 1}
+    - u_EmissiveFactor: {r: 1, g: 1, b: 1, a: 1}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Materials/KhronosShaderSampleBlack.mat.meta b/Assets/Oculus/Avatar2/Example/Common/Materials/KhronosShaderSampleBlack.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..96a537309a1968d42b1e041dd7b6e2cbeac9f6c6
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Materials/KhronosShaderSampleBlack.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: a08ecf32b5ff4fd40a12fde17ee2115c
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Materials/KhronosShaderSampleReflective.mat b/Assets/Oculus/Avatar2/Example/Common/Materials/KhronosShaderSampleReflective.mat
new file mode 100644
index 0000000000000000000000000000000000000000..c0f73988464b19cb717d39a565d54170b8049c15
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Materials/KhronosShaderSampleReflective.mat
@@ -0,0 +1,128 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: KhronosShaderSampleReflective
+  m_Shader: {fileID: 4800000, guid: 2a6c569e922c5e34e9c05f146aaf9c78, type: 3}
+  m_ShaderKeywords: DEBUG_NONE
+  m_LightmapFlags: 4
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ColorRamp0:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ColorRamp1:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_AttributeTexture:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_BaseColorSampler:
+        m_Texture: {fileID: 2800000, guid: 1303047a4a2d85649aebc891948037d0, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_EmissiveSampler:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_MetallicRoughnessSampler:
+        m_Texture: {fileID: 2800000, guid: 325a054c45020654fb52d97d93ab3680, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_NormalSampler:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_OcclusionSampler:
+        m_Texture: {fileID: 2800000, guid: 325a054c45020654fb52d97d93ab3680, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - BRDF_LUT_Mode: 0
+    - Debug: 0
+    - ToneMap: 0
+    - _BumpScale: 1
+    - _Cull: 2
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    - u_BaseColorUVSet: 1
+    - u_DiffuseSmoothingFactor: 1
+    - u_EmissiveSet: 0
+    - u_Exposure: 1
+    - u_EyeGlintFactor: 10
+    - u_F0Factor: 1
+    - u_MetallicFactor: 1
+    - u_MetallicRoughnessUVSet: 0
+    - u_NormalScale: 1
+    - u_NormalUVSet: 0
+    - u_OcclusionSet: 0
+    - u_OcclusionStrength: 1
+    - u_RoughnessFactor: 0
+    m_Colors:
+    - _Color: {r: 1, g: 1, b: 1, a: 1}
+    - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
+    - u_BaseColorFactor: {r: 1, g: 1, b: 1, a: 1}
+    - u_EmissiveFactor: {r: 1, g: 1, b: 1, a: 1}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Materials/KhronosShaderSampleReflective.mat.meta b/Assets/Oculus/Avatar2/Example/Common/Materials/KhronosShaderSampleReflective.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..e7e67e8423c5b670e63d4c4e6941315541a14fa9
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Materials/KhronosShaderSampleReflective.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: f4475b6691081d840b8d2f502a30bbc9
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Materials/KhronosShaderSampleWhite.mat b/Assets/Oculus/Avatar2/Example/Common/Materials/KhronosShaderSampleWhite.mat
new file mode 100644
index 0000000000000000000000000000000000000000..2c9fe9f8d5f4bc2e5a0969a9e4f975d51c7c8da4
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Materials/KhronosShaderSampleWhite.mat
@@ -0,0 +1,128 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: KhronosShaderSampleWhite
+  m_Shader: {fileID: 4800000, guid: 2a6c569e922c5e34e9c05f146aaf9c78, type: 3}
+  m_ShaderKeywords: DEBUG_NONE
+  m_LightmapFlags: 4
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ColorRamp0:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ColorRamp1:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_AttributeTexture:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_BaseColorSampler:
+        m_Texture: {fileID: 2800000, guid: 325a054c45020654fb52d97d93ab3680, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_EmissiveSampler:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_MetallicRoughnessSampler:
+        m_Texture: {fileID: 2800000, guid: ed8192c8839b473429aff481e4c2467b, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_NormalSampler:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_OcclusionSampler:
+        m_Texture: {fileID: 2800000, guid: ed8192c8839b473429aff481e4c2467b, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - BRDF_LUT_Mode: 0
+    - Debug: 0
+    - ToneMap: 0
+    - _BumpScale: 1
+    - _Cull: 2
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    - u_BaseColorUVSet: 1
+    - u_DiffuseSmoothingFactor: 1
+    - u_EmissiveSet: 0
+    - u_Exposure: 1
+    - u_EyeGlintFactor: 10
+    - u_F0Factor: 1
+    - u_MetallicFactor: 1
+    - u_MetallicRoughnessUVSet: 0
+    - u_NormalScale: 1
+    - u_NormalUVSet: 0
+    - u_OcclusionSet: 0
+    - u_OcclusionStrength: 1
+    - u_RoughnessFactor: 1
+    m_Colors:
+    - _Color: {r: 1, g: 1, b: 1, a: 1}
+    - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
+    - u_BaseColorFactor: {r: 1, g: 1, b: 1, a: 1}
+    - u_EmissiveFactor: {r: 1, g: 1, b: 1, a: 1}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Materials/KhronosShaderSampleWhite.mat.meta b/Assets/Oculus/Avatar2/Example/Common/Materials/KhronosShaderSampleWhite.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..19ea01ebb8983102f0e4d4d430e6a4c44e4430e4
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Materials/KhronosShaderSampleWhite.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 4a71bbb7b417e2745bad57c3c62c1ecf
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Materials/LibraryShaderSampleBlack.mat b/Assets/Oculus/Avatar2/Example/Common/Materials/LibraryShaderSampleBlack.mat
new file mode 100644
index 0000000000000000000000000000000000000000..5765af482c59c89807a3e659c34531bd0dd22f27
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Materials/LibraryShaderSampleBlack.mat
@@ -0,0 +1,139 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: LibraryShaderSampleBlack
+  m_Shader: {fileID: 4800000, guid: 3d4ea02cfcf80e344b03f3d685b3aaee, type: 3}
+  m_ShaderKeywords: BRDF_LUT_MODE_ON DEBUG_NONE MATERIAL_MODE_TEXTURE
+  m_LightmapFlags: 4
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ColorRamp0:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ColorRamp1:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_AttributeTexture:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_BaseColorSampler:
+        m_Texture: {fileID: 2800000, guid: 8126abc91ceccd84696395fc6d1e2ffc, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_EmissiveSampler:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_MetallicRoughnessSampler:
+        m_Texture: {fileID: 2800000, guid: ed8192c8839b473429aff481e4c2467b, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_NormalSampler:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_OcclusionSampler:
+        m_Texture: {fileID: 2800000, guid: ed8192c8839b473429aff481e4c2467b, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - BRDF_LUT_Mode: 0
+    - Debug: 0
+    - EYE_GLINTS: 0
+    - EYE_GLINTS_BEHIND: 0
+    - HAS_NORMAL_MAP: 0
+    - Lighting_Mode: 0
+    - Material_Mode: 0
+    - SKIN: 0
+    - ToneMap: 0
+    - _BumpScale: 1
+    - _Cull: 2
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    - u_BaseColorUVSet: 1
+    - u_DiffuseSmoothingFactor: 1
+    - u_EmissiveSet: 0
+    - u_Exposure: 1
+    - u_EyeGlintColorFactor: 1
+    - u_EyeGlintFactor: 10
+    - u_F0Factor: 1
+    - u_MetallicFactor: 1
+    - u_MetallicRoughnessUVSet: 0
+    - u_NormalScale: 1
+    - u_NormalUVSet: 0
+    - u_OcclusionSet: 0
+    - u_OcclusionStrength: 1
+    - u_RoughnessFactor: 1
+    - u_ThicknessFactor: 1
+    m_Colors:
+    - _Color: {r: 1, g: 1, b: 1, a: 1}
+    - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
+    - u_BaseColorFactor: {r: 1, g: 1, b: 1, a: 1}
+    - u_EmissiveFactor: {r: 1, g: 1, b: 1, a: 1}
+    - u_SkinORMFactor: {r: 1, g: 1, b: 1, a: 1}
+    - u_SubsurfaceColor: {r: 0, g: 0, b: 0, a: 1}
+  m_BuildTextureStacks: []
diff --git a/Assets/Oculus/Avatar2/Example/Common/Materials/LibraryShaderSampleBlack.mat.meta b/Assets/Oculus/Avatar2/Example/Common/Materials/LibraryShaderSampleBlack.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..0ddbb7e686a2256a789a67c5bd7c70ea2db4589c
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Materials/LibraryShaderSampleBlack.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: d33c28033c9075c45b22bd0309864810
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 2100000
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Materials/LibraryShaderSampleReflective.mat b/Assets/Oculus/Avatar2/Example/Common/Materials/LibraryShaderSampleReflective.mat
new file mode 100644
index 0000000000000000000000000000000000000000..37e5640461db4e71bdead9f97e5e63a8872eb23a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Materials/LibraryShaderSampleReflective.mat
@@ -0,0 +1,139 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: LibraryShaderSampleReflective
+  m_Shader: {fileID: 4800000, guid: 3d4ea02cfcf80e344b03f3d685b3aaee, type: 3}
+  m_ShaderKeywords: BRDF_LUT_MODE_ON DEBUG_NONE MATERIAL_MODE_TEXTURE
+  m_LightmapFlags: 4
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ColorRamp0:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ColorRamp1:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_AttributeTexture:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_BaseColorSampler:
+        m_Texture: {fileID: 2800000, guid: 1303047a4a2d85649aebc891948037d0, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_EmissiveSampler:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_MetallicRoughnessSampler:
+        m_Texture: {fileID: 2800000, guid: 325a054c45020654fb52d97d93ab3680, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_NormalSampler:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_OcclusionSampler:
+        m_Texture: {fileID: 2800000, guid: 325a054c45020654fb52d97d93ab3680, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - BRDF_LUT_Mode: 0
+    - Debug: 0
+    - EYE_GLINTS: 0
+    - EYE_GLINTS_BEHIND: 0
+    - HAS_NORMAL_MAP: 0
+    - Lighting_Mode: 0
+    - Material_Mode: 0
+    - SKIN: 0
+    - ToneMap: 0
+    - _BumpScale: 1
+    - _Cull: 2
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    - u_BaseColorUVSet: 1
+    - u_DiffuseSmoothingFactor: 1
+    - u_EmissiveSet: 0
+    - u_Exposure: 1
+    - u_EyeGlintColorFactor: 1
+    - u_EyeGlintFactor: 10
+    - u_F0Factor: 1
+    - u_MetallicFactor: 1
+    - u_MetallicRoughnessUVSet: 0
+    - u_NormalScale: 1
+    - u_NormalUVSet: 0
+    - u_OcclusionSet: 0
+    - u_OcclusionStrength: 1
+    - u_RoughnessFactor: 0
+    - u_ThicknessFactor: 1
+    m_Colors:
+    - _Color: {r: 1, g: 1, b: 1, a: 1}
+    - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
+    - u_BaseColorFactor: {r: 1, g: 1, b: 1, a: 1}
+    - u_EmissiveFactor: {r: 1, g: 1, b: 1, a: 1}
+    - u_SkinORMFactor: {r: 1, g: 1, b: 1, a: 1}
+    - u_SubsurfaceColor: {r: 0, g: 0, b: 0, a: 1}
+  m_BuildTextureStacks: []
diff --git a/Assets/Oculus/Avatar2/Example/Common/Materials/LibraryShaderSampleReflective.mat.meta b/Assets/Oculus/Avatar2/Example/Common/Materials/LibraryShaderSampleReflective.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..52d01d1f380e6947552788e418b759b7425558d5
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Materials/LibraryShaderSampleReflective.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 7e9c68727a8d65840bc88c81063028c0
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 2100000
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Materials/LibraryShaderSampleWhite.mat b/Assets/Oculus/Avatar2/Example/Common/Materials/LibraryShaderSampleWhite.mat
new file mode 100644
index 0000000000000000000000000000000000000000..2de714c5484121f586de53f4e36a8c65af96326e
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Materials/LibraryShaderSampleWhite.mat
@@ -0,0 +1,139 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: LibraryShaderSampleWhite
+  m_Shader: {fileID: 4800000, guid: 3d4ea02cfcf80e344b03f3d685b3aaee, type: 3}
+  m_ShaderKeywords: BRDF_LUT_MODE_ON DEBUG_NONE MATERIAL_MODE_TEXTURE
+  m_LightmapFlags: 4
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ColorRamp0:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ColorRamp1:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_AttributeTexture:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_BaseColorSampler:
+        m_Texture: {fileID: 2800000, guid: 325a054c45020654fb52d97d93ab3680, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_EmissiveSampler:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_MetallicRoughnessSampler:
+        m_Texture: {fileID: 2800000, guid: ed8192c8839b473429aff481e4c2467b, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_NormalSampler:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - u_OcclusionSampler:
+        m_Texture: {fileID: 2800000, guid: ed8192c8839b473429aff481e4c2467b, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - BRDF_LUT_Mode: 0
+    - Debug: 0
+    - EYE_GLINTS: 0
+    - EYE_GLINTS_BEHIND: 0
+    - HAS_NORMAL_MAP: 0
+    - Lighting_Mode: 0
+    - Material_Mode: 0
+    - SKIN: 0
+    - ToneMap: 0
+    - _BumpScale: 1
+    - _Cull: 2
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    - u_BaseColorUVSet: 1
+    - u_DiffuseSmoothingFactor: 1
+    - u_EmissiveSet: 0
+    - u_Exposure: 1
+    - u_EyeGlintColorFactor: 1
+    - u_EyeGlintFactor: 10
+    - u_F0Factor: 1
+    - u_MetallicFactor: 1
+    - u_MetallicRoughnessUVSet: 0
+    - u_NormalScale: 1
+    - u_NormalUVSet: 0
+    - u_OcclusionSet: 0
+    - u_OcclusionStrength: 1
+    - u_RoughnessFactor: 1
+    - u_ThicknessFactor: 1
+    m_Colors:
+    - _Color: {r: 1, g: 1, b: 1, a: 1}
+    - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
+    - u_BaseColorFactor: {r: 1, g: 1, b: 1, a: 1}
+    - u_EmissiveFactor: {r: 1, g: 1, b: 1, a: 1}
+    - u_SkinORMFactor: {r: 1, g: 1, b: 1, a: 1}
+    - u_SubsurfaceColor: {r: 0, g: 0, b: 0, a: 1}
+  m_BuildTextureStacks: []
diff --git a/Assets/Oculus/Avatar2/Example/Common/Materials/LibraryShaderSampleWhite.mat.meta b/Assets/Oculus/Avatar2/Example/Common/Materials/LibraryShaderSampleWhite.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..67b59112b63348c51a61cebc6ab0e11f228f3266
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Materials/LibraryShaderSampleWhite.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 5dd56d05a833e5849b1abd35b6b2f52f
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 2100000
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Materials/Skybox.mat b/Assets/Oculus/Avatar2/Example/Common/Materials/Skybox.mat
new file mode 100644
index 0000000000000000000000000000000000000000..3e467d9cff96c13d44ddf8be5ac15e731e05b6b7
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Materials/Skybox.mat
@@ -0,0 +1,84 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: Skybox
+  m_Shader: {fileID: 103, guid: 0000000000000000f000000000000000, type: 0}
+  m_ShaderKeywords: 
+  m_LightmapFlags: 4
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _Tex:
+        m_Texture: {fileID: 8900000, guid: ddb4660a9b687cc4c9635bf262a5cbc4, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _Exposure: 1
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _Rotation: 0
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 1, g: 1, b: 1, a: 1}
+    - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
+    - _Tint: {r: 0.5, g: 0.5, b: 0.5, a: 0.5}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Materials/Skybox.mat.meta b/Assets/Oculus/Avatar2/Example/Common/Materials/Skybox.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..c4e07f3bf89dbecb254328631dd4ff9ce0fe6387
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Materials/Skybox.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 35c4e9d0768becc4eaaab0bc2977967e
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Materials/UnityShaderSampleBlack.mat b/Assets/Oculus/Avatar2/Example/Common/Materials/UnityShaderSampleBlack.mat
new file mode 100644
index 0000000000000000000000000000000000000000..3114e4cb6e6d4716c3b5ebe7c51134c8271e73e0
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Materials/UnityShaderSampleBlack.mat
@@ -0,0 +1,77 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: UnityShaderSampleBlack
+  m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
+  m_ShaderKeywords: 
+  m_LightmapFlags: 4
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.75
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 0, g: 0, b: 0, a: 1}
+    - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Materials/UnityShaderSampleBlack.mat.meta b/Assets/Oculus/Avatar2/Example/Common/Materials/UnityShaderSampleBlack.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..d4434f8e946559faf4ad009baf856a2f9b8fbc97
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Materials/UnityShaderSampleBlack.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 744db510507179846b06b076113095f9
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 2100000
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Materials/UnityShaderSampleReflective.mat b/Assets/Oculus/Avatar2/Example/Common/Materials/UnityShaderSampleReflective.mat
new file mode 100644
index 0000000000000000000000000000000000000000..b918aaec02e912d4934384c1531a6fe1d4bfdb0d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Materials/UnityShaderSampleReflective.mat
@@ -0,0 +1,77 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: UnityShaderSampleReflective
+  m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
+  m_ShaderKeywords: 
+  m_LightmapFlags: 4
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 1
+    - _GlossyReflections: 1
+    - _Metallic: 1
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 0.64, g: 0.64, b: 0.64, a: 1}
+    - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Materials/UnityShaderSampleReflective.mat.meta b/Assets/Oculus/Avatar2/Example/Common/Materials/UnityShaderSampleReflective.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..97c35bb093d21f4061992986b5dd7910feeaf1c4
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Materials/UnityShaderSampleReflective.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 52757b3adf170174793b327af95a04e5
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 2100000
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Materials/UnityShaderSampleWhite.mat b/Assets/Oculus/Avatar2/Example/Common/Materials/UnityShaderSampleWhite.mat
new file mode 100644
index 0000000000000000000000000000000000000000..fe257b64c2f243b0a354b79f6bb1bab86098a634
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Materials/UnityShaderSampleWhite.mat
@@ -0,0 +1,77 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: UnityShaderSampleWhite
+  m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
+  m_ShaderKeywords: 
+  m_LightmapFlags: 4
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.75
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 1, g: 1, b: 1, a: 1}
+    - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Materials/UnityShaderSampleWhite.mat.meta b/Assets/Oculus/Avatar2/Example/Common/Materials/UnityShaderSampleWhite.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..87ab9d1a2b538309a9dc80758dc1cc7bbdee318f
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Materials/UnityShaderSampleWhite.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: d45197c39ccdb6b4780696b65fd88edd
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 2100000
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Materials/WorldText.mat b/Assets/Oculus/Avatar2/Example/Common/Materials/WorldText.mat
new file mode 100644
index 0000000000000000000000000000000000000000..64f18e8c8b0c46a06543da1f5a5a5df29bd5368d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Materials/WorldText.mat
@@ -0,0 +1,84 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: WorldText
+  m_Shader: {fileID: 10762, guid: 0000000000000000f000000000000000, type: 0}
+  m_ShaderKeywords: UNITY_UI_ALPHACLIP
+  m_LightmapFlags: 4
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _BumpScale: 1
+    - _ColorMask: 15
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _Stencil: 0
+    - _StencilComp: 8
+    - _StencilOp: 0
+    - _StencilReadMask: 255
+    - _StencilWriteMask: 255
+    - _UVSec: 0
+    - _UseUIAlphaClip: 1
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 1, g: 1, b: 1, a: 1}
+    - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Materials/WorldText.mat.meta b/Assets/Oculus/Avatar2/Example/Common/Materials/WorldText.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..b9535d23e81a0a8cd5b7be5e001c6012a933d415
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Materials/WorldText.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 09adaff110e0d0544a70f07ec58f53b4
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs.meta b/Assets/Oculus/Avatar2/Example/Common/Prefabs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..e691dfba594616d9a8d6da49213ea2741d9bf5bd
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 3966c6ee8a69e564e81a3e5f3d13dcc6
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarGroups.meta b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarGroups.meta
new file mode 100644
index 0000000000000000000000000000000000000000..edb44adeccd1a42560d69bce5761208455bd8ba3
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarGroups.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 7e7db42e096c13e499e1351376349619
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarGroups/ShowcaseAvatarsComplete.prefab b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarGroups/ShowcaseAvatarsComplete.prefab
new file mode 100644
index 0000000000000000000000000000000000000000..003a24d0c331adc1a72f8ee7a1f809f6d6e72b7a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarGroups/ShowcaseAvatarsComplete.prefab
@@ -0,0 +1,341 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &27126266206158035
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 27126266206158034}
+  m_Layer: 0
+  m_Name: ShowcaseAvatarsComplete
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &27126266206158034
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 27126266206158035}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 1369142822060110032}
+  - {fileID: 1369142822030008607}
+  - {fileID: 1369142822111461422}
+  - {fileID: 1369142823383022598}
+  m_Father: {fileID: 0}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1001 &27126266122859898
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 27126266206158034}
+    m_Modifications:
+    - target: {fileID: 1396242425075152212, guid: dd63b8a05dd83d44b975b3c1dece2159,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: dd63b8a05dd83d44b975b3c1dece2159,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: dd63b8a05dd83d44b975b3c1dece2159,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: dd63b8a05dd83d44b975b3c1dece2159,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: dd63b8a05dd83d44b975b3c1dece2159,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: dd63b8a05dd83d44b975b3c1dece2159,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: dd63b8a05dd83d44b975b3c1dece2159,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: dd63b8a05dd83d44b975b3c1dece2159,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 2
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: dd63b8a05dd83d44b975b3c1dece2159,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: dd63b8a05dd83d44b975b3c1dece2159,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: dd63b8a05dd83d44b975b3c1dece2159,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152213, guid: dd63b8a05dd83d44b975b3c1dece2159,
+        type: 3}
+      propertyPath: m_Name
+      value: ShowcaseAvatarsGroup3
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: dd63b8a05dd83d44b975b3c1dece2159, type: 3}
+--- !u!4 &1369142822111461422 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: 1396242425075152212, guid: dd63b8a05dd83d44b975b3c1dece2159,
+    type: 3}
+  m_PrefabInstance: {fileID: 27126266122859898}
+  m_PrefabAsset: {fileID: 0}
+--- !u!1001 &27126266205570436
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 27126266206158034}
+    m_Modifications:
+    - target: {fileID: 1396242425075152212, guid: 4747de51a127cf948a8c472571f1dd42,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: 4747de51a127cf948a8c472571f1dd42,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: 4747de51a127cf948a8c472571f1dd42,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: -1
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: 4747de51a127cf948a8c472571f1dd42,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: 4747de51a127cf948a8c472571f1dd42,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: 4747de51a127cf948a8c472571f1dd42,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: 4747de51a127cf948a8c472571f1dd42,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: 4747de51a127cf948a8c472571f1dd42,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: 4747de51a127cf948a8c472571f1dd42,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: 4747de51a127cf948a8c472571f1dd42,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: 4747de51a127cf948a8c472571f1dd42,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152213, guid: 4747de51a127cf948a8c472571f1dd42,
+        type: 3}
+      propertyPath: m_Name
+      value: ShowcaseAvatarsGroup1
+      objectReference: {fileID: 0}
+    - target: {fileID: 5779059046781471357, guid: 4747de51a127cf948a8c472571f1dd42,
+        type: 3}
+      propertyPath: m_Name
+      value: Preset 0
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: 4747de51a127cf948a8c472571f1dd42, type: 3}
+--- !u!4 &1369142822060110032 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: 1396242425075152212, guid: 4747de51a127cf948a8c472571f1dd42,
+    type: 3}
+  m_PrefabInstance: {fileID: 27126266205570436}
+  m_PrefabAsset: {fileID: 0}
+--- !u!1001 &27126266237867083
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 27126266206158034}
+    m_Modifications:
+    - target: {fileID: 1396242425075152212, guid: 42b9aeec34cdef84095cf9a474a80fce,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: 42b9aeec34cdef84095cf9a474a80fce,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: 42b9aeec34cdef84095cf9a474a80fce,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: 42b9aeec34cdef84095cf9a474a80fce,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: 42b9aeec34cdef84095cf9a474a80fce,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: 42b9aeec34cdef84095cf9a474a80fce,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: 42b9aeec34cdef84095cf9a474a80fce,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: 42b9aeec34cdef84095cf9a474a80fce,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: 42b9aeec34cdef84095cf9a474a80fce,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: 42b9aeec34cdef84095cf9a474a80fce,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: 42b9aeec34cdef84095cf9a474a80fce,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152213, guid: 42b9aeec34cdef84095cf9a474a80fce,
+        type: 3}
+      propertyPath: m_Name
+      value: ShowcaseAvatarsGroup2
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: 42b9aeec34cdef84095cf9a474a80fce, type: 3}
+--- !u!4 &1369142822030008607 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: 1396242425075152212, guid: 42b9aeec34cdef84095cf9a474a80fce,
+    type: 3}
+  m_PrefabInstance: {fileID: 27126266237867083}
+  m_PrefabAsset: {fileID: 0}
+--- !u!1001 &27126267533461842
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 27126266206158034}
+    m_Modifications:
+    - target: {fileID: 1396242425075152212, guid: e44c77800a51b2c41bb0ce66366a6866,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: e44c77800a51b2c41bb0ce66366a6866,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: e44c77800a51b2c41bb0ce66366a6866,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 2
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: e44c77800a51b2c41bb0ce66366a6866,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: e44c77800a51b2c41bb0ce66366a6866,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: e44c77800a51b2c41bb0ce66366a6866,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: e44c77800a51b2c41bb0ce66366a6866,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: e44c77800a51b2c41bb0ce66366a6866,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 3
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: e44c77800a51b2c41bb0ce66366a6866,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: e44c77800a51b2c41bb0ce66366a6866,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: e44c77800a51b2c41bb0ce66366a6866,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152213, guid: e44c77800a51b2c41bb0ce66366a6866,
+        type: 3}
+      propertyPath: m_Name
+      value: ShowcaseAvatarsGroup4
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: e44c77800a51b2c41bb0ce66366a6866, type: 3}
+--- !u!4 &1369142823383022598 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: 1396242425075152212, guid: e44c77800a51b2c41bb0ce66366a6866,
+    type: 3}
+  m_PrefabInstance: {fileID: 27126267533461842}
+  m_PrefabAsset: {fileID: 0}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarGroups/ShowcaseAvatarsComplete.prefab.meta b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarGroups/ShowcaseAvatarsComplete.prefab.meta
new file mode 100644
index 0000000000000000000000000000000000000000..6a5b510c5f2e11806109454c4dc4e11586d604d3
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarGroups/ShowcaseAvatarsComplete.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: cda13956ffe95854a81b8f6b572c3326
+PrefabImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarGroups/ShowcaseAvatarsGroup1.prefab b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarGroups/ShowcaseAvatarsGroup1.prefab
new file mode 100644
index 0000000000000000000000000000000000000000..840d84b076d361e68459b8b8019bae75b93362e1
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarGroups/ShowcaseAvatarsGroup1.prefab
@@ -0,0 +1,844 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &803721280920188134
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 5270651527924072525}
+  - component: {fileID: 648041123743142986}
+  m_Layer: 0
+  m_Name: Preset 7
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &5270651527924072525
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 803721280920188134}
+  m_LocalRotation: {x: 0, y: 1, z: 0, w: 0}
+  m_LocalPosition: {x: 4, y: 0.5, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1396242425075152212}
+  m_RootOrder: 7
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!114 &648041123743142986
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 803721280920188134}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _creationInfo:
+    features: 342
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+  _activeView: 2
+  _activeManifestation: 2
+  _bodyTracking: {fileID: 0}
+  _lipSync: {fileID: 0}
+  _criticalJointTypes: 
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  _useAdvancedLodSystem: 1
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _loadUserFromCdn: 0
+  _assetSource: 0
+  _assetPaths:
+  - 7_
+  _assets:
+  - source: 0
+    path: 7
+  _assetPostfixDefault: _rift.glb
+  _assetPostfixAndroid: _quest.glb
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!1 &1396242425075152213
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1396242425075152212}
+  m_Layer: 0
+  m_Name: ShowcaseAvatarsGroup1
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1396242425075152212
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1396242425075152213}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 7683831584038353065}
+  - {fileID: 1396242425182533275}
+  - {fileID: 1396242425329006250}
+  - {fileID: 1396242426695299693}
+  - {fileID: 1396242426929053971}
+  - {fileID: 506565571714275925}
+  - {fileID: 4494093224253110679}
+  - {fileID: 5270651527924072525}
+  - {fileID: 2530771540446606009}
+  m_Father: {fileID: 0}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &1396242425182533276
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1396242425182533275}
+  - component: {fileID: 1396242425182533273}
+  m_Layer: 0
+  m_Name: Preset 1
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1396242425182533275
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1396242425182533276}
+  m_LocalRotation: {x: 0, y: 1, z: 0, w: 0}
+  m_LocalPosition: {x: -2, y: 0.5, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1396242425075152212}
+  m_RootOrder: 1
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!114 &1396242425182533273
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1396242425182533276}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _creationInfo:
+    features: 342
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+  _activeView: 2
+  _activeManifestation: 2
+  _bodyTracking: {fileID: 0}
+  _lipSync: {fileID: 0}
+  _criticalJointTypes: 
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  _useAdvancedLodSystem: 1
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _loadUserFromCdn: 0
+  _assetSource: 0
+  _assetPaths:
+  - 1_
+  _assets:
+  - source: 0
+    path: 1
+  _assetPostfixDefault: _rift.glb
+  _assetPostfixAndroid: _quest.glb
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!1 &1396242425329006251
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1396242425329006250}
+  - component: {fileID: 1396242425329006248}
+  m_Layer: 0
+  m_Name: Preset 2
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1396242425329006250
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1396242425329006251}
+  m_LocalRotation: {x: 0, y: 1, z: 0, w: 0}
+  m_LocalPosition: {x: -1, y: 0.5, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1396242425075152212}
+  m_RootOrder: 2
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!114 &1396242425329006248
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1396242425329006251}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _creationInfo:
+    features: 342
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+  _activeView: 2
+  _activeManifestation: 2
+  _bodyTracking: {fileID: 0}
+  _lipSync: {fileID: 0}
+  _criticalJointTypes: 
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  _useAdvancedLodSystem: 1
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _loadUserFromCdn: 0
+  _assetSource: 0
+  _assetPaths:
+  - 2_
+  _assets:
+  - source: 0
+    path: 2
+  _assetPostfixDefault: _rift.glb
+  _assetPostfixAndroid: _quest.glb
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!1 &1396242426695299694
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1396242426695299693}
+  - component: {fileID: 1396242426695299691}
+  m_Layer: 0
+  m_Name: Preset 3
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1396242426695299693
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1396242426695299694}
+  m_LocalRotation: {x: 0, y: 1, z: 0, w: 0}
+  m_LocalPosition: {x: 0, y: 0.5, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1396242425075152212}
+  m_RootOrder: 3
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!114 &1396242426695299691
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1396242426695299694}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _creationInfo:
+    features: 342
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+  _activeView: 2
+  _activeManifestation: 2
+  _bodyTracking: {fileID: 0}
+  _lipSync: {fileID: 0}
+  _criticalJointTypes: 
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  _useAdvancedLodSystem: 1
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _loadUserFromCdn: 0
+  _assetSource: 0
+  _assetPaths:
+  - 3_
+  _assets:
+  - source: 0
+    path: 3
+  _assetPostfixDefault: _rift.glb
+  _assetPostfixAndroid: _quest.glb
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!1 &1396242426929053972
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1396242426929053971}
+  - component: {fileID: 1396242426929053969}
+  m_Layer: 0
+  m_Name: Preset 4
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1396242426929053971
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1396242426929053972}
+  m_LocalRotation: {x: 0, y: 1, z: 0, w: 0}
+  m_LocalPosition: {x: 1, y: 0.5, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1396242425075152212}
+  m_RootOrder: 4
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!114 &1396242426929053969
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1396242426929053972}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _creationInfo:
+    features: 342
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+  _activeView: 2
+  _activeManifestation: 2
+  _bodyTracking: {fileID: 0}
+  _lipSync: {fileID: 0}
+  _criticalJointTypes: 
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  _useAdvancedLodSystem: 1
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _loadUserFromCdn: 0
+  _assetSource: 0
+  _assetPaths:
+  - 4_
+  _assets:
+  - source: 0
+    path: 4
+  _assetPostfixDefault: _rift.glb
+  _assetPostfixAndroid: _quest.glb
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!1 &5779059046781471357
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7683831584038353065}
+  - component: {fileID: 4237050325572324052}
+  m_Layer: 0
+  m_Name: Preset 0
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &7683831584038353065
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 5779059046781471357}
+  m_LocalRotation: {x: -0, y: 1, z: -0, w: 0}
+  m_LocalPosition: {x: -3, y: 0.5, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1396242425075152212}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!114 &4237050325572324052
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 5779059046781471357}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _creationInfo:
+    features: 342
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+  _activeView: 2
+  _activeManifestation: 2
+  _bodyTracking: {fileID: 0}
+  _lipSync: {fileID: 0}
+  _criticalJointTypes: 
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  _useAdvancedLodSystem: 1
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _loadUserFromCdn: 0
+  _assetSource: 0
+  _assetPaths:
+  - 0_
+  _assets:
+  - source: 0
+    path: 0
+  _assetPostfixDefault: _rift.glb
+  _assetPostfixAndroid: _quest.glb
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!1 &6216749447788287993
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 4494093224253110679}
+  - component: {fileID: 8007250017700924530}
+  m_Layer: 0
+  m_Name: Preset 6
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &4494093224253110679
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6216749447788287993}
+  m_LocalRotation: {x: 0, y: 1, z: 0, w: 0}
+  m_LocalPosition: {x: 3, y: 0.5, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1396242425075152212}
+  m_RootOrder: 6
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!114 &8007250017700924530
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6216749447788287993}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _creationInfo:
+    features: 342
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+  _activeView: 2
+  _activeManifestation: 2
+  _bodyTracking: {fileID: 0}
+  _lipSync: {fileID: 0}
+  _criticalJointTypes: 
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  _useAdvancedLodSystem: 1
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _loadUserFromCdn: 0
+  _assetSource: 0
+  _assetPaths:
+  - 6_
+  _assets:
+  - source: 0
+    path: 6
+  _assetPostfixDefault: _rift.glb
+  _assetPostfixAndroid: _quest.glb
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!1 &8928388040547110969
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 506565571714275925}
+  - component: {fileID: 2785419835470461344}
+  m_Layer: 0
+  m_Name: Preset 5
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &506565571714275925
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8928388040547110969}
+  m_LocalRotation: {x: 0, y: 1, z: 0, w: 0}
+  m_LocalPosition: {x: 2, y: 0.5, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1396242425075152212}
+  m_RootOrder: 5
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!114 &2785419835470461344
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8928388040547110969}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _creationInfo:
+    features: 342
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+  _activeView: 2
+  _activeManifestation: 2
+  _bodyTracking: {fileID: 0}
+  _lipSync: {fileID: 0}
+  _criticalJointTypes: 
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  _useAdvancedLodSystem: 1
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _loadUserFromCdn: 0
+  _assetSource: 0
+  _assetPaths:
+  - 5_
+  _assets:
+  - source: 0
+    path: 5
+  _assetPostfixDefault: _rift.glb
+  _assetPostfixAndroid: _quest.glb
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!1001 &1559549408105462325
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 1396242425075152212}
+    m_Modifications:
+    - target: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 8
+      objectReference: {fileID: 0}
+    - target: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 8334773074039846814, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_Name
+      value: Cylinders
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: 548156b48d05cd54f901563c0dcc9900, type: 3}
+--- !u!4 &2530771540446606009 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+    type: 3}
+  m_PrefabInstance: {fileID: 1559549408105462325}
+  m_PrefabAsset: {fileID: 0}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarGroups/ShowcaseAvatarsGroup1.prefab.meta b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarGroups/ShowcaseAvatarsGroup1.prefab.meta
new file mode 100644
index 0000000000000000000000000000000000000000..acfd9a297bc3a75f536d15e0bec4b741b1906151
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarGroups/ShowcaseAvatarsGroup1.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 4747de51a127cf948a8c472571f1dd42
+PrefabImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarGroups/ShowcaseAvatarsGroup2.prefab b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarGroups/ShowcaseAvatarsGroup2.prefab
new file mode 100644
index 0000000000000000000000000000000000000000..0441816df21dd74f891d93dab6da9c768c83d41b
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarGroups/ShowcaseAvatarsGroup2.prefab
@@ -0,0 +1,844 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &803721280920188134
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 5270651527924072525}
+  - component: {fileID: 648041123743142986}
+  m_Layer: 0
+  m_Name: Preset 15
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &5270651527924072525
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 803721280920188134}
+  m_LocalRotation: {x: 0, y: 1, z: 0, w: 0}
+  m_LocalPosition: {x: 4, y: 0.5, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1396242425075152212}
+  m_RootOrder: 7
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!114 &648041123743142986
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 803721280920188134}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _creationInfo:
+    features: 342
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+  _activeView: 2
+  _activeManifestation: 2
+  _bodyTracking: {fileID: 0}
+  _lipSync: {fileID: 0}
+  _criticalJointTypes: 
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  _useAdvancedLodSystem: 1
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _loadUserFromCdn: 0
+  _assetSource: 0
+  _assetPaths:
+  - 15_
+  _assets:
+  - source: 0
+    path: 15
+  _assetPostfixDefault: _rift.glb
+  _assetPostfixAndroid: _quest.glb
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!1 &1396242425075152213
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1396242425075152212}
+  m_Layer: 0
+  m_Name: ShowcaseAvatarsGroup2
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1396242425075152212
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1396242425075152213}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 7683831584038353065}
+  - {fileID: 1396242425182533275}
+  - {fileID: 1396242425329006250}
+  - {fileID: 1396242426695299693}
+  - {fileID: 1396242426929053971}
+  - {fileID: 506565571714275925}
+  - {fileID: 4494093224253110679}
+  - {fileID: 5270651527924072525}
+  - {fileID: 4952173408523882036}
+  m_Father: {fileID: 0}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &1396242425182533276
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1396242425182533275}
+  - component: {fileID: 1396242425182533273}
+  m_Layer: 0
+  m_Name: Preset 9
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1396242425182533275
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1396242425182533276}
+  m_LocalRotation: {x: 0, y: 1, z: 0, w: 0}
+  m_LocalPosition: {x: -2, y: 0.5, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1396242425075152212}
+  m_RootOrder: 1
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!114 &1396242425182533273
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1396242425182533276}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _creationInfo:
+    features: 342
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+  _activeView: 2
+  _activeManifestation: 2
+  _bodyTracking: {fileID: 0}
+  _lipSync: {fileID: 0}
+  _criticalJointTypes: 
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  _useAdvancedLodSystem: 1
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _loadUserFromCdn: 0
+  _assetSource: 0
+  _assetPaths:
+  - 9_
+  _assets:
+  - source: 0
+    path: 9
+  _assetPostfixDefault: _rift.glb
+  _assetPostfixAndroid: _quest.glb
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!1 &1396242425329006251
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1396242425329006250}
+  - component: {fileID: 1396242425329006248}
+  m_Layer: 0
+  m_Name: Preset 10
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1396242425329006250
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1396242425329006251}
+  m_LocalRotation: {x: 0, y: 1, z: 0, w: 0}
+  m_LocalPosition: {x: -1, y: 0.5, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1396242425075152212}
+  m_RootOrder: 2
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!114 &1396242425329006248
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1396242425329006251}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _creationInfo:
+    features: 342
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+  _activeView: 2
+  _activeManifestation: 2
+  _bodyTracking: {fileID: 0}
+  _lipSync: {fileID: 0}
+  _criticalJointTypes: 
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  _useAdvancedLodSystem: 1
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _loadUserFromCdn: 0
+  _assetSource: 0
+  _assetPaths:
+  - 10_
+  _assets:
+  - source: 0
+    path: 10
+  _assetPostfixDefault: _rift.glb
+  _assetPostfixAndroid: _quest.glb
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!1 &1396242426695299694
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1396242426695299693}
+  - component: {fileID: 1396242426695299691}
+  m_Layer: 0
+  m_Name: Preset 11
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1396242426695299693
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1396242426695299694}
+  m_LocalRotation: {x: 0, y: 1, z: 0, w: 0}
+  m_LocalPosition: {x: 0, y: 0.5, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1396242425075152212}
+  m_RootOrder: 3
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!114 &1396242426695299691
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1396242426695299694}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _creationInfo:
+    features: 342
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+  _activeView: 2
+  _activeManifestation: 2
+  _bodyTracking: {fileID: 0}
+  _lipSync: {fileID: 0}
+  _criticalJointTypes: 
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  _useAdvancedLodSystem: 1
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _loadUserFromCdn: 0
+  _assetSource: 0
+  _assetPaths:
+  - 11_
+  _assets:
+  - source: 0
+    path: 11
+  _assetPostfixDefault: _rift.glb
+  _assetPostfixAndroid: _quest.glb
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!1 &1396242426929053972
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1396242426929053971}
+  - component: {fileID: 1396242426929053969}
+  m_Layer: 0
+  m_Name: Preset 12
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1396242426929053971
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1396242426929053972}
+  m_LocalRotation: {x: 0, y: 1, z: 0, w: 0}
+  m_LocalPosition: {x: 1, y: 0.5, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1396242425075152212}
+  m_RootOrder: 4
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!114 &1396242426929053969
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1396242426929053972}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _creationInfo:
+    features: 342
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+  _activeView: 2
+  _activeManifestation: 2
+  _bodyTracking: {fileID: 0}
+  _lipSync: {fileID: 0}
+  _criticalJointTypes: 
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  _useAdvancedLodSystem: 1
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _loadUserFromCdn: 0
+  _assetSource: 0
+  _assetPaths:
+  - 12_
+  _assets:
+  - source: 0
+    path: 12
+  _assetPostfixDefault: _rift.glb
+  _assetPostfixAndroid: _quest.glb
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!1 &5779059046781471357
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7683831584038353065}
+  - component: {fileID: 4237050325572324052}
+  m_Layer: 0
+  m_Name: Preset 8
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &7683831584038353065
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 5779059046781471357}
+  m_LocalRotation: {x: -0, y: 1, z: -0, w: 0}
+  m_LocalPosition: {x: -3, y: 0.5, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1396242425075152212}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!114 &4237050325572324052
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 5779059046781471357}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _creationInfo:
+    features: 342
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+  _activeView: 2
+  _activeManifestation: 2
+  _bodyTracking: {fileID: 0}
+  _lipSync: {fileID: 0}
+  _criticalJointTypes: 
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  _useAdvancedLodSystem: 1
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _loadUserFromCdn: 0
+  _assetSource: 0
+  _assetPaths:
+  - 8_
+  _assets:
+  - source: 0
+    path: 8
+  _assetPostfixDefault: _rift.glb
+  _assetPostfixAndroid: _quest.glb
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!1 &6216749447788287993
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 4494093224253110679}
+  - component: {fileID: 8007250017700924530}
+  m_Layer: 0
+  m_Name: Preset 14
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &4494093224253110679
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6216749447788287993}
+  m_LocalRotation: {x: 0, y: 1, z: 0, w: 0}
+  m_LocalPosition: {x: 3, y: 0.5, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1396242425075152212}
+  m_RootOrder: 6
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!114 &8007250017700924530
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6216749447788287993}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _creationInfo:
+    features: 342
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+  _activeView: 2
+  _activeManifestation: 2
+  _bodyTracking: {fileID: 0}
+  _lipSync: {fileID: 0}
+  _criticalJointTypes: 
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  _useAdvancedLodSystem: 1
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _loadUserFromCdn: 0
+  _assetSource: 0
+  _assetPaths:
+  - 14_
+  _assets:
+  - source: 0
+    path: 14
+  _assetPostfixDefault: _rift.glb
+  _assetPostfixAndroid: _quest.glb
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!1 &8928388040547110969
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 506565571714275925}
+  - component: {fileID: 2785419835470461344}
+  m_Layer: 0
+  m_Name: Preset 13
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &506565571714275925
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8928388040547110969}
+  m_LocalRotation: {x: 0, y: 1, z: 0, w: 0}
+  m_LocalPosition: {x: 2, y: 0.5, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1396242425075152212}
+  m_RootOrder: 5
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!114 &2785419835470461344
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8928388040547110969}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _creationInfo:
+    features: 342
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+  _activeView: 2
+  _activeManifestation: 2
+  _bodyTracking: {fileID: 0}
+  _lipSync: {fileID: 0}
+  _criticalJointTypes: 
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  _useAdvancedLodSystem: 1
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _loadUserFromCdn: 0
+  _assetSource: 0
+  _assetPaths:
+  - 13_
+  _assets:
+  - source: 0
+    path: 13
+  _assetPostfixDefault: _rift.glb
+  _assetPostfixAndroid: _quest.glb
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!1001 &8215161488072279736
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 1396242425075152212}
+    m_Modifications:
+    - target: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 8
+      objectReference: {fileID: 0}
+    - target: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 8334773074039846814, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_Name
+      value: Cylinders
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: 548156b48d05cd54f901563c0dcc9900, type: 3}
+--- !u!4 &4952173408523882036 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+    type: 3}
+  m_PrefabInstance: {fileID: 8215161488072279736}
+  m_PrefabAsset: {fileID: 0}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarGroups/ShowcaseAvatarsGroup2.prefab.meta b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarGroups/ShowcaseAvatarsGroup2.prefab.meta
new file mode 100644
index 0000000000000000000000000000000000000000..c31bd7f44bcba44d0bec43b6ecc6663346831092
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarGroups/ShowcaseAvatarsGroup2.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 42b9aeec34cdef84095cf9a474a80fce
+PrefabImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarGroups/ShowcaseAvatarsGroup3.prefab b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarGroups/ShowcaseAvatarsGroup3.prefab
new file mode 100644
index 0000000000000000000000000000000000000000..a406ced8602cfafcad590318f1a4cbc022bc4035
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarGroups/ShowcaseAvatarsGroup3.prefab
@@ -0,0 +1,844 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &803721280920188134
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 5270651527924072525}
+  - component: {fileID: 648041123743142986}
+  m_Layer: 0
+  m_Name: Preset 23
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &5270651527924072525
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 803721280920188134}
+  m_LocalRotation: {x: 0, y: 1, z: 0, w: 0}
+  m_LocalPosition: {x: 4, y: 0.5, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1396242425075152212}
+  m_RootOrder: 7
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!114 &648041123743142986
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 803721280920188134}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _creationInfo:
+    features: 342
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+  _activeView: 2
+  _activeManifestation: 2
+  _bodyTracking: {fileID: 0}
+  _lipSync: {fileID: 0}
+  _criticalJointTypes: 
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  _useAdvancedLodSystem: 1
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _loadUserFromCdn: 0
+  _assetSource: 0
+  _assetPaths:
+  - 23_
+  _assets:
+  - source: 0
+    path: 23
+  _assetPostfixDefault: _rift.glb
+  _assetPostfixAndroid: _quest.glb
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!1 &1396242425075152213
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1396242425075152212}
+  m_Layer: 0
+  m_Name: ShowcaseAvatarsGroup3
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1396242425075152212
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1396242425075152213}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 7683831584038353065}
+  - {fileID: 1396242425182533275}
+  - {fileID: 1396242425329006250}
+  - {fileID: 1396242426695299693}
+  - {fileID: 1396242426929053971}
+  - {fileID: 506565571714275925}
+  - {fileID: 4494093224253110679}
+  - {fileID: 5270651527924072525}
+  - {fileID: 1376998645472128386}
+  m_Father: {fileID: 0}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &1396242425182533276
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1396242425182533275}
+  - component: {fileID: 1396242425182533273}
+  m_Layer: 0
+  m_Name: Preset 17
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1396242425182533275
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1396242425182533276}
+  m_LocalRotation: {x: 0, y: 1, z: 0, w: 0}
+  m_LocalPosition: {x: -2, y: 0.5, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1396242425075152212}
+  m_RootOrder: 1
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!114 &1396242425182533273
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1396242425182533276}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _creationInfo:
+    features: 342
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+  _activeView: 2
+  _activeManifestation: 2
+  _bodyTracking: {fileID: 0}
+  _lipSync: {fileID: 0}
+  _criticalJointTypes: 
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  _useAdvancedLodSystem: 1
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _loadUserFromCdn: 0
+  _assetSource: 0
+  _assetPaths:
+  - 17_
+  _assets:
+  - source: 0
+    path: 17
+  _assetPostfixDefault: _rift.glb
+  _assetPostfixAndroid: _quest.glb
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!1 &1396242425329006251
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1396242425329006250}
+  - component: {fileID: 1396242425329006248}
+  m_Layer: 0
+  m_Name: Preset 18
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1396242425329006250
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1396242425329006251}
+  m_LocalRotation: {x: 0, y: 1, z: 0, w: 0}
+  m_LocalPosition: {x: -1, y: 0.5, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1396242425075152212}
+  m_RootOrder: 2
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!114 &1396242425329006248
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1396242425329006251}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _creationInfo:
+    features: 342
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+  _activeView: 2
+  _activeManifestation: 2
+  _bodyTracking: {fileID: 0}
+  _lipSync: {fileID: 0}
+  _criticalJointTypes: 
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  _useAdvancedLodSystem: 1
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _loadUserFromCdn: 0
+  _assetSource: 0
+  _assetPaths:
+  - 18_
+  _assets:
+  - source: 0
+    path: 18
+  _assetPostfixDefault: _rift.glb
+  _assetPostfixAndroid: _quest.glb
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!1 &1396242426695299694
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1396242426695299693}
+  - component: {fileID: 1396242426695299691}
+  m_Layer: 0
+  m_Name: Preset 19
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1396242426695299693
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1396242426695299694}
+  m_LocalRotation: {x: 0, y: 1, z: 0, w: 0}
+  m_LocalPosition: {x: 0, y: 0.5, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1396242425075152212}
+  m_RootOrder: 3
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!114 &1396242426695299691
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1396242426695299694}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _creationInfo:
+    features: 342
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+  _activeView: 2
+  _activeManifestation: 2
+  _bodyTracking: {fileID: 0}
+  _lipSync: {fileID: 0}
+  _criticalJointTypes: 
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  _useAdvancedLodSystem: 1
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _loadUserFromCdn: 0
+  _assetSource: 0
+  _assetPaths:
+  - 19_
+  _assets:
+  - source: 0
+    path: 19
+  _assetPostfixDefault: _rift.glb
+  _assetPostfixAndroid: _quest.glb
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!1 &1396242426929053972
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1396242426929053971}
+  - component: {fileID: 1396242426929053969}
+  m_Layer: 0
+  m_Name: Preset 20
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1396242426929053971
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1396242426929053972}
+  m_LocalRotation: {x: 0, y: 1, z: 0, w: 0}
+  m_LocalPosition: {x: 1, y: 0.5, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1396242425075152212}
+  m_RootOrder: 4
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!114 &1396242426929053969
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1396242426929053972}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _creationInfo:
+    features: 342
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+  _activeView: 2
+  _activeManifestation: 2
+  _bodyTracking: {fileID: 0}
+  _lipSync: {fileID: 0}
+  _criticalJointTypes: 
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  _useAdvancedLodSystem: 1
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _loadUserFromCdn: 0
+  _assetSource: 0
+  _assetPaths:
+  - 20_
+  _assets:
+  - source: 0
+    path: 20
+  _assetPostfixDefault: _rift.glb
+  _assetPostfixAndroid: _quest.glb
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!1 &5779059046781471357
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7683831584038353065}
+  - component: {fileID: 4237050325572324052}
+  m_Layer: 0
+  m_Name: Preset 16
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &7683831584038353065
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 5779059046781471357}
+  m_LocalRotation: {x: -0, y: 1, z: -0, w: 0}
+  m_LocalPosition: {x: -3, y: 0.5, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1396242425075152212}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!114 &4237050325572324052
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 5779059046781471357}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _creationInfo:
+    features: 342
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+  _activeView: 2
+  _activeManifestation: 2
+  _bodyTracking: {fileID: 0}
+  _lipSync: {fileID: 0}
+  _criticalJointTypes: 
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  _useAdvancedLodSystem: 1
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _loadUserFromCdn: 0
+  _assetSource: 0
+  _assetPaths:
+  - 16_
+  _assets:
+  - source: 0
+    path: 16
+  _assetPostfixDefault: _rift.glb
+  _assetPostfixAndroid: _quest.glb
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!1 &6216749447788287993
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 4494093224253110679}
+  - component: {fileID: 8007250017700924530}
+  m_Layer: 0
+  m_Name: Preset 22
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &4494093224253110679
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6216749447788287993}
+  m_LocalRotation: {x: 0, y: 1, z: 0, w: 0}
+  m_LocalPosition: {x: 3, y: 0.5, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1396242425075152212}
+  m_RootOrder: 6
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!114 &8007250017700924530
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6216749447788287993}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _creationInfo:
+    features: 342
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+  _activeView: 2
+  _activeManifestation: 2
+  _bodyTracking: {fileID: 0}
+  _lipSync: {fileID: 0}
+  _criticalJointTypes: 
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  _useAdvancedLodSystem: 1
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _loadUserFromCdn: 0
+  _assetSource: 0
+  _assetPaths:
+  - 22_
+  _assets:
+  - source: 0
+    path: 22
+  _assetPostfixDefault: _rift.glb
+  _assetPostfixAndroid: _quest.glb
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!1 &8928388040547110969
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 506565571714275925}
+  - component: {fileID: 2785419835470461344}
+  m_Layer: 0
+  m_Name: Preset 21
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &506565571714275925
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8928388040547110969}
+  m_LocalRotation: {x: 0, y: 1, z: 0, w: 0}
+  m_LocalPosition: {x: 2, y: 0.5, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1396242425075152212}
+  m_RootOrder: 5
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!114 &2785419835470461344
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8928388040547110969}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _creationInfo:
+    features: 342
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+  _activeView: 2
+  _activeManifestation: 2
+  _bodyTracking: {fileID: 0}
+  _lipSync: {fileID: 0}
+  _criticalJointTypes: 
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  _useAdvancedLodSystem: 1
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _loadUserFromCdn: 0
+  _assetSource: 0
+  _assetPaths:
+  - 21_
+  _assets:
+  - source: 0
+    path: 21
+  _assetPostfixDefault: _rift.glb
+  _assetPostfixAndroid: _quest.glb
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!1001 &2713329002683589902
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 1396242425075152212}
+    m_Modifications:
+    - target: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 8
+      objectReference: {fileID: 0}
+    - target: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 8334773074039846814, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_Name
+      value: Cylinders
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: 548156b48d05cd54f901563c0dcc9900, type: 3}
+--- !u!4 &1376998645472128386 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+    type: 3}
+  m_PrefabInstance: {fileID: 2713329002683589902}
+  m_PrefabAsset: {fileID: 0}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarGroups/ShowcaseAvatarsGroup3.prefab.meta b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarGroups/ShowcaseAvatarsGroup3.prefab.meta
new file mode 100644
index 0000000000000000000000000000000000000000..d8922f73f8ce5052018c343aa218566badfe4ea5
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarGroups/ShowcaseAvatarsGroup3.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: dd63b8a05dd83d44b975b3c1dece2159
+PrefabImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarGroups/ShowcaseAvatarsGroup4.prefab b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarGroups/ShowcaseAvatarsGroup4.prefab
new file mode 100644
index 0000000000000000000000000000000000000000..aa5d5eeffde24f310a062d61277657b6fb8aee2c
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarGroups/ShowcaseAvatarsGroup4.prefab
@@ -0,0 +1,844 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &803721280920188134
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 5270651527924072525}
+  - component: {fileID: 648041123743142986}
+  m_Layer: 0
+  m_Name: Preset 31
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &5270651527924072525
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 803721280920188134}
+  m_LocalRotation: {x: 0, y: 1, z: 0, w: 0}
+  m_LocalPosition: {x: 4, y: 0.5, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1396242425075152212}
+  m_RootOrder: 7
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!114 &648041123743142986
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 803721280920188134}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _creationInfo:
+    features: 342
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+  _activeView: 2
+  _activeManifestation: 2
+  _bodyTracking: {fileID: 0}
+  _lipSync: {fileID: 0}
+  _criticalJointTypes: 
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  _useAdvancedLodSystem: 1
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _loadUserFromCdn: 0
+  _assetSource: 0
+  _assetPaths:
+  - 31_
+  _assets:
+  - source: 0
+    path: 31
+  _assetPostfixDefault: _rift.glb
+  _assetPostfixAndroid: _quest.glb
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!1 &1396242425075152213
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1396242425075152212}
+  m_Layer: 0
+  m_Name: ShowcaseAvatarsGroup4
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1396242425075152212
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1396242425075152213}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 7683831584038353065}
+  - {fileID: 1396242425182533275}
+  - {fileID: 1396242425329006250}
+  - {fileID: 1396242426695299693}
+  - {fileID: 1396242426929053971}
+  - {fileID: 506565571714275925}
+  - {fileID: 4494093224253110679}
+  - {fileID: 5270651527924072525}
+  - {fileID: 3294956371155501852}
+  m_Father: {fileID: 0}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &1396242425182533276
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1396242425182533275}
+  - component: {fileID: 1396242425182533273}
+  m_Layer: 0
+  m_Name: Preset 25
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1396242425182533275
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1396242425182533276}
+  m_LocalRotation: {x: 0, y: 1, z: 0, w: 0}
+  m_LocalPosition: {x: -2, y: 0.5, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1396242425075152212}
+  m_RootOrder: 1
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!114 &1396242425182533273
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1396242425182533276}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _creationInfo:
+    features: 342
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+  _activeView: 2
+  _activeManifestation: 2
+  _bodyTracking: {fileID: 0}
+  _lipSync: {fileID: 0}
+  _criticalJointTypes: 
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  _useAdvancedLodSystem: 1
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _loadUserFromCdn: 0
+  _assetSource: 0
+  _assetPaths:
+  - 25_
+  _assets:
+  - source: 0
+    path: 25
+  _assetPostfixDefault: _rift.glb
+  _assetPostfixAndroid: _quest.glb
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!1 &1396242425329006251
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1396242425329006250}
+  - component: {fileID: 1396242425329006248}
+  m_Layer: 0
+  m_Name: Preset 26
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1396242425329006250
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1396242425329006251}
+  m_LocalRotation: {x: 0, y: 1, z: 0, w: 0}
+  m_LocalPosition: {x: -1, y: 0.5, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1396242425075152212}
+  m_RootOrder: 2
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!114 &1396242425329006248
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1396242425329006251}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _creationInfo:
+    features: 342
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+  _activeView: 2
+  _activeManifestation: 2
+  _bodyTracking: {fileID: 0}
+  _lipSync: {fileID: 0}
+  _criticalJointTypes: 
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  _useAdvancedLodSystem: 1
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _loadUserFromCdn: 0
+  _assetSource: 0
+  _assetPaths:
+  - 26_
+  _assets:
+  - source: 0
+    path: 26
+  _assetPostfixDefault: _rift.glb
+  _assetPostfixAndroid: _quest.glb
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!1 &1396242426695299694
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1396242426695299693}
+  - component: {fileID: 1396242426695299691}
+  m_Layer: 0
+  m_Name: Preset 27
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1396242426695299693
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1396242426695299694}
+  m_LocalRotation: {x: 0, y: 1, z: 0, w: 0}
+  m_LocalPosition: {x: 0, y: 0.5, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1396242425075152212}
+  m_RootOrder: 3
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!114 &1396242426695299691
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1396242426695299694}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _creationInfo:
+    features: 342
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+  _activeView: 2
+  _activeManifestation: 2
+  _bodyTracking: {fileID: 0}
+  _lipSync: {fileID: 0}
+  _criticalJointTypes: 
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  _useAdvancedLodSystem: 1
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _loadUserFromCdn: 0
+  _assetSource: 0
+  _assetPaths:
+  - 27_
+  _assets:
+  - source: 0
+    path: 27
+  _assetPostfixDefault: _rift.glb
+  _assetPostfixAndroid: _quest.glb
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!1 &1396242426929053972
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1396242426929053971}
+  - component: {fileID: 1396242426929053969}
+  m_Layer: 0
+  m_Name: Preset 28
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1396242426929053971
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1396242426929053972}
+  m_LocalRotation: {x: 0, y: 1, z: 0, w: 0}
+  m_LocalPosition: {x: 1, y: 0.5, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1396242425075152212}
+  m_RootOrder: 4
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!114 &1396242426929053969
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1396242426929053972}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _creationInfo:
+    features: 342
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+  _activeView: 2
+  _activeManifestation: 2
+  _bodyTracking: {fileID: 0}
+  _lipSync: {fileID: 0}
+  _criticalJointTypes: 
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  _useAdvancedLodSystem: 1
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _loadUserFromCdn: 0
+  _assetSource: 0
+  _assetPaths:
+  - 28_
+  _assets:
+  - source: 0
+    path: 28
+  _assetPostfixDefault: _rift.glb
+  _assetPostfixAndroid: _quest.glb
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!1 &5779059046781471357
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7683831584038353065}
+  - component: {fileID: 4237050325572324052}
+  m_Layer: 0
+  m_Name: Preset 24
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &7683831584038353065
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 5779059046781471357}
+  m_LocalRotation: {x: -0, y: 1, z: -0, w: 0}
+  m_LocalPosition: {x: -3, y: 0.5, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1396242425075152212}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!114 &4237050325572324052
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 5779059046781471357}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _creationInfo:
+    features: 342
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+  _activeView: 2
+  _activeManifestation: 2
+  _bodyTracking: {fileID: 0}
+  _lipSync: {fileID: 0}
+  _criticalJointTypes: 
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  _useAdvancedLodSystem: 1
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _loadUserFromCdn: 0
+  _assetSource: 0
+  _assetPaths:
+  - 24_
+  _assets:
+  - source: 0
+    path: 24
+  _assetPostfixDefault: _rift.glb
+  _assetPostfixAndroid: _quest.glb
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!1 &6216749447788287993
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 4494093224253110679}
+  - component: {fileID: 8007250017700924530}
+  m_Layer: 0
+  m_Name: Preset 30
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &4494093224253110679
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6216749447788287993}
+  m_LocalRotation: {x: 0, y: 1, z: 0, w: 0}
+  m_LocalPosition: {x: 3, y: 0.5, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1396242425075152212}
+  m_RootOrder: 6
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!114 &8007250017700924530
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6216749447788287993}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _creationInfo:
+    features: 342
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+  _activeView: 2
+  _activeManifestation: 2
+  _bodyTracking: {fileID: 0}
+  _lipSync: {fileID: 0}
+  _criticalJointTypes: 
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  _useAdvancedLodSystem: 1
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _loadUserFromCdn: 0
+  _assetSource: 0
+  _assetPaths:
+  - 30_
+  _assets:
+  - source: 0
+    path: 30
+  _assetPostfixDefault: _rift.glb
+  _assetPostfixAndroid: _quest.glb
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!1 &8928388040547110969
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 506565571714275925}
+  - component: {fileID: 2785419835470461344}
+  m_Layer: 0
+  m_Name: Preset 29
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &506565571714275925
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8928388040547110969}
+  m_LocalRotation: {x: 0, y: 1, z: 0, w: 0}
+  m_LocalPosition: {x: 2, y: 0.5, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1396242425075152212}
+  m_RootOrder: 5
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!114 &2785419835470461344
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8928388040547110969}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _creationInfo:
+    features: 342
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+  _activeView: 2
+  _activeManifestation: 2
+  _bodyTracking: {fileID: 0}
+  _lipSync: {fileID: 0}
+  _criticalJointTypes: 
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  _useAdvancedLodSystem: 1
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _loadUserFromCdn: 0
+  _assetSource: 0
+  _assetPaths:
+  - 29_
+  _assets:
+  - source: 0
+    path: 29
+  _assetPostfixDefault: _rift.glb
+  _assetPostfixAndroid: _quest.glb
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!1001 &1946034419535268752
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 1396242425075152212}
+    m_Modifications:
+    - target: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 8
+      objectReference: {fileID: 0}
+    - target: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 8334773074039846814, guid: 548156b48d05cd54f901563c0dcc9900,
+        type: 3}
+      propertyPath: m_Name
+      value: Cylinders
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: 548156b48d05cd54f901563c0dcc9900, type: 3}
+--- !u!4 &3294956371155501852 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: 3943951107066628236, guid: 548156b48d05cd54f901563c0dcc9900,
+    type: 3}
+  m_PrefabInstance: {fileID: 1946034419535268752}
+  m_PrefabAsset: {fileID: 0}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarGroups/ShowcaseAvatarsGroup4.prefab.meta b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarGroups/ShowcaseAvatarsGroup4.prefab.meta
new file mode 100644
index 0000000000000000000000000000000000000000..4aa721ba714a67fd1b4137b5b70baec048d38843
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarGroups/ShowcaseAvatarsGroup4.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: e44c77800a51b2c41bb0ce66366a6866
+PrefabImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarGroups/TestAvatars.prefab b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarGroups/TestAvatars.prefab
new file mode 100644
index 0000000000000000000000000000000000000000..8d6df58b919c28282d565de67d51067b4ed00570
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarGroups/TestAvatars.prefab
@@ -0,0 +1,704 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &1396242425075152213
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1396242425075152212}
+  m_Layer: 0
+  m_Name: TestAvatars
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1396242425075152212
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1396242425075152213}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 7683831584038353065}
+  - {fileID: 1396242425182533275}
+  - {fileID: 1396242425329006250}
+  - {fileID: 1396242426695299693}
+  - {fileID: 4299159583212260341}
+  - {fileID: 4296408205773774975}
+  m_Father: {fileID: 0}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &1396242425182533276
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1396242425182533275}
+  - component: {fileID: 1396242425182533273}
+  m_Layer: 0
+  m_Name: CDN
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1396242425182533275
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1396242425182533276}
+  m_LocalRotation: {x: 0, y: 1, z: 0, w: 0}
+  m_LocalPosition: {x: 1, y: 0.5, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1396242425075152212}
+  m_RootOrder: 1
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!114 &1396242425182533273
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1396242425182533276}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _creationInfo:
+    features: 338
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+      subMeshInclusionFlags: 2047
+  _activeView: 2
+  _activeManifestation: 2
+  _activeSubMeshes: 2047
+  _activeSubMeshesIncludeUntyped: 1
+  _bodyTracking: {fileID: 0}
+  _lipSync: {fileID: 0}
+  _criticalJointTypes: 
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  OnCreatedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnSkeletonLoadedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnDefaultAvatarLoadedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnUserAvatarLoadedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  PreTeardownEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnLoadFailedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  MotionSmoothingSettings: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _highQuality: 0
+  _loadUserFromCdn: 1
+  _deferLoading: 0
+  _assets:
+  - source: 0
+    path: 0
+  _underscorePostfix: 1
+  _overridePostfix: 
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!1 &1396242425329006251
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1396242425329006250}
+  - component: {fileID: 1396242425329006248}
+  m_Layer: 0
+  m_Name: ZipCombined
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1396242425329006250
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1396242425329006251}
+  m_LocalRotation: {x: 0, y: 1, z: 0, w: 0}
+  m_LocalPosition: {x: 0, y: 0.5, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1396242425075152212}
+  m_RootOrder: 2
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!114 &1396242425329006248
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1396242425329006251}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _creationInfo:
+    features: 338
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+      subMeshInclusionFlags: 2047
+  _activeView: 2
+  _activeManifestation: 2
+  _activeSubMeshes: 2047
+  _activeSubMeshesIncludeUntyped: 1
+  _bodyTracking: {fileID: 0}
+  _lipSync: {fileID: 0}
+  _criticalJointTypes: 
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  OnCreatedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnSkeletonLoadedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnDefaultAvatarLoadedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnUserAvatarLoadedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  PreTeardownEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnLoadFailedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  MotionSmoothingSettings: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _highQuality: 0
+  _loadUserFromCdn: 0
+  _deferLoading: 0
+  _assets:
+  - source: 0
+    path: 0
+  _underscorePostfix: 1
+  _overridePostfix: 
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!1 &1396242426695299694
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1396242426695299693}
+  - component: {fileID: 1396242426695299691}
+  m_Layer: 0
+  m_Name: StreamingAssetsCombined
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1396242426695299693
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1396242426695299694}
+  m_LocalRotation: {x: 0, y: 1, z: 0, w: 0}
+  m_LocalPosition: {x: -1, y: 0.5, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1396242425075152212}
+  m_RootOrder: 3
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!114 &1396242426695299691
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1396242426695299694}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _creationInfo:
+    features: 338
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+      subMeshInclusionFlags: 2047
+  _activeView: 2
+  _activeManifestation: 2
+  _activeSubMeshes: 2047
+  _activeSubMeshesIncludeUntyped: 1
+  _bodyTracking: {fileID: 0}
+  _lipSync: {fileID: 0}
+  _criticalJointTypes: 
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  OnCreatedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnSkeletonLoadedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnDefaultAvatarLoadedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnUserAvatarLoadedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  PreTeardownEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnLoadFailedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  MotionSmoothingSettings: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _highQuality: 0
+  _loadUserFromCdn: 0
+  _deferLoading: 0
+  _assets:
+  - source: 1
+    path: models/combined/jill/
+  _underscorePostfix: 0
+  _overridePostfix: 
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!1 &3429477688157955944
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 4296408205773774975}
+  - component: {fileID: 7143144871536510017}
+  m_Layer: 0
+  m_Name: StreamingAssetsSuitOnly
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &4296408205773774975
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3429477688157955944}
+  m_LocalRotation: {x: 0, y: 1, z: 0, w: 0}
+  m_LocalPosition: {x: -3, y: 0.5, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1396242425075152212}
+  m_RootOrder: 5
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!114 &7143144871536510017
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3429477688157955944}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _creationInfo:
+    features: 338
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+      subMeshInclusionFlags: 1
+  _activeView: 2
+  _activeManifestation: 2
+  _activeSubMeshes: 1
+  _activeSubMeshesIncludeUntyped: 0
+  _bodyTracking: {fileID: 0}
+  _lipSync: {fileID: 0}
+  _criticalJointTypes: 
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  OnCreatedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnSkeletonLoadedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnDefaultAvatarLoadedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnUserAvatarLoadedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  PreTeardownEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnLoadFailedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  MotionSmoothingSettings: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _highQuality: 1
+  _loadUserFromCdn: 0
+  _deferLoading: 0
+  _assets:
+  - source: 1
+    path: models/combined/sabersuit/
+  _underscorePostfix: 0
+  _overridePostfix: 
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!1 &5779059046781471357
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7683831584038353065}
+  - component: {fileID: 4237050325572324052}
+  m_Layer: 0
+  m_Name: Default Model
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &7683831584038353065
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 5779059046781471357}
+  m_LocalRotation: {x: -0, y: 1, z: -0, w: 0}
+  m_LocalPosition: {x: 2, y: 0.5, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1396242425075152212}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!114 &4237050325572324052
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 5779059046781471357}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _creationInfo:
+    features: 370
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+      subMeshInclusionFlags: 2047
+  _activeView: 2
+  _activeManifestation: 2
+  _activeSubMeshes: 2047
+  _activeSubMeshesIncludeUntyped: 1
+  _bodyTracking: {fileID: 0}
+  _lipSync: {fileID: 0}
+  _criticalJointTypes: 
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  OnCreatedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnSkeletonLoadedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnDefaultAvatarLoadedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnUserAvatarLoadedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  PreTeardownEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnLoadFailedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  MotionSmoothingSettings: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _highQuality: 0
+  _loadUserFromCdn: 0
+  _deferLoading: 0
+  _assets:
+  - source: 0
+    path: 1
+  _underscorePostfix: 1
+  _overridePostfix: 
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!1 &9164221046699497747
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 4299159583212260341}
+  - component: {fileID: 9185815407905995404}
+  m_Layer: 0
+  m_Name: StreamingAssetsWithoutSuit
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &4299159583212260341
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 9164221046699497747}
+  m_LocalRotation: {x: -0, y: 1, z: -0, w: 0}
+  m_LocalPosition: {x: -2, y: 0.5, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1396242425075152212}
+  m_RootOrder: 4
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!114 &9185815407905995404
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 9164221046699497747}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _creationInfo:
+    features: 338
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+      subMeshInclusionFlags: 2046
+  _activeView: 2
+  _activeManifestation: 2
+  _activeSubMeshes: 2046
+  _activeSubMeshesIncludeUntyped: 1
+  _bodyTracking: {fileID: 0}
+  _lipSync: {fileID: 0}
+  _criticalJointTypes: 
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  OnCreatedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnSkeletonLoadedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnDefaultAvatarLoadedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnUserAvatarLoadedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  PreTeardownEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnLoadFailedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  MotionSmoothingSettings: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _highQuality: 1
+  _loadUserFromCdn: 0
+  _deferLoading: 0
+  _assets:
+  - source: 1
+    path: models/combined/saberjack/
+  _underscorePostfix: 0
+  _overridePostfix: 
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarGroups/TestAvatars.prefab.meta b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarGroups/TestAvatars.prefab.meta
new file mode 100644
index 0000000000000000000000000000000000000000..6697f11d351e20f33b214168be7f6e44fde14b54
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarGroups/TestAvatars.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: cb71a65524741c74d9586cbf034c3051
+PrefabImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerBase.prefab b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerBase.prefab
new file mode 100644
index 0000000000000000000000000000000000000000..04614203737097500c394adeef5c38496bbe1e87
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerBase.prefab
@@ -0,0 +1,169 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &4870205409715631027
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 4870205409715631025}
+  - component: {fileID: 4870205409715631024}
+  - component: {fileID: 114428096727327266}
+  - component: {fileID: 7652054073463162907}
+  - component: {fileID: 2817231646188770880}
+  m_Layer: 0
+  m_Name: AvatarSdkManagerBase
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &4870205409715631025
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4870205409715631027}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 0}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &4870205409715631024
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4870205409715631027}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 0b47856a33046ce40862a2fddb86623b, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  MaxConcurrentAvatarsLoading: 64
+  MaxConcurrentResourcesLoading: 2
+  _minSliceWorkPerFrameMS: 3
+  _maxRequests: 16
+  _maxSendBytesPerSecond: -1
+  _maxReceiveBytesPerSecond: -1
+  _specificationTimeoutMs: -1
+  _assetTimeoutMs: -1
+  _assetLowBandwidthTimeoutSeconds: -1
+  _assetLowBandwidthBytesPerSecond: -1
+  _preloadZipFiles:
+  - SampleAssets/PresetAvatars
+  _preloadUniversalZipFiles: []
+  zipPostfixDefault: Rift
+  zipPostfixAndroid: Quest
+  zipPostfixQuest2: Quest
+  zipFileExtension: .glb
+  streamingAssetPostfixDefault: rift
+  streamingAssetPostfixAndroid: quest1
+  streamingAssetPostfixQuest2: quest2
+  assetVersionDefault: 3
+  assetVersionAndroid: 3
+  assetVersionQuest2: 3
+  streamingAssetFileExtension: .glb.zst
+  ovrAvatar2AssetFolder: Oculus
+  _defaultModelColor: {r: 0.11764706, g: 0.6156863, b: 1, a: 1}
+  ShaderManager: {fileID: 0}
+  _useCriticalJointJobs: 0
+  _enableDevTools: 0
+  _ovrLogLevel: 3
+  _skinnersSupported: 4
+  _skinQualityPerLOD: 
+--- !u!114 &114428096727327266
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4870205409715631027}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f892af86578f82443a9682973148029a, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  MaxLodLevel: 4
+  LODCountPerFrame: 8
+  refreshSeconds: 0
+  cycleProcessingOverFrames: 1
+  _lodCamera: {fileID: 0}
+  extraCameras: []
+  frustumBounds_:
+    m_Center: {x: 0, y: 0, z: 0}
+    m_Extent: {x: 0.175, y: 0.5, z: 0.175}
+  CameraHeight: 1.6
+  screenPercentToUpdateImportanceCurvePower: 0.25
+  screenPercentToUpdateImportanceCurveMultiplier: 1.5
+  cullingDisablesParentGameObject: 0
+  cullingDisablesChildrenGameObjects: 1
+  _jointTypeToCenterOn: 13
+  _jointTypesToCullOn: 0f0000001300000017000000
+  enableDynamicPerformance: 1
+  dynamicLodWantedLogScale: 1.3
+  numDynamicLods: 2
+  dynamicLodMaxTrianglesToRender: 90000
+  maxActiveAvatars: 5
+  maxVerticesToSkin: 30000
+  enableDynamicStreaming: 0
+  dynamicStreamLodBitsPerSecond: 0000000000000000000000000000000000000000000000000000000000000000
+  dynamicStreamLodMaxDistance: 010000000000000003000000000000000900000000000000
+  dynamicStreamLodMaxBitsPerSecond: 3000000
+  firstPersonAvatarLod: {fileID: 0}
+  firstPersonAvatarLodLevel: 0
+  firstPersonUpdateImportance: 10000
+  avatarLodDebugCanvas: {fileID: 1316326085530136, guid: 6c859d5f04a0b2d478aeb2732044266c,
+    type: 3}
+  debug:
+    sceneViewCamera: 1
+    displayLODLabels: 0
+    displayAgeLabels: 0
+    displayLODColors: 0
+    displayLODLabelOffset: {x: 0.3, y: 0, z: 0.3}
+--- !u!114 &7652054073463162907
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4870205409715631027}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: ecd1062ecb075db4a83f03b03ba8100d, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  DefaultSkinningType: 0
+  QualityPerLOD: 0400000004000000040000000400000004000000
+  SourceMorphFormat: 1
+  CombinedMorphFormat: 1
+  SkinnerOutputFormat: 2
+  SkinnerUnormScale: 4
+  SupportApplicationSpaceWarp: 0
+  MotionSmoothing: 0
+  _CombineMorphTargetsShader: {fileID: 4800000, guid: bef2973cf0137a04fbdd59ed0bab09bc,
+    type: 3}
+  _SkinToTextureShader: {fileID: 4800000, guid: ee1f1406edf162945868fafc408691aa,
+    type: 3}
+--- !u!114 &2817231646188770880
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4870205409715631027}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 6937544d08e2ba34da6ff4ab90cbfd84, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _useRuntimeTrackingService: 0
+  _useAsyncBodySolver: 0
+  _ovrCameraRig: {fileID: 0}
+  _debugDrawTrackingLocations: 0
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerBase.prefab.meta b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerBase.prefab.meta
new file mode 100644
index 0000000000000000000000000000000000000000..b4894be52a13cf13f2242b86279389588e9c5ffb
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerBase.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: f11d19b383e9e94489eacb1f0b55e99b
+PrefabImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerEverything.prefab b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerEverything.prefab
new file mode 100644
index 0000000000000000000000000000000000000000..5453039f7a20b51e7f403dc2180e8c57c17a9f18
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerEverything.prefab
@@ -0,0 +1,503 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &364714015976348763
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 6372564233989275950}
+  - component: {fileID: 3131846935198047616}
+  m_Layer: 0
+  m_Name: HumanShaderManager
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &6372564233989275950
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 364714015976348763}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 7119786176084065733}
+  m_RootOrder: 6
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &3131846935198047616
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 364714015976348763}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 637f19aef5eccc146a6ab77a1d4ab0a8, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  DefaultShaderConfigurationInitializer: {fileID: 11400000, guid: 6136296a60f93fa4eaef79f8293bc598,
+    type: 2}
+  FastLoadConfigurationInitializer: {fileID: 11400000, guid: 140ddf9b180432b45b3c030fda428127,
+    type: 2}
+--- !u!1 &2146185719265862950
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 3133577062391832754}
+  - component: {fileID: 6086471172250634898}
+  m_Layer: 0
+  m_Name: KhronosShaderManager
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &3133577062391832754
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2146185719265862950}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 7119786176084065733}
+  m_RootOrder: 1
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &6086471172250634898
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2146185719265862950}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 637f19aef5eccc146a6ab77a1d4ab0a8, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  DefaultShaderConfigurationInitializer: {fileID: 11400000, guid: 4e831d6cee3eb114d8ac7795000c0f7f,
+    type: 2}
+  FastLoadConfigurationInitializer: {fileID: 11400000, guid: 97b3adcf896459e42a47943963d32f6c,
+    type: 2}
+--- !u!1 &2269518206755404845
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 3425466334231157924}
+  - component: {fileID: 137372858795262311}
+  m_Layer: 0
+  m_Name: LibraryShaderManager
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &3425466334231157924
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2269518206755404845}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 7119786176084065733}
+  m_RootOrder: 7
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &137372858795262311
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2269518206755404845}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 637f19aef5eccc146a6ab77a1d4ab0a8, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  DefaultShaderConfigurationInitializer: {fileID: 11400000, guid: 4b6a3518752ad734b93618f043251a60,
+    type: 2}
+  FastLoadConfigurationInitializer: {fileID: 11400000, guid: c325f1f69cda2e745a527acd748ef2c6,
+    type: 2}
+--- !u!1 &3175426741136616727
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 5753622703037430937}
+  - component: {fileID: 5548386639390790070}
+  m_Layer: 0
+  m_Name: UnityStandardShaderManager
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &5753622703037430937
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3175426741136616727}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 7119786176084065733}
+  m_RootOrder: 4
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &5548386639390790070
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3175426741136616727}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 637f19aef5eccc146a6ab77a1d4ab0a8, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  DefaultShaderConfigurationInitializer: {fileID: 11400000, guid: 9ff896b4d4158bf4cba8a921337fe2cc,
+    type: 2}
+  FastLoadConfigurationInitializer: {fileID: 0}
+--- !u!1 &3876983921656373384
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 8269823703180106046}
+  - component: {fileID: 7832877918072220425}
+  m_Layer: 0
+  m_Name: HorizonShaderManager
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &8269823703180106046
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3876983921656373384}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 7119786176084065733}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &7832877918072220425
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3876983921656373384}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 561fe8366de5e7d4fae882c491e3d7dc, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  DefaultShaderConfigurationInitializer: {fileID: 11400000, guid: c77a93072aee9da4b8fc36eeb125dc89,
+    type: 2}
+  ArrayShaderConfigurationInitializer: {fileID: 11400000, guid: c77a93072aee9da4b8fc36eeb125dc89,
+    type: 2}
+  SolidColorShaderConfigurationInitializer: {fileID: 11400000, guid: 260fe00c90ebd8148abcf88bca09cce2,
+    type: 2}
+  TransparentShaderConfigurationInitializer: {fileID: 11400000, guid: c77a93072aee9da4b8fc36eeb125dc89,
+    type: 2}
+  EmmisiveShaderConfigurationInitializer: {fileID: 11400000, guid: c77a93072aee9da4b8fc36eeb125dc89,
+    type: 2}
+  SkinShaderConfigurationInitializer: {fileID: 11400000, guid: 2b815828c466e45498c74d690226c0a4,
+    type: 2}
+  LeftEyeShaderConfigurationInitializer: {fileID: 11400000, guid: d355c44f84f94354385fea0772eb3bc7,
+    type: 2}
+  RightEyeShaderConfigurationInitializer: {fileID: 11400000, guid: 4dc61dd44af9e7d488b6434e88ac167e,
+    type: 2}
+  HairShaderConfigurationInitializer: {fileID: 11400000, guid: c4b830c6fb33d8140833e5b37f7c9e0d,
+    type: 2}
+  FastLoadConfigurationInitializer: {fileID: 11400000, guid: d6a8914844d40284395dca35471c343b,
+    type: 2}
+--- !u!1 &5229240202412072535
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 703573329195536194}
+  - component: {fileID: 5462108814324215237}
+  m_Layer: 0
+  m_Name: KhronosBabylonMatchOptimizedManager
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &703573329195536194
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 5229240202412072535}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 7119786176084065733}
+  m_RootOrder: 3
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &5462108814324215237
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 5229240202412072535}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 637f19aef5eccc146a6ab77a1d4ab0a8, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  DefaultShaderConfigurationInitializer: {fileID: 11400000, guid: bdc73cf1b98d8184c8a6d142874b6733,
+    type: 2}
+  FastLoadConfigurationInitializer: {fileID: 11400000, guid: 97b3adcf896459e42a47943963d32f6c,
+    type: 2}
+--- !u!1 &8330861193908809066
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 2365446712419655743}
+  - component: {fileID: 4990312157792730811}
+  m_Layer: 0
+  m_Name: KhronosBabylonMatchShaderManager
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &2365446712419655743
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8330861193908809066}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 7119786176084065733}
+  m_RootOrder: 2
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &4990312157792730811
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8330861193908809066}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 637f19aef5eccc146a6ab77a1d4ab0a8, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  DefaultShaderConfigurationInitializer: {fileID: 11400000, guid: 8581c3cc799ee524ba06f56986275c6a,
+    type: 2}
+  FastLoadConfigurationInitializer: {fileID: 11400000, guid: 97b3adcf896459e42a47943963d32f6c,
+    type: 2}
+--- !u!1 &8699602504695039848
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 553290503904374642}
+  - component: {fileID: 5491158922536122310}
+  m_Layer: 0
+  m_Name: MobileDiffuseShaderManager
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &553290503904374642
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8699602504695039848}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 7119786176084065733}
+  m_RootOrder: 5
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &5491158922536122310
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8699602504695039848}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 637f19aef5eccc146a6ab77a1d4ab0a8, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  DefaultShaderConfigurationInitializer: {fileID: 11400000, guid: faf5a921c93b47843a50c991e149f023,
+    type: 2}
+  FastLoadConfigurationInitializer: {fileID: 0}
+--- !u!1001 &2402949446493212276
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 0}
+    m_Modifications:
+    - target: {fileID: 4116629804873427435, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 4
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: DefaultShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: c77a93072aee9da4b8fc36eeb125dc89,
+        type: 2}
+    - target: {fileID: 4870205409715631024, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: ShaderManager
+      value: 
+      objectReference: {fileID: 7832877918072220425}
+    - target: {fileID: 4870205409715631024, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: shaderManager
+      value: 
+      objectReference: {fileID: 7832877918072220425}
+    - target: {fileID: 4870205409715631024, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: assetVersionQuest2
+      value: 4
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631024, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: assetVersionAndroid
+      value: 4
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631024, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: assetVersionDefault
+      value: 4
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631027, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_Name
+      value: AvatarSdkManagerEverything
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: f11d19b383e9e94489eacb1f0b55e99b, type: 3}
+--- !u!4 &7119786176084065733 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+    type: 3}
+  m_PrefabInstance: {fileID: 2402949446493212276}
+  m_PrefabAsset: {fileID: 0}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerEverything.prefab.meta b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerEverything.prefab.meta
new file mode 100644
index 0000000000000000000000000000000000000000..31f3436867bf2c0d217c098d3fe00239edd09c42
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerEverything.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 8d5ca0b417eb87a41901203c41394049
+PrefabImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerHorizon.prefab b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerHorizon.prefab
new file mode 100644
index 0000000000000000000000000000000000000000..2c13794b31df425637cd8e9e685e1bbffe9f2cc6
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerHorizon.prefab
@@ -0,0 +1,239 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &8949687899550948275
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 708683025196591057}
+  - component: {fileID: 3255524753290351637}
+  m_Layer: 0
+  m_Name: HorizonShaderManager
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &708683025196591057
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8949687899550948275}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 5848076533691757919}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &3255524753290351637
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8949687899550948275}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 561fe8366de5e7d4fae882c491e3d7dc, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  DefaultShaderConfigurationInitializer: {fileID: 11400000, guid: f0bddb66189f23442a435aed67437255,
+    type: 2}
+  ArrayShaderConfigurationInitializer: {fileID: 11400000, guid: f0bddb66189f23442a435aed67437255,
+    type: 2}
+  SolidColorShaderConfigurationInitializer: {fileID: 11400000, guid: 260fe00c90ebd8148abcf88bca09cce2,
+    type: 2}
+  TransparentShaderConfigurationInitializer: {fileID: 11400000, guid: c77a93072aee9da4b8fc36eeb125dc89,
+    type: 2}
+  EmmisiveShaderConfigurationInitializer: {fileID: 11400000, guid: c77a93072aee9da4b8fc36eeb125dc89,
+    type: 2}
+  SkinShaderConfigurationInitializer: {fileID: 11400000, guid: 2b815828c466e45498c74d690226c0a4,
+    type: 2}
+  LeftEyeShaderConfigurationInitializer: {fileID: 11400000, guid: d355c44f84f94354385fea0772eb3bc7,
+    type: 2}
+  RightEyeShaderConfigurationInitializer: {fileID: 11400000, guid: 4dc61dd44af9e7d488b6434e88ac167e,
+    type: 2}
+  HairShaderConfigurationInitializer: {fileID: 11400000, guid: c4b830c6fb33d8140833e5b37f7c9e0d,
+    type: 2}
+  FastLoadConfigurationInitializer: {fileID: 11400000, guid: d6a8914844d40284395dca35471c343b,
+    type: 2}
+--- !u!1001 &1350790283880007406
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 0}
+    m_Modifications:
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: HairShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: c4b830c6fb33d8140833e5b37f7c9e0d,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: SkinShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 2b815828c466e45498c74d690226c0a4,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: ArrayShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: c77a93072aee9da4b8fc36eeb125dc89,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: DefaultShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: c77a93072aee9da4b8fc36eeb125dc89,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: LeftEyeShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: d355c44f84f94354385fea0772eb3bc7,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: EnnisiveShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: c77a93072aee9da4b8fc36eeb125dc89,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: RightEyeShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 4dc61dd44af9e7d488b6434e88ac167e,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: SolidColorShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 260fe00c90ebd8148abcf88bca09cce2,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: TransparentShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: c77a93072aee9da4b8fc36eeb125dc89,
+        type: 2}
+    - target: {fileID: 4870205409715631024, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: ShaderManager
+      value: 
+      objectReference: {fileID: 3255524753290351637}
+    - target: {fileID: 4870205409715631024, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: shaderManager
+      value: 
+      objectReference: {fileID: 3255524753290351637}
+    - target: {fileID: 4870205409715631024, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: UseFastLoadAvatar
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631024, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: assetVersionQuest2
+      value: 4
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631024, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: assetVersionAndroid
+      value: 4
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631024, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: assetVersionDefault
+      value: 4
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631027, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_Name
+      value: AvatarSdkManagerHorizon
+      objectReference: {fileID: 0}
+    - target: {fileID: 7652054073463162907, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: SourceMorphFormat
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 7652054073463162907, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: CombinedMorphFormat
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 7652054073463162907, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: SkinnerOutputFormat
+      value: 2
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: f11d19b383e9e94489eacb1f0b55e99b, type: 3}
+--- !u!4 &5848076533691757919 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+    type: 3}
+  m_PrefabInstance: {fileID: 1350790283880007406}
+  m_PrefabAsset: {fileID: 0}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerHorizon.prefab.meta b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerHorizon.prefab.meta
new file mode 100644
index 0000000000000000000000000000000000000000..736775f9634bdf12b3c65139e4fbab4ebc248ddb
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerHorizon.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: d0377a811c2a95841ba015ae0b2c8d45
+PrefabImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerHuman.prefab b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerHuman.prefab
new file mode 100644
index 0000000000000000000000000000000000000000..b6bd1002d80e31ea8d30fde28c7f8208aa681f0e
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerHuman.prefab
@@ -0,0 +1,208 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &3580441610474808257
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 5612778290736673687}
+  - component: {fileID: 2081836299234360964}
+  m_Layer: 0
+  m_Name: HumanShaderManager
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &5612778290736673687
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3580441610474808257}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 3157928069488761443}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &2081836299234360964
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3580441610474808257}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 637f19aef5eccc146a6ab77a1d4ab0a8, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  DefaultShaderConfigurationInitializer: {fileID: 11400000, guid: 6136296a60f93fa4eaef79f8293bc598,
+    type: 2}
+  FastLoadConfigurationInitializer: {fileID: 11400000, guid: 140ddf9b180432b45b3c030fda428127,
+    type: 2}
+--- !u!1001 &7513487270613477842
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 0}
+    m_Modifications:
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: HairShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 4e831d6cee3eb114d8ac7795000c0f7f,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: SkinShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 4e831d6cee3eb114d8ac7795000c0f7f,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: ArrayShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 4e831d6cee3eb114d8ac7795000c0f7f,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: DefaultShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 4e831d6cee3eb114d8ac7795000c0f7f,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: LeftEyeShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 4e831d6cee3eb114d8ac7795000c0f7f,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: EnnisiveShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 4e831d6cee3eb114d8ac7795000c0f7f,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: RightEyeShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 4e831d6cee3eb114d8ac7795000c0f7f,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: SolidColorShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 4e831d6cee3eb114d8ac7795000c0f7f,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: TransparentShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 4e831d6cee3eb114d8ac7795000c0f7f,
+        type: 2}
+    - target: {fileID: 4870205409715631024, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: ShaderManager
+      value: 
+      objectReference: {fileID: 2081836299234360964}
+    - target: {fileID: 4870205409715631024, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: shaderManager
+      value: 
+      objectReference: {fileID: 2081836299234360964}
+    - target: {fileID: 4870205409715631024, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: UseFastLoadAvatar
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631024, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: assetVersionQuest2
+      value: 4
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631024, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: assetVersionAndroid
+      value: 4
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631024, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: assetVersionDefault
+      value: 4
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631027, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_Name
+      value: AvatarSdkManagerHuman
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: f11d19b383e9e94489eacb1f0b55e99b, type: 3}
+--- !u!4 &3157928069488761443 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+    type: 3}
+  m_PrefabInstance: {fileID: 7513487270613477842}
+  m_PrefabAsset: {fileID: 0}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerHuman.prefab.meta b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerHuman.prefab.meta
new file mode 100644
index 0000000000000000000000000000000000000000..16ec23e76c8dd02d66b41c0adf43ff97bd2e794d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerHuman.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 7708fbb154bd0b84aa961d0a8de4a0b7
+PrefabImporter:
+  externalObjects: {}
+  userData:
+  assetBundleName:
+  assetBundleVariant:
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerKhronos.prefab b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerKhronos.prefab
new file mode 100644
index 0000000000000000000000000000000000000000..7b784948e1cdb09060fbf75287cd937902b3f99d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerKhronos.prefab
@@ -0,0 +1,203 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &3580441610474808257
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 5612778290736673687}
+  - component: {fileID: 2081836299234360964}
+  m_Layer: 0
+  m_Name: KhronosShaderManager
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &5612778290736673687
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3580441610474808257}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 3157928069488761443}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &2081836299234360964
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3580441610474808257}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 637f19aef5eccc146a6ab77a1d4ab0a8, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  DefaultShaderConfigurationInitializer: {fileID: 11400000, guid: 4e831d6cee3eb114d8ac7795000c0f7f,
+    type: 2}
+  FastLoadConfigurationInitializer: {fileID: 11400000, guid: 97b3adcf896459e42a47943963d32f6c,
+    type: 2}
+--- !u!1001 &7513487270613477842
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 0}
+    m_Modifications:
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: HairShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 4e831d6cee3eb114d8ac7795000c0f7f,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: SkinShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 4e831d6cee3eb114d8ac7795000c0f7f,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: ArrayShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 4e831d6cee3eb114d8ac7795000c0f7f,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: DefaultShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 4e831d6cee3eb114d8ac7795000c0f7f,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: LeftEyeShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 4e831d6cee3eb114d8ac7795000c0f7f,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: EnnisiveShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 4e831d6cee3eb114d8ac7795000c0f7f,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: RightEyeShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 4e831d6cee3eb114d8ac7795000c0f7f,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: SolidColorShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 4e831d6cee3eb114d8ac7795000c0f7f,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: TransparentShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 4e831d6cee3eb114d8ac7795000c0f7f,
+        type: 2}
+    - target: {fileID: 4870205409715631024, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: ShaderManager
+      value: 
+      objectReference: {fileID: 2081836299234360964}
+    - target: {fileID: 4870205409715631024, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: shaderManager
+      value: 
+      objectReference: {fileID: 2081836299234360964}
+    - target: {fileID: 4870205409715631024, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: assetVersionQuest2
+      value: 4
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631024, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: assetVersionAndroid
+      value: 4
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631024, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: assetVersionDefault
+      value: 4
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631027, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_Name
+      value: AvatarSdkManagerKhronos
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: f11d19b383e9e94489eacb1f0b55e99b, type: 3}
+--- !u!4 &3157928069488761443 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+    type: 3}
+  m_PrefabInstance: {fileID: 7513487270613477842}
+  m_PrefabAsset: {fileID: 0}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerKhronos.prefab.meta b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerKhronos.prefab.meta
new file mode 100644
index 0000000000000000000000000000000000000000..6849f22128fc1c5767c83907be2324460eaf1495
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerKhronos.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 4ffb3223bd7352e40bac003a93ba47bd
+PrefabImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerKhronosBabylonMatch.prefab b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerKhronosBabylonMatch.prefab
new file mode 100644
index 0000000000000000000000000000000000000000..f4120c3efbf51db87a08bcf007104846ead76ab9
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerKhronosBabylonMatch.prefab
@@ -0,0 +1,188 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &8023036353539675757
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 8467428327039602454}
+  - component: {fileID: 1829188870742190569}
+  m_Layer: 0
+  m_Name: KhronosBabylonMatchShaderManager
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &8467428327039602454
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8023036353539675757}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 3157928069488761443}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &1829188870742190569
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8023036353539675757}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 637f19aef5eccc146a6ab77a1d4ab0a8, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  DefaultShaderConfigurationInitializer: {fileID: 11400000, guid: 8581c3cc799ee524ba06f56986275c6a,
+    type: 2}
+  FastLoadConfigurationInitializer: {fileID: 11400000, guid: 97b3adcf896459e42a47943963d32f6c,
+    type: 2}
+--- !u!1001 &7513487270613477842
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 0}
+    m_Modifications:
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: HairShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 8581c3cc799ee524ba06f56986275c6a,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: SkinShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 8581c3cc799ee524ba06f56986275c6a,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: ArrayShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 8581c3cc799ee524ba06f56986275c6a,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: DefaultShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 8581c3cc799ee524ba06f56986275c6a,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: LeftEyeShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 8581c3cc799ee524ba06f56986275c6a,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: EnnisiveShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 8581c3cc799ee524ba06f56986275c6a,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: RightEyeShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 8581c3cc799ee524ba06f56986275c6a,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: SolidColorShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 8581c3cc799ee524ba06f56986275c6a,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: TransparentShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 8581c3cc799ee524ba06f56986275c6a,
+        type: 2}
+    - target: {fileID: 4870205409715631024, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: ShaderManager
+      value: 
+      objectReference: {fileID: 1829188870742190569}
+    - target: {fileID: 4870205409715631024, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: shaderManager
+      value: 
+      objectReference: {fileID: 1829188870742190569}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631027, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_Name
+      value: AvatarSdkManagerKhronosBabylonMatch
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: f11d19b383e9e94489eacb1f0b55e99b, type: 3}
+--- !u!4 &3157928069488761443 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+    type: 3}
+  m_PrefabInstance: {fileID: 7513487270613477842}
+  m_PrefabAsset: {fileID: 0}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerKhronosBabylonMatch.prefab.meta b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerKhronosBabylonMatch.prefab.meta
new file mode 100644
index 0000000000000000000000000000000000000000..ae8f88d8d61279b53ec3dbd03e72d561ecde1bcd
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerKhronosBabylonMatch.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 6df47a28fe7c7c444a658aebcc35ad94
+PrefabImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerKhronosBabylonOptimized.prefab b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerKhronosBabylonOptimized.prefab
new file mode 100644
index 0000000000000000000000000000000000000000..741dc05be9b74f67c6ba6bb686cc58787bd00da6
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerKhronosBabylonOptimized.prefab
@@ -0,0 +1,186 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &2246656820621092874
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 6142978148970159867}
+  - component: {fileID: 6480366574452515203}
+  m_Layer: 0
+  m_Name: KhronosBabylonOptimizedShaderManager
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &6142978148970159867
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2246656820621092874}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 3157928069488761443}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &6480366574452515203
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2246656820621092874}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 637f19aef5eccc146a6ab77a1d4ab0a8, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  DefaultShaderConfigurationInitializer: {fileID: 11400000, guid: bdc73cf1b98d8184c8a6d142874b6733,
+    type: 2}
+--- !u!1001 &7513487270613477842
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 0}
+    m_Modifications:
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: HairShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: bdc73cf1b98d8184c8a6d142874b6733,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: SkinShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: bdc73cf1b98d8184c8a6d142874b6733,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: ArrayShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: bdc73cf1b98d8184c8a6d142874b6733,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: DefaultShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: bdc73cf1b98d8184c8a6d142874b6733,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: LeftEyeShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: bdc73cf1b98d8184c8a6d142874b6733,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: EnnisiveShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: bdc73cf1b98d8184c8a6d142874b6733,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: RightEyeShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: bdc73cf1b98d8184c8a6d142874b6733,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: SolidColorShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: bdc73cf1b98d8184c8a6d142874b6733,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: TransparentShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: bdc73cf1b98d8184c8a6d142874b6733,
+        type: 2}
+    - target: {fileID: 4870205409715631024, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: ShaderManager
+      value: 
+      objectReference: {fileID: 6480366574452515203}
+    - target: {fileID: 4870205409715631024, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: shaderManager
+      value: 
+      objectReference: {fileID: 6480366574452515203}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631027, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_Name
+      value: AvatarSdkManagerKhronosBabylonOptimized
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: f11d19b383e9e94489eacb1f0b55e99b, type: 3}
+--- !u!4 &3157928069488761443 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+    type: 3}
+  m_PrefabInstance: {fileID: 7513487270613477842}
+  m_PrefabAsset: {fileID: 0}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerKhronosBabylonOptimized.prefab.meta b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerKhronosBabylonOptimized.prefab.meta
new file mode 100644
index 0000000000000000000000000000000000000000..2d12da3209a0700447d85e54ef35f8d1722a7eea
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerKhronosBabylonOptimized.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 6de0f6f3576634e41b37b7f2403c1fc3
+PrefabImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerLibrary.prefab b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerLibrary.prefab
new file mode 100644
index 0000000000000000000000000000000000000000..f46a17859f09c14c983e8e41a90c3ac5370d89d8
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerLibrary.prefab
@@ -0,0 +1,188 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &3580441610474808257
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 5612778290736673687}
+  - component: {fileID: 2081836299234360964}
+  m_Layer: 0
+  m_Name: LibraryShaderManager
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &5612778290736673687
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3580441610474808257}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 3157928069488761443}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &2081836299234360964
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3580441610474808257}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 637f19aef5eccc146a6ab77a1d4ab0a8, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  DefaultShaderConfigurationInitializer: {fileID: 11400000, guid: 4b6a3518752ad734b93618f043251a60,
+    type: 2}
+  FastLoadConfigurationInitializer: {fileID: 11400000, guid: c325f1f69cda2e745a527acd748ef2c6,
+    type: 2}
+--- !u!1001 &7513487270613477842
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 0}
+    m_Modifications:
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: HairShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 4e831d6cee3eb114d8ac7795000c0f7f,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: SkinShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 4e831d6cee3eb114d8ac7795000c0f7f,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: ArrayShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 4e831d6cee3eb114d8ac7795000c0f7f,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: DefaultShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 4e831d6cee3eb114d8ac7795000c0f7f,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: LeftEyeShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 4e831d6cee3eb114d8ac7795000c0f7f,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: EnnisiveShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 4e831d6cee3eb114d8ac7795000c0f7f,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: RightEyeShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 4e831d6cee3eb114d8ac7795000c0f7f,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: SolidColorShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 4e831d6cee3eb114d8ac7795000c0f7f,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: TransparentShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 4e831d6cee3eb114d8ac7795000c0f7f,
+        type: 2}
+    - target: {fileID: 4870205409715631024, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: ShaderManager
+      value: 
+      objectReference: {fileID: 2081836299234360964}
+    - target: {fileID: 4870205409715631024, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: shaderManager
+      value: 
+      objectReference: {fileID: 2081836299234360964}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631027, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_Name
+      value: AvatarSdkManagerLibrary
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: f11d19b383e9e94489eacb1f0b55e99b, type: 3}
+--- !u!4 &3157928069488761443 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+    type: 3}
+  m_PrefabInstance: {fileID: 7513487270613477842}
+  m_PrefabAsset: {fileID: 0}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerLibrary.prefab.meta b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerLibrary.prefab.meta
new file mode 100644
index 0000000000000000000000000000000000000000..8347e076aa30e0aa1e694dbd6f8c56b997982ba9
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerLibrary.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 425e6ea49dcc520488b469a76c46798a
+PrefabImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerMobileBumpSpec.prefab b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerMobileBumpSpec.prefab
new file mode 100644
index 0000000000000000000000000000000000000000..13eb31304c875bf690bd2d5d1fa6cf8f6dd1df9e
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerMobileBumpSpec.prefab
@@ -0,0 +1,186 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &1726899374204712141
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 843207120309333298}
+  - component: {fileID: 8206166609229121379}
+  m_Layer: 0
+  m_Name: MobileBumpSpecShaderManager
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &843207120309333298
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1726899374204712141}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 6326020593977794297}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &8206166609229121379
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1726899374204712141}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 637f19aef5eccc146a6ab77a1d4ab0a8, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  DefaultShaderConfigurationInitializer: {fileID: 11400000, guid: b24898be37576054abb95cc32f5ab8bd,
+    type: 2}
+--- !u!1001 &1467320476217060680
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 0}
+    m_Modifications:
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: HairShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: b24898be37576054abb95cc32f5ab8bd,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: SkinShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: b24898be37576054abb95cc32f5ab8bd,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: ArrayShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: b24898be37576054abb95cc32f5ab8bd,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: DefaultShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: b24898be37576054abb95cc32f5ab8bd,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: LeftEyeShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: b24898be37576054abb95cc32f5ab8bd,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: EnnisiveShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: b24898be37576054abb95cc32f5ab8bd,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: RightEyeShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: b24898be37576054abb95cc32f5ab8bd,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: SolidColorShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: b24898be37576054abb95cc32f5ab8bd,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: TransparentShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: b24898be37576054abb95cc32f5ab8bd,
+        type: 2}
+    - target: {fileID: 4870205409715631024, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: ShaderManager
+      value: 
+      objectReference: {fileID: 8206166609229121379}
+    - target: {fileID: 4870205409715631024, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: shaderManager
+      value: 
+      objectReference: {fileID: 8206166609229121379}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631027, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_Name
+      value: AvatarSdkManagerMobileBumpSpec
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: f11d19b383e9e94489eacb1f0b55e99b, type: 3}
+--- !u!4 &6326020593977794297 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+    type: 3}
+  m_PrefabInstance: {fileID: 1467320476217060680}
+  m_PrefabAsset: {fileID: 0}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerMobileBumpSpec.prefab.meta b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerMobileBumpSpec.prefab.meta
new file mode 100644
index 0000000000000000000000000000000000000000..0df6456f017d9919fca27ed7c13d15360f261c6e
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerMobileBumpSpec.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 040a40079f7edfd4abdc13f8c0e2016f
+PrefabImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerMobileCustom.prefab b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerMobileCustom.prefab
new file mode 100644
index 0000000000000000000000000000000000000000..22c8a72da17f7958a0d8dba12c79ff11fd20de8c
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerMobileCustom.prefab
@@ -0,0 +1,186 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &6818083362048610210
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1019534908621485152}
+  - component: {fileID: 6958634297984706304}
+  m_Layer: 0
+  m_Name: MobileCustomShaderManager
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1019534908621485152
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6818083362048610210}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 3349172091304945868}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &6958634297984706304
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6818083362048610210}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 637f19aef5eccc146a6ab77a1d4ab0a8, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  DefaultShaderConfigurationInitializer: {fileID: 11400000, guid: f3da74418c1aeb8419a576c15c8f081e,
+    type: 2}
+--- !u!1001 &7920939224514894717
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 0}
+    m_Modifications:
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: HairShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: f3da74418c1aeb8419a576c15c8f081e,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: SkinShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: f3da74418c1aeb8419a576c15c8f081e,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: ArrayShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: f3da74418c1aeb8419a576c15c8f081e,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: DefaultShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: f3da74418c1aeb8419a576c15c8f081e,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: LeftEyeShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: f3da74418c1aeb8419a576c15c8f081e,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: EnnisiveShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: f3da74418c1aeb8419a576c15c8f081e,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: RightEyeShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: f3da74418c1aeb8419a576c15c8f081e,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: SolidColorShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: f3da74418c1aeb8419a576c15c8f081e,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: TransparentShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: f3da74418c1aeb8419a576c15c8f081e,
+        type: 2}
+    - target: {fileID: 4870205409715631024, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: ShaderManager
+      value: 
+      objectReference: {fileID: 6958634297984706304}
+    - target: {fileID: 4870205409715631024, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: shaderManager
+      value: 
+      objectReference: {fileID: 6958634297984706304}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631027, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_Name
+      value: AvatarSdkManagerMobileCustom
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: f11d19b383e9e94489eacb1f0b55e99b, type: 3}
+--- !u!4 &3349172091304945868 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+    type: 3}
+  m_PrefabInstance: {fileID: 7920939224514894717}
+  m_PrefabAsset: {fileID: 0}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerMobileCustom.prefab.meta b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerMobileCustom.prefab.meta
new file mode 100644
index 0000000000000000000000000000000000000000..8b1ac29069ed0bbd18a16dea77e81ef80b510199
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerMobileCustom.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: b59a8c17a333f3142966082f6ba315af
+PrefabImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerMobileDiffuse.prefab b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerMobileDiffuse.prefab
new file mode 100644
index 0000000000000000000000000000000000000000..e9846d193887d11899cd5f4388e73c2de4bee73f
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerMobileDiffuse.prefab
@@ -0,0 +1,188 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &791109286053674664
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 3077367403578354530}
+  - component: {fileID: 921951328335057785}
+  m_Layer: 0
+  m_Name: MobileDiffuseShaderManager
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &3077367403578354530
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 791109286053674664}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 3157928069488761443}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &921951328335057785
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 791109286053674664}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 637f19aef5eccc146a6ab77a1d4ab0a8, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+  DefaultShaderConfigurationInitializer: {fileID: 11400000, guid: faf5a921c93b47843a50c991e149f023,
+    type: 2}
+  FastLoadConfigurationInitializer: {fileID: 11400000, guid: faf5a921c93b47843a50c991e149f023,
+    type: 2}
+--- !u!1001 &7513487270613477842
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 0}
+    m_Modifications:
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: HairShaderConfigurationInitializer
+      value:
+      objectReference: {fileID: 11400000, guid: faf5a921c93b47843a50c991e149f023,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: SkinShaderConfigurationInitializer
+      value:
+      objectReference: {fileID: 11400000, guid: faf5a921c93b47843a50c991e149f023,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: ArrayShaderConfigurationInitializer
+      value:
+      objectReference: {fileID: 11400000, guid: faf5a921c93b47843a50c991e149f023,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: DefaultShaderConfigurationInitializer
+      value:
+      objectReference: {fileID: 11400000, guid: faf5a921c93b47843a50c991e149f023,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: LeftEyeShaderConfigurationInitializer
+      value:
+      objectReference: {fileID: 11400000, guid: faf5a921c93b47843a50c991e149f023,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: EnnisiveShaderConfigurationInitializer
+      value:
+      objectReference: {fileID: 11400000, guid: faf5a921c93b47843a50c991e149f023,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: RightEyeShaderConfigurationInitializer
+      value:
+      objectReference: {fileID: 11400000, guid: faf5a921c93b47843a50c991e149f023,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: SolidColorShaderConfigurationInitializer
+      value:
+      objectReference: {fileID: 11400000, guid: faf5a921c93b47843a50c991e149f023,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: TransparentShaderConfigurationInitializer
+      value:
+      objectReference: {fileID: 11400000, guid: faf5a921c93b47843a50c991e149f023,
+        type: 2}
+    - target: {fileID: 4870205409715631024, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: ShaderManager
+      value:
+      objectReference: {fileID: 921951328335057785}
+    - target: {fileID: 4870205409715631024, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: shaderManager
+      value:
+      objectReference: {fileID: 921951328335057785}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631027, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_Name
+      value: AvatarSdkManagerMobileDiffuse
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: f11d19b383e9e94489eacb1f0b55e99b, type: 3}
+--- !u!4 &3157928069488761443 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+    type: 3}
+  m_PrefabInstance: {fileID: 7513487270613477842}
+  m_PrefabAsset: {fileID: 0}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerMobileDiffuse.prefab.meta b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerMobileDiffuse.prefab.meta
new file mode 100644
index 0000000000000000000000000000000000000000..88e1f7622191c9104f87c6ea2e280357837dc342
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerMobileDiffuse.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: f377dd2931865db43bb1b517684f8c5d
+PrefabImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerMobileVertexLit.prefab b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerMobileVertexLit.prefab
new file mode 100644
index 0000000000000000000000000000000000000000..21e3727d428cbbaf360f2ffa6e587b72aff4218a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerMobileVertexLit.prefab
@@ -0,0 +1,186 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &21780547900519091
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 8123738165662519515}
+  - component: {fileID: 349149570267377489}
+  m_Layer: 0
+  m_Name: MobileVertexLitShaderManager
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &8123738165662519515
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 21780547900519091}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 2364608875103988729}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &349149570267377489
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 21780547900519091}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 637f19aef5eccc146a6ab77a1d4ab0a8, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  DefaultShaderConfigurationInitializer: {fileID: 11400000, guid: 2a617ea217bb9814a96034298e387a43,
+    type: 2}
+--- !u!1001 &7153604319441721416
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 0}
+    m_Modifications:
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: HairShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 2a617ea217bb9814a96034298e387a43,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: SkinShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 2a617ea217bb9814a96034298e387a43,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: ArrayShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 2a617ea217bb9814a96034298e387a43,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: DefaultShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 2a617ea217bb9814a96034298e387a43,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: LeftEyeShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 2a617ea217bb9814a96034298e387a43,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: EnnisiveShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 2a617ea217bb9814a96034298e387a43,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: RightEyeShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 2a617ea217bb9814a96034298e387a43,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: SolidColorShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 2a617ea217bb9814a96034298e387a43,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: TransparentShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 2a617ea217bb9814a96034298e387a43,
+        type: 2}
+    - target: {fileID: 4870205409715631024, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: ShaderManager
+      value: 
+      objectReference: {fileID: 349149570267377489}
+    - target: {fileID: 4870205409715631024, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: shaderManager
+      value: 
+      objectReference: {fileID: 349149570267377489}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631027, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_Name
+      value: AvatarSdkManagerMobileVertexLit
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: f11d19b383e9e94489eacb1f0b55e99b, type: 3}
+--- !u!4 &2364608875103988729 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+    type: 3}
+  m_PrefabInstance: {fileID: 7153604319441721416}
+  m_PrefabAsset: {fileID: 0}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerMobileVertexLit.prefab.meta b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerMobileVertexLit.prefab.meta
new file mode 100644
index 0000000000000000000000000000000000000000..da26f913e2136c169c193e1009ceb8ceb27f65f3
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerMobileVertexLit.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 611edcb31b449364c9f0da9990eeb114
+PrefabImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerUnityStandard.prefab b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerUnityStandard.prefab
new file mode 100644
index 0000000000000000000000000000000000000000..979b69ebc94b144ceadb65d12a1d43ad1a43263b
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerUnityStandard.prefab
@@ -0,0 +1,186 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &387860042632132540
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7782443850407810568}
+  - component: {fileID: 3930274897556587208}
+  m_Layer: 0
+  m_Name: UnityStandardShaderManager
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &7782443850407810568
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 387860042632132540}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 831623686433441900}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &3930274897556587208
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 387860042632132540}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 637f19aef5eccc146a6ab77a1d4ab0a8, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  DefaultShaderConfigurationInitializer: {fileID: 11400000, guid: 9ff896b4d4158bf4cba8a921337fe2cc,
+    type: 2}
+--- !u!1001 &5196300003615947741
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 0}
+    m_Modifications:
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: HairShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 9ff896b4d4158bf4cba8a921337fe2cc,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: SkinShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 9ff896b4d4158bf4cba8a921337fe2cc,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: ArrayShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 9ff896b4d4158bf4cba8a921337fe2cc,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: DefaultShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 9ff896b4d4158bf4cba8a921337fe2cc,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: LeftEyeShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 9ff896b4d4158bf4cba8a921337fe2cc,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: EnnisiveShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 9ff896b4d4158bf4cba8a921337fe2cc,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: RightEyeShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 9ff896b4d4158bf4cba8a921337fe2cc,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: SolidColorShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 9ff896b4d4158bf4cba8a921337fe2cc,
+        type: 2}
+    - target: {fileID: 4870205409715630990, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: TransparentShaderConfigurationInitializer
+      value: 
+      objectReference: {fileID: 11400000, guid: 9ff896b4d4158bf4cba8a921337fe2cc,
+        type: 2}
+    - target: {fileID: 4870205409715631024, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: ShaderManager
+      value: 
+      objectReference: {fileID: 3930274897556587208}
+    - target: {fileID: 4870205409715631024, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: shaderManager
+      value: 
+      objectReference: {fileID: 3930274897556587208}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4870205409715631027, guid: f11d19b383e9e94489eacb1f0b55e99b,
+        type: 3}
+      propertyPath: m_Name
+      value: AvatarSdkManagerUnityStandard
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: f11d19b383e9e94489eacb1f0b55e99b, type: 3}
+--- !u!4 &831623686433441900 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: 4870205409715631025, guid: f11d19b383e9e94489eacb1f0b55e99b,
+    type: 3}
+  m_PrefabInstance: {fileID: 5196300003615947741}
+  m_PrefabAsset: {fileID: 0}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerUnityStandard.prefab.meta b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerUnityStandard.prefab.meta
new file mode 100644
index 0000000000000000000000000000000000000000..f49943e6b0afc5fa980b4f416201e3f05d22792d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/AvatarSdkManagerUnityStandard.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 9754c32a39255a740b882d55378cf268
+PrefabImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/Cylinders.prefab b/Assets/Oculus/Avatar2/Example/Common/Prefabs/Cylinders.prefab
new file mode 100644
index 0000000000000000000000000000000000000000..4980a45386b03d21387abd604ed577c4ba2cba32
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/Cylinders.prefab
@@ -0,0 +1,656 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &3260399198182184348
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 2767280024748680137}
+  - component: {fileID: 205991014865306766}
+  - component: {fileID: 1105588657355382414}
+  m_Layer: 0
+  m_Name: Cylinder (8)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &2767280024748680137
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3260399198182184348}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 4, y: 0.2, z: 0}
+  m_LocalScale: {x: 0.50000006, y: 0.3, z: 0.50000006}
+  m_Children: []
+  m_Father: {fileID: 3943951107066628236}
+  m_RootOrder: 7
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &205991014865306766
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3260399198182184348}
+  m_Mesh: {fileID: 10206, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &1105588657355382414
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3260399198182184348}
+  m_Enabled: 1
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: d84b507c82775dc4bad5d119e5f87ddf, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!1 &4999683516332663666
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7046617233365004897}
+  - component: {fileID: 5828841236867490981}
+  - component: {fileID: 6500196842437011787}
+  m_Layer: 0
+  m_Name: Cylinder (7)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &7046617233365004897
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4999683516332663666}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 3, y: 0.2, z: 0}
+  m_LocalScale: {x: 0.50000006, y: 0.3, z: 0.50000006}
+  m_Children: []
+  m_Father: {fileID: 3943951107066628236}
+  m_RootOrder: 6
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &5828841236867490981
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4999683516332663666}
+  m_Mesh: {fileID: 10206, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &6500196842437011787
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4999683516332663666}
+  m_Enabled: 1
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: d84b507c82775dc4bad5d119e5f87ddf, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!1 &5421227137278253407
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 4015709453787875976}
+  - component: {fileID: 8154099576650245703}
+  - component: {fileID: 4282147511242937238}
+  m_Layer: 0
+  m_Name: Cylinder (6)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &4015709453787875976
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 5421227137278253407}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 2, y: 0.2, z: 0}
+  m_LocalScale: {x: 0.50000006, y: 0.3, z: 0.50000006}
+  m_Children: []
+  m_Father: {fileID: 3943951107066628236}
+  m_RootOrder: 5
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &8154099576650245703
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 5421227137278253407}
+  m_Mesh: {fileID: 10206, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &4282147511242937238
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 5421227137278253407}
+  m_Enabled: 1
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: d84b507c82775dc4bad5d119e5f87ddf, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!1 &6701987838248510037
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 6701987838248510036}
+  - component: {fileID: 6701987838248510041}
+  - component: {fileID: 6701987838248510038}
+  m_Layer: 0
+  m_Name: Cylinder (4)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &6701987838248510036
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6701987838248510037}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0.2, z: 0}
+  m_LocalScale: {x: 0.50000006, y: 0.3, z: 0.50000006}
+  m_Children: []
+  m_Father: {fileID: 3943951107066628236}
+  m_RootOrder: 3
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &6701987838248510041
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6701987838248510037}
+  m_Mesh: {fileID: 10206, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &6701987838248510038
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6701987838248510037}
+  m_Enabled: 1
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: d84b507c82775dc4bad5d119e5f87ddf, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!1 &6701987838477399489
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 6701987838477399493}
+  - component: {fileID: 6701987838477399490}
+  - component: {fileID: 6701987838477399491}
+  m_Layer: 0
+  m_Name: Cylinder (1)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &6701987838477399493
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6701987838477399489}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: -3, y: 0.2, z: 0}
+  m_LocalScale: {x: 0.50000006, y: 0.3, z: 0.50000006}
+  m_Children: []
+  m_Father: {fileID: 3943951107066628236}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &6701987838477399490
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6701987838477399489}
+  m_Mesh: {fileID: 10206, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &6701987838477399491
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6701987838477399489}
+  m_Enabled: 1
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: d84b507c82775dc4bad5d119e5f87ddf, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!1 &6701987838564840664
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 6701987838564840667}
+  - component: {fileID: 6701987838564840668}
+  - component: {fileID: 6701987838564840669}
+  m_Layer: 0
+  m_Name: Cylinder (3)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &6701987838564840667
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6701987838564840664}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: -1, y: 0.2, z: 0}
+  m_LocalScale: {x: 0.50000006, y: 0.3, z: 0.50000006}
+  m_Children: []
+  m_Father: {fileID: 3943951107066628236}
+  m_RootOrder: 2
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &6701987838564840668
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6701987838564840664}
+  m_Mesh: {fileID: 10206, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &6701987838564840669
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6701987838564840664}
+  m_Enabled: 1
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: d84b507c82775dc4bad5d119e5f87ddf, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!1 &6701987839132752085
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 6701987839132752084}
+  - component: {fileID: 6701987839132752089}
+  - component: {fileID: 6701987839132752086}
+  m_Layer: 0
+  m_Name: Cylinder (2)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &6701987839132752084
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6701987839132752085}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: -2, y: 0.2, z: 0}
+  m_LocalScale: {x: 0.50000006, y: 0.3, z: 0.50000006}
+  m_Children: []
+  m_Father: {fileID: 3943951107066628236}
+  m_RootOrder: 1
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &6701987839132752089
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6701987839132752085}
+  m_Mesh: {fileID: 10206, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &6701987839132752086
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6701987839132752085}
+  m_Enabled: 1
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: d84b507c82775dc4bad5d119e5f87ddf, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!1 &6701987839314534823
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 6701987839314534822}
+  - component: {fileID: 6701987839314534827}
+  - component: {fileID: 6701987839314534824}
+  m_Layer: 0
+  m_Name: Cylinder (5)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &6701987839314534822
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6701987839314534823}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 1, y: 0.2, z: 0}
+  m_LocalScale: {x: 0.50000006, y: 0.3, z: 0.50000006}
+  m_Children: []
+  m_Father: {fileID: 3943951107066628236}
+  m_RootOrder: 4
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &6701987839314534827
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6701987839314534823}
+  m_Mesh: {fileID: 10206, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &6701987839314534824
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6701987839314534823}
+  m_Enabled: 1
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: d84b507c82775dc4bad5d119e5f87ddf, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!1 &8334773074039846814
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 3943951107066628236}
+  m_Layer: 0
+  m_Name: Cylinders
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &3943951107066628236
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8334773074039846814}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 6701987838477399493}
+  - {fileID: 6701987839132752084}
+  - {fileID: 6701987838564840667}
+  - {fileID: 6701987838248510036}
+  - {fileID: 6701987839314534822}
+  - {fileID: 4015709453787875976}
+  - {fileID: 7046617233365004897}
+  - {fileID: 2767280024748680137}
+  m_Father: {fileID: 0}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/Cylinders.prefab.meta b/Assets/Oculus/Avatar2/Example/Common/Prefabs/Cylinders.prefab.meta
new file mode 100644
index 0000000000000000000000000000000000000000..d5f952385f17b6b5863d4994586749ba2b48921d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/Cylinders.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 548156b48d05cd54f901563c0dcc9900
+PrefabImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/DebugMaterialSpheres.prefab b/Assets/Oculus/Avatar2/Example/Common/Prefabs/DebugMaterialSpheres.prefab
new file mode 100644
index 0000000000000000000000000000000000000000..a9af3d0ab14d27fbff13e918747d1b1dacfc3061
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/DebugMaterialSpheres.prefab
@@ -0,0 +1,1184 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &1429968844493897382
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 4913926949558178524}
+  - component: {fileID: 6450076243291756979}
+  - component: {fileID: 2114616157497865716}
+  - component: {fileID: 4902055218124950221}
+  m_Layer: 0
+  m_Name: LibraryShaderBlackSphere
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &4913926949558178524
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1429968844493897382}
+  m_LocalRotation: {x: -0, y: 0.078459114, z: -0, w: 0.99691737}
+  m_LocalPosition: {x: -2, y: 1.1, z: 0}
+  m_LocalScale: {x: 0.30000004, y: 0.3, z: 0.30000004}
+  m_Children: []
+  m_Father: {fileID: 7476420626671555111}
+  m_RootOrder: 9
+  m_LocalEulerAnglesHint: {x: 0, y: 9, z: 0}
+--- !u!33 &6450076243291756979
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1429968844493897382}
+  m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &2114616157497865716
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1429968844493897382}
+  m_Enabled: 1
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RayTracingMode: 2
+  m_RayTraceProcedural: 0
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: d33c28033c9075c45b22bd0309864810, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_ReceiveGI: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+  m_AdditionalVertexStreams: {fileID: 0}
+--- !u!135 &4902055218124950221
+SphereCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1429968844493897382}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Radius: 0.5
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &2594402367435815809
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 4388526572667164740}
+  - component: {fileID: 3946719616490702974}
+  - component: {fileID: 2734084879227217204}
+  - component: {fileID: 2756646158664749789}
+  m_Layer: 0
+  m_Name: HumanShaderWhiteSphere
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &4388526572667164740
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2594402367435815809}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: -1.5, y: 1.5, z: 0}
+  m_LocalScale: {x: 0.3, y: 0.3, z: 0.3}
+  m_Children: []
+  m_Father: {fileID: 7476420626671555111}
+  m_RootOrder: 7
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &3946719616490702974
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2594402367435815809}
+  m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &2734084879227217204
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2594402367435815809}
+  m_Enabled: 1
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RayTracingMode: 2
+  m_RayTraceProcedural: 0
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: 1080b5c292ddf2b41b069305b9c8ff60, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_ReceiveGI: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+  m_AdditionalVertexStreams: {fileID: 0}
+--- !u!135 &2756646158664749789
+SphereCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2594402367435815809}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Radius: 0.5
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &4099147986509269285
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 8853897005544293067}
+  - component: {fileID: 384185964953385783}
+  - component: {fileID: 170726074698519}
+  - component: {fileID: 7009571619975162460}
+  m_Layer: 0
+  m_Name: LibraryShaderReflectiveSphere
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &8853897005544293067
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4099147986509269285}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: -2, y: 1.9, z: 0}
+  m_LocalScale: {x: 0.3, y: 0.3, z: 0.3}
+  m_Children: []
+  m_Father: {fileID: 7476420626671555111}
+  m_RootOrder: 11
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &384185964953385783
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4099147986509269285}
+  m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &170726074698519
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4099147986509269285}
+  m_Enabled: 1
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RayTracingMode: 2
+  m_RayTraceProcedural: 0
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: 7e9c68727a8d65840bc88c81063028c0, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_ReceiveGI: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+  m_AdditionalVertexStreams: {fileID: 0}
+--- !u!135 &7009571619975162460
+SphereCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4099147986509269285}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Radius: 0.5
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &6677197661007922689
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 2126142089414513952}
+  - component: {fileID: 2375620727321722406}
+  - component: {fileID: 1447839493827916782}
+  - component: {fileID: 6827283225767593428}
+  m_Layer: 0
+  m_Name: HumanShaderReflectiveSphere
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &2126142089414513952
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6677197661007922689}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: -1.5, y: 1.9, z: 0}
+  m_LocalScale: {x: 0.3, y: 0.3, z: 0.3}
+  m_Children: []
+  m_Father: {fileID: 7476420626671555111}
+  m_RootOrder: 8
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &2375620727321722406
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6677197661007922689}
+  m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &1447839493827916782
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6677197661007922689}
+  m_Enabled: 1
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RayTracingMode: 2
+  m_RayTraceProcedural: 0
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: 3ba89b7e2c4552642b5da8d4ad51fd21, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_ReceiveGI: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+  m_AdditionalVertexStreams: {fileID: 0}
+--- !u!135 &6827283225767593428
+SphereCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6677197661007922689}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Radius: 0.5
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &7476420626610665556
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7476420626610665560}
+  - component: {fileID: 7476420626610665561}
+  - component: {fileID: 7476420626610665562}
+  - component: {fileID: 7476420626610665563}
+  m_Layer: 0
+  m_Name: NativeShaderWhiteSphere
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &7476420626610665560
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7476420626610665556}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: -1, y: 1.5, z: 0}
+  m_LocalScale: {x: 0.3, y: 0.3, z: 0.3}
+  m_Children: []
+  m_Father: {fileID: 7476420626671555111}
+  m_RootOrder: 4
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &7476420626610665561
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7476420626610665556}
+  m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &7476420626610665562
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7476420626610665556}
+  m_Enabled: 1
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RayTracingMode: 2
+  m_RayTraceProcedural: 0
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: 4a71bbb7b417e2745bad57c3c62c1ecf, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_ReceiveGI: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+  m_AdditionalVertexStreams: {fileID: 0}
+--- !u!135 &7476420626610665563
+SphereCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7476420626610665556}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Radius: 0.5
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &7476420626671555104
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7476420626671555111}
+  m_Layer: 0
+  m_Name: DebugMaterialSpheres
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &7476420626671555111
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7476420626671555104}
+  m_LocalRotation: {x: 0, y: 1, z: 0, w: 0}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 7476420627304917835}
+  - {fileID: 7476420626849349159}
+  - {fileID: 7476420626843975745}
+  - {fileID: 7476420626901562413}
+  - {fileID: 7476420626610665560}
+  - {fileID: 7476420626711887578}
+  - {fileID: 543785324448328566}
+  - {fileID: 4388526572667164740}
+  - {fileID: 2126142089414513952}
+  - {fileID: 4913926949558178524}
+  - {fileID: 4604960306171975762}
+  - {fileID: 8853897005544293067}
+  m_Father: {fileID: 0}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!1 &7476420626711887574
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7476420626711887578}
+  - component: {fileID: 7476420626711887579}
+  - component: {fileID: 7476420626711887572}
+  - component: {fileID: 7476420626711887573}
+  m_Layer: 0
+  m_Name: NativeShaderReflectiveSphere
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &7476420626711887578
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7476420626711887574}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: -1, y: 1.9, z: 0}
+  m_LocalScale: {x: 0.3, y: 0.3, z: 0.3}
+  m_Children: []
+  m_Father: {fileID: 7476420626671555111}
+  m_RootOrder: 5
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &7476420626711887579
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7476420626711887574}
+  m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &7476420626711887572
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7476420626711887574}
+  m_Enabled: 1
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RayTracingMode: 2
+  m_RayTraceProcedural: 0
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: f4475b6691081d840b8d2f502a30bbc9, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_ReceiveGI: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+  m_AdditionalVertexStreams: {fileID: 0}
+--- !u!135 &7476420626711887573
+SphereCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7476420626711887574}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Radius: 0.5
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &7476420626843975805
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7476420626843975745}
+  - component: {fileID: 7476420626843975746}
+  - component: {fileID: 7476420626843975747}
+  - component: {fileID: 7476420626843975804}
+  m_Layer: 0
+  m_Name: UnityShaderReflectiveSphere
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &7476420626843975745
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7476420626843975805}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 1, y: 1.9, z: 0}
+  m_LocalScale: {x: 0.3, y: 0.3, z: 0.3}
+  m_Children: []
+  m_Father: {fileID: 7476420626671555111}
+  m_RootOrder: 2
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &7476420626843975746
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7476420626843975805}
+  m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &7476420626843975747
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7476420626843975805}
+  m_Enabled: 1
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RayTracingMode: 2
+  m_RayTraceProcedural: 0
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: 52757b3adf170174793b327af95a04e5, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_ReceiveGI: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+  m_AdditionalVertexStreams: {fileID: 0}
+--- !u!135 &7476420626843975804
+SphereCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7476420626843975805}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Radius: 0.5
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &7476420626849349155
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7476420626849349159}
+  - component: {fileID: 7476420626849349152}
+  - component: {fileID: 7476420626849349153}
+  - component: {fileID: 7476420626849349154}
+  m_Layer: 0
+  m_Name: UnityShaderWhiteSphere
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &7476420626849349159
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7476420626849349155}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 1, y: 1.5, z: 0}
+  m_LocalScale: {x: 0.3, y: 0.3, z: 0.3}
+  m_Children: []
+  m_Father: {fileID: 7476420626671555111}
+  m_RootOrder: 1
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &7476420626849349152
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7476420626849349155}
+  m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &7476420626849349153
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7476420626849349155}
+  m_Enabled: 1
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RayTracingMode: 2
+  m_RayTraceProcedural: 0
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: d45197c39ccdb6b4780696b65fd88edd, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_ReceiveGI: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+  m_AdditionalVertexStreams: {fileID: 0}
+--- !u!135 &7476420626849349154
+SphereCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7476420626849349155}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Radius: 0.5
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &7476420626901562409
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7476420626901562413}
+  - component: {fileID: 7476420626901562414}
+  - component: {fileID: 7476420626901562415}
+  - component: {fileID: 7476420626901562408}
+  m_Layer: 0
+  m_Name: NativeShaderBlackSphere
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &7476420626901562413
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7476420626901562409}
+  m_LocalRotation: {x: -0, y: 0.078459114, z: -0, w: 0.99691737}
+  m_LocalPosition: {x: -1, y: 1.1, z: 0}
+  m_LocalScale: {x: 0.30000004, y: 0.3, z: 0.30000004}
+  m_Children: []
+  m_Father: {fileID: 7476420626671555111}
+  m_RootOrder: 3
+  m_LocalEulerAnglesHint: {x: 0, y: 9, z: 0}
+--- !u!33 &7476420626901562414
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7476420626901562409}
+  m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &7476420626901562415
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7476420626901562409}
+  m_Enabled: 1
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RayTracingMode: 2
+  m_RayTraceProcedural: 0
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: a08ecf32b5ff4fd40a12fde17ee2115c, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_ReceiveGI: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+  m_AdditionalVertexStreams: {fileID: 0}
+--- !u!135 &7476420626901562408
+SphereCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7476420626901562409}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Radius: 0.5
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &7476420627304917831
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7476420627304917835}
+  - component: {fileID: 7476420627304917828}
+  - component: {fileID: 7476420627304917829}
+  - component: {fileID: 7476420627304917830}
+  m_Layer: 0
+  m_Name: UnityShaderBlackSphere
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &7476420627304917835
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7476420627304917831}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 1, y: 1.1, z: 0}
+  m_LocalScale: {x: 0.3, y: 0.3, z: 0.3}
+  m_Children: []
+  m_Father: {fileID: 7476420626671555111}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &7476420627304917828
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7476420627304917831}
+  m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &7476420627304917829
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7476420627304917831}
+  m_Enabled: 1
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RayTracingMode: 2
+  m_RayTraceProcedural: 0
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: 744db510507179846b06b076113095f9, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_ReceiveGI: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+  m_AdditionalVertexStreams: {fileID: 0}
+--- !u!135 &7476420627304917830
+SphereCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7476420627304917831}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Radius: 0.5
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &8192451115302698165
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 4604960306171975762}
+  - component: {fileID: 7825886207565529144}
+  - component: {fileID: 2440301124571515280}
+  - component: {fileID: 4480133345366603063}
+  m_Layer: 0
+  m_Name: LibraryShaderWhiteSphere
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &4604960306171975762
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8192451115302698165}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: -2, y: 1.5, z: 0}
+  m_LocalScale: {x: 0.3, y: 0.3, z: 0.3}
+  m_Children: []
+  m_Father: {fileID: 7476420626671555111}
+  m_RootOrder: 10
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &7825886207565529144
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8192451115302698165}
+  m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &2440301124571515280
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8192451115302698165}
+  m_Enabled: 1
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RayTracingMode: 2
+  m_RayTraceProcedural: 0
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: 5dd56d05a833e5849b1abd35b6b2f52f, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_ReceiveGI: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+  m_AdditionalVertexStreams: {fileID: 0}
+--- !u!135 &4480133345366603063
+SphereCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8192451115302698165}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Radius: 0.5
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &8629138492331227961
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 543785324448328566}
+  - component: {fileID: 7510885712512318543}
+  - component: {fileID: 164297660748313471}
+  - component: {fileID: 1321433623507210976}
+  m_Layer: 0
+  m_Name: HumanShaderBlackSphere
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &543785324448328566
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8629138492331227961}
+  m_LocalRotation: {x: -0, y: 0.078459114, z: -0, w: 0.99691737}
+  m_LocalPosition: {x: -1.5, y: 1.1, z: 0}
+  m_LocalScale: {x: 0.30000004, y: 0.3, z: 0.30000004}
+  m_Children: []
+  m_Father: {fileID: 7476420626671555111}
+  m_RootOrder: 6
+  m_LocalEulerAnglesHint: {x: 0, y: 9, z: 0}
+--- !u!33 &7510885712512318543
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8629138492331227961}
+  m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &164297660748313471
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8629138492331227961}
+  m_Enabled: 1
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RayTracingMode: 2
+  m_RayTraceProcedural: 0
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: bf6bfd4ed28761045ac7d5bb051beb7c, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_ReceiveGI: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+  m_AdditionalVertexStreams: {fileID: 0}
+--- !u!135 &1321433623507210976
+SphereCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8629138492331227961}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Radius: 0.5
+  m_Center: {x: 0, y: 0, z: 0}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/DebugMaterialSpheres.prefab.meta b/Assets/Oculus/Avatar2/Example/Common/Prefabs/DebugMaterialSpheres.prefab.meta
new file mode 100644
index 0000000000000000000000000000000000000000..c49a4575f0185142fe26889410b41e00eada7357
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/DebugMaterialSpheres.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: d155ba4d794ec3f4cb14a1ff300b9b84
+PrefabImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/LipSyncInput.prefab b/Assets/Oculus/Avatar2/Example/Common/Prefabs/LipSyncInput.prefab
new file mode 100644
index 0000000000000000000000000000000000000000..37bd319532acaa259face6c1d439e48d0e20053a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/LipSyncInput.prefab
@@ -0,0 +1,170 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &2430523750807860354
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 2430523750807860366}
+  - component: {fileID: 2430523750807860365}
+  - component: {fileID: 2430523750807860367}
+  - component: {fileID: 2430523750807860364}
+  m_Layer: 0
+  m_Name: LipSyncInput
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &2430523750807860366
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2430523750807860354}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 0}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!82 &2430523750807860365
+AudioSource:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2430523750807860354}
+  m_Enabled: 1
+  serializedVersion: 4
+  OutputAudioMixerGroup: {fileID: 0}
+  m_audioClip: {fileID: 0}
+  m_PlayOnAwake: 1
+  m_Volume: 1
+  m_Pitch: 1
+  Loop: 1
+  Mute: 0
+  Spatialize: 0
+  SpatializePostEffects: 0
+  Priority: 128
+  DopplerLevel: 1
+  MinDistance: 1
+  MaxDistance: 500
+  Pan2D: 0
+  rolloffMode: 0
+  BypassEffects: 0
+  BypassListenerEffects: 0
+  BypassReverbZones: 0
+  rolloffCustomCurve:
+    serializedVersion: 2
+    m_Curve:
+    - serializedVersion: 3
+      time: 0
+      value: 1
+      inSlope: 0
+      outSlope: 0
+      tangentMode: 0
+      weightedMode: 0
+      inWeight: 0.33333334
+      outWeight: 0.33333334
+    - serializedVersion: 3
+      time: 1
+      value: 0
+      inSlope: 0
+      outSlope: 0
+      tangentMode: 0
+      weightedMode: 0
+      inWeight: 0.33333334
+      outWeight: 0.33333334
+    m_PreInfinity: 2
+    m_PostInfinity: 2
+    m_RotationOrder: 4
+  panLevelCustomCurve:
+    serializedVersion: 2
+    m_Curve:
+    - serializedVersion: 3
+      time: 0
+      value: 0
+      inSlope: 0
+      outSlope: 0
+      tangentMode: 0
+      weightedMode: 0
+      inWeight: 0.33333334
+      outWeight: 0.33333334
+    m_PreInfinity: 2
+    m_PostInfinity: 2
+    m_RotationOrder: 4
+  spreadCustomCurve:
+    serializedVersion: 2
+    m_Curve:
+    - serializedVersion: 3
+      time: 0
+      value: 0
+      inSlope: 0
+      outSlope: 0
+      tangentMode: 0
+      weightedMode: 0
+      inWeight: 0.33333334
+      outWeight: 0.33333334
+    m_PreInfinity: 2
+    m_PostInfinity: 2
+    m_RotationOrder: 4
+  reverbZoneMixCustomCurve:
+    serializedVersion: 2
+    m_Curve:
+    - serializedVersion: 3
+      time: 0
+      value: 1
+      inSlope: 0
+      outSlope: 0
+      tangentMode: 0
+      weightedMode: 0
+      inWeight: 0.33333334
+      outWeight: 0.33333334
+    m_PreInfinity: 2
+    m_PostInfinity: 2
+    m_RotationOrder: 4
+--- !u!114 &2430523750807860367
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2430523750807860354}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: a509dbf839e0e4044a7affab5b5a3e55, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _audioSourceType: 1
+  CaptureAudio: 1
+  _mode: 0
+  _smoothing: 0
+  _audioSampleRate: 48000
+--- !u!114 &2430523750807860364
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2430523750807860354}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: cbe2c8f3d0dd0c24aa44600b74586d5c, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _audioSource: {fileID: 2430523750807860365}
+  _lipSyncContext: {fileID: 2430523750807860367}
+  _micInputVolume: 1
+  _micFrequency: 48000
+  _micInputMode: 2
+  _inputButtonName: 
+  _stopRecordingWhilePaused: 1
+  _stopRecordingWhileUnfocused: 1
+  _micCaptureTimeout: 500
+  _preferOculusMic: 1
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/LipSyncInput.prefab.meta b/Assets/Oculus/Avatar2/Example/Common/Prefabs/LipSyncInput.prefab.meta
new file mode 100644
index 0000000000000000000000000000000000000000..acc021418dfc423d2c3bf13f3e904fce0c60d703
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/LipSyncInput.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: c019572605bc29e42b3ed5a058599618
+PrefabImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/Rendering.meta b/Assets/Oculus/Avatar2/Example/Common/Prefabs/Rendering.meta
new file mode 100644
index 0000000000000000000000000000000000000000..b7c4d5061c070c7ec8d9dde354c6acbecb7b5a53
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/Rendering.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: d61cf04ecfb105d429a698baddfd12c8
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/Rendering/AVATAR_LOD_DEBUG_CANVAS.prefab b/Assets/Oculus/Avatar2/Example/Common/Prefabs/Rendering/AVATAR_LOD_DEBUG_CANVAS.prefab
new file mode 100644
index 0000000000000000000000000000000000000000..1d698d9dda43f660795b76e1e4e4ff1c9f9f2029
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/Rendering/AVATAR_LOD_DEBUG_CANVAS.prefab
@@ -0,0 +1,196 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1001 &100100000
+Prefab:
+  m_ObjectHideFlags: 1
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 0}
+    m_Modifications: []
+    m_RemovedComponents: []
+  m_ParentPrefab: {fileID: 0}
+  m_RootGameObject: {fileID: 1316326085530136}
+  m_IsPrefabParent: 1
+--- !u!1 &1316326085530136
+GameObject:
+  m_ObjectHideFlags: 0
+  m_PrefabParentObject: {fileID: 0}
+  m_PrefabInternal: {fileID: 100100000}
+  serializedVersion: 5
+  m_Component:
+  - component: {fileID: 224735513760207478}
+  - component: {fileID: 223132316841687860}
+  - component: {fileID: 114526456786137880}
+  - component: {fileID: 114437660788766970}
+  - component: {fileID: 114618138934430970}
+  m_Layer: 5
+  m_Name: AVATAR_LOD_DEBUG_CANVAS
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!1 &1566992820618488
+GameObject:
+  m_ObjectHideFlags: 0
+  m_PrefabParentObject: {fileID: 0}
+  m_PrefabInternal: {fileID: 100100000}
+  serializedVersion: 5
+  m_Component:
+  - component: {fileID: 224023122903327426}
+  - component: {fileID: 222766432161329816}
+  - component: {fileID: 114832124881084736}
+  m_Layer: 5
+  m_Name: LOD
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!114 &114437660788766970
+MonoBehaviour:
+  m_ObjectHideFlags: 1
+  m_PrefabParentObject: {fileID: 0}
+  m_PrefabInternal: {fileID: 100100000}
+  m_GameObject: {fileID: 1316326085530136}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 1301386320, guid: f70555f144d8491a825f0804e09c671c, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_IgnoreReversedGraphics: 1
+  m_BlockingObjects: 0
+  m_BlockingMask:
+    serializedVersion: 2
+    m_Bits: 4294967295
+--- !u!114 &114526456786137880
+MonoBehaviour:
+  m_ObjectHideFlags: 1
+  m_PrefabParentObject: {fileID: 0}
+  m_PrefabInternal: {fileID: 100100000}
+  m_GameObject: {fileID: 1316326085530136}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 1980459831, guid: f70555f144d8491a825f0804e09c671c, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_UiScaleMode: 0
+  m_ReferencePixelsPerUnit: 100
+  m_ScaleFactor: 1
+  m_ReferenceResolution: {x: 800, y: 600}
+  m_ScreenMatchMode: 0
+  m_MatchWidthOrHeight: 0
+  m_PhysicalUnit: 3
+  m_FallbackScreenDPI: 96
+  m_DefaultSpriteDPI: 96
+  m_DynamicPixelsPerUnit: 10
+--- !u!114 &114618138934430970
+MonoBehaviour:
+  m_ObjectHideFlags: 1
+  m_PrefabParentObject: {fileID: 0}
+  m_PrefabInternal: {fileID: 100100000}
+  m_GameObject: {fileID: 1316326085530136}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 56032097b1ab3a540b3349af62f82310, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  lookatCamera: 1
+  canvasXform: {fileID: 0}
+  camXform: {fileID: 0}
+--- !u!114 &114832124881084736
+MonoBehaviour:
+  m_ObjectHideFlags: 1
+  m_PrefabParentObject: {fileID: 0}
+  m_PrefabInternal: {fileID: 100100000}
+  m_GameObject: {fileID: 1566992820618488}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_Material: {fileID: 0}
+  m_Color: {r: 0, g: 1, b: 0.0004093647, a: 1}
+  m_RaycastTarget: 1
+  m_OnCullStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+    m_TypeName: UnityEngine.UI.MaskableGraphic+CullStateChangedEvent, UnityEngine.UI,
+      Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
+  m_FontData:
+    m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
+    m_FontSize: 40
+    m_FontStyle: 1
+    m_BestFit: 0
+    m_MinSize: 1
+    m_MaxSize: 40
+    m_Alignment: 7
+    m_AlignByGeometry: 0
+    m_RichText: 1
+    m_HorizontalOverflow: 1
+    m_VerticalOverflow: 1
+    m_LineSpacing: 1
+  m_Text: 'LOD: 1'
+--- !u!222 &222766432161329816
+CanvasRenderer:
+  m_ObjectHideFlags: 1
+  m_PrefabParentObject: {fileID: 0}
+  m_PrefabInternal: {fileID: 100100000}
+  m_GameObject: {fileID: 1566992820618488}
+--- !u!223 &223132316841687860
+Canvas:
+  m_ObjectHideFlags: 1
+  m_PrefabParentObject: {fileID: 0}
+  m_PrefabInternal: {fileID: 100100000}
+  m_GameObject: {fileID: 1316326085530136}
+  m_Enabled: 1
+  serializedVersion: 3
+  m_RenderMode: 2
+  m_Camera: {fileID: 0}
+  m_PlaneDistance: 100
+  m_PixelPerfect: 0
+  m_ReceivesEvents: 1
+  m_OverrideSorting: 0
+  m_OverridePixelPerfect: 0
+  m_SortingBucketNormalizedSize: 0
+  m_AdditionalShaderChannelsFlag: 0
+  m_SortingLayerID: 1982328483
+  m_SortingOrder: 1
+  m_TargetDisplay: 0
+--- !u!224 &224023122903327426
+RectTransform:
+  m_ObjectHideFlags: 1
+  m_PrefabParentObject: {fileID: 0}
+  m_PrefabInternal: {fileID: 100100000}
+  m_GameObject: {fileID: 1566992820618488}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 224735513760207478}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+  m_AnchorMin: {x: 0.5, y: 0.5}
+  m_AnchorMax: {x: 0.5, y: 0.5}
+  m_AnchoredPosition: {x: 0, y: 0}
+  m_SizeDelta: {x: 100, y: 100}
+  m_Pivot: {x: 0.5, y: 0.5}
+--- !u!224 &224735513760207478
+RectTransform:
+  m_ObjectHideFlags: 1
+  m_PrefabParentObject: {fileID: 0}
+  m_PrefabInternal: {fileID: 100100000}
+  m_GameObject: {fileID: 1316326085530136}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: -0.01, y: 0.01, z: 0.01}
+  m_Children:
+  - {fileID: 224023122903327426}
+  m_Father: {fileID: 0}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+  m_AnchorMin: {x: 0, y: 0}
+  m_AnchorMax: {x: 0, y: 0}
+  m_AnchoredPosition: {x: 0, y: 0}
+  m_SizeDelta: {x: 50, y: 50}
+  m_Pivot: {x: 0.5, y: 0}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Prefabs/Rendering/AVATAR_LOD_DEBUG_CANVAS.prefab.meta b/Assets/Oculus/Avatar2/Example/Common/Prefabs/Rendering/AVATAR_LOD_DEBUG_CANVAS.prefab.meta
new file mode 100644
index 0000000000000000000000000000000000000000..50f29aff85702a9d001facf87440ad2f33966f99
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Prefabs/Rendering/AVATAR_LOD_DEBUG_CANVAS.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 6c859d5f04a0b2d478aeb2732044266c
+PrefabImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Scripts.meta b/Assets/Oculus/Avatar2/Example/Common/Scripts.meta
new file mode 100644
index 0000000000000000000000000000000000000000..776c0f141641ebc941507e7a8d4dcd8e65ff964f
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Scripts.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: a7c28aca71495fc43ac46d3eb774e655
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Scripts/DelayPermissionRequest.cs b/Assets/Oculus/Avatar2/Example/Common/Scripts/DelayPermissionRequest.cs
new file mode 100644
index 0000000000000000000000000000000000000000..db610b4e1a78c3c165d296cd5df200877bf9cf86
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Scripts/DelayPermissionRequest.cs
@@ -0,0 +1,31 @@
+#if USING_XR_MANAGEMENT && USING_XR_SDK_OCULUS && !OVRPLUGIN_UNSUPPORTED_PLATFORM
+#define USING_XR_SDK
+#endif
+
+#if USING_XR_SDK
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace Oculus.Avatar2
+{
+    public class DelayPermissionRequest : MonoBehaviour
+    {
+        // Start is called before the first frame update
+        void Start()
+        {
+            OvrAvatarManager.Instance.automaticallyRequestPermissions = false;
+        }
+
+        // Update is called once per frame
+        void Update()
+        {
+
+            if (OVRInput.Get(OVRInput.Button.Two))
+            {
+                OvrAvatarManager.Instance.EnablePermissionRequests();
+            }
+        }
+    }
+}
+#endif
diff --git a/Assets/Oculus/Avatar2/Example/Common/Scripts/DelayPermissionRequest.cs.meta b/Assets/Oculus/Avatar2/Example/Common/Scripts/DelayPermissionRequest.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..a9b6b7f327a600d5c02be38c7c181c345c26e55f
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Scripts/DelayPermissionRequest.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 955717a3186a0214fb8f8f0b6ce0f4a6
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Scripts/LipSyncMicInput.cs b/Assets/Oculus/Avatar2/Example/Common/Scripts/LipSyncMicInput.cs
new file mode 100644
index 0000000000000000000000000000000000000000..d37f608437e80b8d211a3790a59ea9ddd95f4912
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Scripts/LipSyncMicInput.cs
@@ -0,0 +1,335 @@
+using System;
+using System.Collections;
+using System.Diagnostics;
+using System.Threading;
+using Oculus.Avatar2;
+using UnityEngine;
+using Debug = UnityEngine.Debug;
+
+#if UNITY_ANDROID && !UNITY_EDITOR
+using UnityEngine.Android;
+#endif
+
+
+public enum LipSyncMicInputMode
+{
+    HoldToSpeak, // Constantly hold a button to enable the mic
+    PushToSpeak, // Press a button to enable the mic; press again to disable
+    ConstantSpeak, // Mic is always on
+    Manual // Call StartMicrophone and StopMicrophone to control externally
+}
+
+[RequireComponent(typeof(AudioSource))]
+public class LipSyncMicInput : MonoBehaviour
+{
+    private const string logScope = "micInput";
+
+    // Serialized Members
+
+    [Tooltip("Manual specification of Audio Source. Default will use any attached to the same object.")]
+    [SerializeField]
+    private AudioSource _audioSource;
+
+    [Range(0f, 1f)]
+    [Tooltip("Microphone input volume control.")]
+    [SerializeField]
+    private float _micInputVolume = 1f;
+
+    [SerializeField]
+    private LipSyncMicInputMode _micInputMode = LipSyncMicInputMode.ConstantSpeak;
+
+    [Tooltip("Button name used to drive HoldToSpeak and PushToSpeak methods of control.")]
+    [SerializeField]
+    private string _inputButtonName = "";
+
+    [SerializeField]
+    private bool _stopRecordingWhilePaused = true;
+    [SerializeField]
+    private bool _stopRecordingWhileUnfocused = true;
+
+    [Tooltip("The max amount of time to wait for mic recording to start.")]
+    [SerializeField]
+    private float _micCaptureTimeout = 5f;
+
+    [SerializeField]
+    private bool _preferOculusMic = true;
+
+    // Public Properties
+
+    public AudioSource audioSource => _audioSource;
+    public int MicFrequency => _micFrequency;
+
+    public float MicInputVolume
+    {
+        get { return _micInputVolume; }
+        set
+        {
+            _micInputVolume = Mathf.Clamp01(value);
+            _audioSource.volume = _micInputVolume;
+        }
+    }
+
+    public bool micSelected => _selectedDevice?.Length > 0;
+
+    // Active will be true whenever the mic should be recording according to the InputMode
+    // Active can still be true when the mic is not recording due to lack of focus or because the app is paused
+    public bool active { get; private set; }
+
+    // Other Private members
+
+    private bool _initialized;
+    private bool _askingPermission;
+    private int _micFrequency = 48000;
+    private bool _focused = true;
+    private bool _paused;
+    private string _selectedDevice;
+    private int _minFreq, _maxFreq;
+
+    // Core Private Functions
+
+    private void Awake()
+    {
+        if (_audioSource == null)
+        {
+            _audioSource = GetComponent<AudioSource>();
+        }
+    }
+
+    private void Start()
+    {
+        _audioSource.loop = true;
+        _audioSource.mute = false;
+        _audioSource.volume = _micInputVolume;
+    }
+
+    private void Update()
+    {
+        // Lazy Microphone initialization (needed on Android)
+        if (!_initialized)
+        {
+#if UNITY_ANDROID && !UNITY_EDITOR
+            if( Permission.HasUserAuthorizedPermission("android.permission.RECORD_AUDIO") )
+            {
+                InitializeMicrophone();
+            }
+            else if(!_askingPermission)
+            {
+                _askingPermission = true;
+
+                OvrAvatarManager.Instance.RequestMicPermission();
+                return;
+            }
+            else
+            {
+                return;
+            }
+#else
+            InitializeMicrophone();
+#endif
+        }
+
+        ProcessMicActivity();
+    }
+
+    // Events
+
+    private void OnApplicationFocus(bool focus)
+    {
+        _focused = focus;
+        if (!_focused && _stopRecordingWhileUnfocused) StopMicrophone_Internal();
+    }
+
+    private void OnApplicationPause(bool pauseStatus)
+    {
+        _paused = pauseStatus;
+        if (_paused && _stopRecordingWhilePaused) StopMicrophone_Internal();
+    }
+
+    // Other Private Functions
+
+    private void InitializeMicrophone()
+    {
+#if !OVR_DISABLE_MICROPHONE
+        if (_initialized) return;
+        if (Microphone.devices.Length == 0) return;
+
+        _selectedDevice = Microphone.devices[0];
+        if (_preferOculusMic)
+        {
+            foreach (var device in Microphone.devices)
+            {
+                if (device.Contains("Oculus") || device.Contains("Rift"))
+                {
+                    _selectedDevice = device;
+                    break;
+                }
+            }
+        }
+        Debug.Log($"Selected microphone {_selectedDevice}");
+
+        GetMicCaps();
+
+        _initialized = true;
+#endif
+    }
+
+    private void ProcessMicActivity()
+    {
+#if !OVR_DISABLE_MICROPHONE
+        //Hold To Speak
+        if (_micInputMode == LipSyncMicInputMode.HoldToSpeak)
+        {
+            active = Input.GetButton(_inputButtonName);
+        }
+        //Push To Talk
+        else if (_micInputMode == LipSyncMicInputMode.PushToSpeak)
+        {
+            if (Input.GetButtonDown(_inputButtonName))
+            {
+                active = !active;
+            }
+        }
+        //Constant Speak
+        else if (_micInputMode == LipSyncMicInputMode.ConstantSpeak)
+        {
+            active = true;
+        }
+
+        // Activation
+        if (active)
+        {
+            if (CanStartMic() && !Microphone.IsRecording(_selectedDevice))
+            {
+                StartMicrophone_Internal();
+            }
+        }
+        else if (Microphone.IsRecording(_selectedDevice))
+        {
+            StopMicrophone_Internal();
+        }
+#endif
+    }
+
+    private void GetMicCaps()
+    {
+#if !OVR_DISABLE_MICROPHONE
+        if (micSelected == false) return;
+
+        //Gets the frequency of the device
+        Microphone.GetDeviceCaps(_selectedDevice, out _minFreq, out _maxFreq);
+
+        if (_minFreq == 0 && _maxFreq == 0)
+        {
+            Debug.Log("OvrAvatarLipSyncMicInput: GetMicCaps warning - min and max frequencies are 0");
+            _minFreq = 48000;
+            _maxFreq = 48000;
+        }
+
+        if (_micFrequency > _maxFreq)
+        {
+            _micFrequency = _maxFreq;
+        }
+#endif
+    }
+
+    private bool CanStartMic()
+    {
+        if (!micSelected) return false;
+        if (!_focused && _stopRecordingWhileUnfocused) return false;
+        if (_paused && _stopRecordingWhilePaused) return false;
+        return true;
+    }
+
+    private void StartMicrophone_Internal()
+    {
+#if !OVR_DISABLE_MICROPHONE
+        Debug.Log($"Starting microphone recording with frequency {_micFrequency}");
+
+        //Starts recording
+        _audioSource.clip = Microphone.Start(_selectedDevice, true, 10, _micFrequency);
+        _audioSource.loop = true;
+
+        Stopwatch timer = Stopwatch.StartNew();
+
+        // Wait until the recording has started
+        while (!(Microphone.GetPosition(_selectedDevice) > 0) && timer.Elapsed.TotalMilliseconds < _micCaptureTimeout)
+        {
+            Thread.Sleep(5);
+        }
+
+        var samplesRecorded = Microphone.GetPosition(_selectedDevice);
+        if (samplesRecorded <= 0)
+        {
+            throw new Exception("Timeout initializing microphone " + _selectedDevice);
+        }
+
+        // Play the audio source
+        var latency = (float)samplesRecorded / _micFrequency;
+        Debug.Log($"Microphone recording started with latency {latency * 1000.0} ms");
+        _audioSource.Play();
+#endif
+    }
+
+    private void StopMicrophone_Internal()
+    {
+#if !OVR_DISABLE_MICROPHONE
+        if (micSelected == false) return;
+
+        Debug.Log($"Stopping microphone recording");
+
+        // Don't stop the audio source if it is overridden with a clip to play
+        if (_audioSource != null &&
+            _audioSource.clip != null &&
+            _audioSource.clip.name == "Microphone")
+        {
+            _audioSource.Stop();
+        }
+
+        Microphone.End(_selectedDevice);
+#endif
+    }
+
+    //:: Public Functions
+
+    public void SetMode(LipSyncMicInputMode newMode)
+    {
+        _micInputMode = newMode;
+        active = false;
+
+        if (_initialized) ProcessMicActivity();
+    }
+
+    public void StartMicrophone()
+    {
+#if !OVR_DISABLE_MICROPHONE
+        if (_micInputMode != LipSyncMicInputMode.Manual)
+        {
+            Debug.LogWarning("Starting Microphone while not in Manual Input Mode.");
+        }
+
+        active = true;
+
+        if (CanStartMic() && !Microphone.IsRecording(_selectedDevice))
+        {
+            StartMicrophone_Internal();
+        }
+#endif
+    }
+
+    public void StopMicrophone()
+    {
+#if !OVR_DISABLE_MICROPHONE
+        if (_micInputMode != LipSyncMicInputMode.Manual)
+        {
+            Debug.LogWarning("Starting Microphone while not in Manual Input Mode.");
+        }
+
+        active = false;
+
+        if (Microphone.IsRecording(_selectedDevice))
+        {
+            StopMicrophone_Internal();
+        }
+#endif
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Scripts/LipSyncMicInput.cs.meta b/Assets/Oculus/Avatar2/Example/Common/Scripts/LipSyncMicInput.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..e779680a16a15fb30417f3a9d084206d4f52de2c
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Scripts/LipSyncMicInput.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: cbe2c8f3d0dd0c24aa44600b74586d5c
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Scripts/OpenAvatarEditor.cs b/Assets/Oculus/Avatar2/Example/Common/Scripts/OpenAvatarEditor.cs
new file mode 100644
index 0000000000000000000000000000000000000000..c368fece81b54d7c4bc6c90ca98b0eeda3811e8a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Scripts/OpenAvatarEditor.cs
@@ -0,0 +1,22 @@
+#if USING_XR_MANAGEMENT && USING_XR_SDK_OCULUS && !OVRPLUGIN_UNSUPPORTED_PLATFORM
+#define USING_XR_SDK
+#endif
+
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+public class OpenAvatarEditor : MonoBehaviour
+{
+    // Update is called once per frame
+    void Update()
+    {
+#if USING_XR_SDK
+        // Button Press
+        if (OVRInput.GetDown(OVRInput.Button.Start, OVRInput.Controller.LTouch | OVRInput.Controller.LHand))
+        {
+            AvatarEditorDeeplink.LaunchAvatarEditor();
+        }
+#endif
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Scripts/OpenAvatarEditor.cs.meta b/Assets/Oculus/Avatar2/Example/Common/Scripts/OpenAvatarEditor.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..69ae088b132ca79324e9acd0fe5b6ccabd2c1206
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Scripts/OpenAvatarEditor.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: df522fdce5f894b4f986a3489491b7de
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Scripts/OvrPlatformInit.cs b/Assets/Oculus/Avatar2/Example/Common/Scripts/OvrPlatformInit.cs
new file mode 100644
index 0000000000000000000000000000000000000000..fb8e3cc2b38dd565a8d9104a701838c2af7139fe
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Scripts/OvrPlatformInit.cs
@@ -0,0 +1,96 @@
+#if USING_XR_MANAGEMENT && USING_XR_SDK_OCULUS && !OVRPLUGIN_UNSUPPORTED_PLATFORM
+#define USING_XR_SDK
+#endif
+
+#if USING_XR_SDK
+
+using System;
+using Oculus.Avatar2;
+using Oculus.Platform;
+using Oculus.Platform.Models;
+
+public enum OvrPlatformInitStatus
+{
+    NotStarted = 0,
+    Initializing,
+    Succeeded,
+    Failed
+}
+
+public static class OvrPlatformInit
+{
+    private const string logScope = "examplePlatformInit";
+
+    public static OvrPlatformInitStatus status { get; private set; } = OvrPlatformInitStatus.NotStarted;
+
+    public static void InitializeOvrPlatform()
+    {
+        if (status == OvrPlatformInitStatus.Succeeded)
+        {
+            OvrAvatarLog.LogWarning("OvrPlatform is already initialized.", logScope);
+            return;
+        }
+
+        try
+        {
+            status = OvrPlatformInitStatus.Initializing;
+            Core.AsyncInitialize().OnComplete(InitializeComplete);
+
+            void InitializeComplete(Message<PlatformInitialize> msg)
+            {
+                if (msg.Data.Result != PlatformInitializeResult.Success)
+                {
+                    status = OvrPlatformInitStatus.Failed;
+                    OvrAvatarLog.LogError("Failed to initialize OvrPlatform", logScope);
+                }
+                else
+                {
+                    Entitlements.IsUserEntitledToApplication().OnComplete(CheckEntitlement);
+                }
+            }
+
+            void CheckEntitlement(Message msg)
+            {
+                if (msg.IsError == false)
+                {
+                    Users.GetAccessToken().OnComplete(GetAccessTokenComplete);
+                }
+                else
+                {
+                    status = OvrPlatformInitStatus.Failed;
+                    var e = msg.GetError();
+                    OvrAvatarLog.LogError($"Failed entitlement check: {e.Code} - {e.Message}", logScope);
+                }
+            }
+
+            void GetAccessTokenComplete(Message<string> msg)
+            {
+                if (String.IsNullOrEmpty(msg.Data))
+                {
+                    string output = "Token is null or empty.";
+                    if (msg.IsError)
+                    {
+                        var e = msg.GetError();
+                        output = $"{e.Code} - {e.Message}";
+                    }
+
+                    status = OvrPlatformInitStatus.Failed;
+                    OvrAvatarLog.LogError($"Failed to retrieve access token: {output}", logScope);
+                }
+                else
+                {
+                    OvrAvatarLog.LogDebug($"Successfully retrieved access token.", logScope);
+                    OvrAvatarEntitlement.SetAccessToken(msg.Data);
+                    status = OvrPlatformInitStatus.Succeeded;
+                }
+            }
+        }
+        catch (Exception e)
+        {
+            status = OvrPlatformInitStatus.Failed;
+            OvrAvatarLog.LogError($"{e.Message}\n{e.StackTrace}", logScope);
+        }
+    }
+}
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Example/Common/Scripts/OvrPlatformInit.cs.meta b/Assets/Oculus/Avatar2/Example/Common/Scripts/OvrPlatformInit.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..909cc1c8193e41c16a36459e803ec230f00b3575
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Scripts/OvrPlatformInit.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 7ad9a3af04821bd4daf4a7324abb80e2
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleAvatarEntity.cs b/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleAvatarEntity.cs
new file mode 100644
index 0000000000000000000000000000000000000000..0e1ab3cad18595692ac96126409e8bb538ee6cdf
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleAvatarEntity.cs
@@ -0,0 +1,721 @@
+#if USING_XR_MANAGEMENT && USING_XR_SDK_OCULUS && !OVRPLUGIN_UNSUPPORTED_PLATFORM
+#define USING_XR_SDK
+#endif
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics;
+using Oculus.Avatar2;
+#if USING_XR_SDK
+using System.Reflection;
+using Oculus.Platform;
+#endif
+using UnityEngine;
+using CAPI = Oculus.Avatar2.CAPI;
+#if UNITY_EDITOR
+using UnityEditor;
+#endif
+
+public class SampleAvatarEntity : OvrAvatarEntity
+{
+    private const string logScope = "sampleAvatar";
+    public enum AssetSource
+    {
+        /// Load from one of the preloaded .zip files
+        Zip,
+
+        /// Load a loose glb file directly from StreamingAssets
+        StreamingAssets,
+    }
+
+    [System.Serializable]
+    private struct AssetData
+    {
+        public AssetSource source;
+        public string path;
+    }
+
+    [Header("Sample Avatar Entity")]
+    [Tooltip("Attempt to load the Avatar model file from the Content Delivery Network (CDN) based on a userID, as opposed to loading from disc.")]
+    [SerializeField]
+    private bool _loadUserFromCdn = true;
+
+    [Tooltip("Make initial requests for avatar and then defer loading until other avatars can make their requests.")]
+    [SerializeField]
+    private bool _deferLoading = false;
+
+    [Header("Assets")]
+    [Tooltip("Asset paths to load, and whether each asset comes from a preloaded zip file or directly from StreamingAssets. See Preset Asset settings on OvrAvatarManager for how this maps to the real file name.")]
+    [SerializeField]
+    private List<AssetData> _assets = new List<AssetData> { new AssetData { source = AssetSource.Zip, path = "0" } };
+
+    [Tooltip("Adds an underscore between the path and the postfix.")]
+    [SerializeField]
+    private bool _underscorePostfix = true;
+
+    [Tooltip("Filename Postfix (WARNING: Typically the postfix is Platform specific, such as \"_rift.glb\")")]
+    [SerializeField]
+    private string _overridePostfix = String.Empty;
+
+    [Header("CDN")]
+    [Tooltip("Automatically retry LoadUser download request on failure")]
+    [SerializeField]
+    protected bool _autoCdnRetry = true;
+
+    [Tooltip("Automatically check for avatar changes")]
+    [SerializeField]
+    protected bool _autoCheckChanges = false;
+
+    [Tooltip("How frequently to check for avatar changes")]
+    [SerializeField]
+    [Range(4.0f, 320.0f)]
+    private float _changeCheckInterval = 8.0f;
+
+#pragma warning disable CS0414
+    [Header("Debug Drawing")]
+    [Tooltip("Draw debug visualizations for avatar gaze targets")]
+    [SerializeField]
+    private bool _debugDrawGazePos;
+
+    [Tooltip("Color for gaze debug visualization")]
+    [SerializeField]
+    private Color _debugDrawGazePosColor = Color.magenta;
+#pragma warning restore CS0414
+
+    private enum OverrideStreamLOD
+    {
+        Default,
+        ForceHigh,
+        ForceMedium,
+        ForceLow,
+    }
+
+    [Header("Sample Networking")]
+    [Tooltip("Streaming quality override, default will not override")]
+    [SerializeField]
+    private OverrideStreamLOD _overrideStreamLod = OverrideStreamLOD.Default;
+
+    private static readonly int DESAT_AMOUNT_ID = Shader.PropertyToID("_DesatAmount");
+    private static readonly int DESAT_TINT_ID = Shader.PropertyToID("_DesatTint");
+    private static readonly int DESAT_LERP_ID = Shader.PropertyToID("_DesatLerp");
+
+
+    protected bool HasLocalAvatarConfigured => _assets.Count > 0;
+
+    private Stopwatch _loadTime = new Stopwatch();
+
+    protected override void Awake()
+    {
+        base.Awake();
+#if USING_XR_SDK
+        // OvrAvatarEntity.Awake calls OvrAvatarEntity.CreateEntity sets eye and face tracking contexts (which asks user permission).
+        // On Oculus SDK version >= v46 Eye tracking and Face tracking need to be explicitely started by the application after permission has been requested.
+        // Note: This API doesn't exist pre v46. If you require to support both v46 and earlier versions, one option is leveraging reflection:
+        // OVRPlugin.StartEyeTracking();
+        // OVRPlugin.StartFaceTracking();
+        // We use reflection here so that there are not compiler errors when using Oculus SDK v45 or below.
+        typeof(OVRPlugin).GetMethod("StartFaceTracking", BindingFlags.Public | BindingFlags.Static)?.Invoke(null, null);
+        typeof(OVRPlugin).GetMethod("StartEyeTracking", BindingFlags.Public | BindingFlags.Static)?.Invoke(null, null);
+        typeof(OVRPlugin).GetMethod("StartBodyTracking", BindingFlags.Public | BindingFlags.Static)?.Invoke(null, null);
+#endif
+    }
+
+    protected virtual IEnumerator Start()
+    {
+
+        if (!_deferLoading)
+        {
+            if (_loadUserFromCdn)
+            {
+                yield return LoadCdnAvatar();
+            }
+            else
+            {
+                LoadLocalAvatar();
+            }
+        }
+
+        switch (_overrideStreamLod)
+        {
+            case OverrideStreamLOD.ForceHigh:
+                ForceStreamLod(StreamLOD.High);
+                break;
+            case OverrideStreamLOD.ForceMedium:
+                ForceStreamLod(StreamLOD.Medium);
+                break;
+            case OverrideStreamLOD.ForceLow:
+                ForceStreamLod(StreamLOD.Low);
+                break;
+        }
+
+#if UNITY_EDITOR
+#if UNITY_2019_3_OR_NEWER
+        SceneView.duringSceneGui += OnSceneGUI;
+#else
+        SceneView.onSceneGUIDelegate += OnSceneGUI;
+#endif
+#endif
+    }
+
+    protected override void OnDestroyCalled()
+    {
+#if UNITY_EDITOR
+#if UNITY_2019_3_OR_NEWER
+        SceneView.duringSceneGui -= OnSceneGUI;
+#else
+        SceneView.onSceneGUIDelegate -= OnSceneGUI;
+#endif
+#endif
+    }
+
+    #region Loading
+    private IEnumerator LoadCdnAvatar()
+    {
+#if USING_XR_SDK
+        // Ensure OvrPlatform is Initialized
+        if (OvrPlatformInit.status == OvrPlatformInitStatus.NotStarted)
+        {
+            OvrPlatformInit.InitializeOvrPlatform();
+        }
+
+        while (OvrPlatformInit.status != OvrPlatformInitStatus.Succeeded)
+        {
+            if (OvrPlatformInit.status == OvrPlatformInitStatus.Failed)
+            {
+                OvrAvatarLog.LogError($"Error initializing OvrPlatform. Falling back to local avatar", logScope);
+                LoadLocalAvatar();
+                yield break;
+            }
+
+            yield return null;
+        }
+
+        // user ID == 0 means we want to load logged in user avatar from CDN
+        if (_userId == 0)
+        {
+            // Get User ID
+            bool getUserIdComplete = false;
+            Users.GetLoggedInUser().OnComplete(message =>
+            {
+                if (!message.IsError)
+                {
+                    _userId = message.Data.ID;
+                }
+                else
+                {
+                    var e = message.GetError();
+                    OvrAvatarLog.LogError($"Error loading CDN avatar: {e.Message}. Falling back to local avatar", logScope);
+                }
+
+                getUserIdComplete = true;
+            });
+
+            while (!getUserIdComplete) { yield return null; }
+        }
+#endif
+        yield return LoadUserAvatar();
+    }
+
+    public void LoadRemoteUserCdnAvatar(ulong userId)
+    {
+        StartLoadTimeCounter();
+        _userId = userId;
+        StartCoroutine(LoadCdnAvatar());
+    }
+
+    public void LoadLoggedInUserCdnAvatar()
+    {
+        StartLoadTimeCounter();
+        _userId = 0;
+        StartCoroutine(LoadCdnAvatar());
+    }
+
+    private IEnumerator LoadUserAvatar()
+    {
+        if (_userId == 0)
+        {
+            LoadLocalAvatar();
+            yield break;
+        }
+
+        yield return Retry_HasAvatarRequest();
+    }
+
+    private void LoadLocalAvatar()
+    {
+        if (!HasLocalAvatarConfigured)
+        {
+            OvrAvatarLog.LogInfo("No local avatar asset configured", logScope, this);
+            return;
+        }
+
+        // Zip asset paths are relative to the inside of the zip.
+        // Zips can be loaded from the OvrAvatarManager at startup or by calling OvrAvatarManager.Instance.AddZipSource
+        // Assets can also be loaded individually from Streaming assets
+        var path = new string[1];
+        foreach (var asset in _assets)
+        {
+            bool isFromZip = (asset.source == AssetSource.Zip);
+
+            string assetPostfix = (_underscorePostfix ? "_" : "")
+                + OvrAvatarManager.Instance.GetPlatformGLBPostfix(isFromZip)
+                + OvrAvatarManager.Instance.GetPlatformGLBVersion(_creationInfo.renderFilters.highQualityFlags != CAPI.ovrAvatar2EntityHighQualityFlags.None, isFromZip)
+                + OvrAvatarManager.Instance.GetPlatformGLBExtension(isFromZip);
+            if (!String.IsNullOrEmpty(_overridePostfix))
+            {
+                assetPostfix = _overridePostfix;
+            }
+
+            path[0] = asset.path + assetPostfix;
+            if (isFromZip)
+            {
+                LoadAssetsFromZipSource(path);
+            }
+            else
+            {
+                LoadAssetsFromStreamingAssets(path);
+            }
+        }
+    }
+    #endregion
+
+    public void ReloadAvatarManually(string newAssetPaths, AssetSource newAssetSource)
+    {
+        string[] tempStringArray = new string[1];
+        tempStringArray[0] = newAssetPaths;
+        ReloadAvatarManually(tempStringArray, newAssetSource);
+    }
+
+    public void ReloadAvatarManually(string[] newAssetPaths, AssetSource newAssetSource)
+    {
+        Teardown();
+        CreateEntity();
+
+        bool isFromZip = (newAssetSource == AssetSource.Zip);
+        string assetPostfix = (_underscorePostfix ? "_" : "")
+            + OvrAvatarManager.Instance.GetPlatformGLBPostfix(isFromZip)
+            + OvrAvatarManager.Instance.GetPlatformGLBVersion(_creationInfo.renderFilters.highQualityFlags != CAPI.ovrAvatar2EntityHighQualityFlags.None, isFromZip)
+            + OvrAvatarManager.Instance.GetPlatformGLBExtension(isFromZip);
+
+        string[] combinedPaths = new string[newAssetPaths.Length];
+        for (var index = 0; index < newAssetPaths.Length; index++)
+        {
+            combinedPaths[index] = $"{newAssetPaths[index]}{assetPostfix}";
+        }
+
+        if (isFromZip)
+        {
+            LoadAssetsFromZipSource(combinedPaths);
+        }
+        else
+        {
+            LoadAssetsFromStreamingAssets(combinedPaths);
+        }
+    }
+
+    public bool LoadPreset(int preset, string namePrefix = "")
+    {
+        StartLoadTimeCounter();
+        bool isFromZip = true;
+        string assetPostfix = (_underscorePostfix ? "_" : "")
+            + OvrAvatarManager.Instance.GetPlatformGLBPostfix(isFromZip)
+            + OvrAvatarManager.Instance.GetPlatformGLBVersion(_creationInfo.renderFilters.highQualityFlags != CAPI.ovrAvatar2EntityHighQualityFlags.None, isFromZip)
+            + OvrAvatarManager.Instance.GetPlatformGLBExtension(isFromZip);
+
+        var assetPath = $"{namePrefix}{preset}{assetPostfix}";
+        return LoadAssetsFromZipSource(new string[] { assetPath });
+    }
+
+    #region Fade/Desat
+
+    private static readonly Color AVATAR_FADE_DEFAULT_COLOR = new Color(33 / 255f, 50 / 255f, 99 / 255f, 0f); // "#213263"
+    private static readonly float AVATAR_FADE_DEFAULT_COLOR_BLEND = 0.7f; // "#213263"
+    private static readonly float AVATAR_FADE_DEFAULT_GRAYSCALE_BLEND = 0;
+
+    [Header("Rendering")]
+    [SerializeField]
+    [Range(0, 1)]
+    private float shaderGrayToSolidColorBlend_ = AVATAR_FADE_DEFAULT_COLOR_BLEND;
+    [SerializeField]
+    [Range(0, 1)]
+    private float shaderDesatBlend_ = AVATAR_FADE_DEFAULT_GRAYSCALE_BLEND;
+    [SerializeField]
+    private Color shaderSolidColor_ = AVATAR_FADE_DEFAULT_COLOR;
+
+    public float ShaderGrayToSolidColorBlend
+    {
+        // Blends grayscale to solid color
+        get => shaderGrayToSolidColorBlend_;
+        set
+        {
+            if (Mathf.Approximately(value, shaderGrayToSolidColorBlend_))
+            {
+                shaderGrayToSolidColorBlend_ = value;
+                UpdateMaterialsWithDesatModifiers();
+            }
+        }
+    }
+
+    public float ShaderDesatBlend
+    {
+        // Blends shader color to result of ShaderGrayToSolidColorBlend
+        get => shaderDesatBlend_;
+        set
+        {
+            if (Mathf.Approximately(value, shaderDesatBlend_))
+            {
+                shaderDesatBlend_ = value;
+                UpdateMaterialsWithDesatModifiers();
+            }
+        }
+    }
+
+    public Color ShaderSolidColor
+    {
+        get => shaderSolidColor_;
+        set
+        {
+            if (shaderSolidColor_ != value)
+            {
+                shaderSolidColor_ = value;
+                UpdateMaterialsWithDesatModifiers();
+            }
+        }
+    }
+
+    public void SetShaderDesat(float desatBlend, float? grayToSolidBlend = null, Color? solidColor = null)
+    {
+        if (solidColor.HasValue)
+        {
+            shaderSolidColor_ = solidColor.Value;
+        }
+        if (grayToSolidBlend.HasValue)
+        {
+            shaderGrayToSolidColorBlend_ = grayToSolidBlend.Value;
+        }
+        shaderDesatBlend_ = desatBlend;
+        UpdateMaterialsWithDesatModifiers();
+    }
+
+    private void UpdateMaterialsWithDesatModifiers()
+    {
+        // TODO: Migrate to `OvrAvatarMaterial` system
+#pragma warning disable 618 // disable deprecated method call warnings
+        SetMaterialKeyword("DESAT", shaderDesatBlend_ > 0.0f);
+        SetMaterialProperties((block, entity) =>
+        {
+            block.SetFloat(DESAT_AMOUNT_ID, entity.shaderDesatBlend_);
+            block.SetColor(DESAT_TINT_ID, entity.shaderSolidColor_);
+            block.SetFloat(DESAT_LERP_ID, entity.shaderGrayToSolidColorBlend_);
+        }, this);
+#pragma warning restore 618 // restore deprecated method call warnings
+    }
+
+    #endregion
+
+    #region Unity Transforms
+
+    public Transform GetSkeletonTransform(CAPI.ovrAvatar2JointType jointType)
+    {
+        if (!_criticalJointTypes.Contains(jointType))
+        {
+            OvrAvatarLog.LogError($"Can't access joint {jointType} unless it is in critical joint set");
+            return null;
+        }
+
+        return GetSkeletonTransformByType(jointType);
+    }
+
+    public CAPI.ovrAvatar2JointType[] GetCriticalJoints()
+    {
+        return _criticalJointTypes;
+    }
+
+    #endregion
+
+    #region Retry
+    protected void UserHasNoAvatarFallback()
+    {
+        OvrAvatarLog.LogError(
+            $"Unable to find user avatar with userId {_userId}. Falling back to local avatar.", logScope, this);
+
+        LoadLocalAvatar();
+    }
+
+    protected virtual IEnumerator Retry_HasAvatarRequest()
+    {
+        const float HAS_AVATAR_RETRY_WAIT_TIME = 4.0f;
+        const int HAS_AVATAR_RETRY_ATTEMPTS = 12;
+
+        int totalAttempts = _autoCdnRetry ? HAS_AVATAR_RETRY_ATTEMPTS : 1;
+        bool continueRetries = _autoCdnRetry;
+        int retriesRemaining = totalAttempts;
+        bool hasFoundAvatar = false;
+        bool requestComplete = false;
+        do
+        {
+            var hasAvatarRequest = OvrAvatarManager.Instance.UserHasAvatarAsync(_userId);
+            while (!hasAvatarRequest.IsCompleted) { yield return null; }
+
+            switch (hasAvatarRequest.Result)
+            {
+                case OvrAvatarManager.HasAvatarRequestResultCode.HasAvatar:
+                    hasFoundAvatar = true;
+                    requestComplete = true;
+                    continueRetries = false;
+
+                    // Now attempt download
+                    yield return AutoRetry_LoadUser(true);
+                    // End coroutine - do not load default
+                    break;
+
+                case OvrAvatarManager.HasAvatarRequestResultCode.HasNoAvatar:
+                    requestComplete = true;
+                    continueRetries = false;
+
+                    OvrAvatarLog.LogDebug(
+                        "User has no avatar. Falling back to local avatar."
+                        , logScope, this);
+                    break;
+
+                case OvrAvatarManager.HasAvatarRequestResultCode.SendFailed:
+                    OvrAvatarLog.LogError(
+                        "Unable to send avatar status request."
+                        , logScope, this);
+                    break;
+
+                case OvrAvatarManager.HasAvatarRequestResultCode.RequestFailed:
+                    OvrAvatarLog.LogError(
+                        "An error occurred while querying avatar status."
+                        , logScope, this);
+                    break;
+
+                case OvrAvatarManager.HasAvatarRequestResultCode.BadParameter:
+                    continueRetries = false;
+
+                    OvrAvatarLog.LogError(
+                        "Attempted to load invalid userId."
+                        , logScope, this);
+                    break;
+
+                case OvrAvatarManager.HasAvatarRequestResultCode.RequestCancelled:
+                    continueRetries = false;
+
+                    OvrAvatarLog.LogInfo(
+                        "HasAvatar request cancelled."
+                        , logScope, this);
+                    break;
+
+                case OvrAvatarManager.HasAvatarRequestResultCode.UnknownError:
+                default:
+                    OvrAvatarLog.LogError(
+                        $"An unknown error occurred {hasAvatarRequest.Result}. Falling back to local avatar."
+                        , logScope, this);
+                    break;
+            }
+
+            continueRetries &= --retriesRemaining > 0;
+            if (continueRetries)
+            {
+                yield return new WaitForSecondsRealtime(HAS_AVATAR_RETRY_WAIT_TIME);
+            }
+        } while (continueRetries);
+
+        if (!requestComplete)
+        {
+            OvrAvatarLog.LogError(
+                $"Unable to query UserHasAvatar {totalAttempts} attempts"
+                , logScope, this);
+        }
+
+        if (!hasFoundAvatar)
+        {
+            // We cannot find an avatar, use local fallback
+            UserHasNoAvatarFallback();
+        }
+
+        // Check for changes unless a local asset is configured, user could create one later
+        // If a local asset is loaded, it will currently conflict w/ the CDN asset
+        if (_autoCheckChanges && (hasFoundAvatar || !HasLocalAvatarConfigured))
+        {
+            yield return PollForAvatarChange();
+        }
+    }
+
+    protected virtual IEnumerator AutoRetry_LoadUser(bool loadFallbackOnFailure)
+    {
+        const float LOAD_USER_POLLING_INTERVAL = 4.0f;
+        const float LOAD_USER_BACKOFF_FACTOR = 1.618033988f;
+        const int CDN_RETRY_ATTEMPTS = 13;
+
+        int totalAttempts = _autoCdnRetry ? CDN_RETRY_ATTEMPTS : 1;
+        int remainingAttempts = totalAttempts;
+        bool didLoadAvatar = false;
+        var currentPollingInterval = LOAD_USER_POLLING_INTERVAL;
+        do
+        {
+            // Initiate user spec load (ie: CDN Avatar)
+            LoadUser();
+
+            CAPI.ovrAvatar2Result status;
+            do
+            {
+                // Wait for retry interval before taking any action
+                yield return new WaitForSecondsRealtime(currentPollingInterval);
+
+                // Check current `entity` status
+                status = this.entityStatus;
+                if (status.IsSuccess() || HasNonDefaultAvatar)
+                {
+                    didLoadAvatar = true;
+                    // Finished downloading - no more retries
+                    remainingAttempts = 0;
+
+                    OvrAvatarLog.LogDebug(
+                        "Load user retry check found successful download, ending retry routine"
+                        , logScope, this);
+                    break;
+                }
+
+                // Increase backoff interval
+                currentPollingInterval *= LOAD_USER_BACKOFF_FACTOR;
+
+                // `while` status is still pending, keep polling the current attempt
+                // Do not start a new request - do not decrement retry attempts
+            } while (status == CAPI.ovrAvatar2Result.Pending);
+            // Decrement retry attempts now that load failure has been confirmed (status != Pending)
+        } while (--remainingAttempts > 0);
+
+        if (loadFallbackOnFailure && !didLoadAvatar)
+        {
+            OvrAvatarLog.LogError(
+                $"Unable to download user after {totalAttempts} retry attempts",
+                logScope, this);
+
+            // We cannot download an avatar, use local fallback (ie: Preset Avatar)
+            UserHasNoAvatarFallback();
+        }
+    }
+
+    private void StartLoadTimeCounter()
+    {
+        _loadTime.Start();
+
+        OnUserAvatarLoadedEvent.AddListener((OvrAvatarEntity entity) =>
+        {
+            _loadTime.Stop();
+        });
+    }
+
+    public long GetLoadTimeMs()
+    {
+        return _loadTime.ElapsedMilliseconds;
+    }
+
+    #endregion // Retry
+
+    #region Change Check
+
+    protected IEnumerator PollForAvatarChange()
+    {
+        var waitForPollInterval = new WaitForSecondsRealtime(_changeCheckInterval);
+
+        bool continueChecking = true;
+        do
+        {
+            yield return waitForPollInterval;
+
+            var checkTask = HasAvatarChangedAsync();
+            while (!checkTask.IsCompleted) { yield return null; }
+
+            switch (checkTask.Result)
+            {
+                case OvrAvatarManager.HasAvatarChangedRequestResultCode.UnknownError:
+                    OvrAvatarLog.LogError(
+                        "Check avatar changed unknown error, aborting."
+                        , logScope, this);
+
+                    // Stop retrying or we'll just spam this error
+                    continueChecking = false;
+                    break;
+                case OvrAvatarManager.HasAvatarChangedRequestResultCode.BadParameter:
+                    OvrAvatarLog.LogError(
+                        "Check avatar changed invalid parameter, aborting."
+                        , logScope, this);
+
+                    // Stop retrying or we'll just spam this error
+                    continueChecking = false;
+                    break;
+
+                case OvrAvatarManager.HasAvatarChangedRequestResultCode.SendFailed:
+                    OvrAvatarLog.LogWarning(
+                        "Check avatar changed send failed."
+                        , logScope, this);
+                    break;
+
+                case OvrAvatarManager.HasAvatarChangedRequestResultCode.RequestFailed:
+                    OvrAvatarLog.LogError(
+                        "Check avatar changed request failed."
+                        , logScope, this);
+                    break;
+
+                case OvrAvatarManager.HasAvatarChangedRequestResultCode.RequestCancelled:
+                    OvrAvatarLog.LogInfo(
+                        "Check avatar changed request cancelled."
+                        , logScope, this);
+
+                    // Stop retrying, this entity has most likely been destroyed
+                    continueChecking = false;
+                    break;
+
+                case OvrAvatarManager.HasAvatarChangedRequestResultCode.AvatarHasNotChanged:
+                    OvrAvatarLog.LogVerbose(
+                        "Avatar has not changed."
+                        , logScope, this);
+                    break;
+
+                case OvrAvatarManager.HasAvatarChangedRequestResultCode.AvatarHasChanged:
+                    // Load new avatar!
+                    OvrAvatarLog.LogInfo(
+                        "Avatar has changed, loading new spec."
+                        , logScope, this);
+
+                    yield return AutoRetry_LoadUser(false);
+                    break;
+            }
+        } while (continueChecking);
+    }
+
+    #endregion // Change Check
+
+    // Debug
+    #region Debug
+
+#if UNITY_EDITOR
+    private void OnSceneGUI(SceneView sceneView)
+    {
+        if (_debugDrawGazePos)
+        {
+            DrawDebugGazePos();
+        }
+    }
+
+    private void DrawDebugGazePos()
+    {
+        if (!IsCreated) { return; }
+
+        var gazePos = GetGazePosition();
+        if (gazePos.HasValue)
+        {
+            Handles.color = _debugDrawGazePosColor;
+            Handles.DrawWireCube(gazePos.Value, new Vector3(0.25f, 0.25f, 0.25f));
+        }
+        else
+        {
+            OvrAvatarLog.LogError("Failed to get gaze pos");
+        }
+    }
+#endif
+    #endregion
+}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleAvatarEntity.cs.meta b/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleAvatarEntity.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..0b58f3fa87f712ec385d24979bbe38f3e1306fe0
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleAvatarEntity.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: f775fcfeef42d0543abbd1e03dfe74fb
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleAvatarEntityFaceTracking.cs b/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleAvatarEntityFaceTracking.cs
new file mode 100644
index 0000000000000000000000000000000000000000..ce647336cd6ebef6b8c8b72d116c1d508d06ad0b
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleAvatarEntityFaceTracking.cs
@@ -0,0 +1,8 @@
+using Oculus.Avatar2;
+using UnityEngine;
+using UnityEngine.Serialization;
+
+[System.Obsolete("SampleAvatarEntityFaceTracking is Obsolete. \nPlease use SampleAvatarEntity",true)]
+public class SampleAvatarEntityFaceTracking : SampleAvatarEntity
+{
+}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleAvatarEntityFaceTracking.cs.meta b/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleAvatarEntityFaceTracking.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..f4c9ba0aa218e94e027d98d0762eeab4d08696b9
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleAvatarEntityFaceTracking.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: c76fad918fb27994da4c7138ea97cbac
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleAvatarGazeTargets.cs b/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleAvatarGazeTargets.cs
new file mode 100644
index 0000000000000000000000000000000000000000..6bcf632810b0d769bec4bd590725bf494667b9b3
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleAvatarGazeTargets.cs
@@ -0,0 +1,39 @@
+using Oculus.Avatar2;
+using System.Collections;
+using UnityEngine;
+
+// When added to a SampleAvatarEntity, this creates gaze targets for this avatar's head and hands
+[RequireComponent(typeof(SampleAvatarEntity))]
+public class SampleAvatarGazeTargets : MonoBehaviour
+{
+    private static readonly CAPI.ovrAvatar2JointType HEAD_GAZE_TARGET_JNT = CAPI.ovrAvatar2JointType.Head;
+    private static readonly CAPI.ovrAvatar2JointType LEFT_HAND_GAZE_TARGET_JNT = CAPI.ovrAvatar2JointType.LeftHandIndexProximal;
+    private static readonly CAPI.ovrAvatar2JointType RIGHT_HAND_GAZE_TARGET_JNT = CAPI.ovrAvatar2JointType.RightHandIndexProximal;
+    private SampleAvatarEntity _avatarEnt;
+
+    protected IEnumerator Start()
+    {
+        _avatarEnt = GetComponent<SampleAvatarEntity>();
+        yield return new WaitUntil(() => _avatarEnt.HasJoints);
+
+        CreateGazeTarget("HeadGazeTarget", HEAD_GAZE_TARGET_JNT, CAPI.ovrAvatar2GazeTargetType.AvatarHead);
+        CreateGazeTarget("LeftHandGazeTarget", LEFT_HAND_GAZE_TARGET_JNT, CAPI.ovrAvatar2GazeTargetType.AvatarHand);
+        CreateGazeTarget("RightHandGazeTarget", RIGHT_HAND_GAZE_TARGET_JNT, CAPI.ovrAvatar2GazeTargetType.AvatarHand);
+    }
+
+    private void CreateGazeTarget(string gameObjectName, CAPI.ovrAvatar2JointType jointType, CAPI.ovrAvatar2GazeTargetType targetType)
+    {
+        Transform jointTransform = _avatarEnt.GetSkeletonTransform(jointType);
+        if (jointTransform)
+        {
+            var gazeTargetObj = new GameObject(gameObjectName);
+            var gazeTarget = gazeTargetObj.AddComponent<OvrAvatarGazeTarget>();
+            gazeTarget.TargetType = targetType;
+            gazeTargetObj.transform.SetParent(jointTransform, false);
+        }
+        else
+        {
+            OvrAvatarLog.LogError($"SampleAvatarGazeTargets: No joint transform found for {gameObjectName}");
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleAvatarGazeTargets.cs.meta b/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleAvatarGazeTargets.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..af64f47f5149f2e8b8cda5791c2f883a6f90aea9
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleAvatarGazeTargets.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 202d697196ae07b499aba5134d698aff
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleAvatarLocomotion.cs b/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleAvatarLocomotion.cs
new file mode 100644
index 0000000000000000000000000000000000000000..8807731932cc3a6d575158b94154f5a8b510d780
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleAvatarLocomotion.cs
@@ -0,0 +1,27 @@
+#if USING_XR_MANAGEMENT && USING_XR_SDK_OCULUS && !OVRPLUGIN_UNSUPPORTED_PLATFORM
+#define USING_XR_SDK
+#endif
+
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+public class SampleAvatarLocomotion : MonoBehaviour
+{
+    [SerializeField]
+    [Tooltip("Controls the speed of movement")]
+    public float movementSpeed = 1.0f;
+
+    // (1, 0, -1)
+    private Vector3 mirrorVector = Vector3.right + Vector3.back;
+
+    void Update()
+    {
+#if USING_XR_SDK
+        // Moves the avatar forward/back and left/right based on primary input
+        var primaryThumbstickVector = OVRInput.Get(OVRInput.Axis2D.PrimaryThumbstick);
+        var translationVector = new Vector3(primaryThumbstickVector.x, 0.0f, primaryThumbstickVector.y);
+        transform.Translate(translationVector * Time.deltaTime * movementSpeed);
+#endif
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleAvatarLocomotion.cs.meta b/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleAvatarLocomotion.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..0f2941e32fff2545a741c0e804bce8b96e752b2e
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleAvatarLocomotion.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 5b0eaccd645fc5641ba1cf708f6fd678
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleEyePoseBehavior.cs b/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleEyePoseBehavior.cs
new file mode 100644
index 0000000000000000000000000000000000000000..3324872adb3e4b3ef64fb7013f8bd2c7f09c981e
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleEyePoseBehavior.cs
@@ -0,0 +1,6 @@
+using Oculus.Avatar2;
+using UnityEngine;
+
+public sealed class SampleEyePoseBehavior : OvrAvatarEyeTrackingBehaviorOvrPlugin
+{
+}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleEyePoseBehavior.cs.meta b/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleEyePoseBehavior.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..15fe9b50addd68994f44e1b87487e51d744a6a03
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleEyePoseBehavior.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 27d9ca4252a965544a4613f39f18d361
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleFacePoseBehavior.cs b/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleFacePoseBehavior.cs
new file mode 100644
index 0000000000000000000000000000000000000000..7af83b91886a40b4433b41c1e132e6f2d276286b
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleFacePoseBehavior.cs
@@ -0,0 +1,6 @@
+using Oculus.Avatar2;
+using UnityEngine;
+
+public sealed class SampleFacePoseBehavior : OvrAvatarFaceTrackingBehaviorOvrPlugin
+{
+}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleFacePoseBehavior.cs.meta b/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleFacePoseBehavior.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..d21569853add18bf9da1d2ab1cc87f15518eef73
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleFacePoseBehavior.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 852db897867e8a0418e5c20c10cb0cb2
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleInputControlDelegate.cs b/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleInputControlDelegate.cs
new file mode 100644
index 0000000000000000000000000000000000000000..a5fabe75292a9b4625d2984b2ba0a52b12ea3d2d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleInputControlDelegate.cs
@@ -0,0 +1,94 @@
+#if USING_XR_MANAGEMENT && USING_XR_SDK_OCULUS && !OVRPLUGIN_UNSUPPORTED_PLATFORM
+#define USING_XR_SDK
+#endif
+
+using Oculus.Avatar2;
+
+#if USING_XR_SDK
+using Button = OVRInput.Button;
+using Touch = OVRInput.Touch;
+#endif
+
+public class SampleInputControlDelegate : OvrAvatarInputControlDelegate
+{
+    public override bool GetInputControlState(out OvrAvatarInputControlState inputControlState)
+    {
+        inputControlState = new OvrAvatarInputControlState();
+        inputControlState.type = GetControllerType();
+
+#if USING_XR_SDK
+        UpdateControllerInput(ref inputControlState.leftControllerState, OVRInput.Controller.LTouch);
+        UpdateControllerInput(ref inputControlState.rightControllerState, OVRInput.Controller.RTouch);
+#endif
+
+        return true;
+    }
+
+#if USING_XR_SDK
+    private void UpdateControllerInput(ref OvrAvatarControllerState controllerState, OVRInput.Controller controller)
+    {
+        controllerState.buttonMask = 0;
+        controllerState.touchMask = 0;
+
+        // Button Press
+        if (OVRInput.Get(Button.One, controller))
+        {
+            controllerState.buttonMask |= CAPI.ovrAvatar2Button.One;
+        }
+        if (OVRInput.Get(Button.Two, controller))
+        {
+            controllerState.buttonMask |= CAPI.ovrAvatar2Button.Two;
+        }
+        if (OVRInput.Get(Button.Three, controller))
+        {
+            controllerState.buttonMask |= CAPI.ovrAvatar2Button.Three;
+        }
+        if (OVRInput.Get(Button.PrimaryThumbstick, controller))
+        {
+            controllerState.buttonMask |= CAPI.ovrAvatar2Button.Joystick;
+        }
+
+        // Button Touch
+        if (OVRInput.Get(Touch.One, controller))
+        {
+            controllerState.touchMask |= CAPI.ovrAvatar2Touch.One;
+        }
+        if (OVRInput.Get(Touch.Two, controller))
+        {
+            controllerState.touchMask |= CAPI.ovrAvatar2Touch.Two;
+        }
+        if (OVRInput.Get(Touch.PrimaryThumbstick, controller))
+        {
+            controllerState.touchMask |= CAPI.ovrAvatar2Touch.Joystick;
+        }
+        if (OVRInput.Get(Touch.PrimaryThumbRest, controller))
+        {
+            controllerState.touchMask |= CAPI.ovrAvatar2Touch.ThumbRest;
+        }
+
+        // Trigger
+        controllerState.indexTrigger = OVRInput.Get(OVRInput.Axis1D.PrimaryIndexTrigger, controller);
+        if (OVRInput.Get(Touch.PrimaryIndexTrigger, controller))
+        {
+            controllerState.touchMask |= CAPI.ovrAvatar2Touch.Index;
+        }
+        else if (controllerState.indexTrigger <= 0f)
+        {
+            // TODO: Not sure if this is the correct way to do this
+            controllerState.touchMask |= CAPI.ovrAvatar2Touch.Pointing;
+        }
+
+        // Grip
+        controllerState.handTrigger = OVRInput.Get(OVRInput.Axis1D.PrimaryHandTrigger, controller);
+
+        // Set ThumbUp if no other thumb-touch is set.
+        // TODO: Not sure if this is the correct way to do this
+        if ((controllerState.touchMask & (CAPI.ovrAvatar2Touch.One | CAPI.ovrAvatar2Touch.Two |
+                                          CAPI.ovrAvatar2Touch.Joystick | CAPI.ovrAvatar2Touch.ThumbRest)) == 0)
+        {
+            controllerState.touchMask |= CAPI.ovrAvatar2Touch.ThumbUp;
+        }
+    }
+#endif
+
+}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleInputControlDelegate.cs.meta b/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleInputControlDelegate.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..e1bcaa1e1de17fe3a1e58343e49ba67797de3531
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleInputControlDelegate.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 0a187b1323ef73b4191a2b50468bd9b9
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleInputManager.cs b/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleInputManager.cs
new file mode 100644
index 0000000000000000000000000000000000000000..d14ee9f2bca52a5f84a98d566fabfbb4d3fd0ab1
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleInputManager.cs
@@ -0,0 +1,148 @@
+#if USING_XR_MANAGEMENT && USING_XR_SDK_OCULUS && !OVRPLUGIN_UNSUPPORTED_PLATFORM
+#define USING_XR_SDK
+#endif
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using Oculus.Avatar2;
+using UnityEngine;
+using UnityEngine.XR;
+#if UNITY_EDITOR
+using UnityEditor;
+#endif
+
+using Node = UnityEngine.XR.XRNode;
+
+/* This is an example class for how to send input and IK transforms to the sdk from any source
+ * InputTrackingDelegate and InputControlDelegate are set on BodyTracking.
+ */
+public class SampleInputManager : OvrAvatarInputManager
+{
+    private const string logScope = "sampleInput";
+
+    [SerializeField]
+    [Tooltip("Optional. If added, it will use input directly from OVRCameraRig instead of doing its own calculations.")]
+#if USING_XR_SDK
+    private OVRCameraRig _ovrCameraRig = null;
+#endif
+    private bool _useOvrCameraRig;
+
+    // Only used in editor, produces warnings when packaging
+#pragma warning disable CS0414 // is assigned but its value is never used
+    [SerializeField]
+    private bool _debugDrawTrackingLocations = false;
+#pragma warning restore CS0414 // is assigned but its value is never used
+
+    protected void Awake()
+    {
+#if USING_XR_SDK
+        _useOvrCameraRig = _ovrCameraRig != null;
+#endif
+
+        // Debug Drawing
+#if UNITY_EDITOR
+#if UNITY_2019_3_OR_NEWER
+        SceneView.duringSceneGui += OnSceneGUI;
+#else
+        SceneView.onSceneGUIDelegate += OnSceneGUI;
+#endif
+#endif
+    }
+
+    private void Start()
+    {
+#if USING_XR_SDK
+        // If OVRCameraRig doesn't exist, we should set tracking origin ourselves
+        if (!_useOvrCameraRig)
+        {
+
+            if (OVRManager.instance == null)
+            {
+                OvrAvatarLog.LogDebug("Creating OVRManager, as one doesn't exist yet.", logScope, this);
+                var go = new GameObject("OVRManager");
+                var manager = go.AddComponent<OVRManager>();
+                manager.trackingOriginType = OVRManager.TrackingOrigin.FloorLevel;
+            }
+            else
+            {
+                OVRManager.instance.trackingOriginType = OVRManager.TrackingOrigin.FloorLevel;
+            }
+
+            OvrAvatarLog.LogInfo("Setting Tracking Origin to FloorLevel", logScope, this);
+
+            var instances = new List<XRInputSubsystem>();
+            SubsystemManager.GetInstances(instances);
+            foreach (var instance in instances)
+            {
+                instance.TrySetTrackingOriginMode(TrackingOriginModeFlags.Floor);
+            }
+        }
+
+        if (BodyTracking != null)
+        {
+            BodyTracking.InputTrackingDelegate = new SampleInputTrackingDelegate(_ovrCameraRig);
+            BodyTracking.InputControlDelegate = new SampleInputControlDelegate();
+        }
+#endif
+    }
+
+    protected override void OnDestroyCalled()
+    {
+#if UNITY_EDITOR
+#if UNITY_2019_3_OR_NEWER
+        SceneView.duringSceneGui -= OnSceneGUI;
+#else
+        SceneView.onSceneGUIDelegate -= OnSceneGUI;
+#endif
+#endif
+
+        base.OnDestroyCalled();
+    }
+
+#if UNITY_EDITOR
+    #region Debug Drawing
+
+    private void OnSceneGUI(SceneView sceneView)
+    {
+        if (_debugDrawTrackingLocations)
+        {
+            DrawTrackingLocations();
+        }
+    }
+
+    private void DrawTrackingLocations()
+    {
+        var inputTrackingState = BodyTracking.InputTrackingState;
+
+        float radius = 0.2f;
+        Quaternion orientation;
+        float outerRadius() => radius + 0.25f;
+        Vector3 forward() => orientation * Vector3.forward;
+
+        Handles.color = Color.blue;
+        Handles.RadiusHandle(Quaternion.identity, inputTrackingState.headset.position, radius);
+
+        orientation = inputTrackingState.headset.orientation;
+        Handles.DrawLine((Vector3)inputTrackingState.headset.position + forward() * radius,
+            (Vector3)inputTrackingState.headset.position + forward() * outerRadius());
+
+        radius = 0.1f;
+        Handles.color = Color.yellow;
+        Handles.RadiusHandle(Quaternion.identity, inputTrackingState.leftController.position, radius);
+
+        orientation = inputTrackingState.leftController.orientation;
+        Handles.DrawLine((Vector3)inputTrackingState.leftController.position + forward() * radius,
+            (Vector3)inputTrackingState.leftController.position + forward() * outerRadius());
+
+        Handles.color = Color.yellow;
+        Handles.RadiusHandle(Quaternion.identity, inputTrackingState.rightController.position, radius);
+
+        orientation = inputTrackingState.rightController.orientation;
+        Handles.DrawLine((Vector3)inputTrackingState.rightController.position + forward() * radius,
+            (Vector3)inputTrackingState.rightController.position + forward() * outerRadius());
+    }
+
+    #endregion
+#endif // UNITY_EDITOR
+}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleInputManager.cs.meta b/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleInputManager.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..70ccb3f3c0b813702f32ab08dc810a6660b03991
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleInputManager.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 6937544d08e2ba34da6ff4ab90cbfd84
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleInputTrackingDelegate.cs b/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleInputTrackingDelegate.cs
new file mode 100644
index 0000000000000000000000000000000000000000..1ae871415e1f0402f0c91e6bf558e20384aa1186
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleInputTrackingDelegate.cs
@@ -0,0 +1,91 @@
+#if USING_XR_MANAGEMENT && USING_XR_SDK_OCULUS && !OVRPLUGIN_UNSUPPORTED_PLATFORM
+#define USING_XR_SDK
+#endif
+
+using Oculus.Avatar2;
+using UnityEngine;
+using Node = UnityEngine.XR.XRNode;
+
+/*
+ *
+ */
+public class SampleInputTrackingDelegate : OvrAvatarInputTrackingDelegate
+{
+
+#if USING_XR_SDK
+    private OVRCameraRig _ovrCameraRig = null;
+#endif
+
+#if USING_XR_SDK
+    public SampleInputTrackingDelegate(OVRCameraRig ovrCameraRig)
+    {
+        _ovrCameraRig = ovrCameraRig;
+    }
+#endif
+    public override bool GetRawInputTrackingState(out OvrAvatarInputTrackingState inputTrackingState)
+    {
+        inputTrackingState = default;
+#if USING_XR_SDK
+        bool leftControllerActive = false;
+        bool rightControllerActive = false;
+        if (OVRInput.GetActiveController() != OVRInput.Controller.Hands)
+        {
+            leftControllerActive = OVRInput.GetControllerOrientationTracked(OVRInput.Controller.LTouch);
+            rightControllerActive = OVRInput.GetControllerOrientationTracked(OVRInput.Controller.RTouch);
+        }
+
+        if (_ovrCameraRig)
+        {
+            inputTrackingState.headsetActive = true;
+            inputTrackingState.leftControllerActive = leftControllerActive;
+            inputTrackingState.rightControllerActive = rightControllerActive;
+            inputTrackingState.leftControllerVisible = false;
+            inputTrackingState.rightControllerVisible = false;
+            inputTrackingState.headset = (CAPI.ovrAvatar2Transform)_ovrCameraRig.centerEyeAnchor;
+            inputTrackingState.leftController = (CAPI.ovrAvatar2Transform)_ovrCameraRig.leftHandAnchor;
+            inputTrackingState.rightController = (CAPI.ovrAvatar2Transform)_ovrCameraRig.rightHandAnchor;
+            return true;
+        }
+        else if (OVRNodeStateProperties.IsHmdPresent())
+        {
+            inputTrackingState.headsetActive = true;
+            inputTrackingState.leftControllerActive = leftControllerActive;
+            inputTrackingState.rightControllerActive = rightControllerActive;
+            inputTrackingState.leftControllerVisible = true;
+            inputTrackingState.rightControllerVisible = true;
+
+            if (OVRNodeStateProperties.GetNodeStatePropertyVector3(Node.CenterEye, NodeStatePropertyType.Position,
+                OVRPlugin.Node.EyeCenter, OVRPlugin.Step.Render, out var headPos))
+            {
+                inputTrackingState.headset.position = headPos;
+            }
+            else
+            {
+                inputTrackingState.headset.position = Vector3.zero;
+            }
+
+            if (OVRNodeStateProperties.GetNodeStatePropertyQuaternion(Node.CenterEye, NodeStatePropertyType.Orientation,
+                OVRPlugin.Node.EyeCenter, OVRPlugin.Step.Render, out var headRot))
+            {
+                inputTrackingState.headset.orientation = headRot;
+            }
+            else
+            {
+                inputTrackingState.headset.orientation = Quaternion.identity;
+            }
+
+            inputTrackingState.headset.scale = Vector3.one;
+
+            inputTrackingState.leftController.position = OVRInput.GetLocalControllerPosition(OVRInput.Controller.LTouch);
+            inputTrackingState.rightController.position = OVRInput.GetLocalControllerPosition(OVRInput.Controller.RTouch);
+            inputTrackingState.leftController.orientation = OVRInput.GetLocalControllerRotation(OVRInput.Controller.LTouch);
+            inputTrackingState.rightController.orientation = OVRInput.GetLocalControllerRotation(OVRInput.Controller.RTouch);
+            inputTrackingState.leftController.scale = Vector3.one;
+            inputTrackingState.rightController.scale = Vector3.one;
+            return true;
+        }
+#endif
+
+        return false;
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleInputTrackingDelegate.cs.meta b/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleInputTrackingDelegate.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..de59de8e235ac5ae7b23adc049e1eaa42d86631c
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleInputTrackingDelegate.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 9f00d5df0671f744fae23e5360096b59
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleXplatformObjectPlacement.cs b/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleXplatformObjectPlacement.cs
new file mode 100644
index 0000000000000000000000000000000000000000..505f1799a2d16b6e68c367f83416ececc474d81c
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleXplatformObjectPlacement.cs
@@ -0,0 +1,72 @@
+#if USING_XR_MANAGEMENT && USING_XR_SDK_OCULUS && !OVRPLUGIN_UNSUPPORTED_PLATFORM
+#define USING_XR_SDK
+#endif
+
+using JetBrains.Annotations;
+using UnityEngine;
+
+public class SampleXplatformObjectPlacement : MonoBehaviour
+{
+
+    public enum Mode
+    {
+        Replace, // Replace existing object position and rotation with new values
+        Additive, // Position and rotation values are added to existing values
+    }
+
+#pragma warning disable 0414
+    [Tooltip("The mode in which the position and rotation value is applied. Replace mode replaces existing values. Additive mode add to exisiting values.")]
+    [SerializeField]
+    private Mode _mode = Mode.Replace;
+
+    [Tooltip("The coordinate space in which the object position and rotation should be applied.")]
+    [SerializeField]
+    private Space _coordinateSpace = Space.Self;
+
+    [Tooltip("Object position")]
+    [SerializeField]
+    private Vector3 _position;
+
+    [Tooltip("Object rotation in euler angles")]
+    [SerializeField]
+    private Vector3 _rotation;
+#pragma warning restore 0414
+
+#if !USING_XR_SDK
+    private void Awake()
+    {
+        var finalPosition = _position;
+        var finalRotation = Quaternion.identity;
+
+        if (_coordinateSpace == Space.World)
+        {
+            if (_mode == Mode.Additive)
+            {
+                finalPosition += transform.position;
+                finalRotation = Quaternion.Euler(_rotation + transform.rotation.eulerAngles);
+            }
+            else
+            {
+                finalRotation = Quaternion.Euler(_rotation);
+            }
+
+            transform.SetPositionAndRotation(finalPosition, finalRotation);
+        }
+        else
+        {
+            if (_mode == Mode.Additive)
+            {
+                finalPosition += transform.localPosition;
+                finalRotation = Quaternion.Euler(_rotation + transform.localRotation.eulerAngles);
+            }
+            else
+            {
+                finalRotation = Quaternion.Euler(_rotation);
+            }
+
+            transform.localPosition = finalPosition;
+            transform.localRotation = finalRotation;
+        }
+    }
+#endif
+}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleXplatformObjectPlacement.cs.meta b/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleXplatformObjectPlacement.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..1ead95e30f9d79f36ae19490ce2f3f540d433043
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Scripts/SampleXplatformObjectPlacement.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 4e93961d3dfb44505a84d80b1f7fffef
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Scripts/TestSceneSwitcher.cs b/Assets/Oculus/Avatar2/Example/Common/Scripts/TestSceneSwitcher.cs
new file mode 100644
index 0000000000000000000000000000000000000000..faa41db17dd2aec435d25127edd51b4624daddc2
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Scripts/TestSceneSwitcher.cs
@@ -0,0 +1,118 @@
+#if USING_XR_MANAGEMENT && USING_XR_SDK_OCULUS && !OVRPLUGIN_UNSUPPORTED_PLATFORM
+#define USING_XR_SDK
+#endif
+
+using System.Collections;
+using Oculus.Avatar2;
+using UnityEngine;
+using UnityEngine.SceneManagement;
+
+/* NOTE: This class will switch scenes in a way that keeps them independent from each other for testing.
+ It will ensure the OvrAvatarManager is destroyed when switching scenes,
+ allowing each scene to be cleanly tested on its own. In a real app, OvrAvatarManager
+ should not be destroyed during scene changes.
+
+ For a real app, simply switch scenes as usual. OvrAvatarManager will be marked DontDestroyOnLoad and carry
+ over to the next scene automatically.
+ */
+public class TestSceneSwitcher : MonoBehaviour
+{
+#if USING_XR_SDK
+    [System.Serializable]
+    public struct InputMask
+    {
+        public OVRInput.Controller controllerMask;
+        public OVRInput.Button buttonMask;
+    }
+
+    [SerializeField]
+    private InputMask _nextSceneInput = new InputMask
+    { controllerMask = OVRInput.Controller.RTouch, buttonMask = OVRInput.Button.One };
+
+    [SerializeField]
+    private InputMask _prevSceneInput = new InputMask
+    { controllerMask = OVRInput.Controller.LTouch, buttonMask = OVRInput.Button.One };
+#endif
+
+    private void Awake()
+    {
+        // Because we destroy the old Avatar Manager for a clean scene change,
+        // we need to give the new one the access token again
+        if (OvrAvatarEntitlement.AccessTokenIsValid())
+        {
+            OvrAvatarEntitlement.ResendAccessToken();
+        }
+    }
+
+    private void Update()
+    {
+        int sceneChange = 0;
+
+#if USING_XR_SDK
+        if (OVRInput.GetActiveController() != OVRInput.Controller.Hands)
+        {
+            if (OVRInput.GetUp(_nextSceneInput.buttonMask, _nextSceneInput.controllerMask))
+            {
+                sceneChange = 1;
+            }
+            else if (OVRInput.GetUp(_prevSceneInput.buttonMask, _prevSceneInput.controllerMask))
+            {
+                sceneChange = -1;
+            }
+        }
+#endif
+        if (sceneChange != 0)
+        {
+            StartCoroutine(SwitchScene(sceneChange));
+        }
+    }
+
+#if !USING_XR_SDK
+    private void OnGUI()
+    {
+        float buttonSize = Screen.height * 0.15f;
+
+        var style = new GUIStyle();
+        style.alignment = TextAnchor.MiddleCenter;
+        GUI.Label(new Rect(0, Screen.height - buttonSize, Screen.width, buttonSize), SceneManager.GetActiveScene().name, style);
+
+        if (GUI.Button(new Rect(0, Screen.height - buttonSize, buttonSize, buttonSize), "Prev"))
+        {
+            StartCoroutine(SwitchScene(-1));
+        }
+        if (GUI.Button(new Rect(Screen.width - buttonSize, Screen.height - buttonSize, buttonSize, buttonSize), "Next"))
+        {
+            StartCoroutine(SwitchScene(1));
+        }
+    }
+#endif
+
+    private IEnumerator SwitchScene(int direction)
+    {
+        // Wait a frame so OVRInput registers the button up event - otherwise it misses it during the scene switch
+        yield return null;
+
+        // Reset OvrAvatarManager so the next scene can create it again with a new configuration
+        OvrAvatarManager.ResetInstance();
+
+        // Change scenes
+        int activeSceneIdx = SceneManager.GetActiveScene().buildIndex;
+        int nextSceneIdx = Wrap(activeSceneIdx + direction, 0, SceneManager.sceneCountInBuildSettings - 1);
+        SceneManager.LoadScene(nextSceneIdx, LoadSceneMode.Single);
+    }
+
+    // Assumes value is only outside min/max by 1
+    private int Wrap(int value, int min, int max)
+    {
+        if (value < min)
+        {
+            return max;
+        }
+        else if (value > max)
+        {
+            return min;
+        }
+
+        return value;
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Scripts/TestSceneSwitcher.cs.meta b/Assets/Oculus/Avatar2/Example/Common/Scripts/TestSceneSwitcher.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..612a9ce60a4e4bc74ec4ce7f2e8369a08fa9f3f8
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Scripts/TestSceneSwitcher.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: ea6e0c6cd67922c48bbb5939267828e9
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Scripts/TransformTrackingInputManager.cs b/Assets/Oculus/Avatar2/Example/Common/Scripts/TransformTrackingInputManager.cs
new file mode 100644
index 0000000000000000000000000000000000000000..1887c58f8083378d012885e1adf52cd9b33f7f44
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Scripts/TransformTrackingInputManager.cs
@@ -0,0 +1,89 @@
+using Oculus.Avatar2;
+using UnityEngine;
+
+public class TrackingTransformsInputControlDelegate : OvrAvatarInputControlDelegate
+{
+    public CAPI.ovrAvatar2ControllerType controllerType = CAPI.ovrAvatar2ControllerType.Invalid;
+
+    public override bool GetInputControlState(out OvrAvatarInputControlState inputControlState)
+    {
+        inputControlState = default;
+        inputControlState.type = controllerType;
+
+        return true;
+    }
+}
+
+public class TrackingTransformsInputTrackingDelegate : OvrAvatarInputTrackingDelegate
+{
+    private TransformTrackingInputManager _transforms;
+
+    public TrackingTransformsInputTrackingDelegate(TransformTrackingInputManager transforms)
+    {
+        _transforms = transforms;
+    }
+
+    public override bool GetRawInputTrackingState(out OvrAvatarInputTrackingState inputTrackingState)
+    {
+        inputTrackingState = default;
+
+        // HACK: Random rotations allow us to pass the BodyAPI "is in hand" check. Without it, BodyAPI overrides and goes into rest pose
+        // I tried to get the random value as small as possible, but at .01 variance, rest pose triggers again :/
+        Quaternion randomRot = Quaternion.Euler(Random.Range(-.1f, .1f), Random.Range(-.1f, .1f), Random.Range(-.1f, .1f));
+
+        if (_transforms.hmd)
+        {
+            inputTrackingState.headset = (CAPI.ovrAvatar2Transform)_transforms.hmd;
+            inputTrackingState.headsetActive = true;
+        }
+
+
+        if (_transforms.leftController)
+        {
+            inputTrackingState.leftController = (CAPI.ovrAvatar2Transform)_transforms.leftController;
+            inputTrackingState.leftController.orientation *= randomRot;
+            inputTrackingState.leftControllerActive = true;
+            inputTrackingState.leftControllerVisible = _transforms.controllersVisible;
+        }
+        else
+        {
+            inputTrackingState.leftControllerActive = false;
+        }
+
+        if (_transforms.rightController)
+        {
+            inputTrackingState.rightController = (CAPI.ovrAvatar2Transform)_transforms.rightController;
+            inputTrackingState.rightController.orientation *= randomRot;
+            inputTrackingState.rightControllerActive = true;
+            inputTrackingState.rightControllerVisible = _transforms.controllersVisible;
+        }
+        else
+        {
+            inputTrackingState.rightControllerActive = false;
+        }
+
+        return true;
+    }
+}
+
+// This class assigns Transform data to body tracking system
+// so that avatar can be controlled without a headset
+public class TransformTrackingInputManager : OvrAvatarInputManager
+{
+    public Transform hmd;
+
+    public Transform leftController;
+
+    public Transform rightController;
+
+    public bool controllersVisible = false;
+
+    private void Start()
+    {
+        if (BodyTracking != null)
+        {
+            BodyTracking.InputControlDelegate = new TrackingTransformsInputControlDelegate();
+            BodyTracking.InputTrackingDelegate = new TrackingTransformsInputTrackingDelegate(this);
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Scripts/TransformTrackingInputManager.cs.meta b/Assets/Oculus/Avatar2/Example/Common/Scripts/TransformTrackingInputManager.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..c8614d4a3816e0dec989b7bcc30ca5e0d9038dfa
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Scripts/TransformTrackingInputManager.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 66a61802bb8f1e947aa50f674d42db85
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders.meta
new file mode 100644
index 0000000000000000000000000000000000000000..27f42ecf6605dd3d76ade4838dc679437ac27ced
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: b567df7ec46e24e48a909a1fd9caf066
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations.meta
new file mode 100644
index 0000000000000000000000000000000000000000..d241f781e642de17db5868179988219d6a305c06
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: a8ce74b77209bb448803bf636fea51ce
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon.meta
new file mode 100644
index 0000000000000000000000000000000000000000..8155e6996a379604c2e41932c169e0d604d7dfad
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: bf1e08c0fa22ced4985c2dd22b7e844e
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonHairConfiguration.asset b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonHairConfiguration.asset
new file mode 100644
index 0000000000000000000000000000000000000000..f67ae57ca088451e2171c1ae14b782acb8dba5c2
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonHairConfiguration.asset
@@ -0,0 +1,49 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 0}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 3861555276e1a07489d3c0971e091e46, type: 3}
+  m_Name: HorizonHairConfiguration
+  m_EditorClassIdentifier: 
+  Material: {fileID: 2100000, guid: 1ddabfc39cb7f4043b0da4f90dc7f05a, type: 2}
+  Shader: {fileID: 4800000, guid: c26a73fa4d4517c4da30333333bf50c2, type: 3}
+  NameTextureParameter_baseColorTexture: _MainTex
+  NameTextureParameter_diffuseTexture: _MainTex
+  NameTextureParameter_metallicRoughnessTexture: _PropertiesMap
+  NameTextureParameter_specularGlossiness: _PropertiesMap
+  NameTextureParameter_normalTexture: _BumpMap
+  NameTextureParameter_occlusionTexture: _PropertiesMap
+  NameTextureParameter_emissiveTexture: _EmissiveMap
+  NameColorParameter_BaseColorFactor: _Color
+  UseColorParameter_BaseColorFactor: 0
+  NameFloatParameter_MetallicFactor: 
+  UseFloatParameter_MetallicFactor: 0
+  NameFloatParameter_RoughnessFactor: 
+  UseFloatParameter_RoughnessFactor: 0
+  NameColorParameter_DiffuseFactor: _Diffuse
+  UseColorParameter_DiffuseFactor: 0
+  KeywordsEnumerations:
+  - _SHADER_TYPE_SOLID_COLOR
+  - _SHADER_TYPE_TEXTURED
+  - _SHADER_TYPE_SKIN
+  - _SHADER_TYPE_HAIR
+  - _SHADER_TYPE_LEFT_EYE
+  - _SHADER_TYPE_RIGHT_EYE
+  - _LIGHTING_SYSTEM_UNITY
+  - _LIGHTING_SYSTEM_VERTEX_GI
+  KeywordsToEnable:
+  - _SHADER_TYPE_HAIR
+  - _LIGHTING_SYSTEM_UNITY
+  NameFloatConstants:
+  - _Shader_Type
+  - _Lighting_System
+  ValueFloatConstants:
+  - 3
+  - 0
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonHairConfiguration.asset.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonHairConfiguration.asset.meta
new file mode 100644
index 0000000000000000000000000000000000000000..80053f565536f8b6b0fb9a651c084297df0f8a0e
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonHairConfiguration.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: c4b830c6fb33d8140833e5b37f7c9e0d
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonHairMaterial.mat b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonHairMaterial.mat
new file mode 100644
index 0000000000000000000000000000000000000000..5c821d9e09aafaef6f1d24a1bb1a7f2310046c30
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonHairMaterial.mat
@@ -0,0 +1,118 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: HorizonHairMaterial
+  m_Shader: {fileID: 4800000, guid: c26a73fa4d4517c4da30333333bf50c2, type: 3}
+  m_ShaderKeywords: _LIGHTING_SYSTEM_UNITY _SHADER_TYPE_HAIR
+  m_LightmapFlags: 4
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EffectsMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _PropertiesMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _AmbientOcclusionEffect: 1
+    - _BacklightScale: 0.2
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DesatAmount: 0
+    - _DesatLerp: 0
+    - _DetailNormalMapScale: 1
+    - _DirectOcclusionEffect: 1
+    - _Distortion: 0.5
+    - _DstBlend: 0
+    - _EyeUVScale: 0.5
+    - _EyeXMax: 1
+    - _EyeXMid: 0.5
+    - _EyeXMin: 0
+    - _EyeYMax: 1
+    - _EyeYMid: 0.5
+    - _EyeYMin: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _HeadC: 0
+    - _IrisScale: 0.5
+    - _LeftEyeRight: 0
+    - _LeftEyeTX: 0
+    - _LeftEyeTY: 0
+    - _LeftEyeUp: 0
+    - _Lighting_System: 0
+    - _Metallic: 0
+    - _MinDiffuse: 0.5
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _PupilScale: 0.5
+    - _RightEyeRight: 0
+    - _RightEyeTX: 0
+    - _RightEyeTY: 0
+    - _RightEyeUp: 0
+    - _Shader_Type: 3
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _TranslucencyPower: 1
+    - _TranslucencyScale: 1
+    - _UVScale: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 1, g: 1, b: 1, a: 1}
+    - _DesatTint: {r: 0.255, g: 0.314, b: 0.502, a: 1}
+    - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
+    - _SecondaryColor: {r: 1, g: 1, b: 1, a: 1}
+    - _TertiaryColor: {r: 1, g: 1, b: 1, a: 0}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonHairMaterial.mat.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonHairMaterial.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..2d2dfd46d712db19e1f3e9082090f74ed94f2710
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonHairMaterial.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 1ddabfc39cb7f4043b0da4f90dc7f05a
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonLeftEyeConfiguration.asset b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonLeftEyeConfiguration.asset
new file mode 100644
index 0000000000000000000000000000000000000000..12c2bd43fc623fcaf141015033ddcb88f2f7a436
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonLeftEyeConfiguration.asset
@@ -0,0 +1,49 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 0}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 3861555276e1a07489d3c0971e091e46, type: 3}
+  m_Name: HorizonLeftEyeConfiguration
+  m_EditorClassIdentifier: 
+  Material: {fileID: 2100000, guid: 1572b9c9d18ce204495f4fd839679656, type: 2}
+  Shader: {fileID: 4800000, guid: c26a73fa4d4517c4da30333333bf50c2, type: 3}
+  NameTextureParameter_baseColorTexture: _MainTex
+  NameTextureParameter_diffuseTexture: _MainTex
+  NameTextureParameter_metallicRoughnessTexture: _PropertiesMap
+  NameTextureParameter_specularGlossiness: _PropertiesMap
+  NameTextureParameter_normalTexture: _BumpMap
+  NameTextureParameter_occlusionTexture: _PropertiesMap
+  NameTextureParameter_emissiveTexture: _EmissiveMap
+  NameColorParameter_BaseColorFactor: _Color
+  UseColorParameter_BaseColorFactor: 0
+  NameFloatParameter_MetallicFactor: 
+  UseFloatParameter_MetallicFactor: 0
+  NameFloatParameter_RoughnessFactor: 
+  UseFloatParameter_RoughnessFactor: 0
+  NameColorParameter_DiffuseFactor: _Diffuse
+  UseColorParameter_DiffuseFactor: 0
+  KeywordsEnumerations:
+  - _SHADER_TYPE_SOLID_COLOR
+  - _SHADER_TYPE_TEXTURED
+  - _SHADER_TYPE_SKIN
+  - _SHADER_TYPE_HAIR
+  - _SHADER_TYPE_LEFT_EYE
+  - _SHADER_TYPE_RIGHT_EYE
+  - _LIGHTING_SYSTEM_UNITY
+  - _LIGHTING_SYSTEM_VERTEX_GI
+  KeywordsToEnable:
+  - _SHADER_TYPE_LEFT_EYE
+  - _LIGHTING_SYSTEM_UNITY
+  NameFloatConstants:
+  - _Shader_Type
+  - _Lighting_System
+  ValueFloatConstants:
+  - 4
+  - 0
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonLeftEyeConfiguration.asset.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonLeftEyeConfiguration.asset.meta
new file mode 100644
index 0000000000000000000000000000000000000000..da416d9fed76cee077fa7687d23239cd8516de7b
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonLeftEyeConfiguration.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: d355c44f84f94354385fea0772eb3bc7
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonLeftEyeMaterial.mat b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonLeftEyeMaterial.mat
new file mode 100644
index 0000000000000000000000000000000000000000..ebfd75a0ecce027757263058df29113ddf053928
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonLeftEyeMaterial.mat
@@ -0,0 +1,118 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: HorizonLeftEyeMaterial
+  m_Shader: {fileID: 4800000, guid: c26a73fa4d4517c4da30333333bf50c2, type: 3}
+  m_ShaderKeywords: _LIGHTING_SYSTEM_UNITY _SHADER_TYPE_LEFT_EYE
+  m_LightmapFlags: 4
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EffectsMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _PropertiesMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _AmbientOcclusionEffect: 1
+    - _BacklightScale: 0.2
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DesatAmount: 0
+    - _DesatLerp: 0
+    - _DetailNormalMapScale: 1
+    - _DirectOcclusionEffect: 1
+    - _Distortion: 0.5
+    - _DstBlend: 0
+    - _EyeUVScale: 0.5
+    - _EyeXMax: 1
+    - _EyeXMid: 0.5
+    - _EyeXMin: 0
+    - _EyeYMax: 1
+    - _EyeYMid: 0.5
+    - _EyeYMin: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _HeadC: 0
+    - _IrisScale: 0.5
+    - _LeftEyeRight: 0
+    - _LeftEyeTX: 0
+    - _LeftEyeTY: 0
+    - _LeftEyeUp: 0
+    - _Lighting_System: 0
+    - _Metallic: 0
+    - _MinDiffuse: 0.5
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _PupilScale: 0.5
+    - _RightEyeRight: 0
+    - _RightEyeTX: 0
+    - _RightEyeTY: 0
+    - _RightEyeUp: 0
+    - _Shader_Type: 4
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _TranslucencyPower: 1
+    - _TranslucencyScale: 1
+    - _UVScale: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 1, g: 1, b: 1, a: 1}
+    - _DesatTint: {r: 0.255, g: 0.314, b: 0.502, a: 1}
+    - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
+    - _SecondaryColor: {r: 1, g: 1, b: 1, a: 1}
+    - _TertiaryColor: {r: 1, g: 1, b: 1, a: 0}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonLeftEyeMaterial.mat.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonLeftEyeMaterial.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..68a2eb9281db2c9da58dfd6047a09283ce96adc7
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonLeftEyeMaterial.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 1572b9c9d18ce204495f4fd839679656
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonRightEyeConfiguration.asset b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonRightEyeConfiguration.asset
new file mode 100644
index 0000000000000000000000000000000000000000..36159ebb7dd77047722e72eb29394c43e418df86
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonRightEyeConfiguration.asset
@@ -0,0 +1,49 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 0}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 3861555276e1a07489d3c0971e091e46, type: 3}
+  m_Name: HorizonRightEyeConfiguration
+  m_EditorClassIdentifier: 
+  Material: {fileID: 2100000, guid: b2a527667a920d64cab0c28f0a3bfe89, type: 2}
+  Shader: {fileID: 4800000, guid: c26a73fa4d4517c4da30333333bf50c2, type: 3}
+  NameTextureParameter_baseColorTexture: _MainTex
+  NameTextureParameter_diffuseTexture: _MainTex
+  NameTextureParameter_metallicRoughnessTexture: _PropertiesMap
+  NameTextureParameter_specularGlossiness: _PropertiesMap
+  NameTextureParameter_normalTexture: _BumpMap
+  NameTextureParameter_occlusionTexture: _PropertiesMap
+  NameTextureParameter_emissiveTexture: _EmissiveMap
+  NameColorParameter_BaseColorFactor: _Color
+  UseColorParameter_BaseColorFactor: 0
+  NameFloatParameter_MetallicFactor: 
+  UseFloatParameter_MetallicFactor: 0
+  NameFloatParameter_RoughnessFactor: 
+  UseFloatParameter_RoughnessFactor: 0
+  NameColorParameter_DiffuseFactor: _Diffuse
+  UseColorParameter_DiffuseFactor: 0
+  KeywordsEnumerations:
+  - _SHADER_TYPE_SOLID_COLOR
+  - _SHADER_TYPE_TEXTURED
+  - _SHADER_TYPE_SKIN
+  - _SHADER_TYPE_HAIR
+  - _SHADER_TYPE_LEFT_EYE
+  - _SHADER_TYPE_RIGHT_EYE
+  - _LIGHTING_SYSTEM_UNITY
+  - _LIGHTING_SYSTEM_VERTEX_GI
+  KeywordsToEnable:
+  - _SHADER_TYPE_RIGHT_EYE
+  - _LIGHTING_SYSTEM_UNITY
+  NameFloatConstants:
+  - _Shader_Type
+  - _Lighting_System
+  ValueFloatConstants:
+  - 5
+  - 0
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonRightEyeConfiguration.asset.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonRightEyeConfiguration.asset.meta
new file mode 100644
index 0000000000000000000000000000000000000000..1749f12063ebe41cd37abfb7276f97bcc459dbe7
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonRightEyeConfiguration.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 4dc61dd44af9e7d488b6434e88ac167e
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonRightEyeMaterial.mat b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonRightEyeMaterial.mat
new file mode 100644
index 0000000000000000000000000000000000000000..6ad7552679a96532a35fa44ffaa62db20f879bff
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonRightEyeMaterial.mat
@@ -0,0 +1,118 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: HorizonRightEyeMaterial
+  m_Shader: {fileID: 4800000, guid: c26a73fa4d4517c4da30333333bf50c2, type: 3}
+  m_ShaderKeywords: _LIGHTING_SYSTEM_UNITY _SHADER_TYPE_RIGHT_EYE
+  m_LightmapFlags: 4
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EffectsMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _PropertiesMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _AmbientOcclusionEffect: 1
+    - _BacklightScale: 0.2
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DesatAmount: 0
+    - _DesatLerp: 0
+    - _DetailNormalMapScale: 1
+    - _DirectOcclusionEffect: 1
+    - _Distortion: 0.5
+    - _DstBlend: 0
+    - _EyeUVScale: 0.5
+    - _EyeXMax: 1
+    - _EyeXMid: 0.5
+    - _EyeXMin: 0
+    - _EyeYMax: 1
+    - _EyeYMid: 0.5
+    - _EyeYMin: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _HeadC: 0
+    - _IrisScale: 0.5
+    - _LeftEyeRight: 0
+    - _LeftEyeTX: 0
+    - _LeftEyeTY: 0
+    - _LeftEyeUp: 0
+    - _Lighting_System: 0
+    - _Metallic: 0
+    - _MinDiffuse: 0.5
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _PupilScale: 0.5
+    - _RightEyeRight: 0
+    - _RightEyeTX: 0
+    - _RightEyeTY: 0
+    - _RightEyeUp: 0
+    - _Shader_Type: 5
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _TranslucencyPower: 1
+    - _TranslucencyScale: 1
+    - _UVScale: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 1, g: 1, b: 1, a: 1}
+    - _DesatTint: {r: 0.255, g: 0.314, b: 0.502, a: 1}
+    - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
+    - _SecondaryColor: {r: 1, g: 1, b: 1, a: 1}
+    - _TertiaryColor: {r: 1, g: 1, b: 1, a: 0}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonRightEyeMaterial.mat.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonRightEyeMaterial.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..94a9dd336db7bfd1d0fd95ef74bf4b880994c8cf
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonRightEyeMaterial.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: b2a527667a920d64cab0c28f0a3bfe89
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonSkinConfiguration.asset b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonSkinConfiguration.asset
new file mode 100644
index 0000000000000000000000000000000000000000..a5a73d2600c88b033bc741a32ca4257f30769039
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonSkinConfiguration.asset
@@ -0,0 +1,49 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 0}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 3861555276e1a07489d3c0971e091e46, type: 3}
+  m_Name: HorizonSkinConfiguration
+  m_EditorClassIdentifier: 
+  Material: {fileID: 2100000, guid: f712472eb4b9e494ca6fd9b89d0c5095, type: 2}
+  Shader: {fileID: 4800000, guid: c26a73fa4d4517c4da30333333bf50c2, type: 3}
+  NameTextureParameter_baseColorTexture: _MainTex
+  NameTextureParameter_diffuseTexture: _MainTex
+  NameTextureParameter_metallicRoughnessTexture: _PropertiesMap
+  NameTextureParameter_specularGlossiness: _PropertiesMap
+  NameTextureParameter_normalTexture: _BumpMap
+  NameTextureParameter_occlusionTexture: _PropertiesMap
+  NameTextureParameter_emissiveTexture: _EmissiveMap
+  NameColorParameter_BaseColorFactor: _Color
+  UseColorParameter_BaseColorFactor: 0
+  NameFloatParameter_MetallicFactor: 
+  UseFloatParameter_MetallicFactor: 0
+  NameFloatParameter_RoughnessFactor: 
+  UseFloatParameter_RoughnessFactor: 0
+  NameColorParameter_DiffuseFactor: _Diffuse
+  UseColorParameter_DiffuseFactor: 0
+  KeywordsEnumerations:
+  - _SHADER_TYPE_SOLID_COLOR
+  - _SHADER_TYPE_TEXTURED
+  - _SHADER_TYPE_SKIN
+  - _SHADER_TYPE_HAIR
+  - _SHADER_TYPE_LEFT_EYE
+  - _SHADER_TYPE_RIGHT_EYE
+  - _LIGHTING_SYSTEM_UNITY
+  - _LIGHTING_SYSTEM_VERTEX_GI
+  KeywordsToEnable:
+  - _SHADER_TYPE_SKIN
+  - _LIGHTING_SYSTEM_UNITY
+  NameFloatConstants:
+  - _Shader_Type
+  - _Lighting_System
+  ValueFloatConstants:
+  - 2
+  - 0
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonSkinConfiguration.asset.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonSkinConfiguration.asset.meta
new file mode 100644
index 0000000000000000000000000000000000000000..cf3b04814858dbe1551f2ad5d64d2062727938dc
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonSkinConfiguration.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 2b815828c466e45498c74d690226c0a4
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonSkinMaterial.mat b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonSkinMaterial.mat
new file mode 100644
index 0000000000000000000000000000000000000000..5bc86cd29761694565bfb3834e19e09ea407bfb3
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonSkinMaterial.mat
@@ -0,0 +1,118 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: HorizonSkinMaterial
+  m_Shader: {fileID: 4800000, guid: c26a73fa4d4517c4da30333333bf50c2, type: 3}
+  m_ShaderKeywords: _LIGHTING_SYSTEM_UNITY _SHADER_TYPE_SKIN
+  m_LightmapFlags: 4
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EffectsMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _PropertiesMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _AmbientOcclusionEffect: 1
+    - _BacklightScale: 0.2
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DesatAmount: 0
+    - _DesatLerp: 0
+    - _DetailNormalMapScale: 1
+    - _DirectOcclusionEffect: 1
+    - _Distortion: 0.5
+    - _DstBlend: 0
+    - _EyeUVScale: 0.5
+    - _EyeXMax: 1
+    - _EyeXMid: 0.5
+    - _EyeXMin: 0
+    - _EyeYMax: 1
+    - _EyeYMid: 0.5
+    - _EyeYMin: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _HeadC: 0
+    - _IrisScale: 0.5
+    - _LeftEyeRight: 0
+    - _LeftEyeTX: 0
+    - _LeftEyeTY: 0
+    - _LeftEyeUp: 0
+    - _Lighting_System: 0
+    - _Metallic: 0
+    - _MinDiffuse: 0.5
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _PupilScale: 0.5
+    - _RightEyeRight: 0
+    - _RightEyeTX: 0
+    - _RightEyeTY: 0
+    - _RightEyeUp: 0
+    - _Shader_Type: 2
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _TranslucencyPower: 1
+    - _TranslucencyScale: 1
+    - _UVScale: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 1, g: 1, b: 1, a: 1}
+    - _DesatTint: {r: 0.255, g: 0.314, b: 0.502, a: 1}
+    - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
+    - _SecondaryColor: {r: 1, g: 1, b: 1, a: 1}
+    - _TertiaryColor: {r: 1, g: 1, b: 1, a: 0}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonSkinMaterial.mat.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonSkinMaterial.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..915e26cdfbaaaaf703cae407acd8f4fa6c6541ce
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonSkinMaterial.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: f712472eb4b9e494ca6fd9b89d0c5095
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonSolidConfiguration.asset b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonSolidConfiguration.asset
new file mode 100644
index 0000000000000000000000000000000000000000..c348ee88b12d63d67ee2209701351a2aac1d251e
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonSolidConfiguration.asset
@@ -0,0 +1,49 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 0}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 3861555276e1a07489d3c0971e091e46, type: 3}
+  m_Name: HorizonSolidConfiguration
+  m_EditorClassIdentifier: 
+  Material: {fileID: 2100000, guid: 2360bf9338991d948af4b283d2fd4efd, type: 2}
+  Shader: {fileID: 4800000, guid: c26a73fa4d4517c4da30333333bf50c2, type: 3}
+  NameTextureParameter_baseColorTexture: _MainTex
+  NameTextureParameter_diffuseTexture: _MainTex
+  NameTextureParameter_metallicRoughnessTexture: _PropertiesMap
+  NameTextureParameter_specularGlossiness: _PropertiesMap
+  NameTextureParameter_normalTexture: _BumpMap
+  NameTextureParameter_occlusionTexture: _PropertiesMap
+  NameTextureParameter_emissiveTexture: _EmissiveMap
+  NameColorParameter_BaseColorFactor: _Color
+  UseColorParameter_BaseColorFactor: 0
+  NameFloatParameter_MetallicFactor: 
+  UseFloatParameter_MetallicFactor: 0
+  NameFloatParameter_RoughnessFactor: 
+  UseFloatParameter_RoughnessFactor: 0
+  NameColorParameter_DiffuseFactor: _Diffuse
+  UseColorParameter_DiffuseFactor: 0
+  KeywordsEnumerations:
+  - _SHADER_TYPE_SOLID_COLOR
+  - _SHADER_TYPE_TEXTURED
+  - _SHADER_TYPE_SKIN
+  - _SHADER_TYPE_HAIR
+  - _SHADER_TYPE_LEFT_EYE
+  - _SHADER_TYPE_RIGHT_EYE
+  - _LIGHTING_SYSTEM_UNITY
+  - _LIGHTING_SYSTEM_VERTEX_GI
+  KeywordsToEnable:
+  - _SHADER_TYPE_SOLID_COLOR
+  - _LIGHTING_SYSTEM_UNITY
+  NameFloatConstants:
+  - _Shader_Type
+  - _Lighting_System
+  ValueFloatConstants:
+  - 0
+  - 0
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonSolidConfiguration.asset.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonSolidConfiguration.asset.meta
new file mode 100644
index 0000000000000000000000000000000000000000..26092f9030cce96aeb5e3fdf48cff92adc7a4ae4
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonSolidConfiguration.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 260fe00c90ebd8148abcf88bca09cce2
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonSolidMaterial.mat b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonSolidMaterial.mat
new file mode 100644
index 0000000000000000000000000000000000000000..a57a9b281f1ce1db8222ca66723f725056a7a8c4
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonSolidMaterial.mat
@@ -0,0 +1,118 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: HorizonSolidMaterial
+  m_Shader: {fileID: 4800000, guid: c26a73fa4d4517c4da30333333bf50c2, type: 3}
+  m_ShaderKeywords: _LIGHTING_SYSTEM_UNITY _SHADER_TYPE_SOLID_COLOR
+  m_LightmapFlags: 4
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EffectsMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _PropertiesMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _AmbientOcclusionEffect: 1
+    - _BacklightScale: 0.2
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DesatAmount: 0
+    - _DesatLerp: 0
+    - _DetailNormalMapScale: 1
+    - _DirectOcclusionEffect: 1
+    - _Distortion: 0.5
+    - _DstBlend: 0
+    - _EyeUVScale: 0.5
+    - _EyeXMax: 1
+    - _EyeXMid: 0.5
+    - _EyeXMin: 0
+    - _EyeYMax: 1
+    - _EyeYMid: 0.5
+    - _EyeYMin: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _HeadC: 0
+    - _IrisScale: 0.5
+    - _LeftEyeRight: 0
+    - _LeftEyeTX: 0
+    - _LeftEyeTY: 0
+    - _LeftEyeUp: 0
+    - _Lighting_System: 0
+    - _Metallic: 0
+    - _MinDiffuse: 0.5
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _PupilScale: 0.5
+    - _RightEyeRight: 0
+    - _RightEyeTX: 0
+    - _RightEyeTY: 0
+    - _RightEyeUp: 0
+    - _Shader_Type: 0
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _TranslucencyPower: 1
+    - _TranslucencyScale: 1
+    - _UVScale: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 1, g: 1, b: 1, a: 1}
+    - _DesatTint: {r: 0.255, g: 0.314, b: 0.502, a: 1}
+    - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
+    - _SecondaryColor: {r: 1, g: 1, b: 1, a: 1}
+    - _TertiaryColor: {r: 1, g: 1, b: 1, a: 0}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonSolidMaterial.mat.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonSolidMaterial.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..85a225302bff600951aff50bc4813463235b4246
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonSolidMaterial.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 2360bf9338991d948af4b283d2fd4efd
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonSubMeshConfiguration.asset b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonSubMeshConfiguration.asset
new file mode 100644
index 0000000000000000000000000000000000000000..d883ffa776647364f97ca415a42a64ff62361dfd
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonSubMeshConfiguration.asset
@@ -0,0 +1,56 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 0}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 3861555276e1a07489d3c0971e091e46, type: 3}
+  m_Name: HorizonSubMeshConfiguration
+  m_EditorClassIdentifier: 
+  Material: {fileID: 2100000, guid: e7273d5cf535ce64d9ef5ce3a8fe3d61, type: 2}
+  Shader: {fileID: 4800000, guid: c26a73fa4d4517c4da30333333bf50c2, type: 3}
+  NameTextureParameter_baseColorTexture: _MainTex
+  NameTextureParameter_diffuseTexture: _MainTex
+  NameTextureParameter_metallicRoughnessTexture: _PropertiesMap
+  NameTextureParameter_specularGlossiness: _PropertiesMap
+  NameTextureParameter_normalTexture: _BumpMap
+  NameTextureParameter_occlusionTexture: _PropertiesMap
+  NameTextureParameter_emissiveTexture: _EmissiveMap
+  NameTextureParameter_flowTexture: _FlowMap
+  NameColorParameter_BaseColorFactor: _Color
+  UseColorParameter_BaseColorFactor: 0
+  NameFloatParameter_MetallicFactor: 
+  UseFloatParameter_MetallicFactor: 0
+  NameFloatParameter_RoughnessFactor: 
+  UseFloatParameter_RoughnessFactor: 0
+  NameColorParameter_DiffuseFactor: _Diffuse
+  UseColorParameter_DiffuseFactor: 0
+  KeywordsEnumerations:
+  - _SHADER_TYPE_SOLID_COLOR
+  - _SHADER_TYPE_TEXTURED
+  - _SHADER_TYPE_SKIN
+  - _SHADER_TYPE_HAIR
+  - _SHADER_TYPE_LEFT_EYE
+  - _SHADER_TYPE_RIGHT_EYE
+  - _SHADER_TYPE_SUBMESH
+  - _LIGHTING_SYSTEM_UNITY
+  - _LIGHTING_SYSTEM_VERTEX_GI
+  - MATERIAL_MODE_TEXTURE
+  - MATERIAL_MODE_VERTEX
+  KeywordsToEnable:
+  - _SHADER_TYPE_TEXTURED
+  - _LIGHTING_SYSTEM_UNITY
+  - MATERIAL_MODE_TEXTURE
+  NameFloatConstants:
+  - _Shader_Type
+  - _Lighting_System
+  - Material_Mode
+  ValueFloatConstants:
+  - 6
+  - 0
+  - 0
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonSubMeshConfiguration.asset.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonSubMeshConfiguration.asset.meta
new file mode 100644
index 0000000000000000000000000000000000000000..e21594d9765f8d5796b19734cd9fd47748ecc359
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonSubMeshConfiguration.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: f0bddb66189f23442a435aed67437255
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonSubMeshMaterial.mat b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonSubMeshMaterial.mat
new file mode 100644
index 0000000000000000000000000000000000000000..e93713b46d31176a90e193c3727d91110a481709
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonSubMeshMaterial.mat
@@ -0,0 +1,119 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: HorizonSubMeshMaterial
+  m_Shader: {fileID: 4800000, guid: c26a73fa4d4517c4da30333333bf50c2, type: 3}
+  m_ShaderKeywords: _LIGHTING_SYSTEM_UNITY _SHADER_TYPE_SUBMESH
+  m_LightmapFlags: 4
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EffectsMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _PropertiesMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _AmbientOcclusionEffect: 1
+    - _BacklightScale: 0.2
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DesatAmount: 0
+    - _DesatLerp: 0
+    - _DetailNormalMapScale: 1
+    - _DirectOcclusionEffect: 1
+    - _Distortion: 0.5
+    - _DstBlend: 0
+    - _EyeGlintFactor: 10
+    - _EyeUVScale: 0.5
+    - _EyeXMax: 1
+    - _EyeXMid: 0.5
+    - _EyeXMin: 0
+    - _EyeYMax: 1
+    - _EyeYMid: 0.5
+    - _EyeYMin: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _HeadC: 0
+    - _IrisScale: 0.5
+    - _LeftEyeRight: 0
+    - _LeftEyeTX: 0
+    - _LeftEyeTY: 0
+    - _LeftEyeUp: 0
+    - _Lighting_System: 0
+    - _Metallic: 0
+    - _MinDiffuse: 0.5
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _PupilScale: 0.5
+    - _RightEyeRight: 0
+    - _RightEyeTX: 0
+    - _RightEyeTY: 0
+    - _RightEyeUp: 0
+    - _Shader_Type: 6
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _TranslucencyPower: 1
+    - _TranslucencyScale: 1
+    - _UVScale: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 1, g: 1, b: 1, a: 1}
+    - _DesatTint: {r: 0.255, g: 0.314, b: 0.502, a: 1}
+    - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
+    - _SecondaryColor: {r: 1, g: 1, b: 1, a: 1}
+    - _TertiaryColor: {r: 1, g: 1, b: 1, a: 0}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonSubMeshMaterial.mat.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonSubMeshMaterial.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..a94d834ef861fa1b610e243047d8c08e65e724a3
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonSubMeshMaterial.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 5afcba06792682d4f956fa53623e5fd9
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonSubMeshVertexConfiguration.asset b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonSubMeshVertexConfiguration.asset
new file mode 100644
index 0000000000000000000000000000000000000000..eea41d837ea8a67fe6b05863b470f888e7bde8a6
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonSubMeshVertexConfiguration.asset
@@ -0,0 +1,56 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 0}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 3861555276e1a07489d3c0971e091e46, type: 3}
+  m_Name: HorizonSubMeshVertexConfiguration
+  m_EditorClassIdentifier: 
+  Material: {fileID: 2100000, guid: e7273d5cf535ce64d9ef5ce3a8fe3d61, type: 2}
+  Shader: {fileID: 4800000, guid: c26a73fa4d4517c4da30333333bf50c2, type: 3}
+  NameTextureParameter_baseColorTexture: _MainTex
+  NameTextureParameter_diffuseTexture: _MainTex
+  NameTextureParameter_metallicRoughnessTexture: _PropertiesMap
+  NameTextureParameter_specularGlossiness: _PropertiesMap
+  NameTextureParameter_normalTexture: _BumpMap
+  NameTextureParameter_occlusionTexture: _PropertiesMap
+  NameTextureParameter_emissiveTexture: _EmissiveMap
+  NameTextureParameter_flowTexture: _FlowMap
+  NameColorParameter_BaseColorFactor: _Color
+  UseColorParameter_BaseColorFactor: 0
+  NameFloatParameter_MetallicFactor: 
+  UseFloatParameter_MetallicFactor: 0
+  NameFloatParameter_RoughnessFactor: 
+  UseFloatParameter_RoughnessFactor: 0
+  NameColorParameter_DiffuseFactor: _Diffuse
+  UseColorParameter_DiffuseFactor: 0
+  KeywordsEnumerations:
+  - _SHADER_TYPE_SOLID_COLOR
+  - _SHADER_TYPE_TEXTURED
+  - _SHADER_TYPE_SKIN
+  - _SHADER_TYPE_HAIR
+  - _SHADER_TYPE_LEFT_EYE
+  - _SHADER_TYPE_RIGHT_EYE
+  - _SHADER_TYPE_SUBMESH
+  - _LIGHTING_SYSTEM_UNITY
+  - _LIGHTING_SYSTEM_VERTEX_GI
+  - MATERIAL_MODE_TEXTURE
+  - MATERIAL_MODE_VERTEX
+  KeywordsToEnable:
+  - _SHADER_TYPE_TEXTURED
+  - _LIGHTING_SYSTEM_UNITY
+  - MATERIAL_MODE_TEXTURE
+  NameFloatConstants:
+  - _Shader_Type
+  - _Lighting_System
+  - Material_Mode
+  ValueFloatConstants:
+  - 6
+  - 0
+  - 0
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonSubMeshVertexConfiguration.asset.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonSubMeshVertexConfiguration.asset.meta
new file mode 100644
index 0000000000000000000000000000000000000000..91f97821e321c8438ebd7e4e8c7d8df4f89f79a0
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonSubMeshVertexConfiguration.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 5b91b15777c951e45a89740a9fb1d75b
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonTexturedConfiguration.asset b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonTexturedConfiguration.asset
new file mode 100644
index 0000000000000000000000000000000000000000..8b0477166a1164f6e811ee8094a0b77180423a42
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonTexturedConfiguration.asset
@@ -0,0 +1,55 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 0}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 3861555276e1a07489d3c0971e091e46, type: 3}
+  m_Name: HorizonTexturedConfiguration
+  m_EditorClassIdentifier: 
+  Material: {fileID: 2100000, guid: e7273d5cf535ce64d9ef5ce3a8fe3d61, type: 2}
+  Shader: {fileID: 4800000, guid: c26a73fa4d4517c4da30333333bf50c2, type: 3}
+  NameTextureParameter_baseColorTexture: _MainTex
+  NameTextureParameter_diffuseTexture: _MainTex
+  NameTextureParameter_metallicRoughnessTexture: _PropertiesMap
+  NameTextureParameter_specularGlossiness: _PropertiesMap
+  NameTextureParameter_normalTexture: _BumpMap
+  NameTextureParameter_occlusionTexture: _PropertiesMap
+  NameTextureParameter_emissiveTexture: _EmissiveMap
+  NameTextureParameter_flowTexture: _FlowMap
+  NameColorParameter_BaseColorFactor: _Color
+  UseColorParameter_BaseColorFactor: 0
+  NameFloatParameter_MetallicFactor: 
+  UseFloatParameter_MetallicFactor: 0
+  NameFloatParameter_RoughnessFactor: 
+  UseFloatParameter_RoughnessFactor: 0
+  NameColorParameter_DiffuseFactor: _Diffuse
+  UseColorParameter_DiffuseFactor: 0
+  KeywordsEnumerations:
+  - _SHADER_TYPE_SOLID_COLOR
+  - _SHADER_TYPE_TEXTURED
+  - _SHADER_TYPE_SKIN
+  - _SHADER_TYPE_HAIR
+  - _SHADER_TYPE_LEFT_EYE
+  - _SHADER_TYPE_RIGHT_EYE
+  - _LIGHTING_SYSTEM_UNITY
+  - _LIGHTING_SYSTEM_VERTEX_GI
+  - MATERIAL_MODE_TEXTURE
+  - MATERIAL_MODE_VERTEX
+  KeywordsToEnable:
+  - _SHADER_TYPE_TEXTURED
+  - _LIGHTING_SYSTEM_UNITY
+  - MATERIAL_MODE_VERTEX
+  NameFloatConstants:
+  - _Shader_Type
+  - _Lighting_System
+  - Material_Mode
+  ValueFloatConstants:
+  - 1
+  - 0
+  - 1
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonTexturedConfiguration.asset.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonTexturedConfiguration.asset.meta
new file mode 100644
index 0000000000000000000000000000000000000000..01dcc6f04c2ab809856af0b235bd0f71471f610a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonTexturedConfiguration.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: c77a93072aee9da4b8fc36eeb125dc89
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonTexturedMaterial.mat b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonTexturedMaterial.mat
new file mode 100644
index 0000000000000000000000000000000000000000..37d63dafd695577ef10864354eae9a754287facd
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonTexturedMaterial.mat
@@ -0,0 +1,121 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: HorizonTexturedMaterial
+  m_Shader: {fileID: 4800000, guid: c26a73fa4d4517c4da30333333bf50c2, type: 3}
+  m_ShaderKeywords: MATERIAL_MODE_TEXTURE _LIGHTING_SYSTEM_UNITY _SHADER_TYPE_TEXTURED
+  m_LightmapFlags: 4
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EffectsMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _PropertiesMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - Material_Mode: 0
+    - _AmbientOcclusionEffect: 1
+    - _BacklightScale: 0.2
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DesatAmount: 0
+    - _DesatLerp: 0
+    - _DetailNormalMapScale: 1
+    - _DirectOcclusionEffect: 1
+    - _Distortion: 0.5
+    - _DstBlend: 0
+    - _EyeGlintFactor: 10
+    - _EyeUVScale: 0.5
+    - _EyeXMax: 1
+    - _EyeXMid: 0.5
+    - _EyeXMin: 0
+    - _EyeYMax: 1
+    - _EyeYMid: 0.5
+    - _EyeYMin: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _HeadC: 0
+    - _IrisScale: 0.5
+    - _LeftEyeRight: 0
+    - _LeftEyeTX: 0
+    - _LeftEyeTY: 0
+    - _LeftEyeUp: 0
+    - _Lighting_System: 0
+    - _Metallic: 0
+    - _MinDiffuse: 0.5
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _PupilScale: 0.5
+    - _RightEyeRight: 0
+    - _RightEyeTX: 0
+    - _RightEyeTY: 0
+    - _RightEyeUp: 0
+    - _Shader_Type: 1
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _TranslucencyPower: 1
+    - _TranslucencyScale: 1
+    - _UVScale: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 1, g: 1, b: 1, a: 1}
+    - _DesatTint: {r: 0.255, g: 0.314, b: 0.502, a: 1}
+    - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
+    - _SecondaryColor: {r: 1, g: 1, b: 1, a: 1}
+    - _TertiaryColor: {r: 1, g: 1, b: 1, a: 0}
+  m_BuildTextureStacks: []
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonTexturedMaterial.mat.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonTexturedMaterial.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..96d5731336229cad48c865d1b4223e73ee2c05b8
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonTexturedMaterial.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: e7273d5cf535ce64d9ef5ce3a8fe3d61
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonVertexConfiguration.asset b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonVertexConfiguration.asset
new file mode 100644
index 0000000000000000000000000000000000000000..960941bbe23000e344fad1f74ee7d569fbd74d20
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonVertexConfiguration.asset
@@ -0,0 +1,55 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 0}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 3861555276e1a07489d3c0971e091e46, type: 3}
+  m_Name: HorizonVertexConfiguration
+  m_EditorClassIdentifier: 
+  Material: {fileID: 2100000, guid: e7273d5cf535ce64d9ef5ce3a8fe3d61, type: 2}
+  Shader: {fileID: 4800000, guid: c26a73fa4d4517c4da30333333bf50c2, type: 3}
+  NameTextureParameter_baseColorTexture: _MainTex
+  NameTextureParameter_diffuseTexture: _MainTex
+  NameTextureParameter_metallicRoughnessTexture: _PropertiesMap
+  NameTextureParameter_specularGlossiness: _PropertiesMap
+  NameTextureParameter_normalTexture: _BumpMap
+  NameTextureParameter_occlusionTexture: _PropertiesMap
+  NameTextureParameter_emissiveTexture: _EmissiveMap
+  NameTextureParameter_flowTexture: _FlowMap
+  NameColorParameter_BaseColorFactor: _Color
+  UseColorParameter_BaseColorFactor: 0
+  NameFloatParameter_MetallicFactor: 
+  UseFloatParameter_MetallicFactor: 0
+  NameFloatParameter_RoughnessFactor: 
+  UseFloatParameter_RoughnessFactor: 0
+  NameColorParameter_DiffuseFactor: _Diffuse
+  UseColorParameter_DiffuseFactor: 0
+  KeywordsEnumerations:
+  - _SHADER_TYPE_SOLID_COLOR
+  - _SHADER_TYPE_TEXTURED
+  - _SHADER_TYPE_SKIN
+  - _SHADER_TYPE_HAIR
+  - _SHADER_TYPE_LEFT_EYE
+  - _SHADER_TYPE_RIGHT_EYE
+  - _LIGHTING_SYSTEM_UNITY
+  - _LIGHTING_SYSTEM_VERTEX_GI
+  - MATERIAL_MODE_TEXTURE
+  - MATERIAL_MODE_VERTEX
+  KeywordsToEnable:
+  - _SHADER_TYPE_TEXTURED
+  - _LIGHTING_SYSTEM_UNITY
+  - MATERIAL_MODE_VERTEX
+  NameFloatConstants:
+  - _Shader_Type
+  - _Lighting_System
+  - Material_Mode
+  ValueFloatConstants:
+  - 1
+  - 0
+  - 1
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonVertexConfiguration.asset.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonVertexConfiguration.asset.meta
new file mode 100644
index 0000000000000000000000000000000000000000..14c13ead7a9f879b02d173c71ffbee29b67377d0
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Horizon/HorizonVertexConfiguration.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: d6a8914844d40284395dca35471c343b
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Human.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Human.meta
new file mode 100644
index 0000000000000000000000000000000000000000..c09021ee6ae14f0cb8ae35ed9d050c45377dafd6
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Human.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 78d3a87b5b592094b9b25269ebde42ff
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Human/HumanConfiguration.asset b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Human/HumanConfiguration.asset
new file mode 100644
index 0000000000000000000000000000000000000000..4015b7cc221c00ac19d0beca0fa611e05bfd76bd
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Human/HumanConfiguration.asset
@@ -0,0 +1,60 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 0}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 3861555276e1a07489d3c0971e091e46, type: 3}
+  m_Name: HumanConfiguration
+  m_EditorClassIdentifier: 
+  Material: {fileID: 0}
+  Shader: {fileID: 4800000, guid: 48f513db188320345a43a70bbb34b7fe, type: 3}
+  NameTextureParameter_baseColorTexture: u_BaseColorSampler
+  NameTextureParameter_diffuseTexture: u_BaseColorSampler
+  NameTextureParameter_metallicRoughnessTexture: u_MetallicRoughnessSampler
+  NameTextureParameter_specularGlossiness: u_MetallicRoughnessSampler
+  NameTextureParameter_normalTexture: u_NormalSampler
+  NameTextureParameter_occlusionTexture: u_OcclusionSampler
+  NameTextureParameter_emissiveTexture: u_EmissiveSampler
+  NameTextureParameter_flowTexture: _FlowMap
+  NameColorParameter_BaseColorFactor: _Color
+  UseColorParameter_BaseColorFactor: 0
+  NameFloatParameter_MetallicFactor: _Metallic
+  UseFloatParameter_MetallicFactor: 0
+  NameFloatParameter_RoughnessFactor: _Roughness
+  UseFloatParameter_RoughnessFactor: 0
+  NameColorParameter_DiffuseFactor: _Diffuse
+  UseColorParameter_DiffuseFactor: 0
+  KeywordsEnumerations:
+  - LIGHTING_MODE_IBL_PLUS_PUNCTUAL
+  - LIGHTING_MODE_SH_PLUS_PUNCTUAL
+  - LIGHTING_MODE_IBL_ONLY
+  - LIGHTING_MODE_SH_ONLY
+  - LIGHTING_MODE_PUNCTUAL_ONLY
+  - MATERIAL_MODE_TEXTURE
+  - MATERIAL_MODE_VERTEX
+  KeywordsToEnable:
+  - LIGHTING_MODE_IBL_PLUS_PUNCTUAL
+  - MATERIAL_MODE_TEXTURE
+  NameFloatConstants:
+  - Material_Mode
+  ValueFloatConstants:
+  - 0
+  ExtensionConfiguration:
+    _extensionNames:
+    - FB_materials_skin
+    _entryNamesPerExtension:
+    - list:
+      - skinORMFactor
+      - subsurfaceColor
+      - thicknessFactor
+    _replacementNamesPerExtension:
+    - list:
+      - u_SkinORMFactor
+      - u_SubsurfaceColor
+      - u_ThicknessFactor
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Human/HumanConfiguration.asset.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Human/HumanConfiguration.asset.meta
new file mode 100644
index 0000000000000000000000000000000000000000..be98d88f2c4405c14f241f55d9744d37caf5e17b
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Human/HumanConfiguration.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 6136296a60f93fa4eaef79f8293bc598
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Human/HumanConfigurationVertex.asset b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Human/HumanConfigurationVertex.asset
new file mode 100644
index 0000000000000000000000000000000000000000..d195bbf095dc944ca50e176cb51c44fec03827eb
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Human/HumanConfigurationVertex.asset
@@ -0,0 +1,60 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 0}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 3861555276e1a07489d3c0971e091e46, type: 3}
+  m_Name: HumanConfigurationVertex
+  m_EditorClassIdentifier: 
+  Material: {fileID: 0}
+  Shader: {fileID: 4800000, guid: 48f513db188320345a43a70bbb34b7fe, type: 3}
+  NameTextureParameter_baseColorTexture: u_BaseColorSampler
+  NameTextureParameter_diffuseTexture: u_BaseColorSampler
+  NameTextureParameter_metallicRoughnessTexture: u_MetallicRoughnessSampler
+  NameTextureParameter_specularGlossiness: u_MetallicRoughnessSampler
+  NameTextureParameter_normalTexture: u_NormalSampler
+  NameTextureParameter_occlusionTexture: u_OcclusionSampler
+  NameTextureParameter_emissiveTexture: u_EmissiveSampler
+  NameTextureParameter_flowTexture: _FlowMap
+  NameColorParameter_BaseColorFactor: _Color
+  UseColorParameter_BaseColorFactor: 0
+  NameFloatParameter_MetallicFactor: _Metallic
+  UseFloatParameter_MetallicFactor: 0
+  NameFloatParameter_RoughnessFactor: _Roughness
+  UseFloatParameter_RoughnessFactor: 0
+  NameColorParameter_DiffuseFactor: _Diffuse
+  UseColorParameter_DiffuseFactor: 0
+  KeywordsEnumerations:
+  - LIGHTING_MODE_IBL_PLUS_PUNCTUAL
+  - LIGHTING_MODE_SH_PLUS_PUNCTUAL
+  - LIGHTING_MODE_IBL_ONLY
+  - LIGHTING_MODE_SH_ONLY
+  - LIGHTING_MODE_PUNCTUAL_ONLY
+  - MATERIAL_MODE_TEXTURE
+  - MATERIAL_MODE_VERTEX
+  KeywordsToEnable:
+  - LIGHTING_MODE_IBL_PLUS_PUNCTUAL
+  - MATERIAL_MODE_VERTEX
+  NameFloatConstants:
+  - Material_Mode
+  ValueFloatConstants:
+  - 1
+  ExtensionConfiguration:
+    _extensionNames:
+    - FB_materials_skin
+    _entryNamesPerExtension:
+    - list:
+      - skinORMFactor
+      - subsurfaceColor
+      - thicknessFactor
+    _replacementNamesPerExtension:
+    - list:
+      - u_SkinORMFactor
+      - u_SubsurfaceColor
+      - u_ThicknessFactor
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Human/HumanConfigurationVertex.asset.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Human/HumanConfigurationVertex.asset.meta
new file mode 100644
index 0000000000000000000000000000000000000000..0dcf4bf9cf1f185de28c33c21dda2f1dfc6b9176
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Human/HumanConfigurationVertex.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 140ddf9b180432b45b3c030fda428127
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Khronos.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Khronos.meta
new file mode 100644
index 0000000000000000000000000000000000000000..52dc1c53937d12d834f32d63f17a996e81b7c04f
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Khronos.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: a09970b6918aaa24b9462ab6c0d2816d
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Khronos/KhronosBabylonMatchConfiguration.asset b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Khronos/KhronosBabylonMatchConfiguration.asset
new file mode 100644
index 0000000000000000000000000000000000000000..f7a72574c8eb127753861d344d341b262f67fafb
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Khronos/KhronosBabylonMatchConfiguration.asset
@@ -0,0 +1,50 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 0}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 3861555276e1a07489d3c0971e091e46, type: 3}
+  m_Name: KhronosBabylonMatchConfiguration
+  m_EditorClassIdentifier: 
+  Material: {fileID: 0}
+  Shader: {fileID: 4800000, guid: 2a6c569e922c5e34e9c05f146aaf9c78, type: 3}
+  NameTextureParameter_baseColorTexture: u_BaseColorSampler
+  NameTextureParameter_diffuseTexture: u_BaseColorSampler
+  NameTextureParameter_metallicRoughnessTexture: u_MetallicRoughnessSampler
+  NameTextureParameter_specularGlossiness: u_MetallicRoughnessSampler
+  NameTextureParameter_normalTexture: u_NormalSampler
+  NameTextureParameter_occlusionTexture: u_OcclusionSampler
+  NameTextureParameter_emissiveTexture: u_EmissiveSampler
+  NameTextureParameter_flowTexture: _FlowMap
+  NameColorParameter_BaseColorFactor: _Color
+  UseColorParameter_BaseColorFactor: 0
+  NameFloatParameter_MetallicFactor: _Metallic
+  UseFloatParameter_MetallicFactor: 0
+  NameFloatParameter_RoughnessFactor: _Roughness
+  UseFloatParameter_RoughnessFactor: 0
+  NameColorParameter_DiffuseFactor: _Diffuse
+  UseColorParameter_DiffuseFactor: 0
+  KeywordsEnumerations:
+  - BRDF_LUT_MODE_ON
+  - BRDF_LUT_MODE_OFF
+  - MATERIAL_MODE_TEXTURE
+  - MATERIAL_MODE_VERTEX
+  KeywordsToEnable:
+  - BRDF_LUT_MODE_OFF
+  - MATERIAL_MODE_TEXTURE
+  NameFloatConstants:
+  - u_F0Factor
+  - u_DiffuseSmoothingFactor
+  - BRDF_LUT_Mode
+  - Material_Mode
+  ValueFloatConstants:
+  - 0
+  - 4
+  - 1
+  - 0
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Khronos/KhronosBabylonMatchConfiguration.asset.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Khronos/KhronosBabylonMatchConfiguration.asset.meta
new file mode 100644
index 0000000000000000000000000000000000000000..25f329f40ce35a48b809de7a231bb11a79ab1414
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Khronos/KhronosBabylonMatchConfiguration.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 8581c3cc799ee524ba06f56986275c6a
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Khronos/KhronosBabylonMatchOptimizedConfiguration.asset b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Khronos/KhronosBabylonMatchOptimizedConfiguration.asset
new file mode 100644
index 0000000000000000000000000000000000000000..0f91ffa0e4c4aef7b9f65ec6ab4279c336559a82
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Khronos/KhronosBabylonMatchOptimizedConfiguration.asset
@@ -0,0 +1,58 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 0}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 3861555276e1a07489d3c0971e091e46, type: 3}
+  m_Name: KhronosBabylonMatchOptimizedConfiguration
+  m_EditorClassIdentifier: 
+  Material: {fileID: 0}
+  Shader: {fileID: 4800000, guid: 2a6c569e922c5e34e9c05f146aaf9c78, type: 3}
+  NameTextureParameter_baseColorTexture: u_BaseColorSampler
+  NameTextureParameter_diffuseTexture: u_BaseColorSampler
+  NameTextureParameter_metallicRoughnessTexture: u_MetallicRoughnessSampler
+  NameTextureParameter_specularGlossiness: u_MetallicRoughnessSampler
+  NameTextureParameter_normalTexture: u_NormalSampler
+  NameTextureParameter_occlusionTexture: u_OcclusionSampler
+  NameTextureParameter_emissiveTexture: u_EmissiveSampler
+  NameTextureParameter_flowTexture: _FlowMap
+  NameColorParameter_BaseColorFactor: _Color
+  UseColorParameter_BaseColorFactor: 0
+  NameFloatParameter_MetallicFactor: _Metallic
+  UseFloatParameter_MetallicFactor: 0
+  NameFloatParameter_RoughnessFactor: _Roughness
+  UseFloatParameter_RoughnessFactor: 0
+  NameColorParameter_DiffuseFactor: _Diffuse
+  UseColorParameter_DiffuseFactor: 0
+  KeywordsEnumerations:
+  - BRDF_LUT_MODE_ON
+  - BRDF_LUT_MODE_OFF
+  - LIGHTING_MODE_IBL_PLUS_PUNCTUAL
+  - LIGHTING_MODE_SH_PLUS_PUNCTUAL
+  - LIGHTING_MODE_IBL_ONLY
+  - LIGHTING_MODE_SH_ONLY
+  - LIGHTING_MODE_PUNCTUAL_ONLY
+  - MATERIAL_MODE_TEXTURE
+  - MATERIAL_MODE_VERTEX
+  KeywordsToEnable:
+  - BRDF_LUT_MODE_OFF
+  - LIGHTING_MODE_IBL_PLUS_PUNCTUAL
+  - MATERIAL_MODE_TEXTURE
+  NameFloatConstants:
+  - u_F0Factor
+  - u_DiffuseSmoothingFactor
+  - BRDF_LUT_Mode
+  - Lighting_Mode
+  - Material_Mode
+  ValueFloatConstants:
+  - 0
+  - 4
+  - 1
+  - 1
+  - 0
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Khronos/KhronosBabylonMatchOptimizedConfiguration.asset.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Khronos/KhronosBabylonMatchOptimizedConfiguration.asset.meta
new file mode 100644
index 0000000000000000000000000000000000000000..11852a5694acdb7b62735becc979d1f97656421a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Khronos/KhronosBabylonMatchOptimizedConfiguration.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: bdc73cf1b98d8184c8a6d142874b6733
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Khronos/KhronosBabylonVertexConfiguration.asset b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Khronos/KhronosBabylonVertexConfiguration.asset
new file mode 100644
index 0000000000000000000000000000000000000000..ecdb0894cb93eb706441ee123057c6e7e3639c08
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Khronos/KhronosBabylonVertexConfiguration.asset
@@ -0,0 +1,50 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 0}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 3861555276e1a07489d3c0971e091e46, type: 3}
+  m_Name: KhronosBabylonVertexConfiguration
+  m_EditorClassIdentifier: 
+  Material: {fileID: 0}
+  Shader: {fileID: 4800000, guid: 2a6c569e922c5e34e9c05f146aaf9c78, type: 3}
+  NameTextureParameter_baseColorTexture: u_BaseColorSampler
+  NameTextureParameter_diffuseTexture: u_BaseColorSampler
+  NameTextureParameter_metallicRoughnessTexture: u_MetallicRoughnessSampler
+  NameTextureParameter_specularGlossiness: u_MetallicRoughnessSampler
+  NameTextureParameter_normalTexture: u_NormalSampler
+  NameTextureParameter_occlusionTexture: u_OcclusionSampler
+  NameTextureParameter_emissiveTexture: u_EmissiveSampler
+  NameTextureParameter_flowTexture: _FlowMap
+  NameColorParameter_BaseColorFactor: _Color
+  UseColorParameter_BaseColorFactor: 0
+  NameFloatParameter_MetallicFactor: _Metallic
+  UseFloatParameter_MetallicFactor: 0
+  NameFloatParameter_RoughnessFactor: _Roughness
+  UseFloatParameter_RoughnessFactor: 0
+  NameColorParameter_DiffuseFactor: _Diffuse
+  UseColorParameter_DiffuseFactor: 0
+  KeywordsEnumerations:
+  - BRDF_LUT_MODE_ON
+  - BRDF_LUT_MODE_OFF
+  - MATERIAL_MODE_TEXTURE
+  - MATERIAL_MODE_VERTEX
+  KeywordsToEnable:
+  - BRDF_LUT_MODE_OFF
+  - MATERIAL_MODE_VERTEX
+  NameFloatConstants:
+  - u_F0Factor
+  - u_DiffuseSmoothingFactor
+  - BRDF_LUT_Mode
+  - Material_Mode
+  ValueFloatConstants:
+  - 0
+  - 4
+  - 1
+  - 1
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Khronos/KhronosBabylonVertexConfiguration.asset.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Khronos/KhronosBabylonVertexConfiguration.asset.meta
new file mode 100644
index 0000000000000000000000000000000000000000..688b1e017c69ec9c1d96e863f1462f6099539914
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Khronos/KhronosBabylonVertexConfiguration.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 97b3adcf896459e42a47943963d32f6c
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Khronos/KhronosConfiguration.asset b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Khronos/KhronosConfiguration.asset
new file mode 100644
index 0000000000000000000000000000000000000000..996284fa87670615f4db03b6c9efbcb2f7ce666c
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Khronos/KhronosConfiguration.asset
@@ -0,0 +1,45 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 0}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 3861555276e1a07489d3c0971e091e46, type: 3}
+  m_Name: KhronosConfiguration
+  m_EditorClassIdentifier: 
+  Material: {fileID: 0}
+  Shader: {fileID: 4800000, guid: 2a6c569e922c5e34e9c05f146aaf9c78, type: 3}
+  NameTextureParameter_baseColorTexture: u_BaseColorSampler
+  NameTextureParameter_diffuseTexture: u_BaseColorSampler
+  NameTextureParameter_metallicRoughnessTexture: u_MetallicRoughnessSampler
+  NameTextureParameter_specularGlossiness: u_MetallicRoughnessSampler
+  NameTextureParameter_normalTexture: u_NormalSampler
+  NameTextureParameter_occlusionTexture: u_OcclusionSampler
+  NameTextureParameter_emissiveTexture: u_EmissiveSampler
+  NameTextureParameter_flowTexture: _FlowMap
+  NameColorParameter_BaseColorFactor: _Color
+  UseColorParameter_BaseColorFactor: 0
+  NameFloatParameter_MetallicFactor: _Metallic
+  UseFloatParameter_MetallicFactor: 0
+  NameFloatParameter_RoughnessFactor: _Roughness
+  UseFloatParameter_RoughnessFactor: 0
+  NameColorParameter_DiffuseFactor: _Diffuse
+  UseColorParameter_DiffuseFactor: 0
+  KeywordsEnumerations:
+  - LIGHTING_MODE_IBL_PLUS_PUNCTUAL
+  - LIGHTING_MODE_SH_PLUS_PUNCTUAL
+  - LIGHTING_MODE_IBL_ONLY
+  - LIGHTING_MODE_SH_ONLY
+  - LIGHTING_MODE_PUNCTUAL_ONLY
+  - MATERIAL_MODE_TEXTURE
+  - MATERIAL_MODE_VERTEX
+  KeywordsToEnable:
+  - LIGHTING_MODE_IBL_PLUS_PUNCTUAL
+  - MATERIAL_MODE_TEXTURE
+  NameFloatConstants: []
+  ValueFloatConstants: []
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Khronos/KhronosConfiguration.asset.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Khronos/KhronosConfiguration.asset.meta
new file mode 100644
index 0000000000000000000000000000000000000000..2c377943f170c7966f9712c36a99330b08b3cd3b
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Khronos/KhronosConfiguration.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 4e831d6cee3eb114d8ac7795000c0f7f
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Library.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Library.meta
new file mode 100644
index 0000000000000000000000000000000000000000..f2a145bea9808db909698e6613a9cd656532fec7
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Library.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 6c086d120a6fb5a49bccb219a82e338a
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Library/LibraryConfiguration.asset b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Library/LibraryConfiguration.asset
new file mode 100644
index 0000000000000000000000000000000000000000..a5f67e7826e069e140701f0d28dda7d968ecd490
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Library/LibraryConfiguration.asset
@@ -0,0 +1,53 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 0}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 3861555276e1a07489d3c0971e091e46, type: 3}
+  m_Name: LibraryConfiguration
+  m_EditorClassIdentifier: 
+  Material: {fileID: 0}
+  Shader: {fileID: 4800000, guid: 3d4ea02cfcf80e344b03f3d685b3aaee, type: 3}
+  NameTextureParameter_baseColorTexture: u_BaseColorSampler
+  NameTextureParameter_diffuseTexture: u_BaseColorSampler
+  NameTextureParameter_metallicRoughnessTexture: u_MetallicRoughnessSampler
+  NameTextureParameter_specularGlossiness: u_MetallicRoughnessSampler
+  NameTextureParameter_normalTexture: u_NormalSampler
+  NameTextureParameter_occlusionTexture: u_OcclusionSampler
+  NameTextureParameter_emissiveTexture: u_EmissiveSampler
+  NameTextureParameter_flowTexture: _FlowMap
+  NameColorParameter_BaseColorFactor: _Color
+  UseColorParameter_BaseColorFactor: 0
+  NameFloatParameter_MetallicFactor: _Metallic
+  UseFloatParameter_MetallicFactor: 0
+  NameFloatParameter_RoughnessFactor: _Roughness
+  UseFloatParameter_RoughnessFactor: 0
+  NameColorParameter_DiffuseFactor: _Diffuse
+  UseColorParameter_DiffuseFactor: 0
+  KeywordsEnumerations:
+  - LIGHTING_MODE_IBL_PLUS_PUNCTUAL
+  - LIGHTING_MODE_SH_PLUS_PUNCTUAL
+  - LIGHTING_MODE_IBL_ONLY
+  - LIGHTING_MODE_SH_ONLY
+  - LIGHTING_MODE_PUNCTUAL_ONLY
+  - HAS_VERTEX_COLOR_float4
+  - MATERIAL_MODE_TEXTURE
+  - MATERIAL_MODE_VERTEX
+  KeywordsToEnable:
+  - LIGHTING_MODE_IBL_PLUS_PUNCTUAL
+  - HAS_VERTEX_COLOR_float4
+  - MATERIAL_MODE_TEXTURE
+  NameFloatConstants:
+  - Material_Mode
+  ValueFloatConstants:
+  - 0
+  ExtensionConfiguration:
+    _extensionNames: []
+    _entryNamesPerExtension: []
+    _replacementNamesPerExtension: []
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Library/LibraryConfiguration.asset.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Library/LibraryConfiguration.asset.meta
new file mode 100644
index 0000000000000000000000000000000000000000..0fec4e92a098ded04e717439b5c5fb16946da252
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Library/LibraryConfiguration.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 4b6a3518752ad734b93618f043251a60
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Library/LibraryConfigurationVertex.asset b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Library/LibraryConfigurationVertex.asset
new file mode 100644
index 0000000000000000000000000000000000000000..876afaffeb47f286ecda71ca4692d56ea25c235b
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Library/LibraryConfigurationVertex.asset
@@ -0,0 +1,53 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 0}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 3861555276e1a07489d3c0971e091e46, type: 3}
+  m_Name: LibraryConfigurationVertex
+  m_EditorClassIdentifier: 
+  Material: {fileID: 0}
+  Shader: {fileID: 4800000, guid: 3d4ea02cfcf80e344b03f3d685b3aaee, type: 3}
+  NameTextureParameter_baseColorTexture: u_BaseColorSampler
+  NameTextureParameter_diffuseTexture: u_BaseColorSampler
+  NameTextureParameter_metallicRoughnessTexture: u_MetallicRoughnessSampler
+  NameTextureParameter_specularGlossiness: u_MetallicRoughnessSampler
+  NameTextureParameter_normalTexture: u_NormalSampler
+  NameTextureParameter_occlusionTexture: u_OcclusionSampler
+  NameTextureParameter_emissiveTexture: u_EmissiveSampler
+  NameTextureParameter_flowTexture: _FlowMap
+  NameColorParameter_BaseColorFactor: _Color
+  UseColorParameter_BaseColorFactor: 0
+  NameFloatParameter_MetallicFactor: _Metallic
+  UseFloatParameter_MetallicFactor: 0
+  NameFloatParameter_RoughnessFactor: _Roughness
+  UseFloatParameter_RoughnessFactor: 0
+  NameColorParameter_DiffuseFactor: _Diffuse
+  UseColorParameter_DiffuseFactor: 0
+  KeywordsEnumerations:
+  - LIGHTING_MODE_IBL_PLUS_PUNCTUAL
+  - LIGHTING_MODE_SH_PLUS_PUNCTUAL
+  - LIGHTING_MODE_IBL_ONLY
+  - LIGHTING_MODE_SH_ONLY
+  - LIGHTING_MODE_PUNCTUAL_ONLY
+  - HAS_VERTEX_COLOR_float4
+  - MATERIAL_MODE_TEXTURE
+  - MATERIAL_MODE_VERTEX
+  KeywordsToEnable:
+  - LIGHTING_MODE_IBL_PLUS_PUNCTUAL
+  - HAS_VERTEX_COLOR_float4
+  - MATERIAL_MODE_TEXTURE
+  NameFloatConstants:
+  - Material_Mode
+  ValueFloatConstants:
+  - 0
+  ExtensionConfiguration:
+    _extensionNames: []
+    _entryNamesPerExtension: []
+    _replacementNamesPerExtension: []
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Library/LibraryConfigurationVertex.asset.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Library/LibraryConfigurationVertex.asset.meta
new file mode 100644
index 0000000000000000000000000000000000000000..4d25e3a21f60b823c4be246f15a940889afcc920
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/Library/LibraryConfigurationVertex.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: c325f1f69cda2e745a527acd748ef2c6
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 11400000
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/UnityMobile.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/UnityMobile.meta
new file mode 100644
index 0000000000000000000000000000000000000000..17f593fe852ed7e53738762beb560208facb1c92
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/UnityMobile.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: d2701f2995db69e40805de2cb08417c1
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/UnityMobile/MobileBumpSpecShaderConfiguration.asset b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/UnityMobile/MobileBumpSpecShaderConfiguration.asset
new file mode 100644
index 0000000000000000000000000000000000000000..c731891575d5ed671315af65a7482c069ce38406
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/UnityMobile/MobileBumpSpecShaderConfiguration.asset
@@ -0,0 +1,39 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 0}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 3861555276e1a07489d3c0971e091e46, type: 3}
+  m_Name: MobileBumpSpecShaderConfiguration
+  m_EditorClassIdentifier: 
+  Material: {fileID: 0}
+  Shader: {fileID: 4800000, guid: 4bbfef03382cb7e4e8ffefdd492e6fa0, type: 3}
+  NameTextureParameter_baseColorTexture: _MainTex
+  NameTextureParameter_diffuseTexture: _MainTex
+  NameTextureParameter_metallicRoughnessTexture: _OcclusionMetallicRoughnessMap
+  NameTextureParameter_specularGlossiness: _OcclusionMetallicRoughnessMap
+  NameTextureParameter_normalTexture: _BumpMap
+  NameTextureParameter_occlusionTexture: _OcclusionMetallicRoughnessMap
+  NameTextureParameter_emissiveTexture: _EmissiveMap
+  NameColorParameter_BaseColorFactor: _Color
+  UseColorParameter_BaseColorFactor: 0
+  NameFloatParameter_MetallicFactor: _Metallic
+  UseFloatParameter_MetallicFactor: 0
+  NameFloatParameter_RoughnessFactor: _Roughness
+  UseFloatParameter_RoughnessFactor: 0
+  NameColorParameter_DiffuseFactor: _Diffuse
+  UseColorParameter_DiffuseFactor: 0
+  KeywordsEnumerations: []
+  KeywordsToEnable: []
+  NameFloatConstants:
+  - _RoughnessFactor
+  - _Shininess
+  ValueFloatConstants:
+  - 0.8
+  - 0.078125
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/UnityMobile/MobileBumpSpecShaderConfiguration.asset.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/UnityMobile/MobileBumpSpecShaderConfiguration.asset.meta
new file mode 100644
index 0000000000000000000000000000000000000000..865c415982fa1a89df059d7bb921c998d6da7294
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/UnityMobile/MobileBumpSpecShaderConfiguration.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: b24898be37576054abb95cc32f5ab8bd
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/UnityMobile/MobileCustomShaderConfiguration.asset b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/UnityMobile/MobileCustomShaderConfiguration.asset
new file mode 100644
index 0000000000000000000000000000000000000000..3b2250b9f183f2e6a3528f6a06fcf3c1510afb94
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/UnityMobile/MobileCustomShaderConfiguration.asset
@@ -0,0 +1,39 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 0}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 3861555276e1a07489d3c0971e091e46, type: 3}
+  m_Name: MobileCustomShaderConfiguration
+  m_EditorClassIdentifier: 
+  Material: {fileID: 0}
+  Shader: {fileID: 4800000, guid: fd3a9e2d35990a74487ef0d8cd8320ad, type: 3}
+  NameTextureParameter_baseColorTexture: _MainTex
+  NameTextureParameter_diffuseTexture: _MainTex
+  NameTextureParameter_metallicRoughnessTexture: _OcclusionMetallicRoughnessMap
+  NameTextureParameter_specularGlossiness: _OcclusionMetallicRoughnessMap
+  NameTextureParameter_normalTexture: _BumpMap
+  NameTextureParameter_occlusionTexture: _OcclusionMetallicRoughnessMap
+  NameTextureParameter_emissiveTexture: _EmissiveMap
+  NameColorParameter_BaseColorFactor: _Color
+  UseColorParameter_BaseColorFactor: 0
+  NameFloatParameter_MetallicFactor: _Metallic
+  UseFloatParameter_MetallicFactor: 0
+  NameFloatParameter_RoughnessFactor: _Roughness
+  UseFloatParameter_RoughnessFactor: 0
+  NameColorParameter_DiffuseFactor: _Diffuse
+  UseColorParameter_DiffuseFactor: 0
+  KeywordsEnumerations: []
+  KeywordsToEnable: []
+  NameFloatConstants:
+  - _RoughnessFactor
+  - _Shininess
+  ValueFloatConstants:
+  - 0.8
+  - 0.078125
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/UnityMobile/MobileCustomShaderConfiguration.asset.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/UnityMobile/MobileCustomShaderConfiguration.asset.meta
new file mode 100644
index 0000000000000000000000000000000000000000..c4d5918160c50c0f3aa40d5e3423f28b08da8801
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/UnityMobile/MobileCustomShaderConfiguration.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: f3da74418c1aeb8419a576c15c8f081e
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/UnityMobile/MobileDiffuseShaderConfiguration.asset b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/UnityMobile/MobileDiffuseShaderConfiguration.asset
new file mode 100644
index 0000000000000000000000000000000000000000..4f92f3d00d86a04564bae7382b7a971c95400c8d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/UnityMobile/MobileDiffuseShaderConfiguration.asset
@@ -0,0 +1,35 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 0}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 3861555276e1a07489d3c0971e091e46, type: 3}
+  m_Name: MobileDiffuseShaderConfiguration
+  m_EditorClassIdentifier: 
+  Material: {fileID: 0}
+  Shader: {fileID: 4800000, guid: ed2dc23baab27c847a0193bc6dce1816, type: 3}
+  NameTextureParameter_baseColorTexture: _MainTex
+  NameTextureParameter_diffuseTexture: _MainTex
+  NameTextureParameter_metallicRoughnessTexture: _MetallicGlossMap
+  NameTextureParameter_specularGlossiness: _Specular
+  NameTextureParameter_normalTexture: _BumpMap
+  NameTextureParameter_occlusionTexture: _OcclusionMap
+  NameTextureParameter_emissiveTexture: _EmissiveMap
+  NameColorParameter_BaseColorFactor: _Color
+  UseColorParameter_BaseColorFactor: 0
+  NameFloatParameter_MetallicFactor: _Metallic
+  UseFloatParameter_MetallicFactor: 0
+  NameFloatParameter_RoughnessFactor: _Roughness
+  UseFloatParameter_RoughnessFactor: 0
+  NameColorParameter_DiffuseFactor: _Diffuse
+  UseColorParameter_DiffuseFactor: 0
+  KeywordsEnumerations: []
+  KeywordsToEnable: []
+  NameFloatConstants: []
+  ValueFloatConstants: []
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/UnityMobile/MobileDiffuseShaderConfiguration.asset.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/UnityMobile/MobileDiffuseShaderConfiguration.asset.meta
new file mode 100644
index 0000000000000000000000000000000000000000..621150a1baa80c995dfcbd5e1cc0a0248cbf3b70
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/UnityMobile/MobileDiffuseShaderConfiguration.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: faf5a921c93b47843a50c991e149f023
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/UnityMobile/MobileVertexLitShaderConfiguration.asset b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/UnityMobile/MobileVertexLitShaderConfiguration.asset
new file mode 100644
index 0000000000000000000000000000000000000000..e9b657c1e3c6c8431f87d9bdf8759adcd1959699
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/UnityMobile/MobileVertexLitShaderConfiguration.asset
@@ -0,0 +1,35 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 0}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 3861555276e1a07489d3c0971e091e46, type: 3}
+  m_Name: MobileVertexLitShaderConfiguration
+  m_EditorClassIdentifier: 
+  Material: {fileID: 0}
+  Shader: {fileID: 4800000, guid: 9b411f78255f62b45a36763ce7382ad1, type: 3}
+  NameTextureParameter_baseColorTexture: _MainTex
+  NameTextureParameter_diffuseTexture: _MainTex
+  NameTextureParameter_metallicRoughnessTexture: _MetallicGlossMap
+  NameTextureParameter_specularGlossiness: _Specular
+  NameTextureParameter_normalTexture: _BumpMap
+  NameTextureParameter_occlusionTexture: _OcclusionMap
+  NameTextureParameter_emissiveTexture: _EmissiveMap
+  NameColorParameter_BaseColorFactor: _Color
+  UseColorParameter_BaseColorFactor: 0
+  NameFloatParameter_MetallicFactor: _Metallic
+  UseFloatParameter_MetallicFactor: 0
+  NameFloatParameter_RoughnessFactor: _Roughness
+  UseFloatParameter_RoughnessFactor: 0
+  NameColorParameter_DiffuseFactor: _Diffuse
+  UseColorParameter_DiffuseFactor: 0
+  KeywordsEnumerations: []
+  KeywordsToEnable: []
+  NameFloatConstants: []
+  ValueFloatConstants: []
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/UnityMobile/MobileVertexLitShaderConfiguration.asset.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/UnityMobile/MobileVertexLitShaderConfiguration.asset.meta
new file mode 100644
index 0000000000000000000000000000000000000000..7818b294c5bbdab9b436e768d994beec21c3fe88
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/UnityMobile/MobileVertexLitShaderConfiguration.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 2a617ea217bb9814a96034298e387a43
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/UnityStandard.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/UnityStandard.meta
new file mode 100644
index 0000000000000000000000000000000000000000..eda5ddcd483b2128ad086a60ae753c780e089e68
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/UnityStandard.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: a98e9f796aeb94a4288e33d7fa6aa492
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/UnityStandard/UnityStandardConfiguration.asset b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/UnityStandard/UnityStandardConfiguration.asset
new file mode 100644
index 0000000000000000000000000000000000000000..6ca55469495d1cb3f7faf937ffe87f78b6f9e267
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/UnityStandard/UnityStandardConfiguration.asset
@@ -0,0 +1,46 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 0}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 3861555276e1a07489d3c0971e091e46, type: 3}
+  m_Name: UnityStandardConfiguration
+  m_EditorClassIdentifier: 
+  Material: {fileID: 0}
+  Shader: {fileID: 4800000, guid: a137d245417b76a4bb089303f90043ce, type: 3}
+  NameTextureParameter_baseColorTexture: _MainTex
+  NameTextureParameter_diffuseTexture: _MainTex
+  NameTextureParameter_metallicRoughnessTexture: _MetallicGlossMap
+  NameTextureParameter_specularGlossiness: _Specular
+  NameTextureParameter_normalTexture: _BumpMap
+  NameTextureParameter_occlusionTexture: _OcclusionMap
+  NameTextureParameter_emissiveTexture: _EmissiveMap
+  NameColorParameter_BaseColorFactor: _Color
+  UseColorParameter_BaseColorFactor: 0
+  NameFloatParameter_MetallicFactor: _Metallic
+  UseFloatParameter_MetallicFactor: 0
+  NameFloatParameter_RoughnessFactor: _Roughness
+  UseFloatParameter_RoughnessFactor: 0
+  NameColorParameter_DiffuseFactor: _Diffuse
+  UseColorParameter_DiffuseFactor: 0
+  KeywordsEnumerations:
+  - _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
+  - _METALLICGLOSSMAP
+  - _AVATAR_ORM_CHANNEL
+  KeywordsToEnable:
+  - _METALLICGLOSSMAP
+  - _AVATAR_ORM_CHANNEL
+  NameFloatConstants:
+  - _OcclusionStrength
+  - _GlossMapScale
+  - _SmoothnessTextureChannel
+  ValueFloatConstants:
+  - 1
+  - 1
+  - 2
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/UnityStandard/UnityStandardConfiguration.asset.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/UnityStandard/UnityStandardConfiguration.asset.meta
new file mode 100644
index 0000000000000000000000000000000000000000..51ef96aac87832fac2a9ca67d53eec42c786502c
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Configurations/UnityStandard/UnityStandardConfiguration.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 9ff896b4d4158bf4cba8a921337fe2cc
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon.meta
new file mode 100644
index 0000000000000000000000000000000000000000..95f96bba3d465a11a24dd0d6b5db46da9b485c20
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 18814c897ea9b2c4ab6028f57a30e726
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Avatar-Horizon.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Avatar-Horizon.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..265c5e28484758219f7cf9dab45871e093298150
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Avatar-Horizon.cginc
@@ -0,0 +1,456 @@
+#include "UnityCG.cginc"
+#include "../../../../Scripts/ShaderUtils/AvatarCustom.cginc"
+#include "AvatarCommon/AvatarShaderFramework.cginc"
+#include "AvatarCommon/AvatarPropertiesMap.cginc"
+
+// Define some variables for holding main tex and effects maps samples (if needed)
+// through all functions
+half4 mainTexSample;
+half4 effectsMapSample;
+float2 mainTexUV;
+half3 vertexColor;
+
+#if defined(_SHADER_TYPE_SOLID_COLOR)
+    #include "AvatarSolidColor/AvatarSolidColorProperties.cginc"
+    #include "AvatarSolidColor/AvatarSolidColorAlbedo.cginc"
+
+    half3 GetAlbedo() {
+        return SolidColorAlbedo(GetPrimaryColor().rgb);
+    }
+
+#elif defined(_SHADER_TYPE_SUBMESH) || defined(_SHADER_TYPE_TEXTURED)
+    #include "AvatarTextured/AvatarTexturedProperties.cginc"
+    #include "AvatarTextured/AvatarTexturedAlbedo.cginc"
+
+#ifdef MATERIAL_MODE_TEXTURE
+    #define SAMPLE_MAIN_TEX 1
+#endif
+
+    half3 GetAlbedo() {
+        return TexturedAlbedo(mainTexSample, vertexColor, GetPrimaryColor().rgb);
+    }
+
+#elif defined(_SHADER_TYPE_SKIN)
+    #include "AvatarSkin/AvatarSkinProperties.cginc"
+    #include "AvatarSkin/AvatarSkinAlbedo.cginc"
+    #include "AvatarSkin/AvatarSkinSurfaceFunctions.cginc"
+    #include "AvatarSkin/AvatarSkinLighting.cginc"
+
+    #define SAMPLE_MAIN_TEX 1
+    #define SAMPLE_EFFECTS_MAP 1
+
+    half3 GetAlbedo() {
+        return SkinAlbedo(mainTexSample, vertexColor, GetTertiaryColor(), effectsMapSample.b);
+    }
+
+#elif defined(_SHADER_TYPE_HAIR)
+    #include "AvatarHair/AvatarHairProperties.cginc"
+    #include "AvatarHair/AvatarHairAlbedo.cginc"
+
+    #define SAMPLE_EFFECTS_MAP 1
+
+    half3 GetAlbedo() {
+        return HairAlbedo(effectsMapSample, GetPrimaryColor().rgb, GetSecondaryColor().rgb);
+    }
+
+#elif defined(_SHADER_TYPE_LEFT_EYE) || defined(_SHADER_TYPE_RIGHT_EYE)
+    #if defined(USE_HEAD_C)
+        #include "AvatarEye/AvatarEyePropertiesHeadC.cginc"
+        #include "AvatarEye/AvatarEyeAlbedoHeadC.cginc"
+        #include "AvatarEye/AvatarEyeInterpolatorsHeadC.cginc"
+
+        // Used when calculating main texture coordinates in pixel shader
+        #define CALCULATE_MAIN_TEX_COORDS 1
+
+
+        // Calculate per pixel texture coordinates
+        float2 GetMainTexCoords(float2 inputUV) {
+            #if defined(_SHADER_TYPE_LEFT_EYE)
+                return GetNormalizedUVForLeftEye(inputUV);
+                #else
+                return GetNormalizedUVForRightEye(inputUV);
+                #endif
+        }
+
+        half3 GetAlbedo() {
+            return EyeAlbedo(mainTexSample, vertexColor, GetPrimaryColor().rgb, mainTexUV);
+        }
+
+    #else
+        #include "AvatarEye/AvatarEyeProperties.cginc"
+        #include "AvatarEye/AvatarEyeAlbedo.cginc"
+        #include "AvatarEye/AvatarEyeInterpolators.cginc"
+
+
+        half3 GetAlbedo() {
+            return EyeAlbedo(mainTexSample, vertexColor, GetPrimaryColor().rgb);
+        }
+    #endif
+
+    #define SAMPLE_MAIN_TEX 1
+
+
+#endif
+
+#if defined(_LIGHTING_SYSTEM_UNITY) || defined(_LIGHTING_SYSTEM_VERTEX_GI)
+    #include "AvatarCommon/AvatarCommonSurfaceFields.cginc"
+#if defined(_SHADER_TYPE_SUBMESH)
+    #include "AvatarSubmesh/AvatarSubmeshLighting.cginc"
+#else
+    #include "AvatarCommon/AvatarCommonLighting.cginc"
+#endif
+
+    ///////////////////////////////
+    // Surface Related Functions //
+    ///////////////////////////////
+
+    struct AvatarComponentSurfaceOutput {
+        AVATAR_COMMON_SURFACE_FIELDS
+
+        // Skin component needs more fields
+        #if defined(_SHADER_TYPE_SKIN)
+            SURFACE_ADDITIONAL_FIELDS_SKIN
+        #endif
+
+        #if defined(_SHADER_TYPE_SUBMESH)
+            float SubMeshType;
+        #endif
+    };
+
+    void Surf(v2f IN, inout AvatarComponentSurfaceOutput o) {
+
+        // All shader types have properties map
+        half4 props = SamplePropertiesMap(IN.propertiesMapUV, IN.ormt);
+
+        #if defined(SAMPLE_MAIN_TEX)
+            #if defined(CALCULATE_MAIN_TEX_COORDS)
+                mainTexUV = GetMainTexCoords(IN.uv);
+                mainTexSample = SampleMainTex(mainTexUV);
+            #else
+                mainTexUV = IN.uv;
+                mainTexSample = SampleMainTex(IN.uv);
+            #endif
+        #endif
+
+        #ifdef MATERIAL_MODE_VERTEX
+          vertexColor = IN.color.rgb;
+        #endif
+
+        #if defined(SAMPLE_EFFECTS_MAP)
+            effectsMapSample = SampleEffectsMap(IN.effectsMapUV);
+        #endif
+
+        SET_AVATAR_SHADER_SURFACE_COMMON_FIELDS(
+            o,
+            GetAlbedo(),
+            props.g, // Roughness in green channel
+            props.b, // Metallic in blue channel
+            1.0, // Alpha of 1 for now
+            props.r, // Occluson in red channel
+            OffsetAndScaleMinimumDiffuse(_MinDiffuse)) // convert min diffuse to [-1,1]
+
+        #if defined(_SHADER_TYPE_SUBMESH)
+            o.SubMeshType = IN.color.a;
+        #endif
+
+        // Skin has some additional fields that need populating
+        #if defined(_SHADER_TYPE_SKIN)
+            half backlightScale = _BacklightScale * (1.0 - effectsMapSample.g); /* Backlight scale i.e. "Should this pixel use backlight" in green channel of effects map */
+            o.Thickness = effectsMapSample.r; /* Thickness stored in red channel of effects map */
+            o.BacklightScale = backlightScale;
+            o.TranslucencyColor = GetPrimaryColor();
+            o.BacklightColor = GetSecondaryColor();
+        #endif
+    }
+
+    // Surf function has different signatures depending on lighting system
+    #if defined(_LIGHTING_SYSTEM_VERTEX_GI)
+        void Surf(v2f IN, inout AvatarComponentSurfaceOutput o, vgi_frag_tmp tmp) {
+            SET_AVATAR_SHADER_SURFACE_NORMAL_FIELD(o, tmp.normal);
+            Surf(IN, o);
+            }
+    #endif
+
+    ///////////////////////
+    // Lighting Function //
+    ///////////////////////
+
+    half4 LightingAvatarComponent(AvatarComponentSurfaceOutput s, half3 viewDir, AvatarShaderGlobalIllumination gi) {
+        AVATAR_SHADER_DECLARE_COMMON_LIGHTING_PARAMS(albedo, normal, perceptualRoughness, perceptualSmoothness, metallic, alpha, minDiffuse, s)
+
+#if defined(_SHADER_TYPE_SUBMESH)
+        float subMeshType = s.SubMeshType;
+#endif
+
+        half4 c = 0.;
+
+        #if defined(_SHADER_TYPE_SKIN)
+            c.rgb = AvatarSkinLighting(
+                albedo,
+                normal,
+                viewDir,
+                minDiffuse,
+                perceptualRoughness,
+                perceptualSmoothness,
+                metallic,
+                s.Thickness,
+                s.BacklightScale,
+                s.TranslucencyColor,
+                s.BacklightColor,
+                s.Occlusion,
+                gi);
+        #elif defined(_SHADER_TYPE_SUBMESH)
+            c.rgb = AvatarLightingSubmesh(
+                albedo,
+                normal,
+                subMeshType,
+                viewDir,
+                minDiffuse,
+                perceptualRoughness,
+                perceptualSmoothness,
+                metallic,
+                directOcclusion,
+                gi);
+        #else
+            // Common PBS lighting
+            c.rgb = AvatarLightingCommon(
+                albedo,
+                normal,
+                viewDir,
+                minDiffuse,
+                perceptualRoughness,
+                perceptualSmoothness,
+                metallic,
+                directOcclusion,
+                gi);
+        #endif
+
+        #if defined(DESAT)
+            c.rgb = Desat(c.rgb);
+        #endif
+
+        #if defined(DEBUG_TINT)
+            c.rgb *= _DebugTint.rgb;
+        #endif
+
+        c.a = alpha;
+        return c;
+    }
+#endif
+
+//////////////////
+// Interpolator //
+//////////////////
+
+void Interpolate(float2 texcoord, inout v2f o) {
+    // _MainTex texture coordinates computations (if applicable)
+    #if defined(_SHADER_TYPE_LEFT_EYE) && !defined(USE_HEAD_C)
+        o.uv.xy = GetNormalizedUVForLeftEye(texcoord);
+        o.propertiesMapUV.xy = texcoord; // properties map and main tex have same UVs
+    #elif defined(_SHADER_TYPE_RIGHT_EYE) && !defined(USE_HEAD_C)
+        o.uv.xy = GetNormalizedUVForRightEye(texcoord);
+        o.propertiesMapUV.xy = texcoord; // properties map and main tex have same UVs
+    #endif
+}
+
+#if defined(_LIGHTING_SYSTEM_VERTEX_GI)
+    // "Generic" interpolation function that fits the function
+    // prototype to be valled from the vertex shader for vertex GI lighting system
+    void Interpolate(appdata v, vgi_vert_tmp tmp, inout v2f o) {
+        // Call specific interpolation function for this shader
+        Interpolate(v.uv, o);
+    }
+#else
+    // "Generic" interpolation function that fits the function
+    // prototype to be valled from the vertex shader for non-vertex GI lighting system
+    void Interpolate(appdata v, OvrVertexData vertexData, inout v2f o) {
+        // Call specific interpolation function for this shader
+        Interpolate(v.uv, o);
+    }
+#endif
+
+struct Light
+{
+    float3 direction;
+    float range;
+
+    float3 color;
+    float intensity;
+
+    float3 position;
+    float innerConeCos;
+
+    float outerConeCos;
+    int type;
+
+    float2 padding;
+};
+
+struct MaterialProperties
+{
+    half3 albedo;
+    half3 normal;
+    half minDiffuse;
+    half smoothness;
+    half metallic;
+    half occlusion;
+    half roughness;
+    half directOcclusion;
+    half oneMinusReflectivity;
+    half3 specColor;
+    half3 diffColor;
+};
+
+MaterialProperties GetMaterialProperties(v2f IN)
+{
+    MaterialProperties props;
+    AVATAR_SHADER_FRAG_INIT_AND_CALL_SURFACE(Surf, AvatarComponentSurfaceOutput);
+
+    props.albedo = GET_AVATAR_SHADER_SURFACE_ALBEDO_FIELD(o);
+    props.normal = GET_AVATAR_SHADER_SURFACE_NORMAL_FIELD(o);
+    props.minDiffuse = GET_AVATAR_SHADER_SURFACE_MIN_DIFFUSE_FIELD(o);
+    props.smoothness = GET_AVATAR_SHADER_SURFACE_SMOOTHNESS_FIELD(o);
+    props.metallic = GET_AVATAR_SHADER_SURFACE_METALLIC_FIELD(o);
+    props.occlusion = GET_AVATAR_SHADER_SURFACE_OCCLUSION_FIELD(o);
+    props.roughness = GET_AVATAR_SHADER_SURFACE_ROUGHNESS_FIELD(o);
+    props.directOcclusion = lerp(1.0f, props.occlusion, _DirectOcclusionEffect);
+
+    half3 specColor;
+    half oneMinusReflectivity;
+    props.diffColor = DiffuseAndSpecularFromMetallic(
+        props.albedo,
+        props.metallic,
+        specColor, /*out*/
+        oneMinusReflectivity); /*out*/
+
+    props.specColor = specColor;
+    props.oneMinusReflectivity = oneMinusReflectivity;
+
+    return props;
+}
+
+half3 DirectAvatarLighting(
+    half3 viewDir,
+    MaterialProperties props,
+    Light light)
+{
+    float3 halfDirection = Unity_SafeNormalize(light.direction + viewDir);
+    half NdotV = saturate(dot(props.normal, viewDir));
+    float NdotH = saturate(dot(props.normal, halfDirection));
+    float LdotH = saturate(dot(light.direction, halfDirection));
+    half rawNdotL = clamp(dot(props.normal, light.direction), -1.0, 1.0); // -1 to 1
+    half NdotL = saturate(rawNdotL); // 0 to 1
+
+    half3 directDiffuse = DirectDiffuseLightingWithOcclusion(
+        props.diffColor,
+        rawNdotL,
+        props.minDiffuse,
+        props.directOcclusion);
+
+    half3 directSpecular = DirectSpecularLightingWithOcclusion(
+        props.specColor,
+        NdotH,
+        LdotH,
+        NdotL,
+        props.roughness,
+        props.directOcclusion);
+
+    return (directDiffuse + directSpecular) * light.color;
+}
+
+Light GetUnityLight(float3 worldPos, float3 lightDir, fixed atten)
+{
+    Light l;
+    float3 pos = _WorldSpaceLightPos0;
+    float3 toLight = _WorldSpaceLightPos0.xyz - worldPos;
+    float3 pointdir = normalize(toLight);
+    float attenuation = 1 / (1 + dot(pointdir, pointdir));
+
+    // instead of using the original attentuation algorithm we utilize the common Unity pattern with vertex shader calculation
+    // l.type = _WorldSpaceLightPos0.w < 0.5 ? LightType_Directional : LightType_Point;
+    //l.type = LightType_Directional;
+
+#if defined(DIRECTIONAL)
+        l.direction = lightDir;
+        l.range = -1.0;
+        l.color = _LightColor0; // includes both the color and amplitude, not normalized
+        l.intensity = 5.0;  // 1.0 in Unity is 5klux, also 0.2 intensity in Unity = 1.0 intensity in GLTF, this will be modulated by the color anyways
+        l.position = pos;
+        l.innerConeCos = 1.0;
+        l.outerConeCos = 0.5;
+#else
+    float3 lightCoord = mul(unity_WorldToLight, float4(worldPos, 1)).xyz;
+    float range = length(toLight) / length(lightCoord);
+
+    l.direction = lightDir;
+    l.range = range;
+    l.color = _LightColor0 * atten; // includes both the color and amplitude, not normalized
+    l.intensity = 5.0; // 1.0 in Unity is 5klux, also 0.2 intensity in Unity = 1.0 intensity in GLTF, this will be modulated by the color anyways
+    l.position = pos;
+    l.innerConeCos = 1.0;
+    l.outerConeCos = 0.5;
+#endif
+
+    return l;
+}
+
+//////////////////
+//  ForwadBase  //
+//////////////////
+
+v2f vertForwardBase(appdata v)
+{
+    v2f o = AvatarShaderVertInit(v);
+    OvrVertexData vertexData = AvatarShaderVertTransform(v, o);
+    AvatarShaderVertLighting(v, o);
+    Interpolate(v, vertexData, o);
+    return o;
+}
+
+fixed4 fragForwardBase(v2f IN) : SV_Target
+{
+    AvatarShaderFragInit(IN);
+    float3 worldPos = AvatarShaderFragGetWorldPos(IN);
+    fixed3 lightDir = AvatarShaderFragGetLightDir(worldPos);
+    float3 worldViewDir = AvatarShaderFragGetWorldViewDir(worldPos);
+    AVATAR_SHADER_FRAG_INIT_AND_CALL_SURFACE(Surf, AvatarComponentSurfaceOutput);
+    AVATAR_SHADER_FRAG_LIGHTING(LightingAvatarComponent);
+}
+
+//////////////////
+//   ForwadAdd  //
+//////////////////
+
+v2f vertForwardAdd(appdata v)
+{
+    v2f o = AvatarShaderVertInit(v);
+    OvrVertexData vertexData = AvatarShaderVertTransform(v, o);
+    AvatarShaderVertLighting(v, o);
+    Interpolate(v, vertexData, o);
+    return o;
+}
+
+fixed4 fragForwardAdd(v2f IN) : SV_Target
+{
+    AvatarShaderFragInit(IN);
+    float3 worldPos = AvatarShaderFragGetWorldPos(IN);
+    fixed3 lightDir = AvatarShaderFragGetLightDir(worldPos);
+    float3 worldViewDir = AvatarShaderFragGetWorldViewDir(worldPos);
+
+    MaterialProperties props = GetMaterialProperties(IN);
+
+    /* compute lighting & shadowing factor */
+    UNITY_LIGHT_ATTENUATION(atten, IN, worldPos)
+    /* setup lighting environment */
+    Light light = GetUnityLight(worldPos, lightDir, atten);
+    /* realtime lighting: call lighting function */
+    fixed4 color = 0;
+    color.rgb += DirectAvatarLighting(worldViewDir, props, light);
+    /* apply fog */
+    UNITY_APPLY_FOG(_unity_fogCoord, color);
+
+    color.rgb = OverideColorWithDebug(color.rgb, IN);
+    UNITY_OPAQUE_ALPHA(color.a);
+
+    return color;
+}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Avatar-Horizon.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Avatar-Horizon.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..e602848654d57e129c7fdcabae7f0c58cd050a6c
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Avatar-Horizon.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: a169f58a736540748b9e326b8f7fd21f
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Avatar-Horizon.shader b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Avatar-Horizon.shader
new file mode 100644
index 0000000000000000000000000000000000000000..31c8d2d30009de42506ad0399ce67be8c9fcab50
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Avatar-Horizon.shader
@@ -0,0 +1,264 @@
+Shader "Avatar/Horizon"
+{
+    Properties
+    {
+      [NoScaleOffset]
+      [HideIfKeyword(_SHADER_TYPE_SOLID_COLOR, _SHADER_TYPE_HAIR)] // Hide main tex if solid color or hair
+      _MainTex ("Main Texture", 2D) = "white" {}
+
+      _Color ("Color", Color) = (1, 1, 1, 1)
+
+      [ShowIfKeyword(_SHADER_TYPE_HAIR, _SHADER_TYPE_SKIN)]
+      _SecondaryColor ("Secondary Color", Color) = (1, 1, 1, 1)
+
+      [ShowIfKeyword(_SHADER_TYPE_SKIN)]
+      _TertiaryColor ("Tertiary Color", Color) = (1, 1, 1, 0)
+
+      [NoScaleOffset]
+      _PropertiesMap("Properties Map", 2D) = "white" {}
+
+      [NoScaleOffset]
+      [ShowIfKeyword(_SHADER_TYPE_SKIN, _SHADER_TYPE_HAIR)] // Skin and hair have effects map
+      _EffectsMap("Effects Map", 2D) = "black" {}
+
+      _EyeGlintFactor("Eye Glint Factor", Range(1, 20)) = 10.0    // Amplifys the size of the spec highlight known as the eye glint
+
+      // The following three values diverge from the PBR standard and are mostly decide upon by art direction.
+      // They are valid within the range [0.0.1.0], but if they are never changed from these defaults, it's best to optimize them out for production.
+      _MinDiffuse("Minimum Diffuse Bias", Range(0, 1)) = 0.5    // Note: if we don't ever change this value from 0.5 at production time we should remove it
+      _AmbientOcclusionEffect("Occlusion effect on Ambient Light", Range(0, 1)) = 1.0    // Note: if we don't ever change this value from 1.0 at production time we should remove it
+      _DirectOcclusionEffect("Occlusion effect on Direct Light", Range(0, 1)) = 1.0    // Note: if we don't ever change this value from 1.0 at production time we should remove it
+
+      // Hair properties
+
+      // Eye Properties
+
+      // TODO* Once Head C is on permanently, remove some properties
+      [HideIfKeyword(_SHADER_TYPE_LEFT_EYE, false, USE_HEAD_C, true)]
+      _LeftEyeUp("Left Eye Up", Float) = 0
+
+      [HideIfKeyword(_SHADER_TYPE_LEFT_EYE, false, USE_HEAD_C, true)]
+      _LeftEyeRight("Left Eye Right", Float) = 0
+
+      [HideIfKeyword(_SHADER_TYPE_RIGHT_EYE, false, USE_HEAD_C, true)]
+      _RightEyeUp("Right Eye Up", Float) = 0
+
+      [HideIfKeyword(_SHADER_TYPE_RIGHT_EYE, false, USE_HEAD_C, true)]
+      _RightEyeRight("Right Eye Right", Float) = 0
+
+      // (A || B) && !C
+      [ShowIfKeywordAnd(_SHADER_TYPE_LEFT_EYE, _SHADER_TYPE_RIGHT_EYE, true, USE_HEAD_C, false)]
+      _UVScale("UV Scale", Range(0, 10)) = 1
+
+      [HideIfKeyword(_SHADER_TYPE_LEFT_EYE, USE_HEAD_C, false)]
+      _LeftEyeTX("Left Eye X Interpolation", Range(-1, 1)) = 0
+
+      [HideIfKeyword(_SHADER_TYPE_LEFT_EYE, USE_HEAD_C, false)]
+      _LeftEyeTY("Left Eye Y Interpolation", Range(-1, 1)) = 0
+
+      [HideIfKeyword(_SHADER_TYPE_RIGHT_EYE, USE_HEAD_C, false)]
+      _RightEyeTX("Right Eye X Interpolation", Range(-1, 1)) = 0
+
+      [HideIfKeyword(_SHADER_TYPE_RIGHT_EYE, USE_HEAD_C, false)]
+      _RightEyeTY("Right Eye Y Interpolation", Range(-1, 1)) = 0
+
+      // (A || B) && C
+      [ShowIfKeywordAnd(_SHADER_TYPE_LEFT_EYE, _SHADER_TYPE_RIGHT_EYE, true, USE_HEAD_C, true)]
+      _EyeXMin("Eye Min X", Float) = 0
+
+      // (A || B) && C
+      [ShowIfKeywordAnd(_SHADER_TYPE_LEFT_EYE, _SHADER_TYPE_RIGHT_EYE, true, USE_HEAD_C, true)]
+      _EyeXMid("Eye Mid X", Float) = 0.5
+
+      // (A || B) && C
+      [ShowIfKeywordAnd(_SHADER_TYPE_LEFT_EYE, _SHADER_TYPE_RIGHT_EYE, true, USE_HEAD_C, true)]
+      _EyeXMax("Eye Max X", Float) = 1
+
+      // (A || B) && C
+      [ShowIfKeywordAnd(_SHADER_TYPE_LEFT_EYE, _SHADER_TYPE_RIGHT_EYE, true, USE_HEAD_C, true)]
+      _EyeYMin("Eye Min Y", Float) = 0
+
+      // (A || B) && C
+      [ShowIfKeywordAnd(_SHADER_TYPE_LEFT_EYE, _SHADER_TYPE_RIGHT_EYE, true, USE_HEAD_C, true)]
+      _EyeYMid("Eye Mid Y", Float) = 0.5
+
+      // (A || B) && C
+      [ShowIfKeywordAnd(_SHADER_TYPE_LEFT_EYE, _SHADER_TYPE_RIGHT_EYE, true, USE_HEAD_C, true)]
+      _EyeYMax("Eye Max Y", Float) = 1
+
+      // (A || B) && C
+      [ShowIfKeywordAnd(_SHADER_TYPE_LEFT_EYE, _SHADER_TYPE_RIGHT_EYE, true, USE_HEAD_C, true)]
+      _PupilScale("Pupil Scale", Range(0, 1)) = 0.5
+
+      // (A || B) && C
+      [ShowIfKeywordAnd(_SHADER_TYPE_LEFT_EYE, _SHADER_TYPE_RIGHT_EYE, true, USE_HEAD_C, true)]
+      _IrisScale("Iris Scale", Range(0, 1)) = 0.5
+
+      [ShowIfKeywordAnd(_SHADER_TYPE_LEFT_EYE, _SHADER_TYPE_RIGHT_EYE, true, USE_HEAD_C, true)]
+      _EyeUVScale("Eye Scale", Range(0, 3)) = 0.5
+
+      // Skin Properties
+      [ShowIfKeyword(_SHADER_TYPE_SKIN)]
+      _Distortion("Translucency Distortion", Range(0, 1)) = 0.5
+
+      [ShowIfKeyword(_SHADER_TYPE_SKIN)]
+      _TranslucencyPower("Translucency Power", Range(0, 1)) = 1.0
+
+      [ShowIfKeyword(_SHADER_TYPE_SKIN)]
+      _TranslucencyScale("Translucency Scale", Range(0, 2)) = 1.0
+
+      [ShowIfKeyword(_SHADER_TYPE_SKIN)]
+      _BacklightScale("Backlight Scale", Range(0, 1)) = 0.2
+
+      [Header (Desaturation Mode Properties)]
+      _DesatAmount("AFK Desaturate", Range(0,1)) = 0
+      _DesatTint("Desaturation Tint", Color) = (0.255, 0.314, 0.502, 1)
+      _DesatLerp("Desaturation Fade", Range(0, 1)) = 0
+
+      // DEBUG_MODES: Uncomment to use Debug modes
+      // [KeywordEnum(None, Diffuse, Specular, Indirect_Diffuse, Indirect_Specular, Backlight, Translucency, Vertex Color, UVs, World Normal, World Position, SH)] _Render_Debug("Debug Render", Float) = 0
+
+      [Header (Lighting Systems)]
+      [KeywordEnum(Unity, Vertex GI)] _Lighting_System("Lighting System", Float) = 0
+
+      [Header (Shader Type)]
+      [KeywordEnum(Solid Color, Textured, Skin, Hair, Left Eye, Right Eye, SubMesh)] _Shader_Type("Shader Type", Float) = 0
+
+      [Header (Material Mode)]
+      [KeywordEnum(Texture, Vertex)] Material_Mode("Material Mode", Float) = 0
+
+      // Will set "USE_HEAD_C" shader keyword when set.
+      [Toggle(USE_HEAD_C)] _HeadC ("Use Head C Eye Props?", Float) = 0
+    }
+
+    SubShader
+    {
+        Tags{
+            "RenderPipeline" = "UniversalPipeline"
+            "RenderType" = "Opaque"
+        }
+        LOD 100
+
+        Pass
+        {
+            Tags { "LightMode" = "UniversalForward" }
+
+            CGPROGRAM
+
+            #pragma target 3.5 // necessary for use of SV_VertexID
+            #pragma vertex vertForwardBase
+            #pragma fragment fragForwardBase
+
+            #pragma multi_compile __ LIGHTMAP_ON
+            #pragma multi_compile __ LIGHTPROBE_SH
+            #pragma multi_compile __ DIRECTIONAL_LIGHT
+            #pragma multi_compile __ SHADOWMAP_STATIC_VSM
+            #pragma multi_compile MATERIAL_MODE_TEXTURE MATERIAL_MODE_VERTEX
+
+            #pragma multi_compile __ DESAT
+            #pragma multi_compile __ DEBUG_TINT
+
+            #pragma shader_feature __ USE_HEAD_C
+
+            // DEBUG_MODES: Uncomment to use Debug modes
+            // #pragma multi_compile __ _RENDER_DEBUG_DIFFUSE _RENDER_DEBUG_SPECULAR _RENDER_DEBUG_INDIRECT_DIFFUSE _RENDER_DEBUG_INDIRECT_SPECULAR _RENDER_DEBUG_BACKLIGHT  _RENDER_DEBUG_TRANSLUCENCY _RENDER_DEBUG_VERTEX_COLOR _RENDER_DEBUG_UVS _RENDER_DEBUG_WORLD_NORMAL _RENDER_DEBUG_WORLD_POSITION _RENDER_DEBUG_SH
+
+            #pragma shader_feature _LIGHTING_SYSTEM_UNITY _LIGHTING_SYSTEM_VERTEX_GI
+            #pragma shader_feature _SHADER_TYPE_SOLID_COLOR _SHADER_TYPE_TEXTURED _SHADER_TYPE_SKIN _SHADER_TYPE_HAIR _SHADER_TYPE_LEFT_EYE _SHADER_TYPE_RIGHT_EYE _SHADER_TYPE_SUBMESH
+
+            #include "Avatar-Horizon.cginc"
+
+            ENDCG
+        }
+
+        Pass
+        {
+            Name "MotionVectors"
+            Tags{ "LightMode" = "MotionVectors"}
+            Tags { "RenderType" = "Opaque" }
+
+            HLSLPROGRAM
+
+            #pragma target 3.5 // necessary for use of SV_VertexID
+            #pragma vertex OvrMotionVectorsVertProgram
+            #pragma fragment OvrMotionVectorsFragProgram
+
+            #include "../../../../Scripts/ShaderUtils/OvrAvatarMotionVectorsCore.hlsl"
+
+            ENDHLSL
+        }
+    }
+
+    SubShader
+    {
+        Tags { "RenderType"="Opaque" }
+        LOD 100
+
+        Pass
+        {
+            Tags { "LightMode" = "ForwardBase" }
+
+            CGPROGRAM
+
+            #pragma target 3.5 // necessary for use of SV_VertexID
+            #pragma vertex vertForwardBase
+            #pragma fragment fragForwardBase
+
+            #pragma multi_compile __ LIGHTMAP_ON
+            #pragma multi_compile __ LIGHTPROBE_SH
+            #pragma multi_compile __ DIRECTIONAL_LIGHT
+            #pragma multi_compile __ SHADOWMAP_STATIC_VSM
+            #pragma multi_compile MATERIAL_MODE_TEXTURE MATERIAL_MODE_VERTEX
+
+            #pragma multi_compile __ DESAT
+            #pragma multi_compile __ DEBUG_TINT
+
+            #pragma shader_feature __ USE_HEAD_C
+
+            // DEBUG_MODES: Uncomment to use Debug modes
+            // #pragma multi_compile __ _RENDER_DEBUG_DIFFUSE _RENDER_DEBUG_SPECULAR _RENDER_DEBUG_INDIRECT_DIFFUSE _RENDER_DEBUG_INDIRECT_SPECULAR _RENDER_DEBUG_BACKLIGHT  _RENDER_DEBUG_TRANSLUCENCY _RENDER_DEBUG_VERTEX_COLOR _RENDER_DEBUG_UVS _RENDER_DEBUG_WORLD_NORMAL _RENDER_DEBUG_WORLD_POSITION _RENDER_DEBUG_SH
+
+            #pragma shader_feature _LIGHTING_SYSTEM_UNITY _LIGHTING_SYSTEM_VERTEX_GI
+            #pragma shader_feature _SHADER_TYPE_SOLID_COLOR _SHADER_TYPE_TEXTURED _SHADER_TYPE_SKIN _SHADER_TYPE_HAIR _SHADER_TYPE_LEFT_EYE _SHADER_TYPE_RIGHT_EYE _SHADER_TYPE_SUBMESH
+
+            #include "Avatar-Horizon.cginc"
+
+            ENDCG
+        }
+
+        Pass
+        {
+            Tags { "LightMode" = "ForwardAdd" }
+
+            Blend One One
+            ZWrite Off
+
+            CGPROGRAM
+            #pragma target 3.5 // necessary for use of SV_VertexID
+            #pragma vertex vertForwardAdd
+            #pragma fragment fragForwardAdd
+
+            #pragma multi_compile __ LIGHTMAP_ON
+            #pragma multi_compile __ LIGHTPROBE_SH
+            #pragma multi_compile __ DIRECTIONAL_LIGHT
+            #pragma multi_compile __ SHADOWMAP_STATIC_VSM
+            #pragma multi_compile MATERIAL_MODE_TEXTURE MATERIAL_MODE_VERTEX
+
+            #pragma multi_compile __ DESAT
+            #pragma multi_compile __ DEBUG_TINT
+
+            #pragma shader_feature __ USE_HEAD_C
+
+            // DEBUG_MODES: Uncomment to use Debug modes
+            // #pragma multi_compile __ _RENDER_DEBUG_DIFFUSE _RENDER_DEBUG_SPECULAR _RENDER_DEBUG_INDIRECT_DIFFUSE _RENDER_DEBUG_INDIRECT_SPECULAR _RENDER_DEBUG_BACKLIGHT  _RENDER_DEBUG_TRANSLUCENCY _RENDER_DEBUG_VERTEX_COLOR _RENDER_DEBUG_UVS _RENDER_DEBUG_WORLD_NORMAL _RENDER_DEBUG_WORLD_POSITION _RENDER_DEBUG_SH
+
+            #pragma shader_feature _LIGHTING_SYSTEM_UNITY _LIGHTING_SYSTEM_VERTEX_GI
+            #pragma shader_feature _SHADER_TYPE_SOLID_COLOR _SHADER_TYPE_TEXTURED _SHADER_TYPE_SKIN _SHADER_TYPE_HAIR _SHADER_TYPE_LEFT_EYE _SHADER_TYPE_RIGHT_EYE _SHADER_TYPE_SUBMESH
+
+            #include "Avatar-Horizon.cginc"
+
+            ENDCG
+        }
+    }
+
+}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Avatar-Horizon.shader.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Avatar-Horizon.shader.meta
new file mode 100644
index 0000000000000000000000000000000000000000..ff83b6bb7d04c7a13eb5ab583d189252d739b024
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Avatar-Horizon.shader.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: c26a73fa4d4517c4da30333333bf50c2
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon.meta
new file mode 100644
index 0000000000000000000000000000000000000000..6454746faab5c45f242793a774443f2cf9571288
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 3faf76864c15371409c3628ada74b1d6
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarCommonDither.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarCommonDither.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..95306cbd99dc33ba33cdf63d6b153f0b046aa535
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarCommonDither.cginc
@@ -0,0 +1,45 @@
+
+// create MSAA sample mask from alpha value for to emulate transparency
+// used by DitherCoverageFromMaskMSAA4(), don't call this function
+// for 4x MSAA, also works for more MSAA but at same quality, FFR friendly 
+// @param alpha 0..1
+uint _CoverageFromMaskMSAA4(float alpha) {
+  // can be optimized futher
+  uint Coverage = 0x00;
+  if (alpha > 0 / 4.0) Coverage = 0x88;
+  if (alpha > 1 / 4.0) Coverage = 0x99;
+  if (alpha > 2 / 4.0) Coverage = 0xDD;
+  if (alpha > 3 / 4.0) Coverage = 0xFF;
+  return Coverage;
+}
+
+// see https://developer.oculus.com/blog/tech-note-shader-snippets-for-efficient-2d-dithering
+// used by DitherCoverageFromMaskMSAA4(), renamed Dither17() to avoid name conflict, can be unified
+float Dither17b(float2 svPosition, float frameIndexMod4) {
+  float3 k0 = float3(2, 7, 23);
+  float Ret = dot(float3(svPosition, frameIndexMod4), k0 / 17.0f);
+  return frac(Ret);
+}
+
+// create MSAA sample mask from alpha value for to emulate transparency,
+// for 4x MSAA,  can show artifacts with FFR
+// @param alpha 0..1, outside range should behave the same as saturate(alpha)
+// @param svPos xy from SV_Position
+// @param true: with per pixel dither, false: few shades
+uint CoverageFromMaskMSAA4(float alpha, float2 svPos, bool dither) {
+  // using a constant in the parameters means the dynamic branch (if) gets compiled out,
+  if (dither) {
+    // the pattern is not animated over time, no extra perf cost
+    float frameIndexMod4 = 0;
+    // /4: to have the dithering happening with less visual impact as 4
+    //     shades are already implemented with MSAA subsample rejection.
+    // -=: subtraction because the function CoverageFromMaskMSAA4() shows visuals 
+    //     effect >0 and we want to have some pixels not have effect depending on dithering.
+    alpha -= Dither17b(svPos, frameIndexMod4) / 4;
+  }
+  else {
+    // no dithering, no FFR artifacts with Quest
+    alpha -= 0.5f / 4;
+  }
+  return _CoverageFromMaskMSAA4(alpha);
+}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarCommonDither.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarCommonDither.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..da01ce32c5fe30e6dfa5a604ebb47e5e0cd56927
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarCommonDither.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 377352d07ff105c40ad22c9a3f2707d3
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarCommonLighting.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarCommonLighting.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..b4b33dfbb10d5a7392b9bb294d409ee236f3c851
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarCommonLighting.cginc
@@ -0,0 +1,230 @@
+#ifndef AVATAR_COMMON_LIGHTING_CGINC
+#define AVATAR_COMMON_LIGHTING_CGINC
+
+#include "Horizon/MacroRemapper.cginc"
+
+// Lighting functions that all avatar shaders share
+
+#include "UnityPBSLighting.cginc"
+
+#include "AvatarShaderTypes.cginc"
+#include "AvatarCommonProperties.cginc"
+#include "AvatarCommonSurfaceFIelds.cginc"
+#include "AvatarCommonUtils.cginc"
+
+#define AVATAR_COMMON_DEBUG_LIGHTING_ENABLED defined(_RENDER_DEBUG_DIFFUSE) || defined(_RENDER_DEBUG_SPECULAR) || defined(_RENDER_DEBUG_INDIRECT_DIFFUSE) || defined(_RENDER_DEBUG_INDIRECT_SPECULAR)
+
+half OffsetAndScaleMinimumDiffuse(half texChannel) {
+    // channel will define the minimum diffuse, but texture stores values from 0 to 1, but
+    // minimum diffuse can be from -1 to 1
+    return texChannel * 2.0 - 1.0;
+}
+
+half3 DirectDiffuseLighting(half3 diffColor, half NdotL, half minDiffuse) {
+    // The vertex GI lighting system has a specific keyword
+    // for enabling direct lighting at all, so, pay attention to that
+    #if !defined(__LIGHTING_SYSTEM_VERTEX_GI) || defined(DIRECTIONAL_LIGHT)
+        // For direct lighting, per art direction, no area is to go to all black,
+        // so there needs to be some shading even on the "other side" of the light.
+        // To accomplish this, instead of the lighting contribution being from 0 -> 1
+        // based on a saturated NdotL (thus only lighting the front hemisphere),
+        // the complete range of NdoL will be in use, and there will be a defined
+        // minimum at the "back pole" of the hemisphere.
+        half diffuseIntensity = saturate(lerp(minDiffuse, 1.0h, NdotL * 0.5 + 0.5));
+        return diffColor * diffuseIntensity;
+    #else
+        return 0.0;
+    #endif
+}
+
+half3 DirectDiffuseLightingWithOcclusion(half3 diffColor, half NdotL, half minDiffuse, half directOcclusion) {
+    return DirectDiffuseLighting(diffColor, NdotL, minDiffuse) * directOcclusion;
+}
+
+half3 DirectSpecularLighting(half3 specColor, float NdotH, float LdotH, half NdotL, float roughness) {
+    // The vertex GI lighting system has a specific keyword
+    // for enabling direct lighting at all, so, pay attention to that
+    #if !defined(__LIGHTING_SYSTEM_VERTEX_GI) || defined(DIRECTIONAL_LIGHT)
+        // From SIGGRAPH 2015 paper, but pulled from Unity shader source
+        // https://community.arm.com/cfs-file/__key/communityserver-blogs-components-weblogfiles/00-00-00-20-66/siggraph2015_2D00_mmg_2D00_renaldas_2D00_slides.pdf
+        float a = roughness;
+        float a2 = a * a;
+
+        float d = NdotH * NdotH * (a2 - 1.0f) + 1.00001f;
+        float specularTerm = a2 / (max(0.1f, LdotH * LdotH) * (roughness + 0.5f) * (d * d) * 4);
+
+        // on mobiles (where half actually means something) denominator have risk of overflow
+        // clamp below was added specifically to "fix" that, but dx compiler (we convert bytecode to metal/gles)
+        // sees that specularTerm have only non-negative terms, so it skips max(0,..) in clamp (leaving only min(100,...))
+        #if defined (SHADER_API_MOBILE)
+            specularTerm = specularTerm - 1e-4f;
+            specularTerm = clamp(specularTerm, 0.0, 100.0); // Prevent FP16 overflow on mobiles
+        #endif
+
+        return (specularTerm * specColor) * NdotL;
+    #else
+        return 0.0h;
+    #endif
+}
+
+half3 DirectSpecularLightingWithOcclusion(half3 specColor, float NdotH, float LdotH, half NdotL, float roughness, half directOcclusion) {
+    return DirectSpecularLighting(specColor, NdotH, LdotH, NdotL, roughness) * directOcclusion;
+}
+
+half3 IndirectDiffuseLighting(half3 diffColor, AvatarShaderIndirect indirect) {
+    return indirect.diffuse * diffColor;
+}
+
+half3 IndirectSpecularLighting(
+    half3 specColor,
+    float roughness,
+    half perceptualRoughness,
+    half perceptualSmoothness,
+    half oneMinusReflectivity,
+    half NdotV,
+    AvatarShaderIndirect indirect)
+{
+    // Pulled from Unity Shader source
+    // surfaceReduction = Int D(NdotH) * NdotH * Id(NdotL>0) dH = 1/(realRoughness^2+1)
+
+    // 1-0.28*x^3 as approximation for (1/(x^4+1))^(1/2.2) on the domain [0;1]
+    // 1-x^3*(0.6-0.08*x)   approximation for 1/(x^4+1)
+    half surfaceReduction = (0.6 - 0.08 * perceptualRoughness);
+
+    surfaceReduction = 1.0 - roughness * perceptualRoughness * surfaceReduction;
+
+    half grazingTerm = saturate(perceptualSmoothness + (1 - oneMinusReflectivity));
+    return surfaceReduction * indirect.specular * FresnelLerpFast (specColor, grazingTerm, NdotV);
+}
+
+fixed3 Desat(fixed3 color) {
+    fixed3 desatGrayscaleColor = Luminance(color);
+    fixed3 desatColor = lerp(desatGrayscaleColor, _DesatTint, _DesatLerp);
+    return lerp(color, desatColor, _DesatAmount);
+}
+
+
+half3 FinalLightingCombine(
+    half3 directDiffuse,
+    half3 directSpecular,
+    half3 indirectDiffuse,
+    half3 indirectSpecular,
+    half3 lightColor)
+{
+  #if defined(_RENDER_DEBUG_DIFFUSE)
+    return directDiffuse * lightColor;
+  #elif defined(_RENDER_DEBUG_SPECULAR)
+    return directSpecular * lightColor;
+  #elif defined(_RENDER_DEBUG_INDIRECT_DIFFUSE)
+    return indirectDiffuse;
+  #elif defined(_RENDER_DEBUG_INDIRECT_SPECULAR)
+    return indirectSpecular;
+  #else
+    // Full lighting
+    half3 directLighting = (directDiffuse + directSpecular) * lightColor;
+   return directLighting + indirectDiffuse + indirectSpecular;
+  #endif
+}
+
+// Caller supplied diffColor, specColor, and direct specular
+// Computing direct diffuse, indirect lighting
+half3 AvatarLightingCommon(
+    half3 diffColor,
+    half3 specColor,
+    half3 directSpecular,
+    half3 normal,
+    half3 viewDir,
+    half minDiffuse,
+    float roughness,
+    half perceptualRoughness,
+    half perceptualSmoothness,
+    half metallic,
+    half oneMinusReflectivity,
+    half rawNdotL,
+    half NdotL,
+    half directOcclusion,
+    AvatarShaderGlobalIllumination gi)
+{
+    half NdotV = saturate(dot(normal, viewDir));
+
+    half3 directDiffuse = DirectDiffuseLightingWithOcclusion(diffColor, rawNdotL, minDiffuse, directOcclusion);
+
+    half3 indirectDiffuse = IndirectDiffuseLighting(diffColor, gi.indirect);
+    half3 indirectSpecular = IndirectSpecularLighting(
+        specColor,
+        roughness,
+        perceptualRoughness,
+        perceptualSmoothness,
+        oneMinusReflectivity,
+        NdotV,
+        gi.indirect);
+
+    return FinalLightingCombine(directDiffuse, directSpecular, indirectDiffuse, indirectSpecular, gi.light.color);
+}
+
+// Computes all lighting (direct diffuse, direct specular, indirect diffuse, indirect specular)
+half3 AvatarLightingCommon(
+    half3 albedo,
+    float3 normal,
+    float3 viewDir,
+    half minDiffuse,
+    half perceptualRoughness,
+    half perceptualSmoothness,
+    half metallic,
+    half directOcclusion,
+    AvatarShaderGlobalIllumination gi)
+{
+    half oneMinusReflectivity;
+    half3 specColor;
+    half3 diffColor = DiffuseAndSpecularFromMetallic(
+        albedo,
+        metallic,
+        /*out*/ specColor,
+        /*out*/ oneMinusReflectivity);
+
+    float3 lightDirection = gi.light.direction;
+    float3 halfDirection = Unity_SafeNormalize(lightDirection + viewDir);
+
+    half rawNdotL = clamp(dot(normal, lightDirection), -1.0, 1.0); // -1 to 1
+    half NdotL = saturate(rawNdotL); // 0 to 1
+    float roughness = PerceptualRoughnessToRoughness(perceptualRoughness);
+
+    float NdotH = saturate(dot(normal, halfDirection));
+    float LdotH = saturate(dot(lightDirection, halfDirection));
+
+    half3 directSpecular;
+    directSpecular = DirectSpecularLightingWithOcclusion(specColor, NdotH, LdotH, NdotL, roughness, directOcclusion);
+
+    half3 returnVal = AvatarLightingCommon(
+        diffColor,
+        specColor,
+        directSpecular,
+        normal,
+        viewDir,
+        minDiffuse,
+        roughness,
+        perceptualRoughness,
+        perceptualSmoothness,
+        metallic,
+        oneMinusReflectivity,
+        rawNdotL,
+        NdotL,
+        directOcclusion,
+        gi);
+
+    return returnVal;
+}
+
+#define AVATAR_SHADER_DECLARE_COMMON_LIGHTING_PARAMS(AlbedoVar, NormVar, RoughVar, SmoothVar, MetalVar, AlphaVar, MinDiffuseVar, SurfaceOutputVar) \
+    half3 NormVar = GET_AVATAR_SHADER_SURFACE_NORMAL_FIELD(SurfaceOutputVar); \
+    fixed3 AlbedoVar = GET_AVATAR_SHADER_SURFACE_ALBEDO_FIELD(SurfaceOutputVar); \
+    \
+    half MetalVar = GET_AVATAR_SHADER_SURFACE_METALLIC_FIELD(SurfaceOutputVar); \
+    half AlphaVar = GET_AVATAR_SHADER_SURFACE_ALPHA_FIELD(SurfaceOutputVar); \
+    \
+    half RoughVar = GET_AVATAR_SHADER_SURFACE_ROUGHNESS_FIELD(SurfaceOutputVar); \
+    half SmoothVar = GET_AVATAR_SHADER_SURFACE_SMOOTHNESS_FIELD(SurfaceOutputVar); \
+    half MinDiffuseVar = GET_AVATAR_SHADER_SURFACE_MIN_DIFFUSE_FIELD(SurfaceOutputVar); \
+    half occlusion = GET_AVATAR_SHADER_SURFACE_OCCLUSION_FIELD(SurfaceOutputVar); \
+    half directOcclusion = lerp(1.0f, occlusion, _DirectOcclusionEffect);
+#endif
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarCommonLighting.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarCommonLighting.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..5d3e0ee971329ae2c7e174214f1695d3b364c02e
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarCommonLighting.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: badafbe34a4535d4dbd898cea0e4d3b7
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarCommonProperties.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarCommonProperties.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..d1148721be39ff02f3d78c70bb72571514279189
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarCommonProperties.cginc
@@ -0,0 +1,17 @@
+#ifndef AVATAR_COMMON_PROPERTIES_CGINC
+#define AVATAR_COMMON_PROPERTIES_CGINC
+
+///////////////////////
+// Common properties //
+///////////////////////
+
+fixed4 _DebugTint;
+
+fixed4 _DesatTint;
+half _DesatLerp;
+fixed _DesatAmount;
+half _MinDiffuse;
+half _AmbientOcclusionEffect;
+half _DirectOcclusionEffect;
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarCommonProperties.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarCommonProperties.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..0a4ed090c717fb2f711909813bb5cdfaabcf0b9d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarCommonProperties.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 95110e6ca49653f47aa229b0b2dd32ab
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarCommonSurfaceFields.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarCommonSurfaceFields.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..cf4c6e61907032ebeed89958269ff62d7dacf689
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarCommonSurfaceFields.cginc
@@ -0,0 +1,55 @@
+#ifndef AVATAR_COMMON_SURFACE_FIELDS_CGINC
+#define AVATAR_COMMON_SURFACE_FIELDS_CGINC
+
+#include "AvatarCommonUtils.cginc"
+
+// Min Diffuse
+#define AVATAR_SHADER_SURFACE_MIN_DIFFUSE_FIELD_NAME MinDiffuse
+#define AVATAR_SHADER_SURFACE_MIN_DIFFUSE_FIELD_DECLARATION \
+    half AVATAR_SHADER_SURFACE_MIN_DIFFUSE_FIELD_NAME;
+
+#define SET_AVATAR_SHADER_SURFACE_MIN_DIFFUSE_FIELD(SurfaceOutputVariable, Value) \
+    SET_AVATAR_SHADER_SURFACE_FIELD(SurfaceOutputVariable, AVATAR_SHADER_SURFACE_MIN_DIFFUSE_FIELD_NAME, Value)
+
+#define GET_AVATAR_SHADER_SURFACE_MIN_DIFFUSE_FIELD(SurfaceOutputVariable) \
+    GET_AVATAR_SHADER_SURFACE_FIELD(SurfaceOutputVariable, AVATAR_SHADER_SURFACE_MIN_DIFFUSE_FIELD_NAME)
+
+// Common surface fields across all avatar shaders
+#if defined(_LIGHTING_SYSTEM_UNITY) || defined(_LIGHTING_SYSTEM_VERTEX_GI)
+
+    #if defined(_LIGHTING_SYSTEM_UNITY)
+
+        #include "../UnityLighting/AvatarUnitySurfaceFields.cginc"
+
+        #define AVATAR_COMMON_SURFACE_FIELDS \
+            AVATAR_SHADER_UNITY_SURFACE_REQUIRED_FIELDS \
+            AVATAR_SHADER_SURFACE_MIN_DIFFUSE_FIELD_DECLARATION \
+            AVATAR_SHADER_SURFACE_ROUGHNESS_FIELD_DECLARATION \
+            AVATAR_SHADER_SURFACE_ALPHA_FIELD_DECLARATION
+
+    #else
+
+        #define AVATAR_COMMON_SURFACE_FIELDS \
+            AVATAR_SHADER_SURFACE_ALBEDO_FIELD_DECLARATION \
+            AVATAR_SHADER_SURFACE_NORMAL_FIELD_DECLARATION \
+            AVATAR_SHADER_SURFACE_ROUGHNESS_FIELD_DECLARATION \
+            AVATAR_SHADER_SURFACE_SMOOTHNESS_FIELD_DECLARATION \
+            AVATAR_SHADER_SURFACE_METALLIC_FIELD_DECLARATION \
+            AVATAR_SHADER_SURFACE_MIN_DIFFUSE_FIELD_DECLARATION \
+            AVATAR_SHADER_SURFACE_ALPHA_FIELD_DECLARATION \
+            AVATAR_SHADER_SURFACE_OCCLUSION_FIELD_DECLARATION
+
+    #endif
+
+    #define SET_AVATAR_SHADER_SURFACE_COMMON_FIELDS(SurfOutput, AlbedoVal, RoughVal, MetalVal, AlphaVal, OcclusionVal, MinDiffuseVal) \
+        half clampedRoughness = ClampPerceptualRoughness(RoughVal); \
+        SET_AVATAR_SHADER_SURFACE_ALBEDO_FIELD(SurfOutput, AlbedoVal) \
+        SET_AVATAR_SHADER_SURFACE_ROUGHNESS_FIELD(SurfOutput, clampedRoughness) \
+        SET_AVATAR_SHADER_SURFACE_SMOOTHNESS_FIELD(SurfOutput, 1 - clampedRoughness) \
+        SET_AVATAR_SHADER_SURFACE_METALLIC_FIELD(SurfOutput, MetalVal) \
+        SET_AVATAR_SHADER_SURFACE_ALPHA_FIELD(SurfOutput, AlphaVal) \
+        SET_AVATAR_SHADER_SURFACE_OCCLUSION_FIELD(SurfOutput, OcclusionVal) \
+        SET_AVATAR_SHADER_SURFACE_MIN_DIFFUSE_FIELD(SurfOutput, MinDiffuseVal)
+#endif
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarCommonSurfaceFields.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarCommonSurfaceFields.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..5af6a5b0025988c6bce0846c798706fd13c71b6f
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarCommonSurfaceFields.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 2c3445f39660ccc40b5d7edeb46e24d8
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarCommonUtils.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarCommonUtils.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..cbccdb0226337ae07a0471b1336337b823182da0
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarCommonUtils.cginc
@@ -0,0 +1,18 @@
+#ifndef AVATAR_COMMON_UTILS_CGINC
+#define AVATAR_COMMON_UTILS_CGINC
+
+#define PI 3.14159265359
+
+#include "UnityStandardBRDF.cginc"
+
+#define MIN_PERCEPTUAL_ROUGHNESS 0.045
+
+float powApprox(float a, float b) {
+  return a / ((1.0f - b) * a + b);
+}
+
+float ClampPerceptualRoughness(float perceptualRoughness) {
+    return clamp(perceptualRoughness, MIN_PERCEPTUAL_ROUGHNESS, 1.0);
+}
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarCommonUtils.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarCommonUtils.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..ac12e87e8b6853c53d04d897f82f572a038c024c
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarCommonUtils.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 17953edb2643bfe449972eb1e783605c
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarEffectsMap.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarEffectsMap.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..f7d96dffca464a458e21db0064e5012e916b4e40
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarEffectsMap.cginc
@@ -0,0 +1,21 @@
+#ifndef AVATAR_EFFECTS_MAP_CGINC
+#define AVATAR_EFFECTS_MAP_CGINC
+
+#include "UnityCG.cginc"
+
+#if defined(AVATAR_SHADER_EFFECTS_MAP_ARRAY)
+  UNITY_DECLARE_TEX2DARRAY(_EffectsMapArray);
+
+  half4 SampleEffectsMap(float3 coords) {
+    return UNITY_SAMPLE_TEX2DARRAY(_EffectsMapArray, coords);
+  }
+
+#else
+  sampler2D _EffectsMap;
+
+  half4 SampleEffectsMap(float2 coords) {
+    return tex2D(_EffectsMap, coords);
+  }
+#endif
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarEffectsMap.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarEffectsMap.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..bbe262642200455761650fcdfeec8d36b2fa0d1c
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarEffectsMap.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: df6dea3d4a1e0504c8d3af7bad9c6ed6
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarMainTex.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarMainTex.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..f7ef0468962a4229f23190ae39ebafe5de3568a4
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarMainTex.cginc
@@ -0,0 +1,21 @@
+#ifndef AVATAR_MAIN_TEX_CGINC
+#define AVATAR_MAIN_TEX_CGINC
+
+#include "UnityCG.cginc"
+
+#if defined(AVATAR_SHADER_MAIN_TEX_ARRAY)
+  UNITY_DECLARE_TEX2DARRAY(_MainTexArray);
+
+  half4 SampleMainTex(float3 coords) {    
+    return UNITY_SAMPLE_TEX2DARRAY(_MainTexArray, coords);
+  }
+
+#else
+  sampler2D _MainTex;
+
+  half4 SampleMainTex(float2 coords) {
+    return tex2D(_MainTex, coords);
+  }
+#endif
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarMainTex.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarMainTex.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..60a1a4f760a3bc6fbd2ef09f1f0d0bdaeaea5a9a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarMainTex.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 5ba0eb7d2627583469d4b7c87c0e2809
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarPrimaryColorProperty.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarPrimaryColorProperty.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..4abaa24f9c28f73079787f465dbfe05060256151
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarPrimaryColorProperty.cginc
@@ -0,0 +1,22 @@
+#ifndef AVATAR_PRIMARY_COLOR_PROPERTY_CGINC
+#define AVATAR_PRIMARY_COLOR_PROPERTY_CGINC
+
+// A color parameter as individual color or array of colors
+
+#if defined(AVATAR_SHADER_COLOR_ARRAY)
+  half3 _ColorArray[16];
+
+  half3 GetPrimaryColor(uint index) {
+    return _ColorArray[index];
+  }
+#else
+  #if !defined(_LIGHTING_SYSTEM_VERTEX_GI)
+    half3 _Color;
+  #endif
+
+  half3 GetPrimaryColor() {
+    return _Color;
+  }
+#endif
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarPrimaryColorProperty.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarPrimaryColorProperty.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..2ff4e34196ec3262eb44664c3e40670104aeac3b
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarPrimaryColorProperty.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 08ca523ea6c12624991f3031b53c26f4
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarPropertiesMap.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarPropertiesMap.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..e5cf4849bf68e8985bade13f82167617226653b9
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarPropertiesMap.cginc
@@ -0,0 +1,27 @@
+#ifndef AVATAR_PROPERTIES_MAP_CGINC
+#define AVATAR_PROPERTIES_MAP_CGINC
+
+#include "UnityCG.cginc"
+
+
+#ifdef MATERIAL_MODE_TEXTURE
+#if defined(AVATAR_SHADER_PROPERTIES_MAP_ARRAY)
+  UNITY_DECLARE_TEX2DARRAY(_PropertiesMapArray);
+
+  half4 SamplePropertiesMap(float3 coords, half4 vertORMT) {
+    return UNITY_SAMPLE_TEX2DARRAY(_PropertiesMapArray, coords);
+  }
+#else
+  sampler2D _PropertiesMap;
+
+  half4 SamplePropertiesMap(float2 coords, half4 vertORMT) {
+    return tex2D(_PropertiesMap, coords);
+  }
+#endif
+#else
+  half4 SamplePropertiesMap(float2 coords, half4 vertORMT) {
+    return vertORMT;
+  }
+#endif
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarPropertiesMap.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarPropertiesMap.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..4c89065199d7a90fa978bd3187203b9d940399e2
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarPropertiesMap.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: b7ae04461e7e312428fbeb68944df956
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarSecondaryColorProperty.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarSecondaryColorProperty.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..4a983902e4773eb117dff01282d381ff752132f9
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarSecondaryColorProperty.cginc
@@ -0,0 +1,18 @@
+#ifndef AVATAR_SECONDARY_COLOR_PROPERTY_CGINC
+#define AVATAR_SECONDARY_COLOR_PROPERTY_CGINC
+
+#if defined(AVATAR_SHADER_SECONDARY_COLOR_ARRAY)
+    half3 _SecondaryColorArray[16];
+
+    half3 GetSecondaryColor(int index) {
+        return _SecondaryColorArray[index];
+    }
+#else
+    half3 _SecondaryColor;
+
+    half3 GetSecondaryColor() {
+        return _SecondaryColor;
+    }
+#endif
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarSecondaryColorProperty.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarSecondaryColorProperty.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..1b9eda13f2ca811f4a02213e6dbad098625f7257
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarSecondaryColorProperty.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 6d60728e788b97342b226308c61b2069
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarShaderFramework.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarShaderFramework.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..fb3455b9e89e611e015b31eab4eb32cfa30a0e8c
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarShaderFramework.cginc
@@ -0,0 +1,30 @@
+#ifndef AVATAR_SHADER_FRAMEWORK_CGINC
+#define AVATAR_SHADER_FRAMEWORK_CGINC
+
+#include "AvatarShaderTypes.cginc"
+#include "AvatarShaderMacros.cginc"
+
+///////////////////////
+// Generation Macros //
+///////////////////////
+#if defined(_LIGHTING_SYSTEM_UNITY)
+
+    #include "../UnityLighting/AvatarUnityLighting.cginc"
+
+    #define GENERATE_AVATAR_SHADER_VERT_FRAG(VertProgName, FragProgName, VertFunc, SurfFunc, SurfOutputType, LightingFunc) \
+        GENERATE_AVATAR_UNITY_LIGHTING_VERTEX_PROGRAM(VertProgName, VertFunc) \
+        GENERATE_AVATAR_UNITY_LIGHTING_FRAGMENT_PROGRAM(FragProgName, SurfFunc, SurfOutputType, LightingFunc)
+
+#elif defined(_LIGHTING_SYSTEM_VERTEX_GI)
+
+    #define DYNAMIC 1
+
+    #include "../AvatarVGI/AvatarVGIFramework.cginc"
+
+    #define GENERATE_AVATAR_SHADER_VERT_FRAG(VertProgName, FragProgName, VertFunc, SurfFunc, SurfOutputType, LightingFunc) \
+        DEFINE_AVATAR_VGI_VERT(VertProgName, VertFunc) \
+        DEFINE_AVATAR_VGI_FRAG_PBS(FragProgName, SurfFunc, SurfOutputType, LightingFunc)
+
+#endif
+
+#endif //end (ifndef AVATAR_SHADER_FRAMEWORK_CGINC)
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarShaderFramework.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarShaderFramework.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..9c58dca874f481dcc989c059401529b657166fef
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarShaderFramework.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: ee0c38175c3bd624e8b98ab40865a2d5
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarShaderMacros.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarShaderMacros.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..d22c4c8b5385b0ae8ab1feebd5204e9ab0142ed6
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarShaderMacros.cginc
@@ -0,0 +1,92 @@
+#ifndef AVATAR_SHADER_MACROS_CGINC
+#define AVATAR_SHADER_MACROS_CGINC
+
+// Field related functions
+
+#define SET_AVATAR_SHADER_SURFACE_FIELD(SurfaceOutputVariable, FieldName, Value) \
+    SurfaceOutputVariable.FieldName = Value;
+
+#define GET_AVATAR_SHADER_SURFACE_FIELD(SurfaceOutputVariable, FieldName) \
+    SurfaceOutputVariable.FieldName
+
+// Define some commonly used fields
+
+// Smoothness
+#define AVATAR_SHADER_SURFACE_SMOOTHNESS_FIELD_NAME Smoothness
+#define AVATAR_SHADER_SURFACE_SMOOTHNESS_FIELD_DECLARATION \
+    half AVATAR_SHADER_SURFACE_SMOOTHNESS_FIELD_NAME;
+
+#define SET_AVATAR_SHADER_SURFACE_SMOOTHNESS_FIELD(SurfaceOutputVariable, Value) \
+    SET_AVATAR_SHADER_SURFACE_FIELD(SurfaceOutputVariable, AVATAR_SHADER_SURFACE_SMOOTHNESS_FIELD_NAME, Value)
+
+#define GET_AVATAR_SHADER_SURFACE_SMOOTHNESS_FIELD(SurfaceOutputVariable) \
+    GET_AVATAR_SHADER_SURFACE_FIELD(SurfaceOutputVariable, AVATAR_SHADER_SURFACE_SMOOTHNESS_FIELD_NAME)
+
+// Roughness
+#define AVATAR_SHADER_SURFACE_ROUGHNESS_FIELD_NAME Roughness
+#define AVATAR_SHADER_SURFACE_ROUGHNESS_FIELD_DECLARATION \
+    half AVATAR_SHADER_SURFACE_ROUGHNESS_FIELD_NAME;
+
+#define SET_AVATAR_SHADER_SURFACE_ROUGHNESS_FIELD(SurfaceOutputVariable, Value) \
+    SET_AVATAR_SHADER_SURFACE_FIELD(SurfaceOutputVariable, AVATAR_SHADER_SURFACE_ROUGHNESS_FIELD_NAME, Value)
+
+#define GET_AVATAR_SHADER_SURFACE_ROUGHNESS_FIELD(SurfaceOutputVariable) \
+    GET_AVATAR_SHADER_SURFACE_FIELD(SurfaceOutputVariable, AVATAR_SHADER_SURFACE_ROUGHNESS_FIELD_NAME)
+
+// Normal
+#define AVATAR_SHADER_SURFACE_NORMAL_FIELD_NAME Normal
+#define AVATAR_SHADER_SURFACE_NORMAL_FIELD_DECLARATION \
+    float3 AVATAR_SHADER_SURFACE_NORMAL_FIELD_NAME;
+
+#define SET_AVATAR_SHADER_SURFACE_NORMAL_FIELD(SurfaceOutputVariable, Value) \
+    SET_AVATAR_SHADER_SURFACE_FIELD(SurfaceOutputVariable, AVATAR_SHADER_SURFACE_NORMAL_FIELD_NAME, Value)
+
+#define GET_AVATAR_SHADER_SURFACE_NORMAL_FIELD(SurfaceOutputVariable) \
+    GET_AVATAR_SHADER_SURFACE_FIELD(SurfaceOutputVariable, AVATAR_SHADER_SURFACE_NORMAL_FIELD_NAME)
+
+// Albedo
+#define AVATAR_SHADER_SURFACE_ALBEDO_FIELD_NAME Albedo
+#define AVATAR_SHADER_SURFACE_ALBEDO_FIELD_DECLARATION \
+    fixed3 AVATAR_SHADER_SURFACE_ALBEDO_FIELD_NAME;
+
+#define SET_AVATAR_SHADER_SURFACE_ALBEDO_FIELD(SurfaceOutputVariable, Value) \
+    SET_AVATAR_SHADER_SURFACE_FIELD(SurfaceOutputVariable, AVATAR_SHADER_SURFACE_ALBEDO_FIELD_NAME, Value)
+
+#define GET_AVATAR_SHADER_SURFACE_ALBEDO_FIELD(SurfaceOutputVariable) \
+    GET_AVATAR_SHADER_SURFACE_FIELD(SurfaceOutputVariable, AVATAR_SHADER_SURFACE_ALBEDO_FIELD_NAME)
+
+// Metallic
+#define AVATAR_SHADER_SURFACE_METALLIC_FIELD_NAME Metallic
+#define AVATAR_SHADER_SURFACE_METALLIC_FIELD_DECLARATION \
+    half AVATAR_SHADER_SURFACE_METALLIC_FIELD_NAME;
+
+#define SET_AVATAR_SHADER_SURFACE_METALLIC_FIELD(SurfaceOutputVariable, Value) \
+    SET_AVATAR_SHADER_SURFACE_FIELD(SurfaceOutputVariable, AVATAR_SHADER_SURFACE_METALLIC_FIELD_NAME, Value)
+
+#define GET_AVATAR_SHADER_SURFACE_METALLIC_FIELD(SurfaceOutputVariable) \
+    GET_AVATAR_SHADER_SURFACE_FIELD(SurfaceOutputVariable, AVATAR_SHADER_SURFACE_METALLIC_FIELD_NAME)
+
+// Occlusion
+#define AVATAR_SHADER_SURFACE_OCCLUSION_FIELD_NAME Occlusion
+#define AVATAR_SHADER_SURFACE_OCCLUSION_FIELD_DECLARATION \
+    half AVATAR_SHADER_SURFACE_OCCLUSION_FIELD_NAME;
+
+
+#define SET_AVATAR_SHADER_SURFACE_OCCLUSION_FIELD(SurfaceOutputVariable, Value) \
+    SET_AVATAR_SHADER_SURFACE_FIELD(SurfaceOutputVariable, AVATAR_SHADER_SURFACE_OCCLUSION_FIELD_NAME, Value)
+
+#define GET_AVATAR_SHADER_SURFACE_OCCLUSION_FIELD(SurfaceOutputVariable) \
+    GET_AVATAR_SHADER_SURFACE_FIELD(SurfaceOutputVariable, AVATAR_SHADER_SURFACE_OCCLUSION_FIELD_NAME)
+
+// Alpha
+#define AVATAR_SHADER_SURFACE_ALPHA_FIELD_NAME Alpha
+#define AVATAR_SHADER_SURFACE_ALPHA_FIELD_DECLARATION \
+    half AVATAR_SHADER_SURFACE_ALPHA_FIELD_NAME;
+
+#define SET_AVATAR_SHADER_SURFACE_ALPHA_FIELD(SurfaceOutputVariable, Value) \
+    SET_AVATAR_SHADER_SURFACE_FIELD(SurfaceOutputVariable, AVATAR_SHADER_SURFACE_ALPHA_FIELD_NAME, Value)
+
+#define GET_AVATAR_SHADER_SURFACE_ALPHA_FIELD(SurfaceOutputVariable) \
+    GET_AVATAR_SHADER_SURFACE_FIELD(SurfaceOutputVariable, AVATAR_SHADER_SURFACE_ALPHA_FIELD_NAME)
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarShaderMacros.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarShaderMacros.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..68278ce56bb8b60ace38710e7798da34bb846707
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarShaderMacros.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 65f01a61af3fd4d42af01584f9071c9b
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarShaderTypes.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarShaderTypes.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..4c69503f4be3806b34e2e06d22e4e5697c18e8bd
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarShaderTypes.cginc
@@ -0,0 +1,36 @@
+#ifndef AVATAR_SHADER_TYPES_CGINC
+#define AVATAR_SHADER_TYPES_CGINC
+
+#include "UnityInstancing.cginc"
+#include "UnityCG.cginc"
+
+struct AvatarShaderLight {
+    half3 direction;
+    half3 color;
+};
+
+struct AvatarShaderIndirect {
+    half3 diffuse;
+    half3 specular;
+};
+
+struct AvatarShaderGlobalIllumination {
+    AvatarShaderLight light;
+    AvatarShaderIndirect indirect;
+};
+
+#if defined(_LIGHTING_SYSTEM_VERTEX_GI)
+
+    #include "../AvatarVGI/AvatarVGITypes.cginc"
+
+#elif defined(_LIGHTING_SYSTEM_UNITY)
+
+    #include "../UnityLighting/AvatarUnityLightingTypes.cginc"
+
+#elif defined(_LIGHTING_SYSTEM_UNLIT)
+
+     #include "../Unlit/UnlitTypes.cginc"
+
+#endif
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarShaderTypes.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarShaderTypes.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..69741ef073b3f10b2e9ea8a12363ffaf6b6565b8
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarShaderTypes.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: e9676dc477fcc744bba2fb4e36bb7ad5
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarTertiaryColorProperty.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarTertiaryColorProperty.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..05ff960fc9992705c3a25ed26c3d107bebc034e3
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarTertiaryColorProperty.cginc
@@ -0,0 +1,18 @@
+#ifndef AVATAR_TERTIARY_COLOR_PROPERTY_CGINC
+#define AVATAR_TERTIARY_COLOR_PROPERTY_CGINC
+
+#if defined(AVATAR_SHADER_TERTIARY_COLOR_ARRAY)
+    half4 _TertiaryColorArray[16];
+
+    half4 GetTertiaryColor(int index) {
+        return _TertiaryColorArray[index];
+    }
+#else
+    half4 _TertiaryColor;
+
+    half4 GetTertiaryColor() {
+        return _TertiaryColor;
+    }
+#endif
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarTertiaryColorProperty.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarTertiaryColorProperty.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..6886279b63f80a7f40f10e6d5992b0c7177eee0b
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarCommon/AvatarTertiaryColorProperty.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 5fd4cdc52dcd23e4990522515222070b
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye.meta
new file mode 100644
index 0000000000000000000000000000000000000000..aa56883d07cc396b68c09ff6ca31403f49907eb9
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: db76c026fca5a1046ad923ae931767e4
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye/AvatarEyeAlbedo.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye/AvatarEyeAlbedo.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..02e7098dacf9c9e50e174a88f30d61c5df6b0e71
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye/AvatarEyeAlbedo.cginc
@@ -0,0 +1,12 @@
+#ifndef AVATAR_EYE_ALBEDO_CGINC
+#define AVATAR_EYE_ALBEDO_CGINC
+
+// Albedo function for eyes is the same as for textured component, so
+// just import that file here and call the function
+#include "../AvatarTextured/AvatarTexturedAlbedo.cginc"
+
+half3 EyeAlbedo(half4 mainTex, half3 vertColor, half3 color) {
+  return TexturedAlbedo(mainTex, vertColor, color);
+}
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye/AvatarEyeAlbedo.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye/AvatarEyeAlbedo.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..c28c4deaaed311348e7497d5b733e43be5001145
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye/AvatarEyeAlbedo.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: f37f9ea82374f5a42945827a921dd5b8
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye/AvatarEyeAlbedoHeadC.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye/AvatarEyeAlbedoHeadC.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..da499323209dd3f08b227735ead75c46868923c1
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye/AvatarEyeAlbedoHeadC.cginc
@@ -0,0 +1,22 @@
+#ifndef AVATAR_EYE_ALBEDO_HEAD_C_CGINC
+#define AVATAR_EYE_ALBEDO_HEAD_C_CGINC
+
+// Albedo function for eyes is similar to the function for textured component, so
+// just import that file here and call the function
+#include "../AvatarTextured/AvatarTexturedAlbedo.cginc"
+
+half3 EyeAlbedo(half4 mainTex, half3 color, float2 uv) {
+  float2 center = 0.5;
+
+  half3 texturedAlbedo = TexturedAlbedo(mainTex, color);
+
+  // Force pupil to be black at pupil scale
+  float2 diff = uv - center;
+  float len = length(diff);
+
+  // ASSUMPTION/HARD CODE: Iris in texture is 0.175 radius
+  float pupilEdgeLen = 0.175 * _PupilScale;
+  return texturedAlbedo * smoothstep(pupilEdgeLen, pupilEdgeLen + 0.01, len);
+}
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye/AvatarEyeAlbedoHeadC.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye/AvatarEyeAlbedoHeadC.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..a1148357827e7e8e3032b11ced4a0268482c16c9
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye/AvatarEyeAlbedoHeadC.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: cb229d0979b328b429d068bec289c53e
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye/AvatarEyeGlint.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye/AvatarEyeGlint.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..104f28da182eec95bcbdf93cd5d77751cfbfa174
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye/AvatarEyeGlint.cginc
@@ -0,0 +1,33 @@
+#ifndef AVATAR_EYE_GLINT_CGINC
+#define AVATAR_EYE_GLINT_CGINC
+
+#include "../AvatarCommon/AvatarCommonLighting.cginc"
+#include "../AvatarEye/AvatarEyeProperties.cginc"
+
+//////////////////////////////////////
+// Eye Glint specific calculations. //
+//////////////////////////////////////
+
+half3 DirectSpecularLightingWithOcclusionWithEyeGlints(half3 specColor, float NdotH, float LdotH, half NdotL, float roughness, half directOcclusion, float3 normal, float3 lightDirection, float3 viewDir) {
+    // first cseate a version of the original spec light, amplified by _EyeGlintFactor
+#ifdef EYE_GLINTS
+    LdotH /= _EyeGlintFactor;
+    NdotL *= _EyeGlintFactor;
+#endif
+    half3 directSpecular = DirectSpecularLightingWithOcclusion(specColor, NdotH, LdotH, NdotL, roughness, directOcclusion);
+
+#ifdef EYE_GLINTS_BEHIND
+    // create a second reflected spec light to maintain an eye glint from the backside, original spec intensity
+    lightDirection = float3(-lightDirection.x, lightDirection.y, -lightDirection.z);
+    float3 halfDirection = Unity_SafeNormalize(lightDirection + viewDir);
+    half rawNdotL = clamp(dot(normal, lightDirection), -1.0, 1.0); // -1 to 1
+    NdotL = saturate(rawNdotL); // 0 to 1
+    NdotH = saturate(dot(normal, halfDirection));
+    LdotH = saturate(dot(lightDirection, halfDirection));
+    directSpecular += max(float3(0,0,0),DirectSpecularLightingWithOcclusion(specColor, NdotH, LdotH, NdotL, roughness, directOcclusion));
+#endif
+    return directSpecular;
+}
+
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye/AvatarEyeGlint.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye/AvatarEyeGlint.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..7af9d7dacfbd82937a56fec19ffcd3aa178109a7
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye/AvatarEyeGlint.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 3907c9f011beeea4b816fae7840bf500
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye/AvatarEyeInterpolators.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye/AvatarEyeInterpolators.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..8b68a3a268b8d2d067ad962e4ae16a7feaef106d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye/AvatarEyeInterpolators.cginc
@@ -0,0 +1,27 @@
+#ifndef AVATAR_EYE_INTERPOLATORS_CGINC
+#define AVATAR_EYE_INTERPOLATORS_CGINC
+
+#include "AvatarEyeProperties.cginc"
+
+////////////////////////////////
+// Eye Specific Interpolators //
+////////////////////////////////
+
+float2 GetNormalizedUVForEye(float2 origNormalizedUVs, float right, float up) {
+    float2 center = float2(0.5f, 0.5f);
+    float2 newUVs = (origNormalizedUVs - center) * _UVScale + center + float2(right, up);
+
+    // Explicitly do a a clamp here in case of atlassed textures, textured mode of clamp
+    // will be insufficient
+    return saturate(newUVs);
+}
+
+float2 GetNormalizedUVForLeftEye(float2 origNormalizedUVs) {
+    return GetNormalizedUVForEye(origNormalizedUVs, _LeftEyeRight, _LeftEyeUp);
+}
+
+float2 GetNormalizedUVForRightEye(float2 origNormalizedUVs) {
+    return GetNormalizedUVForEye(origNormalizedUVs, _RightEyeRight, _RightEyeUp);
+}
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye/AvatarEyeInterpolators.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye/AvatarEyeInterpolators.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..93dfc8ecfee145704f26fc4cc504b0c2f6ea3a77
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye/AvatarEyeInterpolators.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 30c814dc662b7b2419dff0d89410d636
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye/AvatarEyeInterpolatorsHeadC.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye/AvatarEyeInterpolatorsHeadC.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..8d2806a0f7845a61e7468d6c53e77adbab12536d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye/AvatarEyeInterpolatorsHeadC.cginc
@@ -0,0 +1,75 @@
+#ifndef AVATAR_EYE_INTERPOLATORS_HEAD_C_CGINC
+#define AVATAR_EYE_INTERPOLATORS_HEAD_C_CGINC
+
+#include "AvatarEyePropertiesHeadC.cginc"
+
+///////////////////////////////////////
+// Head C Eye Specific Interpolators //
+///////////////////////////////////////
+
+float cubic_interpolation_3point(float a, float b, float c, float t) {
+    float result =  c * (2.0 * t * t - t) + b * (-4.0 * t * t + 4.0 * t) + a * (2.0 * t * t - 3.0 * t + 1.0);
+    return result;
+}
+
+float2 GetNormalizedUVForEye(float2 origNormalizedUVs, float xMin, float xMid, float xMax, float yMin, float yMid, float yMax, float tX, float tY) {
+    float2 center = 0.5;
+
+    float scale =  1.0 / max(0.000001, _EyeUVScale);
+    float2 newUVs = (origNormalizedUVs - center) * scale + center;
+
+    // do a cubic interpolation between min and max through the mid point
+    // (This code pulled from maya shader)
+    float remapped_tX = cubic_interpolation_3point(xMin, xMid, xMax, (tX + 1.0) * 0.5);
+    float remapped_tY = cubic_interpolation_3point(yMin, yMid, yMax, (tY + 1.0) * 0.5);
+
+    newUVs.x = newUVs.x + remapped_tX;
+    newUVs.y = newUVs.y + remapped_tY;
+
+    // ASSUMPTION/HARD CODE: Iris in texture is 0.175 radius in the texture, apply iris scale
+
+    // Iris scale of 1.0 means the iris goes to the "edge" of the eye
+    // and iris scale of 0 means no iris.
+
+    // Since the iris in the texture is centered at 0.5 and has 0.175 radius, an
+    // iris scale of 0.35 means that the underlying UVs should not change.
+    // Iris scale of 1.0 means UVs instead of being from 0 -> 1, will be from 0 -> 0.35
+
+    // NOTE: To work around a bug in the Maya shader (so that artists don't have to re-export art),
+    // this shader is also implementing the same behavior. Behavior is that _IrisScale of 0.5 means
+    // "the whole eye" (instead of 1.0)
+    scale = 0.35 / max(0.000001, _IrisScale * 2.0); // 2.0 here due to replicating Maya shader bug
+    newUVs = (newUVs - center) * scale + center;
+
+    // Explicitly do a a clamp here in case of atlassed textures, textured mode of clamp
+    // will be insufficient
+    return saturate(newUVs);
+}
+
+float2 GetNormalizedUVForLeftEye(float2 origNormalizedUVs) {
+    return GetNormalizedUVForEye(
+        origNormalizedUVs,
+        _EyeXMin,
+        _EyeXMid,
+        _EyeXMax,
+        _EyeYMin,
+        _EyeYMid,
+        _EyeYMax,
+        _LeftEyeTX,
+        _LeftEyeTY);
+}
+
+float2 GetNormalizedUVForRightEye(float2 origNormalizedUVs) {
+    return GetNormalizedUVForEye(
+        origNormalizedUVs,
+        _EyeXMin * -1.0,
+        _EyeXMid * -1.0,
+        _EyeXMax * -1.0,
+        _EyeYMin,
+        _EyeYMid,
+        _EyeYMax,
+        _RightEyeTX * -1.0,
+        _RightEyeTY);
+}
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye/AvatarEyeInterpolatorsHeadC.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye/AvatarEyeInterpolatorsHeadC.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..ff9b68f028e4ce525dd98bb4a62a6bd89e24a7e2
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye/AvatarEyeInterpolatorsHeadC.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 165895dcfc8439249b812357afe1785b
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye/AvatarEyeProperties.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye/AvatarEyeProperties.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..e34953deb45ea1e81499f1b3e8961115d2c2d95e
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye/AvatarEyeProperties.cginc
@@ -0,0 +1,26 @@
+#ifndef AVATAR_EYE_PROPERTIES_CGINC
+#define AVATAR_EYE_PROPERTIES_CGINC
+
+#include "../AvatarTextured/AvatarTexturedProperties.cginc"
+
+
+////////////////////////////////////
+// Properties for the Eye Glints. //
+////////////////////////////////////
+
+#define EYE_GLINTS             // amplifys the specular power for eyes, creating an artistic eye glint
+#define EYE_GLINTS_BEHIND      // mirrors the spec light arond the Y axis such that an eye glint is almost always visible
+
+half _EyeGlintFactor;
+
+//////////////////////////////
+// Eye Specific Properties //
+//////////////////////////////
+
+float _LeftEyeUp;
+float _LeftEyeRight;
+float _RightEyeUp;
+float _RightEyeRight;
+half _UVScale;
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye/AvatarEyeProperties.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye/AvatarEyeProperties.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..b839f8ecf2a025c90d5871843e6c91eee0c5255a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye/AvatarEyeProperties.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 2c128b96f61accd4f85a68b140d994f9
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye/AvatarEyePropertiesHeadC.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye/AvatarEyePropertiesHeadC.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..be5e5d0cc495ab45cd20bb48ae58887071f86cea
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye/AvatarEyePropertiesHeadC.cginc
@@ -0,0 +1,27 @@
+#ifndef AVATAR_EYE_PROPERTIES_HEAD_C_CGINC
+#define AVATAR_EYE_PROPERTIES_HEAD_C_CGINC
+
+#include "../AvatarTextured/AvatarTexturedProperties.cginc"
+
+////////////////////////////////////
+// Head C Eye Specific Properties //
+////////////////////////////////////
+
+float _LeftEyeTX;
+float _LeftEyeTY;
+float _RightEyeTX;
+float _RightEyeTY;
+
+float _EyeXMin;
+float _EyeXMid;
+float _EyeXMax;
+
+float _EyeYMin;
+float _EyeYMid;
+float _EyeYMax;
+
+half _IrisScale;
+half _PupilScale;
+half _EyeUVScale;
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye/AvatarEyePropertiesHeadC.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye/AvatarEyePropertiesHeadC.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..239abafcba757cba0660ab59974542337a34a891
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarEye/AvatarEyePropertiesHeadC.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: b113bd471cb11334bb205cdeff30641a
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarHair.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarHair.meta
new file mode 100644
index 0000000000000000000000000000000000000000..a6580723b686a860350eaa3086f1ff3bbaa26a18
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarHair.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: e92b50bd597fec2408f3fbc679b25016
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarHair/AvatarHairAlbedo.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarHair/AvatarHairAlbedo.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..b7dbd4a0fe9c2625e4d1b60b71f4fc8137fae93c
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarHair/AvatarHairAlbedo.cginc
@@ -0,0 +1,8 @@
+#ifndef AVATAR_HAIR_ALBEDO_CGINC
+#define AVATAR_HAIR_ALBEDO_CGINC
+
+half3 HairAlbedo(half4 mainTex, half3 color, half3 secondaryColor) {
+  return lerp(color.rgb, secondaryColor.rgb, mainTex.r);
+}
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarHair/AvatarHairAlbedo.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarHair/AvatarHairAlbedo.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..6d697040a8a404373ef144cbd3105563686d1d35
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarHair/AvatarHairAlbedo.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 6830790fc8a1a2d4aa9c402933590b9a
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarHair/AvatarHairProperties.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarHair/AvatarHairProperties.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..fe0a947cd154839a801665943e0358dc7e208cee
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarHair/AvatarHairProperties.cginc
@@ -0,0 +1,10 @@
+#ifndef AVATAR_HAIR_PROPERTIES_CGINC
+#define AVATAR_HAIR_PROPERTIES_CGINC
+
+#include "../AvatarCommon/AvatarCommonProperties.cginc"
+#include "../AvatarCommon/AvatarPrimaryColorProperty.cginc"
+#include "../AvatarCommon/AvatarSecondaryColorProperty.cginc"
+#include "../AvatarCommon/AvatarEffectsMap.cginc"
+#include "../AvatarCommon/AvatarPropertiesMap.cginc"
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarHair/AvatarHairProperties.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarHair/AvatarHairProperties.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..764b67131043a8efd4b21b56bd2e339082b0a241
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarHair/AvatarHairProperties.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 40d2a0de07fa6db40bcea7bc9aaedb97
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSkin.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSkin.meta
new file mode 100644
index 0000000000000000000000000000000000000000..8bb3087e6a942b867bc5c1036a0a29e3c11c3091
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSkin.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: a68007d53c963354a8ad0808f9edcf60
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSkin/AvatarSkinAlbedo.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSkin/AvatarSkinAlbedo.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..70f39b6237bf87786440bdb1bbc15156f84d59f4
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSkin/AvatarSkinAlbedo.cginc
@@ -0,0 +1,30 @@
+#ifndef AVATAR_SKIN_ALBEDO_CGINC
+#define AVATAR_SKIN_ALBEDO_CGINC
+
+// BlendScreen and BlendMultiply correspond to screen and multiply blend modes in image editing applications like Photoshop.
+// More information here: https://en.wikipedia.org/wiki/Blend_modes#Multiply_and_Screen
+
+half3 BlendScreen(half3 base, half3 blend) {
+  return 1. - ((1. - base) * (1. - blend));
+}
+
+half3 BlendScreen(half3 base, half3 blend, half opacity) {
+  return lerp(base, BlendScreen(base, blend), opacity);
+}
+
+half3 BlendMultiply(half3 base, half3 blend, half opacity) {
+  return lerp(base, base * blend, opacity);
+}
+
+half3 SkinAlbedo(half4 mainTex, half3 vertColor, half4 stubbleColor, half stubbleMask) {
+#ifdef MATERIAL_MODE_TEXTURE
+  half3 albedo = mainTex.rgb;
+#else
+  half3 albedo = vertColor;
+#endif
+  return lerp(BlendMultiply(albedo, stubbleColor.rgb, stubbleMask), 
+              BlendScreen(albedo, stubbleColor.rgb, stubbleMask), 
+              step(.5, stubbleColor.a));
+}
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSkin/AvatarSkinAlbedo.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSkin/AvatarSkinAlbedo.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..b94e83137f98b54560467f39157646f7f52482d4
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSkin/AvatarSkinAlbedo.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: a7ad987de33a5614a9b9620e2756f326
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSkin/AvatarSkinLighting.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSkin/AvatarSkinLighting.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..abb2a427675a9fbac7199c718a5035576e61d7ac
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSkin/AvatarSkinLighting.cginc
@@ -0,0 +1,141 @@
+#ifndef AVATAR_SKIN_LIGHTING_CGINC
+#define AVATAR_SKIN_LIGHTING_CGINC
+
+#include "AvatarSkinProperties.cginc"
+#include "../AvatarCommon/AvatarCommonLighting.cginc"
+
+////////////////////////////////////////////////////
+// Surface and Lighting Functions and Definitions //
+////////////////////////////////////////////////////
+
+half3 Translucency(
+    half3 N,
+    half3 L,
+    half3 V,
+    half power,
+    half scale,
+    half thickness,
+    half3 translucencyColor,
+    half translucencyDistortion)
+{
+    // The vertex GI lighting system has a specific keyword
+    // for enabling direct lighting at all, so, pay attention to that
+    #if !defined(__LIGHTING_SYSTEM_VERTEX_GI) || defined(DIRECTIONAL_LIGHT)
+        // Calculate intensity of backlight (light translucent).
+        // This is pulled from a "fast subsurface scattering" work here
+        // https://www.alanzucconi.com/2017/08/30/fast-subsurface-scattering-1
+
+        // H here isn't between L and V, but between L and N
+        half3 H = normalize(L + N * translucencyDistortion);
+        float VdotH = powApprox(saturate(dot(V, -H)), power * 64.0) * scale;
+        return translucencyColor * VdotH * thickness;
+    #else
+        return 0.0h;
+    #endif
+}
+
+half3 BacklitWarmth(half3 NegNdotL, half maximumContribution, half3 backlightColor) {
+    // The vertex GI lighting system has a specific keyword
+    // for enabling direct lighting at all, so, pay attention to that
+    #if !defined(__LIGHTING_SYSTEM_VERTEX_GI) || defined(DIRECTIONAL_LIGHT)
+        // Take the back light from 0 to a max over the 0 to 1 range of NegNdotL
+        half amount = lerp(0.0h, maximumContribution, saturate(NegNdotL));
+        return backlightColor * amount;
+    #else
+        return 0.0h;
+    #endif
+}
+
+half3 FinalLightingCombine(
+    half3 directDiffuse,
+    half3 directSpecular,
+    half3 indirectDiffuse,
+    half3 indirectSpecular,
+    half3 translucency,
+    half3 backlitWarmth,
+    half3 lightColor)
+{
+  #if defined(_RENDER_DEBUG_TRANSLUCENCY)
+    return translucency * lightColor;
+  #elif defined(_RENDER_DEBUG_BACKLIGHT)
+    return backlitWarmth * lightColor;
+  #elif AVATAR_COMMON_DEBUG_LIGHTING_ENABLED
+    // Return the "AvatarCommonLighting.cginc" version (without backlitWamrth and translucency) to handle the debug rendering
+    return FinalLightingCombine(directDiffuse, directSpecular, indirectDiffuse, indirectSpecular);
+  #else
+    // Full lighting
+    half3 directLighting = (directDiffuse + directSpecular + translucency + backlitWarmth) * lightColor;
+    return directLighting + indirectDiffuse + indirectSpecular;
+
+  #endif
+}
+
+half3 AvatarSkinLighting(
+    half3 albedo,
+    float3 normal,
+    float3 viewDir,
+    half minDiffuse,
+    half perceptualRoughness,
+    half perceptualSmoothness,
+    half metallic,
+    half thickness,
+    half backlightScale,
+    half3 translucencyColor,
+    half3 backlightColor,
+    half occlusion,
+    AvatarShaderGlobalIllumination gi)
+{
+    half oneMinusReflectivity;
+    half3 specColor;
+    half3 diffColor = DiffuseAndSpecularFromMetallic(
+        albedo,
+        metallic,
+        /*out*/ specColor,
+        /*out*/ oneMinusReflectivity);
+
+    float3 lightDirection = gi.light.direction;
+    float3 halfDirection = Unity_SafeNormalize(lightDirection + viewDir);
+
+    half rawNdotL = clamp(dot(normal, lightDirection), -1.0, 1.0); // -1 to 1
+    half NdotL = saturate(rawNdotL); // 0 to 1
+    float roughness = PerceptualRoughnessToRoughness(perceptualRoughness);
+
+    float NdotH = saturate(dot(normal, halfDirection));
+    float LdotH = saturate(dot(lightDirection, halfDirection));
+
+    half NdotV = saturate(dot(normal, viewDir));
+    
+    half directOcclusion = lerp(1.0f, occlusion, _DirectOcclusionEffect);
+
+    // For the moment, everything has the same direct specular
+    half3 directSpecular = DirectSpecularLightingWithOcclusion(specColor, NdotH, LdotH, NdotL, roughness, directOcclusion);
+
+    // All components have same direct diffuse and indirect lighting
+    half3 directDiffuse = DirectDiffuseLightingWithOcclusion(diffColor, rawNdotL, minDiffuse, directOcclusion);
+    half3 indirectDiffuse = IndirectDiffuseLighting(diffColor, gi.indirect);
+    half3 indirectSpecular = IndirectSpecularLighting(
+        specColor,
+        roughness,
+        perceptualRoughness,
+        perceptualSmoothness,
+        oneMinusReflectivity,
+        NdotV,
+        gi.indirect);
+
+    // Skin component needs some additional lighting functions
+    half3 translucency = Translucency(
+        normal,
+        lightDirection,
+        viewDir,
+        _TranslucencyPower,
+        _TranslucencyScale,
+        thickness,
+        translucencyColor,
+        _Distortion);
+
+    half3 backlitWarmth = BacklitWarmth(-rawNdotL, backlightScale, backlightColor);
+
+    return FinalLightingCombine(directDiffuse, directSpecular, indirectDiffuse, indirectSpecular, translucency, backlitWarmth, gi.light.color);
+}
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSkin/AvatarSkinLighting.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSkin/AvatarSkinLighting.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..5a5126ff291cdccef01686c3d78ae386397238d9
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSkin/AvatarSkinLighting.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 5941532386297ab4a8bbe95e2b3f9847
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSkin/AvatarSkinProperties.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSkin/AvatarSkinProperties.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..b1273cf28b42fa15eedbdc68da7dc1d3fd86ecc4
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSkin/AvatarSkinProperties.cginc
@@ -0,0 +1,21 @@
+#ifndef AVATAR_SKIN_PROPERTIES_CGINC
+#define AVATAR_SKIN_PROPERTIES_CGINC
+
+#include "../AvatarCommon/AvatarCommonProperties.cginc"
+#include "../AvatarCommon/AvatarPrimaryColorProperty.cginc"
+#include "../AvatarCommon/AvatarSecondaryColorProperty.cginc"
+#include "../AvatarCommon/AvatarTertiaryColorProperty.cginc"
+#include "../AvatarCommon/AvatarMainTex.cginc"
+#include "../AvatarCommon/AvatarPropertiesMap.cginc"
+#include "../AvatarCommon/AvatarEffectsMap.cginc"
+
+//////////////////////////////
+// Skin Specific Properties //
+//////////////////////////////
+
+half _Distortion;
+half _TranslucencyPower;
+half _TranslucencyScale;
+half _BacklightScale;
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSkin/AvatarSkinProperties.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSkin/AvatarSkinProperties.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..f59b048ca578f088be0915ed2fe59bc3c03f4660
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSkin/AvatarSkinProperties.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 879bed1f3292c15418f3098c8bf662fc
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSkin/AvatarSkinSurfaceFunctions.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSkin/AvatarSkinSurfaceFunctions.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..4da35235226ceef34a86a0c27f0cd91af139a362
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSkin/AvatarSkinSurfaceFunctions.cginc
@@ -0,0 +1,16 @@
+#ifndef AVATAR_SKIN_SURFACE_FUNCTIONS_CGINC
+#define AVATAR_SKIN_SURFACE_FUNCTIONS_CGINC
+
+#if defined(_LIGHTING_SYSTEM_VERTEX_GI) || defined (_LIGHTING_SYSTEM_UNITY)
+
+    // Utilities
+    #define SURFACE_ADDITIONAL_FIELDS_SKIN \
+        half Thickness; \
+        half BacklightScale; \
+        half3 TranslucencyColor; \
+        half3 BacklightColor;
+
+#endif
+
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSkin/AvatarSkinSurfaceFunctions.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSkin/AvatarSkinSurfaceFunctions.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..c6fe47fd88f8ba5f6c35a14245a8d09c8e5d58f1
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSkin/AvatarSkinSurfaceFunctions.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: c7780de73fce2ff4da483fc6fba0eff8
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSolidColor.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSolidColor.meta
new file mode 100644
index 0000000000000000000000000000000000000000..ac0e76b88841104d05f175fffa0e4b14421b2265
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSolidColor.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 71a08231d802abc4183dc1e050f7778d
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSolidColor/AvatarSolidColorAlbedo.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSolidColor/AvatarSolidColorAlbedo.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..bae5e70e75c5c17b1bd9ba4ebd0bdaff36fabc1d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSolidColor/AvatarSolidColorAlbedo.cginc
@@ -0,0 +1,8 @@
+#ifndef AVATAR_SOLID_COLOR_ALBEDO_CGINC
+#define AVATAR_SOLID_COLOR_ALBEDO_CGINC
+
+half3 SolidColorAlbedo(half3 color) {
+  return color;
+}
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSolidColor/AvatarSolidColorAlbedo.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSolidColor/AvatarSolidColorAlbedo.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..10b32ae6a26c09605ab4db5eb2e3b6a24ac1420f
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSolidColor/AvatarSolidColorAlbedo.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 7ae8c228c492b7a42a5ec3f3ca53b6eb
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSolidColor/AvatarSolidColorProperties.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSolidColor/AvatarSolidColorProperties.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..211047ccff2dee1d71bca2da081dc7ade683aacb
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSolidColor/AvatarSolidColorProperties.cginc
@@ -0,0 +1,8 @@
+#ifndef AVATAR_SOLID_COLOR_PROPERTIES_CGINC
+#define AVATAR_SOLID_COLOR_PROPERTIES_CGINC
+
+#include "../AvatarCommon/AvatarCommonProperties.cginc"
+#include "../AvatarCommon/AvatarPrimaryColorProperty.cginc"
+#include "../AvatarCommon/AvatarPropertiesMap.cginc"
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSolidColor/AvatarSolidColorProperties.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSolidColor/AvatarSolidColorProperties.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..219874c99fe8ff5cb4bcc3a73948a104e4244e79
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSolidColor/AvatarSolidColorProperties.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: dc3ba2c4d686adf46b79ef572098c623
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSubmesh.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSubmesh.meta
new file mode 100644
index 0000000000000000000000000000000000000000..7e2c30da25f754995cf0fa873cb4570fab035755
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSubmesh.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 2791034b58579aa4bbdf894f67999369
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSubmesh/AvatarSubmeshLighting.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSubmesh/AvatarSubmeshLighting.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..4c80f10538be4cb88e6bc2607b9ffb3de46672ed
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSubmesh/AvatarSubmeshLighting.cginc
@@ -0,0 +1,133 @@
+#ifndef AVATAR_SUBMESH_LIGHTING_CGINC
+#define AVATAR_SUBMESH_LIGHTING_CGINC
+
+#include "../Horizon/MacroRemapper.cginc"
+
+// Lighting functions utilized by the submesh technique to make different sub meshes in the material look different
+
+#include "..\AvatarSubmesh\AvatarSubmeshProperties.cginc"
+#include "..\AvatarCommon\AvatarCommonLighting.cginc"
+#include "..\AvatarEye\AvatarEyeGlint.cginc"
+
+#define AVATAR_SUBMESH_DEBUG_LIGHTING_ENABLED defined(_RENDER_DEBUG_DIFFUSE) || defined(_RENDER_DEBUG_SPECULAR) || defined(_RENDER_DEBUG_INDIRECT_DIFFUSE) || defined(_RENDER_DEBUG_INDIRECT_SPECULAR)
+
+// Caller supplied diffColor, specColor, and direct specular
+// Computing direct diffuse, indirect lighting
+half3 AvatarLightingSubmesh(
+    half3 diffColor,
+    half3 specColor,
+    half3 directSpecular,
+    half3 normal,
+    float subMeshType,
+    half3 viewDir,
+    half minDiffuse,
+    float roughness,
+    half perceptualRoughness,
+    half perceptualSmoothness,
+    half metallic,
+    half oneMinusReflectivity,
+    half rawNdotL,
+    half NdotL,
+    half directOcclusion,
+    AvatarShaderGlobalIllumination gi)
+{
+    half NdotV = saturate(dot(normal, viewDir));
+
+    half3 directDiffuse = DirectDiffuseLightingWithOcclusion(diffColor, rawNdotL, minDiffuse, directOcclusion);
+
+    half3 indirectDiffuse = IndirectDiffuseLighting(diffColor, gi.indirect);
+    half3 indirectSpecular = IndirectSpecularLighting(
+        specColor,
+        roughness,
+        perceptualRoughness,
+        perceptualSmoothness,
+        oneMinusReflectivity,
+        NdotV,
+        gi.indirect);
+
+    return FinalLightingCombine(directDiffuse, directSpecular, indirectDiffuse, indirectSpecular, gi.light.color);
+}
+
+// Computes all lighting (direct diffuse, direct specular, indirect diffuse, indirect specular)
+half3 AvatarLightingSubmesh(
+    half3 albedo,
+    float3 normal,
+    float subMeshType,
+    float3 viewDir,
+    half minDiffuse,
+    half perceptualRoughness,
+    half perceptualSmoothness,
+    half metallic,
+    half directOcclusion,
+    AvatarShaderGlobalIllumination gi)
+{
+    half oneMinusReflectivity;
+    half3 specColor;
+    half3 diffColor = DiffuseAndSpecularFromMetallic(
+        albedo,
+        metallic,
+        /*out*/ specColor,
+        /*out*/ oneMinusReflectivity);
+
+    float3 lightDirection = gi.light.direction;
+    float3 halfDirection = Unity_SafeNormalize(lightDirection + viewDir);
+
+    half rawNdotL = clamp(dot(normal, lightDirection), -1.0, 1.0); // -1 to 1
+    half NdotL = saturate(rawNdotL); // 0 to 1
+    float roughness = PerceptualRoughnessToRoughness(perceptualRoughness);
+
+    float NdotH = saturate(dot(normal, halfDirection));
+    float LdotH = saturate(dot(lightDirection, halfDirection));
+
+    bool useEyeGlint =
+        ((subMeshType > (OVR_SUBMESH_TYPE_L_EYE - OVR_SUBMESH_TYPE_BUFFER) / 255.0) &&
+         (subMeshType < (OVR_SUBMESH_TYPE_R_EYE + OVR_SUBMESH_TYPE_BUFFER) / 255.0));
+
+    half3 directSpecular;
+#ifdef EYE_GLINTS
+    [branch]
+    if (useEyeGlint) {
+      directSpecular = DirectSpecularLightingWithOcclusionWithEyeGlints(specColor, NdotH, LdotH, NdotL, roughness, directOcclusion, normal, lightDirection, viewDir);
+    }
+    else
+#endif
+    {
+      directSpecular = DirectSpecularLightingWithOcclusion(specColor, NdotH, LdotH, NdotL, roughness, directOcclusion);
+    }
+
+    half3 returnVal = AvatarLightingSubmesh(
+        diffColor,
+        specColor,
+        directSpecular,
+        normal,
+        subMeshType,
+        viewDir,
+        minDiffuse,
+        roughness,
+        perceptualRoughness,
+        perceptualSmoothness,
+        metallic,
+        oneMinusReflectivity,
+        rawNdotL,
+        NdotL,
+        directOcclusion,
+        gi);
+
+    return returnVal;
+}
+
+#define AVATAR_SHADER_DECLARE_SUBMESH_LIGHTING_PARAMS(AlbedoVar, NormVar, RoughVar, SmoothVar, MetalVar, AlphaVar, MinDiffuseVar, subMeshTypeVar, SurfaceOutputVar) \
+    half3 NormVar = GET_AVATAR_SHADER_SURFACE_NORMAL_FIELD(SurfaceOutputVar); \
+    fixed3 AlbedoVar = GET_AVATAR_SHADER_SURFACE_ALBEDO_FIELD(SurfaceOutputVar); \
+    \
+    half MetalVar = GET_AVATAR_SHADER_SURFACE_METALLIC_FIELD(SurfaceOutputVar); \
+    half AlphaVar = GET_AVATAR_SHADER_SURFACE_ALPHA_FIELD(SurfaceOutputVar); \
+    \
+    half RoughVar = GET_AVATAR_SHADER_SURFACE_ROUGHNESS_FIELD(SurfaceOutputVar); \
+    half SmoothVar = GET_AVATAR_SHADER_SURFACE_SMOOTHNESS_FIELD(SurfaceOutputVar); \
+    half MinDiffuseVar = GET_AVATAR_SHADER_SURFACE_MIN_DIFFUSE_FIELD(SurfaceOutputVar); \
+    half occlusion = GET_AVATAR_SHADER_SURFACE_OCCLUSION_FIELD(SurfaceOutputVar); \
+    half directOcclusion = lerp(1.0f, occlusion, _DirectOcclusionEffect); \
+    float subMeshTypeVar = SurfaceOutputVar.SubMeshType;
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSubmesh/AvatarSubmeshLighting.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSubmesh/AvatarSubmeshLighting.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..20acdaebe0f74fcd7d429fb0c75fbdfdb557e98a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSubmesh/AvatarSubmeshLighting.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 67666cde9d4ce334d8949afe62eca4de
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSubmesh/AvatarSubmeshProperties.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSubmesh/AvatarSubmeshProperties.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..b3993229644937d5f51e46973c38226bafbfe5df
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSubmesh/AvatarSubmeshProperties.cginc
@@ -0,0 +1,19 @@
+#ifndef AVATAR_SUBMESH_PROPERTIES_CGINC
+#define AVATAR_SUBMESH_PROPERTIES_CGINC
+
+#define OVR_SUBMESH_TYPE_NONE 0
+#define OVR_SUBMESH_TYPE_OUTFIT     (float)(1<<0)
+#define OVR_SUBMESH_TYPE_BODY       (float)(1<<1)
+#define OVR_SUBMESH_TYPE_HEAD       (float)(1<<2)
+#define OVR_SUBMESH_TYPE_HAIR       (float)(1<<3)
+#define OVR_SUBMESH_TYPE_EYEBROW    (float)(1<<4)
+#define OVR_SUBMESH_TYPE_L_EYE      (float)(1<<5)
+#define OVR_SUBMESH_TYPE_R_EYE      (float)(1<<6)
+#define OVR_SUBMESH_TYPE_LASHES     (float)(1<<7)
+#define OVR_SUBMESH_TYPE_FACIALHAIR (float)(1<<8)
+#define OVR_SUBMESH_TYPE_HEADWEAR   (float)(1<<9)
+#define OVR_SUBMESH_TYPE_EARRINGS   (float)(1<<10)
+
+#define OVR_SUBMESH_TYPE_BUFFER 0.5
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSubmesh/AvatarSubmeshProperties.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSubmesh/AvatarSubmeshProperties.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..0090100fc0ccf6d3f8947e967dfb5ae231cb337c
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarSubmesh/AvatarSubmeshProperties.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: e8962feed356dbd4083e7cf1da02315d
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarTextured.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarTextured.meta
new file mode 100644
index 0000000000000000000000000000000000000000..8b4ca3488f427e09cd246742221aec6ff0988ac7
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarTextured.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: f06afa352085ccf4b8c4575d380725d7
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarTextured/AvatarTexturedAlbedo.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarTextured/AvatarTexturedAlbedo.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..bfc790367c55d8b2bffde68b93b85946437ae95f
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarTextured/AvatarTexturedAlbedo.cginc
@@ -0,0 +1,12 @@
+#ifndef AVATAR_TEXTURED_ALBEDO_CGINC
+#define AVATAR_TEXTURED_ALBEDO_CGINC
+
+half3 TexturedAlbedo(half4 mainTex, half3 vertColor, half3 color) {
+#ifdef MATERIAL_MODE_TEXTURE
+  return mainTex.rgb * color;
+#else
+  return vertColor * color;
+#endif
+}
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarTextured/AvatarTexturedAlbedo.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarTextured/AvatarTexturedAlbedo.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..dad0f9f4187616a07a62c1921441dabc66948ba8
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarTextured/AvatarTexturedAlbedo.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 67eac781bce8795458a8f234f7e74384
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarTextured/AvatarTexturedProperties.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarTextured/AvatarTexturedProperties.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..19290ff341dd4c4e97d0f5429155ff83205c547d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarTextured/AvatarTexturedProperties.cginc
@@ -0,0 +1,9 @@
+#ifndef AVATAR_TEXTURED_PROPERTIES_CGINC
+#define AVATAR_TEXTURED_PROPERTIES_CGINC
+
+#include "../AvatarCommon/AvatarCommonProperties.cginc"
+#include "../AvatarCommon/AvatarPrimaryColorProperty.cginc"
+#include "../AvatarCommon/AvatarMainTex.cginc"
+#include "../AvatarCommon/AvatarPropertiesMap.cginc"
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarTextured/AvatarTexturedProperties.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarTextured/AvatarTexturedProperties.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..c0967e811da8b4d6dcfcb842429ec2e6b84df447
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarTextured/AvatarTexturedProperties.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 26dc55a644fb6e342a6ece9306db9185
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarVGI.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarVGI.meta
new file mode 100644
index 0000000000000000000000000000000000000000..e6f9563970e3bf274a8a0b110fd946f2cde353dc
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarVGI.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 18accd87fcf87c34a82db54ebe2765b6
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarVGI/AvatarVGIFramework.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarVGI/AvatarVGIFramework.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..94327ea8bcc02e49c588f208ed351cd0b64f377b
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarVGI/AvatarVGIFramework.cginc
@@ -0,0 +1,196 @@
+//================================================================================
+// Common shader framework for Avatar Shaders using RenderToolkit's vertex-based global illumination
+//
+// This file should be included by common shaders that don't need to take over
+// the main control flow
+//
+// See https://fb.quip.com/j99FAwxuOGCX
+//================================================================================
+
+#ifndef AVATAR_VGI_FRAMEWORK_CGINC
+#define AVATAR_VGI_FRAMEWORK_CGINC
+
+#include "../AvatarCommon/AvatarShaderTypes.cginc"
+#include "../AvatarCommon/AvatarCommonProperties.cginc"
+#include "../../../../../Scripts/ShaderUtils/AvatarCustom.cginc"
+#include "AvatarVGITypes.cginc"
+#include "../Horizon/VertexGI/SurfaceShader.cginc"
+
+#ifdef FADE_ON
+  #define DECLARE_COVERAGE_MASK  , out uint outputCoverageMask : SV_Coverage
+  #define SET_COVERAGE_MASK outputCoverageMask = o.CoverageMask;
+#else
+  #define DECLARE_COVERAGE_MASK
+  #define SET_COVERAGE_MASK
+#endif
+
+//================================
+// Vertex program
+//================================
+
+void AvatarVGIVertInit(appdata v) {
+    OVR_INITIALIZE_VERTEX_FIELDS(v);
+}
+
+void AvatarVGIVertTransform(appdata v, inout vgi_vert_tmp tmp) {
+    OvrVertexData vData = OVR_CREATE_VERTEX_DATA(v);
+    // Call the default transform
+    vgi_vert_transform(v, vData, tmp);
+}
+
+#define DEFINE_DEFAULT_AVATAR_VGI_VERT(VertProgramName) \
+    v2f VertProgramName(appdata v) { \
+      /* Call the vertex-GI vertex program */ \
+      vgi_vert_tmp  tmp; \
+      UNITY_INITIALIZE_OUTPUT(vgi_vert_tmp, tmp);\
+      /* Step 0: Initialize vertex input */ \
+      AvatarVGIVertInit(v); \
+      /* Step 1: transform position and normal */ \
+      AvatarVGIVertTransform(v, tmp); \
+      /* Step 2: transfer from vertex data and tmp to interpolators */ \
+      v2f o = vgi_vert(v, tmp); \
+      o.propertiesMapUV.xy = v.uv.xy;\
+      o.effectsMapUV.xy = v.uv.xy;\
+      return o; \
+    }
+
+#define DEFINE_AVATAR_VGI_VERT(VertProgramName, vert_func) \
+    v2f VertProgramName(appdata v) { \
+      /* Call the vertex-GI vertex program */ \
+      vgi_vert_tmp  tmp; \
+      UNITY_INITIALIZE_OUTPUT(vgi_vert_tmp, tmp) \
+      /* Step 0: Initialize vertex input */ \
+      AvatarVGIVertInit(v); \
+      /* Step 1: transform position and normal */ \
+      AvatarVGIVertTransform(v, tmp); \
+      \
+      /* Call GI code to return the fragment color */ \
+      v2f o = vgi_vert(v, tmp); \
+      o.propertiesMapUV.xy = v.uv.xy;\
+      o.effectsMapUV.xy = v.uv.xy;\
+      /* Call additional vert_func to further alter the v2f */ \
+      vert_func(v, tmp, o); \
+      return o; \
+    }
+
+AvatarShaderGlobalIllumination GetAvatarShaderGlobalIllumination(v2f i, vgi_frag_tmp tmp) {
+  AvatarShaderLight light;
+  light.direction = _LightVector.xyz;
+  light.color = _LightColor.rgb * tmp.shadowFactor;
+
+  AvatarShaderIndirect indirect;
+  indirect.diffuse = calc_indirect_diffuse_light(i, tmp);
+  indirect.specular = indirect.diffuse;
+
+  AvatarShaderGlobalIllumination avatarGI;
+  avatarGI.light = light;
+  avatarGI.indirect = indirect;
+
+  return avatarGI;
+}
+
+// Does not include "specular intensity" (fresnel/energy conservation) factor to mimic
+// the behavior of the UnityGlobalIllumination
+float VGIAmbientSpecular(half3 viewDir, half3 normal, half smooth, half metal) {
+  half3 r = reflect(-viewDir, normal);
+  // artificially force the surface to be less smooth, and return a less bright reflection (T65635945)
+  return ambient_specular_from_cube_map(r, smooth*.5, metal)*.25;
+}
+
+AvatarShaderGlobalIllumination GetAvatarShaderGlobalIllumination(v2f i, half3 viewDir, vgi_frag_tmp tmp, half smoothness, half metallic, half occlusion) {
+  AvatarShaderLight light;
+
+  light.direction = _LightVector.xyz;
+  light.color = _LightColor.rgb * tmp.shadowFactor;
+
+  AvatarShaderIndirect indirect;
+  half ambientOcclusion = lerp(1.0f, occlusion, _AmbientOcclusionEffect);
+  indirect.diffuse = calc_indirect_diffuse_light(i, tmp) * ambientOcclusion;
+  indirect.specular = indirect.diffuse * VGIAmbientSpecular(viewDir, tmp.normal, smoothness, metallic) * ambientOcclusion;
+
+  AvatarShaderGlobalIllumination avatarGI;
+  avatarGI.light = light;
+  avatarGI.indirect = indirect;
+
+  return avatarGI;
+}
+
+//================================
+// Fragment program
+//================================
+
+#define DEFINE_AVATAR_VGI_FRAG(FragProgramName, SurfFuncName, SurfaceOutputType, LightingFunc) \
+    float4 FragProgramName(v2f i) : COLOR { \
+      vgi_frag_tmp  tmp; \
+      UNITY_INITIALIZE_OUTPUT(vgi_frag_tmp, tmp);\
+\
+      /* Call the first stage of the fragment program:\
+       'tmp' initialization and normal mapping */ \
+      vgi_frag_init(i, tmp); \
+\
+      /* Call the shadowmapping code. This is optional. \
+         If you don't want dynamic shadows, just don't call this.*/  \
+      vgi_frag_dynamicshadow(i, tmp); \
+\
+      /* Call "surf" function */ \
+      SurfaceOutputType o; \
+      UNITY_INITIALIZE_OUTPUT(SurfaceOutputType, o); \
+      SurfFuncName(i, o, tmp); \
+\
+      /* Parse out lighting information from the fragment input */ \
+      AvatarShaderGlobalIllumination gi = GetAvatarShaderGlobalIllumination(i, tmp); \
+\
+      /* Call lighting function, expects normalized view direction vector */ \
+      half4 result = LightingFunc(o, -normalize(i.wPos - _WorldSpaceCameraPos), gi); \
+\
+      /* Call the final stage: \
+         Fog and tone mapping */ \
+      float4 finalColor = vgi_frag_final(i, result.rgb); \
+      finalColor.a = result.a; \
+\
+      return finalColor; \
+    }
+
+#define DEFINE_AVATAR_VGI_FRAG_PBS(FragProgramName, SurfFuncName, SurfaceOutputType, LightingFunc) \
+    float4 FragProgramName(v2f i \
+      DECLARE_COVERAGE_MASK) : COLOR { \
+      vgi_frag_tmp  tmp; \
+      UNITY_INITIALIZE_OUTPUT(vgi_frag_tmp, tmp);\
+\
+      /* Call the first stage of the fragment program:\
+       'tmp' initialization and normal mapping */ \
+      vgi_frag_init(i, tmp); \
+\
+      /* Call the shadowmapping code. This is optional. \
+         If you don't want dynamic shadows, just don't call this.*/  \
+      vgi_frag_dynamicshadow(i, tmp); \
+\
+      /* Call "surf" function */ \
+      SurfaceOutputType o; \
+      UNITY_INITIALIZE_OUTPUT(SurfaceOutputType, o); \
+      SurfFuncName(i, o, tmp); \
+\
+      /* Grab some fields from surface output that are required to be there */ \
+      half3 viewDir = -normalize(i.wPos - _WorldSpaceCameraPos); \
+      half smoothness = GET_AVATAR_SHADER_SURFACE_SMOOTHNESS_FIELD(o); \
+      half metallic = GET_AVATAR_SHADER_SURFACE_METALLIC_FIELD(o); \
+      half occlusion = GET_AVATAR_SHADER_SURFACE_OCCLUSION_FIELD(o); \
+\
+      /* Parse out lighting information from the fragment input */ \
+      AvatarShaderGlobalIllumination gi = GetAvatarShaderGlobalIllumination(i, viewDir, tmp, smoothness, metallic, occlusion); \
+\
+      /* Call lighting function, expects normalized view direction vector */ \
+      half4 result = LightingFunc(o, viewDir, gi); \
+\
+      /* Call the final stage: \
+         Fog and tone mapping */ \
+      float4 finalColor = vgi_frag_final(i, result.rgb); \
+      finalColor.a = result.a; \
+\
+      SET_COVERAGE_MASK \
+\
+      return finalColor; \
+    }
+
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarVGI/AvatarVGIFramework.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarVGI/AvatarVGIFramework.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..73d1e683c1204cb3a5d435d20e707b1e3b43a2ed
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarVGI/AvatarVGIFramework.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: ea4315dac0c556240b56f90901b6ce7a
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarVGI/AvatarVGITypes.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarVGI/AvatarVGITypes.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..03e1320a175ff485538864f04ff9f4d5e2b74e26
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarVGI/AvatarVGITypes.cginc
@@ -0,0 +1,17 @@
+#ifndef AVATAR_VGI_TYPES_CGINC
+#define AVATAR_VGI_TYPES_CGINC
+
+#define ALPHA_MATERIAL 1
+
+#include "Horizon/VertexGI/Interface.cginc"
+
+#define AVATAR_VGI_V2F_ADDITIONAL_SEMANTICS(idx1, idx2) \
+    float4 propertiesMapUV : TEXCOORD##idx1; \
+    float4 effectsMapUV : TEXCOORD##idx2;
+
+struct v2f {
+    VGI_V2F_ATTRIBUTES
+    AVATAR_VGI_V2F_ADDITIONAL_SEMANTICS(VGI_V2F_NEXT_AVAILABLE_TEXCOORD_SEMANTIC, VGI_V2F_SECOND_AVAILABLE_TEXCOORD_SEMANTIC)
+};
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarVGI/AvatarVGITypes.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarVGI/AvatarVGITypes.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..9a31ce1c54e19ca56720f8641cb19ebe092c23dc
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/AvatarVGI/AvatarVGITypes.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: d45c5a0fbf2f0794eb0f77e38369ec7c
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon.meta
new file mode 100644
index 0000000000000000000000000000000000000000..c9f2c2b19ed09382838fac4c271ce8504ee089e7
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 6bc48ccd735c95d4ea2dc37ee702b3cf
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/FBFog.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/FBFog.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..0b1161f7d2c7ac1b87c1db1694d54c5712ee4552
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/FBFog.cginc
@@ -0,0 +1,181 @@
+//======================================================================================
+// Common fog / haze code
+// Supports constant fog density and density fall-off with altitude (height fog).
+// Sun position-based scattering is a work in progress
+//======================================================================================
+
+// rgb:color, a:density*density
+float4 _fbFogColor;
+
+// [0]: xyzw: fogStartDistance, fogEndDistance, fogDensity, fogScale
+// [1]: xyzw: fogDensityAltFalloffExp, fogDensityAltFalloffStart, 1/(fogEndDistance-fogStartDistance), 0
+float4 _fbFogParams[2];
+
+#ifdef fbFOG_CUBEMAP
+samplerCUBE  _fbFogSkyBoxTexture;
+#endif
+
+#if (fbFOG_VTX && fbFOG_CUBEMAP) || fbFOG_EXP
+#define VERT_NEED_WPOS  1  // World position is needed in the vertex shader
+#define FRAG_NEED_WPOS  1  // World position is needed in the fragment shader
+#endif
+
+#if fbFOG_VTX
+#define VERT_NEED_WPOS  1
+#endif
+
+//
+// Macro for the fragment shader protion of fbFog
+//
+#if fbFOG_VTX
+  // Vertex shader-based fog.
+
+  // todo: use exp2()
+  #define fbFog_vert(mDensitySq, mCameraPos, mPos, mOutW)\
+  {\
+    float fogScale = _fbFogParams[0].w;\
+    mOutW = exp(-mDensitySq * lengthSq((mCameraPos - mPos) * fogScale));\
+  }
+
+  #if fbFOG_CUBEMAP
+    #define fbFog_frag(mResult, mLerpW)\
+    {\
+      mResult.rgb = fbFogVert(mResult.rgb, _WorldSpaceCameraPos, i.wPos.xyz, mLerpW);\
+    }
+  #else
+    #define fbFog_frag(mResult, mLerpW)\
+    {\
+      mResult.rgb = lerp(_fbFogColor.rgb, mResult.rgb, mLerpW);\
+    }
+  #endif
+#else
+  #define fbFog_vert(mDensitySq, mCameraPos, mPos, mOutW)
+
+  // Fragment shader-based fog. It allows for more flexibility.
+  #if fbFOG_EXP
+    #define fbFog_frag(mResult, mLerpW)\
+    {\
+      mResult.rgb = fbFog(mResult.rgb, _WorldSpaceCameraPos, i.wPos.xyz);\
+    }
+  #else
+    #define fbFog_frag(mResult, mLerpW)
+  #endif
+#endif
+
+
+
+float lengthSq(float3 v) {
+  return dot(v, v);
+}
+
+
+half3 fbFogVert(half3 color, float3 worldSpaceCameraPos, float3 worldSpacePos, half lerpW) 
+{
+  float3  viewRay = worldSpacePos - worldSpaceCameraPos;
+  half3  fogColor = _fbFogColor.rgb;
+
+#ifdef fbFOG_CUBEMAP
+  fogColor = texCUBE(_fbFogSkyBoxTexture, viewRay).rgb;
+#endif
+
+  return lerp(fogColor, color, lerpW);
+}
+
+
+half3 fbFog(half3 color, float3 worldSpaceCameraPos, float3 worldSpacePos)
+{
+  float3  viewRay = worldSpacePos - worldSpaceCameraPos;
+  half3  fogColor = _fbFogColor.rgb;
+
+#ifdef fbFOG_CUBEMAP
+  fogColor = texCUBE(_fbFogSkyBoxTexture, viewRay).rgb;
+#endif
+  
+  float fogStartDistance = _fbFogParams[0].x;
+  float fogEndDistance = _fbFogParams[0].y;
+  //  1 / (fogEndDistance - fogStartDistance)
+  float fogInvFogRange = _fbFogParams[1].z;
+  float fogDensity = _fbFogParams[0].z;
+  float fogScale = _fbFogParams[0].w;
+
+  // linear
+//  float   viewDistance = length(viewRay) * fogScale;
+//  float fogT = saturate((fogEndDistance - viewDistance) * fogInvFogRange);
+//  half3  result = lerp(fogColor.rgb, color, fogT);
+
+#ifdef fbFOG_EXP
+  
+  float fogDensityAltFalloffExp = _fbFogParams[1].x;
+  float fogDensityAltFalloffStart = _fbFogParams[1].y;
+
+  // Exponential-squared fog, density decreasing with altitude
+  float localFogDensity = fogDensity / (1.0 + pow((fogDensityAltFalloffStart + worldSpacePos.y * fogScale), fogDensityAltFalloffExp));
+  float viewDistance = max(0.0, length(viewRay) * fogScale - fogStartDistance);
+  float fogF = viewDistance * localFogDensity;
+  half fogT = exp(- fogF * fogF);     // todo: use exp2()
+// We can optimize out the sqrt in length(viewRay), if fogStartDistance is 0.
+//float  viewRayS = viewRay * _fbFogScale;float   viewDistanceSq = dot(viewRayS, viewRayS), fogFSq = viewDistanceSq * _fbFogColor.a, fogT = exp(-fogFSq);
+  half3  result = lerp(fogColor, color, fogT);
+
+#else
+
+  half3  result = color;
+
+#endif
+  
+  return result;
+}
+
+
+//
+// More accurate fog with in-scatter from the Sun
+// In progress
+//
+float3 fbFog_InScatter(float3 color, float3 worldSpaceCameraPos, float3 worldSpacePos, float4 lightVector)
+{
+  float3  viewRay = worldSpacePos - worldSpaceCameraPos;
+  float   viewDistance = length(viewRay);
+  float3  viewDir = viewRay / viewDistance;
+  
+  float fogDensity = _fbFogParams[0].z;
+
+// Exponential fog
+//	float  fogT = 1.0f / pow(e, viewDistance * fogDensity);
+
+// Exponential-squared fog
+  float fogF = viewDistance * fogDensity;
+  float fogT = exp(-fogF * fogF);     // todo: use exp2()
+  float  fogTInv = 1.0f - fogT;
+  float3  result = color * fogT + _fbFogColor * fogTInv;
+//	float   inScatter = max(0.0f, dot(viewDir, lightVector.xyz));
+//	result += inScatter * _fbFogColor;
+  return result;
+}
+
+
+//
+// More accurate fog with volumetric shadows
+// In progress
+//
+float3 fbFog_Vol(float3 color, float3 worldSpaceCameraPos, float3 worldSpacePos, float4 lightVector, int rayMarchSteps)
+{
+  float3  viewRay = worldSpacePos - worldSpaceCameraPos;
+  float   viewDistance = length(viewRay);
+  float3  viewDir = viewRay / viewDistance;
+
+  float fogDensity = _fbFogParams[0].z;
+
+// Exponential fog
+//	float  fogT = 1.0f / pow(e, viewDistance * fogDensity);
+
+// Exponential-squared fog
+  float   fogF = viewDistance * fogDensity, fogT = exp(- fogF * fogF);     // todo: use exp2()
+  float   fogTInv = 1.0f - fogT;
+  float3  result = color * fogT + _fbFogColor * fogTInv;
+  float   inScatter = max(0.0f, dot(viewDir, lightVector.xyz));
+
+//float shadowFactor = UNITY_SAMPLE_SHADOW(_ShadowMap, shadowDepth).r;
+
+  result += inScatter * _fbFogColor;
+  return result;
+}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/FBFog.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/FBFog.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..4b5a01f520d3af246907d80a10fed2357036a333
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/FBFog.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: da3999129dae6194393591b1bd8c680d
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/FBSafeMode.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/FBSafeMode.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..9f383a782aeb63e305c72c733a2834bf2669afdf
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/FBSafeMode.cginc
@@ -0,0 +1,41 @@
+// 0.0: normal rendering. 1.0 full safe mode.
+float     _SafeModeFactor;
+float     _SafeModeBubbleRadius;
+
+//
+// Determine blend amount from edge of safe mode bubble to outside of bubble
+//
+
+float fbSafeModeBlend(float viewDistance) {
+  float  safeModeFactor = clamp(_SafeModeBubbleRadius - viewDistance, 0.0, 1.0);         // is the pixel within safe mode bubble?
+  return lerp(0.0, lerp(1.0, 0.0, safeModeFactor) , _SafeModeFactor);
+}
+
+float fbSafeModeAlpha(float viewDistance) {
+  return fbSafeModeBlend(viewDistance);
+}
+
+//
+// Return desaturated / transparent color based on "safe mode" settings
+//
+float4 fbSafeModeColor(float3 col, float viewDistance) {
+  float darkValue = 0.05;
+  float3 safeModeMeshColor = float3(darkValue, darkValue, darkValue);
+  
+  float  safeModeFactor = fbSafeModeBlend(viewDistance);
+  float  saturation = 1 - lerp(1.0, safeModeFactor, _SafeModeFactor);
+  
+  //  Simple color desaturation
+  float  luma = col.r * 0.3 + col.g * 0.59 + col.g * 0.11;
+  luma = pow(luma, 0.14) * 0.75;
+
+  // col = lerp(float3(luma, luma, luma), col, saturation);
+  col = lerp(float3(luma, luma, luma) * darkValue, col, saturation);
+
+  return float4(col, 1.0);
+}
+
+float4 fbSafeModeGrid(float4 col, float3 pos, float viewDistance) {
+	// Code moved to FBSafeModeWire.shader as USE_MATH_METHOD
+	return col;
+}
\ No newline at end of file
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/FBSafeMode.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/FBSafeMode.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..3e859151f342aa0afcfa634d6ba88b9eac688585
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/FBSafeMode.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 81dd5e36083cc2744ad0f79d21644bf7
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/FBTonemapping.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/FBTonemapping.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..403e95a7f024fb1aa761df89cd77d01c125775c0
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/FBTonemapping.cginc
@@ -0,0 +1,197 @@
+//================================================
+// Common color management code
+// - tonemapping
+// - device-specific colorspace correction
+// - texture color linearization
+//================================================
+
+#ifndef FBToneMapping_cginc
+#define FBToneMapping_cginc
+
+
+// Workaround for darkness issues on Quest/Android
+// see: https://fb.facebook.com/groups/1684852458198687/permalink/2344960525521207/
+inline void _applyAndroidGammaWorkaround(inout float3 color) {
+#ifdef ANDROID_GAMMA_WORKAROUND
+//  color = pow(color, 1.0 / 2.2);
+//  color = sqrt(color);  // Cheaper approximation of gamma 2.2
+#endif
+}
+
+// Tonemapping global variables, set by a script
+// x:Gain, y:Gamma, z:1/Gamma, w:unused
+half4 _fbTonemapperParams;
+
+
+//========================================================================================================
+// De-gamma-correct a color. Disabled for now, as we use sRGB textures that the HW can linearize
+half3 fbDeGamma(half3 inColor)
+{
+  return(inColor);
+//return pow(inColor, 2.0);
+}
+
+
+//========================================================================================
+// Device-dependent final color correction
+// On some devices, this function may be compiled out, but to ensure cross-device
+// color consistency, either this function or fbToneMap must be called from every shader.
+//========================================================================================
+half3 fbDeviceColor(half3 inColor) {
+  half3  color = inColor;
+  _applyAndroidGammaWorkaround(color.rgb);
+  return inColor;
+}
+
+
+// from http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html
+half sRGB2Linear(half C_srgb) {
+	// ~  C_lin_1 = pow(C_srgb, 2.2);
+
+	half C_lin;
+
+	[flatten] if (C_srgb <= 0.04045f)
+		C_lin = C_srgb / 12.92f;
+	else
+		C_lin = pow((C_srgb + 0.055f) / 1.055f, 2.4f);
+
+	return C_lin;
+}
+half3 sRGB2Linear(half3 C_srgb)
+{
+	return half3(sRGB2Linear(C_srgb.r), sRGB2Linear(C_srgb.g), sRGB2Linear(C_srgb.b));
+}
+// from http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html
+half Linear2sRGB(half C_lin) {
+	// ~  C_srgb = pow(C_lin, 1 / 2.2);
+
+	half C_srgb;
+
+	[flatten] if (C_lin <= 0.0031308f)
+		C_srgb = C_lin * 12.92;
+	else
+		C_srgb = 1.055f * pow(C_lin, 1.0f / 2.4f) - 0.055f;
+
+	return C_srgb;
+}
+half3 Linear2sRGB(half3 C_lin) {
+	return half3(Linear2sRGB(C_lin.r), Linear2sRGB(C_lin.g), Linear2sRGB(C_lin.b));
+}
+
+// https://developer.oculus.com/blog/tech-note-shader-snippets-for-efficient-2d-dithering
+float Dither17(float2 svPosition) {
+ 	float2 k0 = float2(2.0f, 7.0f) / 17.0f;
+ 	float Ret = dot(svPosition, k0);
+ 	return frac(Ret);
+}
+
+// 0:off (default), 1:on (never check in like this)
+// red dither: shader is affected but setting is low quality
+// green dither: shader is affected but setting is low quality  
+#define TEST_DITHER 0
+
+// good read: http://loopit.dk/banding_in_games.pdf
+// @param linearRGB LDR color, usually in 0..1 range
+// @param svPosition xy from SV_Position
+void Dither8BitRGB(inout half3 linearRGB, float2 svPosition) {
+  // 0..1
+  // for more functions see: https://developer.oculus.com/blog/tech-note-shader-snippets-for-efficient-2d-dithering
+  half dither = Dither17(svPosition);
+            
+            
+  // -3: if we output in sRGB (e.g. Unity in Gamma mode) we can use this simple shader
+  // -2: visualize pattern
+  // -1: off
+  //  0: reference (slow)
+  //  1: approximated with ^2, optimized
+  //  2: same: apply (a+b)^2 = a*a + 4*a*b + b*b, faster?
+  //  3: further approximated (removed d*d)
+  //  4: further approximated (removed offset)
+  int mode = -1;
+  
+#if SHADER_API_D3D11
+  // A blue noise texture would be higher quality,
+  // here we use math for integration simplicity.
+  mode = 1;
+#else
+  #if HIGH_QUALITY
+    mode = 4;
+  #endif
+  // low quality is accepting banding
+#endif
+
+#if TEST_DITHER
+  #if HIGH_QUALITY
+    linearRGB = float3(0,1,0) + dither; // green
+  #else 
+    linearRGB = float3(1,0,0) + dither; // red
+  #endif
+#endif
+    
+  if(mode == 0) {
+    float3 sRGB = Linear2sRGB(linearRGB);
+    sRGB += (dither - 0.5f) / 256.0f;
+    linearRGB = sRGB2Linear(sRGB);
+
+  } else if(mode == 1) {
+    half3 sRGB = sqrt(linearRGB);
+    sRGB += (dither - 0.5f) / 256.0f;
+    linearRGB = sRGB * sRGB;
+
+  } else if(mode == 2) {
+    float d = (dither - 0.5f) / 256.0f;
+    linearRGB += sqrt(linearRGB) * (4.0f * d) + d * d;
+
+  } else if(mode == 3) {
+    float d = (dither - 0.5f) / 256.0f;
+    linearRGB += sqrt(linearRGB) * (4.0f * d);
+
+  } else if(mode == 4) {
+    linearRGB += sqrt(linearRGB) * (dither / 64.0f);
+
+  } else if(mode == -2) {
+    linearRGB = sRGB2Linear(dither < svPosition.x / 512.0f);
+
+  } else if(mode == -3) {
+    linearRGB += (dither - 0.5f) / 256.0f;
+
+  }
+}
+
+
+//========================================================================================
+// Apply tonemapping and device-dependent final color correction
+//========================================================================================
+// @param svPosition xy from SV_Position
+half3 fbToneMap(half3 inColor, float2 svPosition) {
+
+  half gain = _fbTonemapperParams.r;
+  half gamma = _fbTonemapperParams.g;
+  half invGamma = _fbTonemapperParams.b;
+
+//  float3  color = pow(float3(1.0, 1.0, 1.0) - exp(-inColor * _fbGain), 1.0 / _fbGamma);
+
+  half3  color;
+
+// Gain and gamma with a nice "adaptive" exponential tonemapper
+#if fbTmINVEXP
+  // todo: exp(y) = pow(e, y) = exp2(y * log2(e))
+  color = pow(half3(1.0, 1.0, 1.0) - exp(-inColor * gain), invGamma);
+#else
+  #if fbTmGAIN_GAMMA
+  // Gain_and_gamma
+  color = pow(inColor * gain, invGamma);
+  #else
+  color = inColor;
+  #endif
+#endif
+
+  // uncomment to create test content to debug Dither8BitRGB()
+//  color.rgb = (svPosition.x / 20000) + (int)(svPosition.y / 32) / (1024 / 32.0f);
+
+  Dither8BitRGB(color.rgb, svPosition);
+
+  _applyAndroidGammaWorkaround(color.rgb);
+  return color;
+}
+#endif // FBToneMapping_cginc
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/FBTonemapping.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/FBTonemapping.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..1c513a3501318688ed79e6fd1838b6dd483454a9
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/FBTonemapping.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 47b7f40726c6a404eb8826d6ee905c1f
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/MacroRemapper.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/MacroRemapper.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..43e788dd9fd077dec9e550e118e9201377ba5c1a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/MacroRemapper.cginc
@@ -0,0 +1,13 @@
+
+      #ifdef SUN_LIGHT_MODE1
+        #define DIRECTIONAL_LIGHT 1    // directional light activates three components: ability to have shader, color of diffuse light, specualar highlight
+      #endif
+      #ifdef SUN_LIGHT_MODE2
+        #define DIRECTIONAL_LIGHT 1    // directional light activates three components: ability to have shader, color of diffuse light, specualar highlight
+        #define SHADOWMAP_STATIC_VSM 1 // use variance shadow mapping for the environment/static shadow
+      #endif
+      #ifdef SUN_LIGHT_MODE3
+        #define DIRECTIONAL_LIGHT 1    // directional light activates three components: ability to have shader, color of diffuse light, specualar highlight
+        #define SHADOWMAP_STATIC_VSM 1 // use variance shadow mapping for the environment/static shadow
+        #define DYNAMIC_SHADOWS 1      // take a seperate sample to combine the shadows of dynamic objects with the static shadows
+      #endif
\ No newline at end of file
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/MacroRemapper.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/MacroRemapper.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..2a86fa56cfb21759eb991d07b9b221cfa428e179
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/MacroRemapper.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 7e8bf288082cc1c4b8e228066f7b0e9e
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/Shadows.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/Shadows.meta
new file mode 100644
index 0000000000000000000000000000000000000000..569fff659a76613191be8688bdf86c2eb8c72025
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/Shadows.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 843750e39f8e5b94d965c8fb5d41b96e
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/Shadows/BlobShadows.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/Shadows/BlobShadows.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..4c0924e505732faf932d60db49c6a0799d351a96
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/Shadows/BlobShadows.cginc
@@ -0,0 +1,272 @@
+// how much quad vertices are pushed out. allows for shadow footprint to be larger than object. must be >= 1.
+#define BLOB_QUAD_MULT 1.0
+
+// distance in world space, how fast to fade off dynamic shadow map at edge
+#define BLOB_SHADOW_MAP_EDGE 1.0
+
+// maximum value for a surface to be considered vertical and to get no shadow (y component of normal)
+#define BLOB_MIN_VERTICAL_SURFACE 0.3
+
+// max value for blob shadow
+#define BLOB_SHADOW_MAX_FACTOR 0.5
+
+// tint casters green and receivers red.
+#define BLOB_SHADOWS_SURFACE_DEBUG 0
+
+// objects with a shadow footprint dimension between A and B will fade to 0 influence.
+// this should be tuned along with the current shadowmap orthosize and resolution.
+#define BLOB_DIMENSION_FADE_MIN 0.050
+#define BLOB_DIMENSION_FADE_MAX 0.150
+
+// uniforms
+
+// location of blob shadow camera in world space
+float3 _BlobCameraWorldPos;
+
+// 1 / (size property of orthographic projection blob camera)
+float _BlobOrthoScaleFactor;
+
+// the far plane distance for the camera casting shadows
+float _BlobCasterFarClip; 
+
+// shadow map texture
+sampler2D _BlobShadowMap;
+
+// provided by Unity: { 1/w, 1/h, w, h }
+float4 _BlobShadowMap_TexelSize;
+
+// vertex shader for shadow map :
+// - generates unrotated uniform-scale quads
+// - blob footprint can be larger than blob itself (currently by a fixed factor)
+void blobCasterVertToFrag(in float4 localVertex, in float2 uvIn, in float4x4 localToWorld, out float4 screenPos, out float2 uvOut, out float3 worldSpherePos, out float2 worldSphereNormal, out float2 worldSphereScale) {
+  // 1. extract the axis scales
+  float3 xaxis = mul(localToWorld, float4(1, 0, 0, 0)).xyz;
+  float3 yaxis = mul(localToWorld, float4(0, 1, 0, 0)).xyz;
+  float3 zaxis = mul(localToWorld, float4(0, 0, 1, 0)).xyz;
+  float3 invScale = rcp(float3(length(xaxis), length(yaxis), length(zaxis)));
+  float3 scaleXZ = float3(length(xaxis.xz), length(yaxis.xz), length(zaxis.xz));
+
+  // find the bbox face whose downward facing area is largest.
+  float xface = xaxis.y * invScale.x * scaleXZ.y * scaleXZ.z;
+  float yface = yaxis.y * invScale.y * scaleXZ.x * scaleXZ.z;
+  float zface = zaxis.y * invScale.z * scaleXZ.x * scaleXZ.y;
+  float maxaface = max(abs(xface), max(abs(yface), abs(zface)));
+
+  float2 u;
+  float3 normal;
+  if (abs(xface) == maxaface) {
+    u = normalize(float2(yaxis.x, yaxis.z));
+    normal = xaxis * invScale.x;
+
+    float alpha1 = scaleXZ.y * invScale.y;
+    float alpha2 = scaleXZ.z * invScale.z;
+    if (abs(yface) >= abs(zface)) {
+      // next axis is y
+      worldSphereScale.x = alpha1 * scaleXZ.y + (1 - alpha1) * scaleXZ.x;
+      worldSphereScale.y = alpha2 * scaleXZ.z + (1 - alpha2) * scaleXZ.z;
+      //normal = alpha1 * xaxis * invScale.x + alpha2 * yaxis * invScale.y;
+    } else {
+      // next axis is z
+      worldSphereScale.x = alpha1 * scaleXZ.y + (1 - alpha1) * scaleXZ.x;
+      worldSphereScale.y = alpha2 * scaleXZ.z + (1 - alpha2) * scaleXZ.y;
+      //normal = alpha1 * xaxis * invScale.x + alpha2 * zaxis * invScale.z;
+    }
+  } else if (abs(yface) == maxaface) {
+    u = normalize(float2(xaxis.x, xaxis.z));
+    normal = yaxis * invScale.y;
+
+    float alpha1 = scaleXZ.x * invScale.x;
+    float alpha2 = scaleXZ.z * invScale.z;
+    if (abs(xface) >= abs(zface)) {
+      // next axis is x
+      worldSphereScale.x = alpha1 * scaleXZ.x + (1 - alpha1) * scaleXZ.y;
+      worldSphereScale.y = alpha2 * scaleXZ.z + (1 - alpha2) * scaleXZ.z;
+      //normal = alpha1 * yaxis * invScale.y + alpha2 * xaxis * invScale.x;
+    } else {
+      // next axis is z
+      worldSphereScale.x = alpha1 * scaleXZ.x + (1 - alpha1) * scaleXZ.x;
+      worldSphereScale.y = alpha2 * scaleXZ.z + (1 - alpha2) * scaleXZ.y;
+      //normal = alpha1 * yaxis * invScale.y + alpha2 * zaxis * invScale.z;
+    }
+  } else {
+    u = normalize(float2(xaxis.x, xaxis.z));
+    normal = zaxis * invScale.z;
+
+    float alpha1 = scaleXZ.x * invScale.x;
+    float alpha2 = scaleXZ.y * invScale.y;
+    if (abs(xface) >= abs(yface)) {
+      // next axis is x
+      worldSphereScale.x = alpha1 * scaleXZ.x + (1 - alpha1) * scaleXZ.y;
+      worldSphereScale.y = alpha2 * scaleXZ.y + (1 - alpha2) * scaleXZ.z;
+      //normal = alpha1 * zaxis * invScale.z + alpha2 * xaxis * invScale.x;
+    }
+    else {
+      // next axis is y
+      worldSphereScale.x = alpha1 * scaleXZ.x + (1 - alpha1) * scaleXZ.x;
+      worldSphereScale.y = alpha2 * scaleXZ.y + (1 - alpha2) * scaleXZ.z;
+      //normal = alpha1 * zaxis * invScale.z + alpha2 * yaxis * invScale.y;
+    }
+  }
+
+  float2 v = float2(-u.y, u.x);
+  u *= worldSphereScale.x;
+  v *= worldSphereScale.y;
+
+  worldSphereNormal = normal.xz;
+
+  // 2. extract position for placement of quad
+  worldSpherePos = mul(localToWorld, float4(0, 0, 0, 1)).xyz;
+
+  // 3. form new matrix which has no rotation, only x-z scale
+  float4 newx = float4(u.x, 0.0, v.x, worldSpherePos.x);
+  float4 newy = float4(0.0, 1.0, 0.0, worldSpherePos.y);
+  float4 newz = float4(u.y, 0.0, v.y, worldSpherePos.z);
+  float4 neww = float4(0, 0, 0, 1);
+  float4x4 decalToWorld = float4x4(newx, newy, newz, neww);
+
+  float4 scaledVertex = float4(localVertex.xyz * BLOB_QUAD_MULT, 1); 
+
+  // 4. use rebuilt transform to generate quad in xz plane.
+  screenPos = mul(mul(UNITY_MATRIX_VP, decalToWorld), scaledVertex);
+  uvOut = uvIn;
+}
+
+//fragment shader for shadow map :
+// - stores an occlusion factor (lower at edges of sphere, higher at center) + min and max
+// - normalized to ortho camera frustum
+// - min is calculated from top of frustum, max from bottom (camera is above looking down)
+float4 blobCasterFrag(in float4 screenPos, in float2 uv, in float3 worldCenter, in float2 worldNormal, in float2 worldSphereScale) {
+  // here we're trying to draw an ellipsoid height map on a flat quad.
+  // imagine a graph of x^2/a^2 + y^2/b^2 + z^2/c^2 = 1, centered at the origin.
+  // y will be our output channels, and we shift the uv's to use as our x and z.
+
+  // offset uv to be centered at 0,0 and reject any pixel outside a 0.5 radius circle
+  float2 offsetUV = uv - float2(0.5, 0.5);
+  float uvLength2 = dot(offsetUV, offsetUV);
+
+  // max radius squared (in uv space) of any circle inscribed within the quad
+  const float influenceLength2 = 0.5 * 0.5;
+  if (uvLength2 > influenceLength2) {
+    return float4(0,0,0,0); 
+  }
+
+  float heightOffset = -dot(worldNormal, offsetUV * worldSphereScale);
+
+  float2 pixelUV = float2(1.0 - 2.0 * screenPos.x * _BlobShadowMap_TexelSize.x, 1.0 - 2.0 * screenPos.y * _BlobShadowMap_TexelSize.y);
+  float pixelR = sqrt(pixelUV.x * pixelUV.x + pixelUV.y * pixelUV.y);
+  float innerR = ((1.0 / _BlobOrthoScaleFactor) - BLOB_SHADOW_MAP_EDGE) * _BlobOrthoScaleFactor;
+  const float outerR = 1.0f;
+  float edgeFade = 1.0 - saturate((pixelR - innerR)/(outerR - innerR));
+
+  // fade out the shadow map at the top and bottom of the shadow frustum
+  float frustumMidpoint = _BlobCameraWorldPos.y - _BlobCasterFarClip * 0.5f;
+  float frustumTopMarginOffset = _BlobCasterFarClip * 0.5f - BLOB_SHADOW_MAP_EDGE;
+  float objectOffset = abs(worldCenter.y - frustumMidpoint);
+  float heightFade = 1.0f - saturate((objectOffset - frustumTopMarginOffset) / BLOB_SHADOW_MAP_EDGE);
+
+  // 0 at edge of circle, 1 at origin.
+  float occlusionFactor = edgeFade * heightFade * (1.0 - uvLength2 / influenceLength2);
+
+  // hMin is the point on the sphere furthest from the caster camera.
+  // it ranges from 0 to 1, with 0 being at camera.y and 1 at the far clip plane
+  float diskWorldY = worldCenter.y + heightOffset;
+  float invFarClip = 1.0f / _BlobCasterFarClip;
+  float hMin = (_BlobCameraWorldPos.y - diskWorldY) * invFarClip;
+
+  // hMax is the point on the sphere closest to (or behind) the caster camera.
+  // it ranges from 0 to 1, with 0 being at the far clip plane and 1 at camera.y
+  float hMax = (diskWorldY - (_BlobCameraWorldPos.y - _BlobCasterFarClip)) * invFarClip;
+
+  return float4(occlusionFactor, hMin, hMax, min(worldSphereScale.x, worldSphereScale.y));
+}
+
+// fragment shader for drawing shadow map:
+//  reject empty pixels and just show the occlusion factor and min height as R and G
+float4 blobCasterDebug(in sampler2D shadowMap, in float2 uv) {
+  float4 shadowParams = tex2D(shadowMap, uv);
+  if (shadowParams.x == 0) {
+    return float4(0, 0, 0, 1);
+  }
+  return float4(shadowParams.x, shadowParams.y, 0, 1);
+}
+
+// surface shader tweak: 
+//  takes final output color from surface shader and tints a thing green if it casts blob shadows, red if it receives blob shadows.
+float4 blobSurfaceDebug(in float4 color) {
+#if !defined(DYNAMIC)
+    // color shadow receivers reddish
+    color.r = clamp(color.r * 1.2, 0, 1);
+    color.gb *= 0.8;
+#else
+    // color shadow casters greenish
+    color.g = clamp(color.r * 1.2, 0, 1);
+    color.rb *= 0.8;
+#endif
+    return color;
+}
+
+float blobShadowFactorInternal(in float2 coord, in float groundY, in float cameraY, in float cameraFarClip, in sampler2D shadowMap) {
+  // retrieve shadow value for world space pixel, remove bias
+  float4 shadowParams = tex2D(shadowMap, coord);
+
+  // detect uninitialized value = no shadows
+  float occlusionFactor = shadowParams.x;
+  if (occlusionFactor < 0.0f) {
+    return 0.0;
+  }
+
+  // reconstruct min and max height in world space
+  float heightMin = cameraY - shadowParams.y * cameraFarClip;
+  float heightMax = cameraY - cameraFarClip * ( 1 - shadowParams.z );
+  float minDim = shadowParams.w;
+
+  // surfaces above or below blob influence heights = no shadows
+  if (groundY > heightMax) {
+    return 0.0;
+  }
+
+  // 0 = no shadow, 1 = full shadow
+  float pixelHeightFactor = 1.0 - (heightMin - groundY) / _BlobCasterFarClip;
+
+  // we approach 0 faster with higher powers, so edges look good, but there is also more sausage-linking
+  float pixelOcclusionFactor = pow(occlusionFactor, 2.0);
+
+  // things that only have a couple pixels in the shadow map are weaker
+  float thinFactor = smoothstep(0.0, 1.0, (minDim - BLOB_DIMENSION_FADE_MIN) / (BLOB_DIMENSION_FADE_MAX - BLOB_DIMENSION_FADE_MIN));
+
+  // (pixellated samples are weaker) *
+  // (higher shadows are lighter / lower are darker) * 
+  // (pixels in the center of spheres are darker than ones on the edge) *
+  // (no darker than max factor)
+  return thinFactor * pixelHeightFactor * pixelOcclusionFactor * BLOB_SHADOW_MAX_FACTOR;
+}
+
+// returns a value in [0,1] to be multiplied with diffuse color to darken it. 
+// 0 = full shadow / black, 1 = no shadow / surface color unchanged.
+float blobShadowFactorGround(in float3 worldPos, in float3 worldNormal) {
+
+  // convert world space to texture coordinate
+  float2 coord = float2(0.5, 0.5) + 0.5 * (worldPos.xz - _BlobCameraWorldPos.xz) * _BlobOrthoScaleFactor;
+  if (coord.x <= 0 || coord.x >= 1.0 || coord.x <= 0 || coord.x >= 1.0) {
+    // skip any further calculations if we're outside the shadow map
+    return 1.0;
+  }
+
+  // this introduces a band around shaded hovering things. the tradeoff is that undersides of things are not shadowed.
+  float normalFactor = saturate((worldNormal.y + BLOB_MIN_VERTICAL_SURFACE) / (2.0 * BLOB_MIN_VERTICAL_SURFACE));
+  float pixelFactor = blobShadowFactorInternal(coord, worldPos.y, _BlobCameraWorldPos.y, _BlobCasterFarClip, _BlobShadowMap);
+
+  // things that receive blob shadowing: 
+  // - (no contribution on surfaces pointing down) * (common shadow factor from above)
+  // - subtract from 1.0 to put into the scale surface shader expects.
+  return 1.0 - normalFactor * pixelFactor;
+}
+
+// called from surface shader, different paths if something receives shadows or casts them (which would get self shadow)
+float blobShadowFactor(in float3 worldPos, in float3 worldNormal) {
+#if !defined(DYNAMIC)
+  return blobShadowFactorGround(worldPos, worldNormal);
+#else
+  return 1.0;
+#endif
+}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/Shadows/BlobShadows.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/Shadows/BlobShadows.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..1c3e386c790835eba0bf504fcce94aec29b04acd
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/Shadows/BlobShadows.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 11e4dc66d0f7857478a74f6d0dabcd9d
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/Shadows/VSM.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/Shadows/VSM.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..4e5f73bccb2f8acb46e3b42530413bd91205666a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/Shadows/VSM.cginc
@@ -0,0 +1,52 @@
+//========================================================
+// Variance shadow-mapping
+//========================================================
+
+
+// xyz: varianceShadowOffset, varianceShadowExpansion, 1/(1-varianceShadowExpansion), w:unused
+float4 _VarianceShadowParams;
+
+// @param samp from texture lookup
+// @return 0..1, 0:in shadow 1:not in shadow
+half ComputeVSMShadowFactor(float2 samp, float fragmentDepth) {
+  // Must be >0, to avoid division by 0
+  // Used for alleviating light bleeding, by expanding the shadows to fill in the gaps.
+  float varianceShadowExpansion = _VarianceShadowParams.y;
+  // Variance shadowmap depth offset. Applied before storing depth values in the variance shadowmap,
+  // to improve precision, by moving values closer to 0.0f.
+  // May be needed when a low precision VSM (e.g. RGHalf) is used.
+  float vSMOffset = _VarianceShadowParams.x;
+
+  // https://www.gdcvault.com/play/1023808/Rendering-Antialiased-Shadows-with-Moment
+  // https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch08.html
+  // The moments of the fragment live in "_shadowTex"
+  float2  s = samp.rg;
+  
+  fragmentDepth -= vSMOffset;
+
+  // Average / expected depth and depth^2 across the texels
+  // E(x) and E(x^2)
+  float  x = s.r; 
+  float  x2 = s.g;
+
+  // Variance of the texel, based on var = E(x^2) - E(x)^2
+  // https://en.wikipedia.org/wiki/Algebraic_formula_for_the_variance#Proof
+  float  var = x2 - x*x; 
+
+  // Calculate initial probability based on the basic depths
+  // If our depth is closer than x, then the fragment has a 100%
+  // probability of being lit (p=1)
+  float  p = fragmentDepth <= x;
+
+  // Calculate the upper bound of the probability using Chebyshev's inequality
+  // https://en.wikipedia.org/wiki/Chebyshev%27s_inequality
+  float  delta = fragmentDepth - x;
+  float  p_max = var / (var + delta*delta);
+
+  float invQuotient = _VarianceShadowParams.z;
+
+  // To alleviate light bleeding, expand the shadows to fill in the gaps
+  p_max = saturate((p_max - varianceShadowExpansion) * invQuotient);
+
+  return max(p, p_max);
+}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/Shadows/VSM.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/Shadows/VSM.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..e0dee23a444a138f60e72b39724306b851786cfe
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/Shadows/VSM.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 63ef3468aa01e7346bff884b566b20f7
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/VertexGI.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/VertexGI.meta
new file mode 100644
index 0000000000000000000000000000000000000000..7cca77e45960cd6d6c9a85c2285249a2764e6dc1
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/VertexGI.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: a64672226a9dd28439d31ea9c6f9efd8
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/VertexGI/Interface.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/VertexGI/Interface.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..0370c2221add5841f2ca9dc14c0e130f58a2414f
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/VertexGI/Interface.cginc
@@ -0,0 +1,223 @@
+//================================================================================
+// Common shader interface to RenderToolkit's vertex-based global illumination
+//
+// This file should be directly included only by shaders that need to take over
+// the main control flow. For simpler shaders, use VertexGI/Framework.cginc
+// See Framework.cginc for an example on how to call the vertex-GI code.
+//
+// VertexGI uses interpolant semantics up to TEXCOORD11
+//
+// See https://fb.quip.com/j99FAwxuOGCX
+// For usage, see VertexGI/Framework.cginc
+//================================================================================
+
+#include "../MacroRemapper.cginc"
+
+#include "../FBToneMapping.cginc"
+
+#if fbSAFE_MODE || fbFOG_CUBEMAP || ALPHA_MATERIAL
+#define FRAG_NEED_WPOS  1  // World position is needed in the fragment shader
+#endif
+
+#if fbFOG_VTX
+#define VGI_V2F_FIELDS_FOG\
+	/* .w is used for fog  */\
+	half4 l1 : TEXCOORD5;
+#else
+  #if fbFOG_EXP
+    #define FRAG_NEED_WPOS  1
+    #define VGI_V2F_FIELDS_FOG\
+      half3  l1   : TEXCOORD5;
+  #else
+    #define VGI_V2F_FIELDS_FOG\
+      half3  l1   : TEXCOORD5;
+  #endif
+#endif // fbFOG_VTX
+
+#define VGI_V2F_FIELDS_ALPHAMAT\
+	/* rgb:linear albedo/glow/avatar color, .a:smooth for ALPHA_MATERIAL might be nointerpolation */\
+	half4 color : TEXCOORD6;\
+	/* .x: metal(0/1), y: specMul(0/0.25/2), z:glow(0/1) for ALPHA_MATERIAL, TEXCOORD15 to not collide with Avatars */\
+  nointerpolation half3 materialProps : TEXCOORD15;
+
+#ifdef NORMAL_MAPPING
+#define VGI_V2F_FIELDS_NORMAL_MAPPING\
+	float4  tangent	  : TEXCOORD8;\
+	float4  bitangent : TEXCOORD9;\
+	half3  l2        : TEXCOORD10;\
+	half3  l3        : TEXCOORD11;
+#else
+#define VGI_V2F_FIELDS_NORMAL_MAPPING
+#endif
+
+#if FRAG_NEED_WPOS
+  #define VGI_V2F_FIELDS_WPOS\
+    float4  wPos : TEXCOORD4;
+#else
+  #define VGI_V2F_FIELDS_WPOS
+#endif
+
+#ifdef TEXTURE_ARRAY
+  #define VGI_TEXTURE_COORD_FIELD\
+    float3  uv : TEXCOORD1;
+#else
+  #define VGI_TEXTURE_COORD_FIELD\
+    float2  uv : TEXCOORD1;
+#endif
+
+// NOTE: Update these definitions if more interpolators become required
+#define VGI_V2F_NEXT_AVAILABLE_TEXCOORD_SEMANTIC 12
+#define VGI_V2F_SECOND_AVAILABLE_TEXCOORD_SEMANTIC 13
+
+// Vertex-to-fragment attributes (interpolants) for GI
+//
+#define VGI_V2F_ATTRIBUTES\
+	float4  pos                : SV_POSITION;\
+	float3  normal             : TEXCOORD0;\
+	VGI_TEXTURE_COORD_FIELD\
+	/* .w is the normalized, non-zerocentered distance to the light, used for variance shadowmaps */\
+	float4  staticShadowOffset : TEXCOORD2;\
+	/* .xy UV location, z for depth comparison  *\
+	/* .w is euclidian distance to inner cascade (1 at border) to do transition  */\
+	/* NOTE: this should be conditional, too. For shaders that don't want shadow maps */\
+	float4  shadowPos          : TEXCOORD3;\
+\
+  VGI_V2F_FIELDS_WPOS\
+  VGI_V2F_FIELDS_FOG\
+  VGI_V2F_FIELDS_ALPHAMAT\
+  VGI_V2F_FIELDS_NORMAL_MAPPING\
+  UNITY_VERTEX_INPUT_INSTANCE_ID
+
+
+// Temporary data for the vertex-GI code's vertex program
+// This allows us to hold variables, but split up the code into logical chunks.
+struct vgi_vert_tmp {
+  float3  position;
+  float3  normal;
+  half3  l0;
+#ifdef NORMAL_MAPPING
+  float3 tangent;
+  half3  l1, l2;
+#endif
+};
+
+
+// Temporary data for the vertex-GI code's fragment program.
+// This allows us to hold variables, but split up the code into logical chunks.
+struct vgi_frag_tmp {
+  float3  normal;
+  // rgb:linear albedo/glow, .a: for smoothness for ALPHA_MATERIAL
+  half4   materialColor;
+  // .x: metal(0/1), y: specMul(0/0.25/2), z:glow(0/1) for ALPHA_MATERIAL
+  half3   materialProps;
+  half    shadowFactor;
+
+#if defined(NORMAL_MAPPING)
+  float3  tangent;
+  float4  rawNT;
+#endif
+};
+
+
+//
+// Common components
+//
+
+// keep this synced with BatchManager.kMaxBatchObjectCount
+#ifdef PER_PRIM_PROPS
+#define MAX_BATCH_OBJECT_COUNT 50
+#else
+#if defined(PER_PRIM_PROPS_GROUP) || defined(GROUP)
+#define MAX_BATCH_OBJECT_COUNT 50
+#endif
+#endif
+
+// keep this synced with BatchManager.kMaxGroupsPerBatch
+#define MAX_BATCH_GROUP_COUNT 20
+
+#define M_PI  3.14159265358979323846
+// we use 9 float4's to represent 3-band RGB spherical harmonics; the w component is unused
+#define LIGHT_PROBE_SIZE 9
+
+#define LIGHT_PROBE_COUNT 1
+
+// Only use these defines if we are a dynamic subd object
+// or else we break the UI shader which uses instancing,
+// but only 1 probe for all instances
+#ifdef DYNAMIC
+#ifdef UNITY_INSTANCING_ENABLED
+  #define LIGHT_PROBE_COUNT 50
+#else
+#if defined(PER_PRIM_PROPS_GROUP) || defined(GROUP)
+  #define LIGHT_PROBE_COUNT (MAX_BATCH_GROUP_COUNT)
+#endif
+#endif
+#endif
+
+#define LIGHT_PROBE_ARRAY_SIZE (LIGHT_PROBE_COUNT*LIGHT_PROBE_SIZE)
+
+// This is set by the "sunlight" script
+//
+float4  _MainCameraLightProbe[LIGHT_PROBE_SIZE];
+
+// we always use interpolated light probes,
+// but we may want a shader variant later
+#define INTERPOLATED_LIGHT_PROBE 1
+
+#if INTERPOLATED_LIGHT_PROBE
+// Current time in seconds since program started
+// that may be offset for long running programs for better accuraccy
+float _LightProbeTime;
+
+//========================================================
+// Return radiance using a 4-coefficient SH light probe
+// plus a 4-coefficient delta for interpolation
+// for a smooth transition from an old light probe value to new one
+//========================================================
+half3 vgi_ProbeRadianceIndexed(
+    float4 probeData[LIGHT_PROBE_ARRAY_SIZE],
+    int index,
+    // n assumed to be normalized
+    float3 n,
+    float attenuation) {
+  int offset = LIGHT_PROBE_SIZE * index;
+  float startTime = probeData[0 + offset].w;
+  // reciprocal of interpolation time
+  float recDeltaTime = probeData[1 + offset].w;
+
+  float t = saturate((_LightProbeTime - startTime) * recDeltaTime);
+  half3 result = (probeData[0 + offset].xyz + probeData[4 + offset].xyz * t) * M_PI +
+      (probeData[1 + offset].xyz + probeData[5 + offset].xyz * t) * n.y +
+      (probeData[2 + offset].xyz + probeData[6 + offset].xyz * t) * n.z +
+      (probeData[3 + offset].xyz + probeData[7 + offset].xyz * t) * n.x;
+
+  return max(half3(0., 0., 0.), result);
+}
+#else
+//========================================================
+// Return radiance, using a 9-coefficient SH light probe
+//========================================================
+half3 vgi_ProbeRadianceIndexed(float4 probeData[LIGHT_PROBE_ARRAY_SIZE], int index, float3 n, float attenuation) {
+  int offset = LIGHT_PROBE_SIZE * index;
+  half3 result =
+    max(float3(0, 0, 0),
+        (probeData[0 + offset].xyz * 0.282095 + probeData[1 + offset].xyz * ((-0.488603 * 2. / 3.) * n.y) +
+         probeData[2 + offset].xyz * ((0.488603 * 2. / 3.) * n.z) +
+         probeData[3 + offset].xyz * ((-0.488603 * 2. / 3.) * n.x) +
+         probeData[4 + offset].xyz * ((1.092548 / 4.) * n.x * n.y) +
+         probeData[5 + offset].xyz * ((-1.092548 / 4.) * n.y * n.z) +
+         probeData[6 + offset].xyz * ((0.315392 / 4.) * (3.0f * n.z * n.z - 1.0f)) +
+         probeData[7 + offset].xyz * ((-1.092548 / 4.) * n.x * n.z) +
+         probeData[8 + offset].xyz * ((0.546274 / 4.) * (n.x * n.x - n.y * n.y))) *
+            M_PI);
+
+  // if (n.y < 0)
+  //   result *= saturate(1 + n.y * (1 - attenuation));
+
+  return result;
+}
+#endif
+
+half3 vgi_ProbeRadiance(float4 probeData[LIGHT_PROBE_ARRAY_SIZE], float3 n, float attenuation) {
+  return vgi_ProbeRadianceIndexed(probeData, 0, n, attenuation);
+}
\ No newline at end of file
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/VertexGI/Interface.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/VertexGI/Interface.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..174b4afc839eee3b925b8ba2549f74ca703f6ec5
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/VertexGI/Interface.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: cd3c9969ad6895d4b9c528d5fd7f6d87
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/VertexGI/SurfaceShader.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/VertexGI/SurfaceShader.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..ceb0582cd00d18d2a734365716e86a2f577b05fa
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/VertexGI/SurfaceShader.cginc
@@ -0,0 +1,1070 @@
+//========================================================================
+// Dynamic GI shader core for the RenderToolkit shader system
+//========================================================================
+
+#pragma exclude_renderers xbox360 gles
+
+// enable only for debugging, do not check in for production
+// #define QUALITY_LEVEL_DEBUG 1
+
+// 0: 2x2 dither(fast), 1: ~4x4 dither with 16 values (smoother quality but standing pattern),
+// at some ALU cost
+#define SHADOW_DITHER_QUALITY 0
+
+// Uncoment this to enable variance shadowmaps. Sync this manually with the same #define in Sunlight.cs,
+// so we don't create too many shader variants for mobile
+// However, during the current transition period, we are using a runtime / UI switch (2x shader variants),
+// so we can enable VSM one world at a time. During the transition, keep this commented out.
+//#define SHADOWMAP_STATIC_VSM  1
+
+// It's s slight optimization to deactivate specular highlighting from the direct sunlight,
+// without it, all spec has to come from the ambient light. However, disabling it does
+// cause a noticable visual divergence.
+#if defined(DIRECTIONAL_LIGHT)
+#define DIRECT_SPECULAR 1
+#else
+#define DIRECT_SPECULAR 0
+#endif
+
+// We use a function to return the value, so we don't break coding style. The style doesn't allow us to
+// use non-uppercase #defines, but this name must match C#, which doesn't support #defines with values
+int nShadowCascadeTiles() {
+  return 1;
+}
+
+#include "../FBSafeMode.cginc"
+#include "../../../../Scripts/ShaderUtils/AvatarCustom.cginc"
+
+float4x4  _shadowMatrix;
+#if defined(DISPLACEMENT) || defined (BLEND_SHAPES)
+sampler2D _DispMap;
+float     _DispScale;
+#endif
+
+#ifdef BLEND_SHAPES
+float4x4 _HeadTransform;
+#endif
+
+float4  _LightColor;
+float4  _LightVector;
+float3  _SurfaceColor;
+float3  _ViewVector;
+float4  _EyeLightVector;
+float4  _CameraVector;
+
+#ifdef TEXTURE_COLOR
+sampler2D  _MainTex;
+#endif
+float4     _MainTex_ST;
+
+#ifdef NORMAL_MAPPING
+#ifdef USE_NRML_MAP_NAME
+sampler2D _NrmlMap; // To get around Unity "normal map warning"
+#else
+sampler2D _NormalMap;
+#endif
+sampler2D _ICMap;
+#endif
+
+#if ALPHA_MATERIAL
+// reflection map for specular reflection
+samplerCUBE _ReflectionCubeMap;
+#endif
+
+#ifdef SSS
+sampler2D _SSSMap;
+#endif
+
+#ifdef SHADOWMAP_STATIC_VSM
+float4x4  _LightWorldToLocalMatrix;
+float     _ShadowMapCameraZFarInv;
+float     _VarianceShadowBias;
+#endif
+
+// Used for the dynamic shadows:
+UNITY_DECLARE_SHADOWMAP(_ShadowMap);
+
+// Used for the static shadows:
+#ifdef SHADOWMAP_STATIC_VSM
+    // Variance shadowmaps need 2 channels, so they can't use the specialized shadow sampler.
+    // Also, on mobile / GLES platforms, sampler2D defaults to low precision, so we need to explicitly specify this type.
+    sampler2D_float  _ShadowMapStatic;
+#else
+  #if DYNAMIC_SHADOWS
+    UNITY_DECLARE_SHADOWMAP(_ShadowMapStatic);
+  #else
+    sampler2D_float  _ShadowMapStatic;
+  #endif
+#endif
+
+float4  _ShadowMapSize;
+float4  _ShadowDither1;
+float4  _ShadowDither2;
+
+float4x4  _WorldToSunlight;
+float4x4  _StaticWorldToSunlight;
+float4x4  _ReflectionRotation;
+float     _EditMode;
+
+#ifdef DYNAMIC
+#define LIGHT_PROBE 1
+#endif
+
+#ifdef LIGHT_PROBE
+#ifdef UNITY_INSTANCING_ENABLED
+CBUFFER_START(InstancingLightProbeData)
+float4 _LightProbeArray[LIGHT_PROBE_ARRAY_SIZE];
+CBUFFER_END
+#else
+float4  _LightProbe[LIGHT_PROBE_ARRAY_SIZE];
+#endif
+#endif
+
+#ifdef ALPHA_MATERIAL
+float4  _MaterialProperties[8];
+#endif
+
+#include "../FBFog.cginc"
+#include "../Shadows/VSM.cginc"
+#if defined(BLOB_SHADOWS)
+#include "../Shadows/BlobShadows.cginc"
+#endif
+
+#ifdef UNITY_INSTANCING_ENABLED
+UNITY_INSTANCING_BUFFER_START(Props)
+UNITY_DEFINE_INSTANCED_PROP(float4, _Color)
+UNITY_DEFINE_INSTANCED_PROP(int, _LightProbeIndex)
+UNITY_INSTANCING_BUFFER_END(Props)
+#endif
+
+#ifndef UNITY_INSTANCING_ENABLED
+float4  _Color;
+#endif
+float  _Lerp;
+
+#ifdef SMOOTH_LOD_TRANSITION
+float2  _LODData[8];
+#endif
+
+#if defined(DISPLACEMENT) && defined(BLEND_SHAPES)
+int _DebugMode;
+#endif
+
+// per-object-id render props (used with batching)
+// todo: optimize data packing
+#if defined(PER_PRIM_PROPS) || defined(PER_PRIM_PROPS_GROUP)
+float4 _ColorsById[MAX_BATCH_OBJECT_COUNT];
+float4x4 _LocalToWorldById[MAX_BATCH_OBJECT_COUNT];
+float4x4 _WorldToLocalById[MAX_BATCH_OBJECT_COUNT];
+// due to a Quest-specific Unity bug, we're forced to use float4 here when we could use float.
+// data in x component 0:hide, 1: visible
+// data in y component is tint factor
+float4 _VisibilityById[MAX_BATCH_OBJECT_COUNT];
+#endif
+
+#if defined(PER_PRIM_PROPS_GROUP) || defined(GROUP)
+#ifdef LIGHT_PROBE
+float4 _GroupLightProbeById[MAX_BATCH_GROUP_COUNT * LIGHT_PROBE_SIZE];
+#endif
+float4x4 _GroupLocalToWorldById[MAX_BATCH_GROUP_COUNT];
+float4x4 _GroupWorldToLocalById[MAX_BATCH_GROUP_COUNT];
+#endif
+
+// a multiplicative factor applied to vertex colors; defaulted by the app to 1.0 (untinted)
+float _GlobalTint;
+
+struct appdata {
+  OVR_REQUIRED_VERTEX_FIELDS
+  float4  uv : OVR_FIRST_AVAILABLE_VERTEX_TEXCOORD_SEMANTIC;    // todo: why not float2?
+  float4  uv2 : OVR_SECOND_AVAILABLE_VERTEX_TEXCOORD_SEMANTIC;    // todo: why not float2?
+  float4  uv3 : OVR_THIRD_AVAILABLE_VERTEX_TEXCOORD_SEMANTIC;    // todo: why not float2?
+#if defined(SMOOTH_LOD_TRANSITION) || defined(SMOOTH_ANIMATION)
+  float4  lastPos : OVR_FOURTH_AVAILABLE_VERTEX_TEXCOORD_SEMANTIC;    // todo: why not float3?
+  float4  lastNorm : OVR_FIFTH_AVAILABLE_VERTEX_TEXCOORD_SEMANTIC;    // todo: why not float3?
+  float4  lastGI : OVR_SIXTH_AVAILABLE_VERTEX_TEXCOORD_SEMANTIC;    // todo: why not float3?
+#ifdef NORMAL_MAPPING
+  float4 lastTan : OVR_SEVENTH_AVAILABLE_VERTEX_TEXCOORD_SEMANTIC;
+#endif
+#endif
+  float2 id : OVR_EIGHTH_AVAILABLE_VERTEX_TEXCOORD_SEMANTIC; // .x: PPP batchId if GROUP, PER_PRIM_PROPS or PER_PRIM_PROPS_GROUP, .y: groupId if PER_PRIM_PROPS_GROUP or GROUP
+   // .rgb:sRGB albedo/emissive color, .a for material properites if ALPHA_MATERIAL
+  float4  color : COLOR;
+  UNITY_VERTEX_INPUT_INSTANCE_ID
+};
+
+struct compactAppdata {
+  // float3 on C++ side
+  float4  position : POSITION;
+  // RG: packed normal range, B: PPP batchId A: PPP group id
+  float4  packedNormal : NORMAL;
+   // .rgb:sRGB albedo/emissive color, .a for material properites if ALPHA_MATERIAL
+  float4  color : COLOR;
+  float4  uv : TEXCOORD0;
+  float4  packedLighting : TEXCOORD1;
+  UNITY_VERTEX_INPUT_INSTANCE_ID
+};
+
+// https://blog.selfshadow.com/2011/10/17/perp-vectors
+// Hughes-Möller perpendicular vector generation
+float3 perp_hm(float3 u) {
+  float3 a = abs(u);
+  float3 v;
+  if (a.x <= a.y && a.x <= a.z)
+    v = float3(0, -u.z, u.y);
+  else if (a.y <= a.x && a.y <= a.z)
+    v = float3(-u.z, 0, u.x);
+  else
+    v = float3(-u.y, u.x, 0);
+  return v;
+}
+
+// ----------------------------------------
+// https://knarkowicz.wordpress.com/2014/04/16/octahedron-normal-vector-encoding
+float2 OctWrap(float2 v) {
+  return (1.0 - abs( v.yx ) ) * ( v.xy >= 0.0 ? 1.0 : -1.0);
+}
+// @parma n normalized normal
+// @return  f in 0..1 range
+float2 EncodeOct(float3 n) {
+  n /= ( abs( n.x ) + abs( n.y ) + abs( n.z ) );
+  n.xy = n.z >= 0.0 ? n.xy : OctWrap( n.xy );
+  n.xy = n.xy * 0.5 + 0.5;
+  return n.xy;
+}
+// @param f in 0..1 range
+// @return normalized normal
+float3 DecodeOct(float2 f) {
+  f = f * 2.0 - 1.0;
+   // https://twitter.com/Stubbesaurus/status/937994790553227264
+  float3 n = float3( f.x, f.y, 1.0 - abs( f.x ) - abs( f.y ) );
+  float t = saturate( -n.z );
+  n.xy += n.xy >= 0.0 ? -t : t;
+  return normalize( n );
+}
+// ----------------------------------------
+
+
+// ----------------------------------------
+// decode HDR color stored in 32 bit, represents 0 exactly
+// see same function in SubDTools.cpp for more info
+// @param RGBe from 32bit RGBA
+float3 FromRGBe(float4 RGBe) {
+  float fexp = exp2(RGBe.a * 255.0f - 128);
+
+  return RGBe.rgb * fexp;
+}
+// ----------------------------------------
+
+appdata Decompress(compactAppdata c) {
+  appdata ret;
+
+  OVR_SET_VERTEX_POSITION_FIELD(ret, c.position);
+  OVR_SET_VERTEX_NORMAL_FIELD(ret, DecodeOct(c.packedNormal.xy));
+//  ret.normal = normalize(c.packedNormal.xyz - 127.0f / 255.0f);
+  // todo: support normal mapping
+
+  OVR_SET_VERTEX_TANGENT_FIELD(ret, float4(perp_hm(ret.normal), 1));
+  ret.uv = c.uv;
+  float3 HDRLighting = FromRGBe(c.packedLighting);
+  ret.uv2 = float4(HDRLighting.rg, 0.0f, 0.0f);
+  ret.uv3 = float4(HDRLighting.b, 0.0f, 0.0f, 0.0f);
+  // todo: support smooth transition
+#if defined(SMOOTH_LOD_TRANSITION) || defined(SMOOTH_ANIMATION)
+  ret.lastPos = OVR_GET_VERTEX_POSITION_FIELD(ret);
+  ret.lastNorm = float4(OVR_GET_VERTEX_NORMAL_FIELD(ret), 0);
+  ret.lastGI = float4(ret.uv2.xy, ret.uv3.xy);
+#ifdef NORMAL_MAPPING
+  ret.lastTan = OVR_GET_VERTEX_TANGENT_FIELD(ret);
+#endif
+#endif
+  //
+  ret.id = float2((int)(c.packedNormal.b * 255.0f + 0.5f), (int)(c.packedNormal.a * 255.0f + 0.5f));
+  // .a for material properites if ALPHA_MATERIAL
+  ret.color = c.color;
+  UNITY_TRANSFER_INSTANCE_ID(ret, c);
+
+  return ret;
+}
+
+float Pow(float t, float n) {
+  if (n < 1)
+    t = 1;
+
+  return t / (t - n * t + n);
+}
+
+float lengthSquared(float3 v) {
+  return dot(v, v);
+}
+
+
+
+#define addSphereLight(positionX, colorX, r2)\
+{\
+  float3 v = (positionX - wpos.xyz);\
+  float d2 = dot(v, v);\
+  v /= sqrt(d2);\
+  float3 irr = colorX * r2 / (d2 + r2);\
+  float3 w = saturate(float3(dot(v, lv1), dot(v, lv2), dot(v, lv3)) + 0.1);\
+  /*irr = colorX; */\
+  ir0 += irr * w.x;\
+  ir1 += irr * w.y;\
+  ir2 += irr * w.z;\
+}
+
+#ifdef NORMAL_MAPPING
+#define addSkyLight(color, v, spread)\
+{\
+  float3 irr = color;\
+  float3 w = saturate(float3(dot(v, lv1), dot(v, lv2), dot(v, lv3)) + spread);\
+  ir0 += irr * w.x;\
+  ir1 += irr * w.y;\
+  ir2 += irr * w.z;\
+}
+#else
+#define addSkyLight(color, v, spread)\
+{\
+  float3 irr = color;\
+  float w = saturate(dot(v, n) + spread);\
+  ir0 += color * w;\
+}
+#endif
+
+
+float3 unpackIrradiance(float src) {
+  float  red = floor(src / 65536.);
+  float  green = floor(src / 256.);
+  float  blue = src - green * 256.;
+  green -= red * 256.;
+
+  return float3(red, green, blue);
+}
+
+
+void vgi_vert_transform(appdata v, OvrVertexData vertData, inout vgi_vert_tmp tmp) {
+  tmp.position = vertData.position.xyz;
+  tmp.normal = vertData.normal;
+
+#if defined(NORMAL_MAPPING)
+  #if defined(OVR_VERTEX_HAS_TANGENTS)
+    tmp.tangent = vertData.tangent.xyz;
+  #else
+    tmp.tangent = float3(1.0, 0.0, 0.0); // some sensible default
+  #endif
+#endif
+
+#if defined(NORMAL_MAPPING) && !defined(DYNAMIC)
+  float  scale = v.uv2.x;
+  tmp.l0 = unpackIrradiance(v.uv2.y) * scale;
+  tmp.l1 = unpackIrradiance(v.uv3.x) * scale;
+  tmp.l2 = unpackIrradiance(v.uv3.y) * scale;
+#else
+  tmp.l0 = half3(v.uv2.xy, v.uv3.x);
+#endif
+
+	{
+#if defined(SMOOTH_LOD_TRANSITION) || defined(SMOOTH_ANIMATION)
+#ifdef SMOOTH_LOD_TRANSITION
+    float  lodGroup = frac(v.tangent.w) * 8.;
+    float  w = _LODData[lodGroup].x == v.tangent.w ? _LODData[lodGroup].y : 0.;
+#endif
+#ifdef SMOOTH_ANIMATION
+  float w = _Lerp;
+#endif
+#ifndef SHADER_API_MOBILE
+    if (_EditMode == 0.) {
+#endif
+      tmp.position = lerp(tmp.position, v.lastPos.xyz, w);
+      tmp.normal = lerp(tmp.normal, v.lastNorm.xyz, w);
+#ifdef NORMAL_MAPPING
+      tmp.tangent = lerp(tmp.tangent, v.lastTan.xyz, w);
+#endif
+#ifndef SHADER_API_MOBILE
+    }
+#endif
+
+#if defined(NORMAL_MAPPING) && !defined(DYNAMIC)
+    float3  lastL0 = unpackIrradiance(v.lastGI.y) * v.lastGI.x;
+    float3  lastL1 = unpackIrradiance(v.lastGI.z) * v.lastGI.x;
+    float3  lastL2 = unpackIrradiance(v.lastGI.w) * v.lastGI.x;
+
+    tmp.l0 = lerp(tmp.l0, lastL0, w);
+    tmp.l1 = lerp(tmp.l1, lastL1, w);
+    tmp.l2 = lerp(tmp.l2, lastL2, w);
+#else
+    tmp.l0 = lerp(tmp.l0, v.lastGI.xyz, w);
+#endif
+#endif // SMOOTH_LOD_TRANSITION
+  }
+}
+
+//================================
+// Vertex program
+//================================
+v2f vgi_vert(appdata v, vgi_vert_tmp tmp) {
+  v2f  o = (v2f)0;
+  UNITY_INITIALIZE_OUTPUT(v2f, o);
+  UNITY_SETUP_INSTANCE_ID(v);
+  UNITY_TRANSFER_INSTANCE_ID(v, o);
+
+  float4x4 objectToWorld = unity_ObjectToWorld;
+  float4x4 worldToObject = unity_WorldToObject;
+
+#if defined(PER_PRIM_PROPS) || defined(PER_PRIM_PROPS_GROUP)
+  int primId = v.id.x;
+#endif
+
+#ifdef PER_PRIM_PROPS
+  // if enabled, apply per-object-id transform
+  objectToWorld = _LocalToWorldById[primId];
+  worldToObject = _WorldToLocalById[primId];
+#endif
+
+#ifdef PER_PRIM_PROPS_GROUP
+  int groupId = v.id.y;
+  // _LocalToWorldById means "local to group" and _WorldToLocalById means "group to local" when PER_PRIM_PROPS_GROUP is defined
+  objectToWorld = mul(_GroupLocalToWorldById[groupId], _LocalToWorldById[primId]);
+  worldToObject = mul(_WorldToLocalById[primId], _GroupWorldToLocalById[groupId]); // since (AB)^-1 = (B^-1)(A^-1)
+#endif
+
+#ifdef GROUP
+  int groupId = v.id.y;
+  objectToWorld = _GroupLocalToWorldById[groupId];
+  worldToObject = _GroupWorldToLocalById[groupId];
+#endif
+
+
+  half3  l0 = tmp.l0;
+#ifdef NORMAL_MAPPING
+  half3  l1 = tmp.l1;
+  half3  l2 = tmp.l2;
+#endif
+
+#ifdef CUSTOM_VERTEX_CODE
+  CUSTOM_VERTEX_CODE
+#endif
+
+  float3  n = normalize(mul(tmp.normal, (float3x3)worldToObject));
+#ifdef NORMAL_MAPPING
+  float3  t = mul(tmp.tangent, (float3x3)worldToObject);
+  float3  b = normalize(cross(n, t));
+  t = cross(b, n); // note: no mirrored uv support (yet)
+  o.tangent = t.xyzz;
+  o.bitangent = b.xyzz;
+#endif
+
+  float3 obj_pos = tmp.position;
+
+#if defined(DISPLACEMENT) && defined(NORMAL_MAPPING)
+  float3 d = float3(0., 0., 0.); // = v.color.rgb - 0.5;
+#ifdef BLEND_SHAPES
+  d = tex2Dlod(_DispMap, float4(v.uv.xy * float2(2., 1.) + float2(0., .5), 0, 0)).rgb;  // LowerLeft Quadrant
+  d.x = -d.x;
+  obj_pos += mul(d, (float3x3)_HeadTransform) * _DispScale;
+#endif
+#endif
+
+  float4 wpos = mul(objectToWorld, float4(obj_pos, 1));
+
+  // 0:off (stock Unity), 1: fixed T64913801: improve z buffer precison with content far from 0,0,0
+  #define CAMERA_RELATIVE_POSITION 1
+
+  if(CAMERA_RELATIVE_POSITION) {
+    float4x4 unityMatrixV = UNITY_MATRIX_V;
+    float3 translatedWPos = wpos.xyz - _WorldSpaceCameraPos;
+
+    // remove translation in UNITY_MATRIX_V
+    unityMatrixV[0][3] = 0;
+    unityMatrixV[1][3] = 0;
+    unityMatrixV[2][3] = 0;
+
+    float4x4 unityMatrixVP = mul(UNITY_MATRIX_P, unityMatrixV);
+
+    o.pos = mul(unityMatrixVP, float4(translatedWPos, 1));
+  } else {
+    o.pos = mul(UNITY_MATRIX_VP, wpos);
+  }
+
+#if defined(PER_PRIM_PROPS) || defined(PER_PRIM_PROPS_GROUP)
+  // look up per-primitive visibility with vertex primId. if invisible, set the clip space position
+  // to outside of the range [-1, 1] for all components so it's clipped away
+  o.pos = lerp(float4(2, 2, 2, 1), o.pos, _VisibilityById[primId].x);
+#endif
+
+  o.normal = n.xyz;
+#ifdef TEXTURE_ARRAY
+  o.uv.xy = TRANSFORM_TEX(v.uv, _TexArray);
+  // integer part of x is the slice
+  o.uv.z = floor(v.uv.x);
+#else
+  o.uv.xy = TRANSFORM_TEX(v.uv, _MainTex);
+#endif
+
+/*
+  float  groundY = lightProbe[0].w;
+  float  y = mul(_Object2World, pos).y - groundY;
+  float  groundAttenuation = saturate(y*6+0.2);
+  groundAttenuation *= groundAttenuation;
+*/
+  float3  ir0 = float3(0, 0, 0);
+#ifdef NORMAL_MAPPING
+  float3  ir1 = float3(0, 0, 0);
+  float3  ir2 = float3(0, 0, 0);
+#endif
+
+#if defined(LIGHT_PROBE) || !SHADER_API_MOBILE
+#ifndef NORMAL_MAPPING
+  float3  t = mul((float3x3)objectToWorld, OVR_GET_VERTEX_TANGENT_FIELD(v).xyz);
+  float3  b = normalize(cross(n, t));
+  t = cross(b, n); // note: no mirrored normal map support yet
+#endif
+
+  float3  nn = n * 0.949;
+  float3  tt = t * 0.316;
+  float3  bb = b * 0.316 * 0.866;
+
+  float3  lv1 = nn + tt;
+  float3  lv2 = nn + bb - tt * 0.5;
+  float3  lv3 = nn - bb - tt * 0.5;
+#endif
+
+#ifdef LIGHT_PROBE
+  float  groundAttenuation = 0.;
+
+#ifdef NORMAL_MAPPING
+  half3 ao = l0.xyz;
+  l0 = vgi_ProbeRadiance9(_LightProbe, lv1, 0.) * ao.x;
+  l1 = vgi_ProbeRadiance9(_LightProbe, lv2, 0.) * ao.y;
+  l2 = vgi_ProbeRadiance9(_LightProbe, lv3, 0.) * ao.z;
+#else
+#ifdef UNITY_INSTANCING_ENABLED
+  int probeIndex = UNITY_ACCESS_INSTANCED_PROP(Props, _LightProbeIndex);
+  l0 = vgi_ProbeRadianceIndexed(_LightProbeArray, probeIndex, n, 0.);
+#else
+
+#if defined(PER_PRIM_PROPS_GROUP) || defined(GROUP)
+  half3 sh = vgi_ProbeRadianceIndexed(_GroupLightProbeById, groupId, n, 0.);
+#else
+  half3 sh = vgi_ProbeRadiance(_LightProbe, n, 0.);
+#endif
+
+  half3 ao = l0.xyz;
+  l0 = sh * dot(ao, 1. / 3.);
+#endif
+#endif
+#endif
+
+#ifndef SHADER_API_MOBILE
+  if (_EditMode) {
+    addSkyLight(float3(1., 1., 1) * 0.5, float3(0, 1, 0), .75);
+#ifdef NORMAL_MAPPING
+    l0 = ir0;
+    l1 = ir1;
+    l2 = ir2;
+#else
+    l0 = ir0;
+#endif
+  }
+#endif
+
+#ifdef NORMAL_MAPPING
+  o.l1.xyz = l0;
+  o.l2 = l1;
+  o.l3 = l2;
+#else
+  o.l1.xyz = l0;
+#endif
+
+
+  // Shadow mapping
+  //
+#if defined(BLOB_SHADOWS)
+  o.shadowPos = wpos;
+#elif defined(DIRECTIONAL_LIGHT)
+  {
+		float  shadowBias = _LightColor.w;
+
+    // See Interface.cginc for the description of shadowPos
+		float3  shadowPos = mul(_WorldToSunlight, wpos).xyz;
+		o.shadowPos.w = float4(sqrt(dot(shadowPos.xy, shadowPos.xy)), 0., 0., 0.);
+
+#if SHADOWMAP_STATIC_VSM
+    float4  lightSpacePos = mul(_LightWorldToLocalMatrix, wpos);
+    float   depth = lightSpacePos.z * _ShadowMapCameraZFarInv;
+		o.staticShadowOffset.w = depth;
+#endif
+
+    shadowPos = shadowPos * .5 + .5;  // Zero-center shadowPos (0..1 -> -0.5..0.5)
+
+    shadowPos.x *= 0.5;
+    float2  offset0 = float2(0.5, 0.5);
+    float2  offset1 = float2(0.5, 0.5);
+
+#ifdef UNITY_REVERSED_Z
+		shadowPos.z = 1. - shadowPos.z;
+		o.staticShadowOffset.xyz = mul(_StaticWorldToSunlight, wpos).xyz * float3(offset0, -0.5) +
+				float3(offset1, .5 + shadowBias * 2.) - shadowPos;
+		shadowPos.z += shadowBias;
+#else
+		o.staticShadowOffset.xyz = mul(_StaticWorldToSunlight, wpos).xyz * float3(offset0, 0.5) +
+				float3(offset1, .5 - shadowBias * 2.) - shadowPos;
+		shadowPos.z -= shadowBias;
+#endif
+
+		o.shadowPos.xyz = shadowPos;
+  }
+#endif // BLOB_SHADOWS, DIRECTIONAL_LIGHT
+
+
+#ifdef FRAG_NEED_WPOS
+  o.wPos = wpos;
+#endif
+
+  fbFog_vert(_fbFogColor.a, _WorldSpaceCameraPos, wpos.xyz, o.l1.w);
+
+  // compute o.color and o.materialProps
+  {
+    half4 sRGBWithAlpha = 1;
+
+#ifdef VERTEX_COLOR
+#if defined(PER_PRIM_PROPS) || defined(PER_PRIM_PROPS_GROUP)
+    // if enabled, apply per-prim-id properties
+    sRGBWithAlpha = _ColorsById[primId];
+#else
+    sRGBWithAlpha = v.color;
+#endif
+#endif
+    // ^2 is sRGB to linear appoximation
+    o.color = half4(sRGBWithAlpha.rgb * sRGBWithAlpha.rgb, sRGBWithAlpha.a);
+
+#ifdef VERTEX_COLOR
+#if defined(PER_PRIM_PROPS) || defined(PER_PRIM_PROPS_GROUP)
+    o.color.rgb *= _VisibilityById[primId].y; // PPP tint factor
+#else
+    o.color.rgb *= _GlobalTint;
+#endif
+#endif
+
+#ifdef SOLID_COLOR
+#ifdef UNITY_INSTANCING_ENABLED
+    o.color *= UNITY_ACCESS_INSTANCED_PROP(Props, _Color);
+#else
+    o.color *= _Color;
+#endif
+#endif
+
+#if ALPHA_MATERIAL
+    // 0..7, search "materialProperties" in C#
+    half matIndex;
+    // 0:rough..1:polished
+    half smooth;
+    {
+      // extract matIndex (3 bits) and smooth (5 bits) from alpha channel (8 bits)
+      half tmp = o.color.a * (255. / 32.);
+      matIndex = floor(tmp);
+      smooth = saturate((tmp - matIndex - 2. / 32.) * 32./29.);
+    }
+
+    half4 materialProperties = _MaterialProperties[matIndex];
+    half metal = materialProperties.x;
+    half specMul = materialProperties.z;
+    half glow = materialProperties.w;
+
+    o.color.a = smooth;
+    o.materialProps = half3(metal, specMul, glow);
+#else
+    o.color.a = 0;
+    // compiler should remove this dead code
+    o.materialProps = half3(0, 0, 0);
+#endif
+  }
+
+  return o;
+}
+
+float myPow(float a, float b) {
+  return a / ((1. - b) * a + b);
+}
+
+float rgbToLuma(float3 c) {
+  return dot(c, float3(0.299, 0.587, 0.114));
+}
+
+// 2 fmul, 1 fadd, 1 frac from:
+// https://developer.oculus.com/blog/tech-note-shader-snippets-for-efficient-2d-dithering
+float Dither16(float2 Pos, int frameIndexMod4)
+{
+	uint3  k0 = uint3(36, 7, 26);
+
+	float  Ret = dot(float3(Pos.xy, frameIndexMod4), k0 / 16.0f);
+
+	return frac(Ret);
+}
+
+// 2 fmul, 1 fadd, 1 frac// to not align with Dither16, could be put into one function
+float Dither17(float2 Pos, int frameIndexMod4)
+{
+  // Note: was uint3(2, 7, 23), the new variant seems to align better with:
+  // Dither16   uint3 k0 = uint3(7, 2, 23);
+   uint3 k0 = uint3(7, 2, 23);
+
+   float Ret = dot(float3(Pos.xy, frameIndexMod4), k0 / 17.0f);
+
+   return frac(Ret);
+}
+
+#ifdef NORMAL_MAPPING
+half4 sample_normal_map(float2 uv) {
+#ifdef USE_NRML_MAP_NAME
+    return tex2D(_NrmlMap, uv);
+#else
+    return tex2D(_NormalMap, uv);
+#endif
+}
+#endif
+
+
+//================================
+// Fragment program first stage
+// - 'tmp' initialization
+// - normal mapping
+//================================
+void vgi_frag_init(v2f i, inout vgi_frag_tmp tmp) {
+#ifdef NORMAL_MAPPING
+#ifdef BLEND_SHAPES
+  half4 nT = tex2D(_DispMap, i.uv * float2(2., 1.)); // _DispMap is floating point between -1 and 1
+  nT += sample_normal_map(i.uv) * 2. - 1.; // Static normal map is 8 bit RGBA between 0 and 1. Convert to -1 to 1
+
+  // rawNT is value from 0 to 1
+  half4 rawNT = nT * 0.5 + 0.5;
+#else
+  half4  rawNT = sample_normal_map(i.uv);
+  float3  nT = rawNT.xyz * 2. - 1; // Convert to -1 to 1
+#endif
+  float3  n = i.tangent.xyz * nT.x - i.bitangent.xyz * nT.y + i.normal.xyz * nT.z;
+  tmp.rawNT = rawNT;
+#else
+  float3  n = i.normal.xyz;
+#endif
+
+  tmp.normal = normalize(n);
+
+  tmp.materialColor = i.color;
+  tmp.materialProps = i.materialProps;
+
+  // Initialize to 1, so vgi_frag_dynamicshadow is not called, shadowing (or lack thereof) will work as expected
+  tmp.shadowFactor = 1.;
+}
+
+
+
+
+//========================================
+// Fragment program lighting stage
+// This may be replaced by custom code.
+// See Framework.cginc
+//========================================
+void vgi_frag_dynamicshadow(v2f i, inout vgi_frag_tmp tmp) {
+#if defined(BLOB_SHADOWS)
+  tmp.shadowFactor = blobShadowFactor(i.shadowPos, tmp.normal);
+#else
+  float3  shadowDepthOrig = i.shadowPos.xyz;
+  float3  shadowDepth = shadowDepthOrig;
+  float   shadowDistance = i.shadowPos.w;
+
+#if SHADOW_DITHER_QUALITY
+  // 0 .. 0.5
+  // 6 fmul, 2 fadd, 2 frac (2 fmul could be saved if output range 0..1 is used)
+  float2  halfDither = float2(Dither16(i.pos.xy, 0), Dither17(i.pos.xy, 0)) * .5;
+  shadowDepth.xy += (halfDither - .25) * _ShadowMapSize.zw;
+  // Softer PCF comparison (should align with depth buffer precision, todo: verify)
+  shadowDepth.z += (halfDither - .25) * 0.0002f;
+  // Soft transition between cascades
+  shadowDistance += halfDither.x * (1.5 * 0.1);
+#else // SHADOW_DITHER_QUALITY
+  // 0 .. 0.5
+  // 2 fmul, 2 fadd, 1 frac
+  half2  offset = frac((i.pos.xy - .5) * .5);
+  offset.y = abs(offset.x - offset.y);  // x ^= y
+
+  shadowDepth.xy += (offset - .25) * _ShadowMapSize.zw;
+  // test if uv coordinates fall outside dynamic shadow range. If so, use static shadow
+  // (0 .. 1.5) * 0.1
+  shadowDistance += (offset.x * 2. + offset.y) * .1;
+#endif // SHADOW_DITHER_QUALITY
+
+  half  shadowFactor;
+
+// Only static objects were rendered into the global shadow cascade: blend / use both cascades
+  {
+#ifdef SHADOWMAP_STATIC_VSM
+    // Use a variance shadow map for static shadows
+    float   depth = i.staticShadowOffset.w + _VarianceShadowBias;
+    float2  shadowmapSample = tex2D(_ShadowMapStatic, shadowDepthOrig + i.staticShadowOffset.xyz).rg;
+    shadowFactor = ComputeVSMShadowFactor(shadowmapSample, depth);
+#else
+  #if DYNAMIC_SHADOWS
+    // static shadow sample
+    shadowFactor = UNITY_SAMPLE_SHADOW(_ShadowMapStatic, shadowDepth + i.staticShadowOffset.xyz).r;
+    // dynamic shadow sample
+    if(shadowDistance < 1.000) {
+      shadowFactor *= UNITY_SAMPLE_SHADOW(_ShadowMap, shadowDepth).r;
+    }
+  #else
+    // static shadow sample, alternative to UNITY_SAMPLE_SHADOW to maintain simplicity
+    float3 shadowCoords = shadowDepth + i.staticShadowOffset.xyz;
+    shadowFactor = step(shadowCoords.z, tex2D(_ShadowMapStatic, shadowCoords.xy).r);
+    // no dynamic shadow in low quality :(
+  #endif // DYNAMIC_SHADOWS
+#endif  // SHADOWMAP_STATIC_VSM
+  }
+
+  tmp.shadowFactor = shadowFactor;
+#endif // BLOB_SHADOWS
+}
+
+half3 calc_indirect_diffuse_light(v2f i, vgi_frag_tmp tmp, half3 sss) {
+#ifdef NORMAL_MAPPING
+  half3 diffuseIC = tex2D(_ICMap, tmp.rawNT.xy).rgb * 2 - 1. / 3.;
+
+  // Alpha value of normal map/displacement map is an occlusion value,
+  // scale the indirect lighting by that occlusion
+  half3 indirectDiffuse =
+    (i.l1.rgb * diffuseIC.x + i.l2.rgb * diffuseIC.y + i.l3.rgb * diffuseIC.z);
+
+    // Only multiply by alpha if not using blend shapes (as the AO will hopefully be
+    // calculated by some other method)
+    // NOTE: This should be temporary until assets properly map out AO
+#ifndef BLEND_SHAPES
+    indirectDiffuse = indirectDiffuse * tmp.rawNT.a;
+#endif
+
+#ifdef SSS
+  return lerp(indirectDiffuse, (i.l1.rgb + i.l2.rgb + i.l3.rgb) * (1. / 3.), sss);
+#else
+  return indirectDiffuse;
+#endif // ifdef SSS
+#else // ifndef NORMAL_MAPPING
+  // Not normal mapping, just one color instead of 3
+  return i.l1;
+#endif
+}
+
+half3 calc_indirect_diffuse_light(v2f i, vgi_frag_tmp tmp) {
+    return calc_indirect_diffuse_light(i, tmp, 0.0);
+}
+
+
+#if defined(ALPHA_MATERIAL)
+half default_ambient_specular(float3 reflectVector, float specPower, half specIntensity) {
+  float3  rAbs = abs(reflectVector); // fake 6 directional light specular in +x, -x, +y, -y, +z, -z
+  // Use max instead of adding to keep lowest intensity below .5
+  return myPow(max(max(rAbs.x, rAbs.y), rAbs.z) + 0.005, specPower) * specIntensity;
+}
+
+half ambient_specular_from_cube_map(half3 reflectVector, half smooth, half metal) {
+  // Use the top 5 mip levels only to support small cube maps (size 64) and avoid artifacts
+  // due to compression and creation of cubemaps from 2D texure data
+  // todo: use shader variable for mip level multiplier instead of hardcoding it to 4
+  // We use 1/2 intensity maps and scale up here so low-dynamic-range get higher range
+  return texCUBElod(_ReflectionCubeMap, half4(reflectVector, (1. - smooth) * 4.)).r * 2.;
+}
+#endif
+
+//========================================
+// Fragment program lighting stage
+// This may be replaced by custom code.
+// See Framework.cginc
+//========================================
+half3 vgi_frag_lighting(v2f i, vgi_frag_tmp tmp) {
+  UNITY_SETUP_INSTANCE_ID(i);
+  float2  uv = i.uv.xy;
+
+#ifdef SSS
+  half3 sss = tex2D(_SSSMap, uv).rgb;
+#endif
+
+#ifdef CUSTOM_FRAGMENT_CODE
+  CUSTOM_FRAGMENT_CODE
+#endif
+
+  {
+    // Note: .a: was changed from (smooth + matIndex)/ 8.0f to smoothness only
+    // we can consider *2 and saturate to allow textures to got rough or smooth
+
+#ifdef TEXTURE_COLOR
+    tmp.materialColor *= tex2D(_MainTex, uv);
+#endif
+#ifdef TEXTURE_ARRAY
+    tmp.materialColor *= UNITY_SAMPLE_TEX2DARRAY(_TexArray, i.uv.xyz);
+#endif
+  }
+
+  // assume normalized vectors
+ half nDotL = dot(tmp.normal, _LightVector.xyz);
+#ifdef SSS
+  half3 diffuseIntensity = saturate(lerp(
+      dot(tmp.normal, _LightVector.xyz).xxx,
+      (dot(i.normal.xyz, _LightVector.xyz) + .3) / 1.3,
+      sss));
+  half3 indirectDiffuse = calc_indirect_diffuse_light(i, tmp, sss);
+#else
+  half diffuseIntensity = saturate(nDotL);
+  half3 indirectDiffuse = calc_indirect_diffuse_light(i, tmp);
+#endif // SSS
+
+  half3 result = tmp.materialColor.rgb * ( indirectDiffuse
+#if defined(DIRECTIONAL_LIGHT)
+    + _LightColor.rgb * (tmp.shadowFactor * diffuseIntensity)
+#endif // DIRECTIONAL_LIGHT
+    );
+
+#if defined(BLOB_SHADOWS)
+  result *= tmp.shadowFactor;
+#endif // BLOB_SHADOWS
+
+#if ALPHA_MATERIAL
+  half smooth = tmp.materialColor.w;
+  half metal = tmp.materialProps.x;
+  half specMul = tmp.materialProps.y;
+  half glow = tmp.materialProps.z;
+
+  half notMetal = 1. - metal;
+
+  half3 eye = normalize(i.wPos.xyz - _WorldSpaceCameraPos);
+  half f = saturate(1.1 + dot(tmp.normal, eye) + metal);
+  half specIntensity = specMul *
+    f*f // fresnel
+    // adjust intenstiy and remove non-metal specular at 0 smoothness
+      * (metal + smooth * notMetal * 4.);
+
+  float3 r = eye - tmp.normal * (2. * dot(tmp.normal, eye));
+
+  // antialias reflections by lowering smoothness
+  // where there is a large change to the reflection vector
+  // from pixel to pixel. Factor of 16 found by experimentation
+  // and power-of-2 multiply very efficient on target mobile GPU
+  smooth *= 1. - saturate((lengthSquared(ddx(r)) + lengthSquared(ddy(r))) * 16.);
+  half ambientSpecular = ambient_specular_from_cube_map(r, smooth, metal);
+#if DIRECT_SPECULAR
+  // half sunSpecular = reflectionMapData.b * saturate(nDotL + .25);
+  half sunSpecular =
+      myPow(saturate(dot(r, _LightVector.xyz)) + 0.003, smooth * (80. + 160. * notMetal)) *
+      saturate(nDotL + .25);
+#endif // DIRECT_SPECULAR
+
+  result = result * notMetal +
+    (indirectDiffuse * ambientSpecular
+  #if DIRECT_SPECULAR
+       + _LightColor.rgb * (tmp.shadowFactor * sunSpecular)
+  #endif // DIRECT_SPECULAR
+      ) *
+      (notMetal + tmp.materialColor.rgb * metal) * specIntensity +
+    glow * tmp.materialColor.rgb; // emissive/glow
+#endif // ALPHA_MATERIAL
+
+// LIGHTING DEBUGGER: Here is most of the logic that can be enabled via multi_compile in the shader files.
+#if defined(_RENDER_DEBUG_DIRECT_DIFFUSE)
+  #if defined(DIRECTIONAL_LIGHT)
+    result.rgb = diffuseIntensity * _LightColor.rgb;
+  #else
+    result.rgb = 0;
+  #endif
+#elif defined(_RENDER_DEBUG_DIRECT_SPECULAR)
+  #if DIRECT_SPECULAR && ALPHA_MATERIAL
+    result.rgb = (notMetal + txColor.rgb * metal) * specIntensity + sunSpecular * _LightColor.rgb;
+  #else
+    result.rgb = 0;
+  #endif
+#elif defined(_RENDER_DEBUG_INDIRECT_DIFFUSE)
+    result.rgb = indirectDiffuse;
+#elif defined(_RENDER_DEBUG_INDIRECT_SPECULAR)
+  #if ALPHA_MATERIAL
+    result.rgb = ambientSpecular;
+  #else
+    result.rgb = 0;
+  #endif
+#elif defined(_RENDER_DEBUG_SHADOW)
+    result.rgb = tmp.shadowFactor;
+#elif defined(_RENDER_DEBUG_VERTEX_COLOR)
+  result.rgb = tmp.materialColor;
+#elif defined(_RENDER_DEBUG_TEXTURE_COLOR)
+    result.rgb = txColor;
+#elif defined(_RENDER_DEBUG_REFLECTION)
+  #if ALPHA_MATERIAL
+    result.rgb = reflectionMapData;
+  #else
+    result.rgb = 0;
+  #endif
+#elif defined(_RENDER_DEBUG_NORMALS)
+  #if defined(NORMAL_MAPPING)
+    result = tmp.rawNT;  // Normal
+  #else
+    result.rgb = 0;
+  #endif
+#elif defined(_RENDER_DEBUG_DISPLACEMENT)
+  #if defined(DISPLACEMENT) && defined(NORMAL_MAPPING)
+    result = tex2D(_DispMap, uv*(float2(2., 1.)) + float2(0., .5)).xyz;  // Displacement
+  #else
+    result.rgb = 0;
+  #endif
+#elif defined(_RENDER_DEBUG_SSS)
+  #ifdef SSS
+    result.rgb = sss;
+  #else
+    result.rgb = 0;
+  #endif
+#elif defined(_RENDER_DEBUG_FOG)
+    result.rgb = 0;
+#else
+    // return the result that was calculated by the original combined shader.
+#endif
+
+  return result;
+}
+
+//================================
+// Fragment program final stage
+// - fog
+// - tonemapping
+//================================
+float4 vgi_frag_final(v2f i, half3 color) {
+  half4  result = float4(color, 1.0);
+
+  // In "safe mode", desaturate colors and reduce contrast beyond a radius around the user
+#if fbSAFE_MODE
+  float3  viewDistance = length(i.wPos.xyz - _WorldSpaceCameraPos);
+  // result = fbSafeModeGrid(result, i.wPos.xyz, viewDistance);
+  // clip(result.a - 0.1);
+#elif !defined(_RENDER_DEBUG_DIRECT_DIFFUSE) \
+   && !defined(_RENDER_DEBUG_DIRECT_SPECULAR) \
+   && !defined(_RENDER_DEBUG_INDIRECT_DIFFUSE) \
+   && !defined(_RENDER_DEBUG_INDIRECT_SPECULAR) \
+   && !defined(_RENDER_DEBUG_SHADOW) \
+   && !defined(_RENDER_DEBUG_VERTEX_COLOR) \
+   && !defined(_RENDER_DEBUG_TEXTURE_COLOR) \
+   && !defined(_RENDER_DEBUG_REFLECTION) \
+   && !defined(_RENDER_DEBUG_NORMALS) \
+   && !defined(_RENDER_DEBUG_DISPLACEMENT) \
+   && !defined(_RENDER_DEBUG_SSS)
+  fbFog_frag(result, i.l1.w);
+#endif
+  result.rgb = fbToneMap(result.rgb, i.pos.xy);
+
+// Quality level debugger
+#if QUALITY_LEVEL_DEBUG
+#if LOW_QUALITY
+#endif
+#if HIGH_QUALITY
+    result.r = 1.0;
+#else
+    result.b = 1.0;
+#endif
+#endif // QUALITY_LEVEL_DEBUG
+
+#if defined(BLOB_SHADOWS) && BLOB_SHADOWS_SURFACE_DEBUG
+  result = blobSurfaceDebug(result);
+#endif
+
+  return result;
+}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/VertexGI/SurfaceShader.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/VertexGI/SurfaceShader.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..cdfe4913cc7ad07ea88de2b4666deb2586d25f23
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Horizon/VertexGI/SurfaceShader.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: a60821d72c583bb4498d6f3376a51f4c
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/UnityLighting.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/UnityLighting.meta
new file mode 100644
index 0000000000000000000000000000000000000000..893f6ef80d8609d0de3af236a784e6264075ccef
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/UnityLighting.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: ea40f0ff833352c4586b47c8a221fbd1
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/UnityLighting/AvatarUnityLighting.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/UnityLighting/AvatarUnityLighting.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..9f1acbae0c2f7595be74465c78fb116e1bed21e4
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/UnityLighting/AvatarUnityLighting.cginc
@@ -0,0 +1,304 @@
+#ifndef AVATAR_UNITY_LIGHTING_CGINC
+#define AVATAR_UNITY_LIGHTING_CGINC
+
+#include "UnityGlobalIllumination.cginc"
+
+#include "AvatarCommon\AvatarCommonProperties.cginc"
+#include "AvatarUnitySurfaceFields.cginc"
+#include "AvatarUnityLightingTypes.cginc"
+
+UnityGIInput GetGlobalIlluminationInput(
+  v2f IN,
+  fixed3 lightDir,
+  float3 worldPos,
+  float3 worldViewDir,
+  fixed attenuation)
+{
+  // Setup lighting environment
+  UnityGI gi;
+  UNITY_INITIALIZE_OUTPUT(UnityGI, gi);
+
+  gi.indirect.diffuse = 0;
+  gi.indirect.specular = 0;
+  gi.light.color = _LightColor0.rgb;
+  gi.light.dir = lightDir;
+
+  // Call GI (lightmaps/SH/reflections) lighting function
+  UnityGIInput giInput;
+  UNITY_INITIALIZE_OUTPUT(UnityGIInput, giInput);
+  giInput.light = gi.light;
+  giInput.worldPos = worldPos;
+  giInput.worldViewDir = worldViewDir;
+  giInput.atten = attenuation;
+
+#if defined(LIGHTMAP_ON) || defined(DYNAMICLIGHTMAP_ON)
+  giInput.lightmapUV = IN.lmap;
+#else
+  giInput.lightmapUV = 0.0;
+#endif
+
+#if UNITY_SHOULD_SAMPLE_SH && !UNITY_SAMPLE_FULL_SH_PER_PIXEL
+  giInput.ambient = IN.sh;
+#else
+  giInput.ambient.rgb = 0.0;
+#endif
+
+  giInput.probeHDR[0] = unity_SpecCube0_HDR;
+  giInput.probeHDR[1] = unity_SpecCube1_HDR;
+#if defined(UNITY_SPECCUBE_BLENDING) || defined(UNITY_SPECCUBE_BOX_PROJECTION)
+  giInput.boxMin[0] = unity_SpecCube0_BoxMin; // .w holds lerp value for blending
+#endif
+
+#ifdef UNITY_SPECCUBE_BOX_PROJECTION
+  giInput.boxMax[0] = unity_SpecCube0_BoxMax;
+  giInput.probePosition[0] = unity_SpecCube0_ProbePosition;
+  giInput.boxMax[1] = unity_SpecCube1_BoxMax;
+  giInput.boxMin[1] = unity_SpecCube1_BoxMin;
+  giInput.probePosition[1] = unity_SpecCube1_ProbePosition;
+#endif
+
+  return giInput;
+}
+
+AvatarShaderGlobalIllumination GetGlobalIllumination(
+  UnityGIInput data,
+  half smoothness,
+  half metallic,
+  half occlusion,
+  half3 albedo,
+  half3 normal)
+{
+  Unity_GlossyEnvironmentData g = UnityGlossyEnvironmentSetup(
+    smoothness,
+    data.worldViewDir,
+    normal,
+    lerp(unity_ColorSpaceDielectricSpec.rgb, albedo, metallic));
+
+  UnityGI gi;
+  UNITY_INITIALIZE_OUTPUT(UnityGI, gi);
+  half ambientOcclusion = lerp(1.0f, occlusion, _AmbientOcclusionEffect);
+  gi = UnityGlobalIllumination(data, ambientOcclusion, normal, g);
+
+  AvatarShaderLight light;
+  light.direction = gi.light.dir;
+  light.color = gi.light.color;
+
+  AvatarShaderIndirect indirect;
+  indirect.diffuse = gi.indirect.diffuse;
+  indirect.specular = gi.indirect.specular;
+
+  AvatarShaderGlobalIllumination avatarGI;
+  avatarGI.light = light;
+  avatarGI.indirect = indirect;
+
+  return avatarGI;
+}
+
+///////////////////////////////////////
+// vertex shader funcs and generator //
+///////////////////////////////////////
+v2f AvatarShaderVertInit(appdata v) {
+  OVR_INITIALIZE_VERTEX_FIELDS(v)
+  UNITY_SETUP_INSTANCE_ID(v);
+  v2f o;
+  UNITY_INITIALIZE_OUTPUT(v2f,o);
+  UNITY_TRANSFER_INSTANCE_ID(v,o);
+  UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
+
+  return o;
+}
+OvrVertexData AvatarShaderVertTransform(appdata v, inout v2f o) {
+  OvrVertexData vertexData = OVR_CREATE_VERTEX_DATA(v);
+  float4 objPos = vertexData.position;
+  float3 objNormal = vertexData.normal;
+
+  o.pos = UnityObjectToClipPos(objPos);
+  o.color = v.color;
+  o.uv = v.uv;
+  o.ormt = v.ormt;
+
+  // Assumes only caring about xy component on v.uv
+  // Duplicate it (by default) for properties map and effects map UVs
+  o.propertiesMapUV.xy = o.uv.xy;
+  o.effectsMapUV.xy = o.uv.xy;
+
+  float3 worldPos = mul(unity_ObjectToWorld, objPos).xyz;
+  float3 worldNormal = UnityObjectToWorldNormal(objNormal);
+
+#if defined(LIGHTMAP_ON) && defined(DIRLIGHTMAP_COMBINED) && !defined(UNITY_HALF_PRECISION_FRAGMENT_SHADER_REGISTERS)
+  float4 objTangent = vertexData.tangent;
+  fixed3 worldTangent = UnityObjectToWorldDir(objTangent.xyz);
+  fixed tangentSign = objTangent.w * unity_WorldTransformParams.w;
+  fixed3 worldBinormal = cross(worldNormal, worldTangent) * tangentSign;
+
+  o.tSpace0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);
+  o.tSpace1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);
+  o.tSpace2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);
+#endif
+
+  o.worldPos.xyz = worldPos;
+  o.worldNormal = worldNormal;
+
+  return vertexData;
+}
+
+void AvatarShaderVertLighting(appdata v, inout v2f o) {
+  float3 worldNormal = o.worldNormal;
+
+#ifdef DYNAMICLIGHTMAP_ON
+  o.lmap.zw = v.texcoord2.xy * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw;
+#endif
+#ifdef LIGHTMAP_ON
+  o.lmap.xy = v.texcoord1.xy * unity_LightmapST.xy + unity_LightmapST.zw;
+#endif
+
+  // SH/ambient and vertex lights
+#ifndef LIGHTMAP_ON
+  #if UNITY_SHOULD_SAMPLE_SH && !UNITY_SAMPLE_FULL_SH_PER_PIXEL
+    o.sh = 0;
+    // Approximated illumination from non-important point lights
+    #ifdef VERTEXLIGHT_ON
+      o.sh += Shade4PointLights (
+        unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0,
+        unity_LightColor[0].rgb, unity_LightColor[1].rgb, unity_LightColor[2].rgb, unity_LightColor[3].rgb,
+        unity_4LightAtten0, worldPos, worldNormal);
+    #endif
+    o.sh = ShadeSHPerVertex (worldNormal, o.sh);
+  #endif
+#endif // !LIGHTMAP_ON
+
+  UNITY_TRANSFER_LIGHTING(o, v.texcoord1.xy); // pass shadow and, possibly, light cookie coordinates to pixel shader
+#ifdef FOG_COMBINED_WITH_TSPACE
+  UNITY_TRANSFER_FOG_COMBINED_WITH_TSPACE(o, o.pos); // pass fog coordinates to pixel shader
+#elif defined (FOG_COMBINED_WITH_WORLD_POS)
+  UNITY_TRANSFER_FOG_COMBINED_WITH_WORLD_POS(o, o.pos); // pass fog coordinates to pixel shader
+#else
+  UNITY_TRANSFER_FOG(o, o.pos); // pass fog coordinates to pixel shader
+#endif
+}
+
+#define GENERATE_AVATAR_UNITY_LIGHTING_DEFAULT_VERTEX_PROGRAM(VertProgramName) \
+  v2f VertProgramName(appdata v) { \
+    v2f o = AvatarShaderVertInit(v); \
+    \
+    AvatarShaderVertTransform(v, o); \
+    \
+    AvatarShaderVertLighting(v, o); \
+    \
+    return o; \
+  }
+
+#define GENERATE_AVATAR_UNITY_LIGHTING_VERTEX_PROGRAM(VertProgramName, CustomVertFunc) \
+  v2f VertProgramName(appdata v) { \
+    v2f o = AvatarShaderVertInit(v); \
+    \
+    OvrVertexData vertexData = AvatarShaderVertTransform(v, o); \
+    \
+    AvatarShaderVertLighting(v, o); \
+    \
+    CustomVertFunc(v, vertexData, o); \
+    \
+    return o; \
+  }
+
+/////////////////////////////////////////
+// fragment shader funcs and generator //
+/////////////////////////////////////////
+
+void AvatarShaderFragInit(inout v2f IN) {
+  UNITY_SETUP_INSTANCE_ID(IN);
+
+  #ifdef FOG_COMBINED_WITH_TSPACE
+    UNITY_EXTRACT_FOG_FROM_TSPACE(IN);
+  #elif defined (FOG_COMBINED_WITH_WORLD_POS)
+    UNITY_EXTRACT_FOG_FROM_WORLD_POS(IN);
+  #else
+    UNITY_EXTRACT_FOG(IN);
+  #endif
+}
+
+float3 AvatarShaderFragGetWorldPos(v2f IN) {
+  return IN.worldPos.xyz;
+}
+
+fixed3 AvatarShaderFragGetLightDir(float3 worldPos) {
+  // Directional light only used the lightDir as a normalized direction
+  // instead of a light position as well
+#ifndef USING_DIRECTIONAL_LIGHT
+  return normalize(UnityWorldSpaceLightDir(worldPos));
+#else
+  return _WorldSpaceLightPos0.xyz;
+#endif
+}
+
+float3 AvatarShaderFragGetWorldViewDir(float3 worldPos) {
+  return normalize(UnityWorldSpaceViewDir(worldPos));
+}
+
+float3 OverideColorWithDebug(float3 original, in v2f IN) {
+#if defined(_RENDER_DEBUG_VERTEX_COLOR)
+  original.rgb = 1; // IN.color.rgb;
+#elif defined(_RENDER_DEBUG_UVS)
+  original.rg = IN.uv.rg;
+  original.b = 0;
+#elif defined(_RENDER_DEBUG_WORLD_NORMAL)
+  original.rgb = IN.worldNormal.xyz;
+#elif defined(_RENDER_DEBUG_WORLD_POSITION)
+  original.rgb = IN.worldPos.xyz;
+#elif defined(_RENDER_DEBUG_SH)
+  original.rgb = IN.sh.xyz;
+#endif
+  return original;
+}
+
+#define AVATAR_SHADER_FRAG_INIT_AND_CALL_SURFACE(SurfaceFuncName, SurfaceOutputType) \
+  SurfaceOutputType o; \
+  UNITY_INITIALIZE_OUTPUT(SurfaceOutputType, o); \
+  \
+  /* Initialize required values */ \
+  SET_AVATAR_SHADER_SURFACE_ALBEDO_FIELD(o, 0.0); \
+  SET_AVATAR_SHADER_SURFACE_SMOOTHNESS_FIELD(o, 0.0); \
+  SET_AVATAR_SHADER_SURFACE_METALLIC_FIELD(o, 0.0); \
+  SET_AVATAR_SHADER_SURFACE_OCCLUSION_FIELD(o, 1.0); \
+  SET_AVATAR_SHADER_SURFACE_NORMAL_FIELD(o, normalize(IN.worldNormal)); \
+  /* call surface function */ \
+  SurfaceFuncName(IN, o);
+
+#define AVATAR_SHADER_FRAG_LIGHTING(LightingFuncName) \
+  /* compute lighting & shadowing factor */ \
+  UNITY_LIGHT_ATTENUATION(atten, IN, worldPos) \
+  fixed4 c = 0; \
+  \
+  /* Setup lighting environment */ \
+  UnityGIInput giInput = GetGlobalIlluminationInput(IN, lightDir, worldPos, worldViewDir, atten); \
+  \
+  half smoothness = GET_AVATAR_SHADER_SURFACE_SMOOTHNESS_FIELD(o); \
+  half metallic = GET_AVATAR_SHADER_SURFACE_METALLIC_FIELD(o); \
+  half occlusion = GET_AVATAR_SHADER_SURFACE_OCCLUSION_FIELD(o); \
+  \
+  half3 albedo = GET_AVATAR_SHADER_SURFACE_ALBEDO_FIELD(o); \
+  half3 normal = GET_AVATAR_SHADER_SURFACE_NORMAL_FIELD(o); \
+  \
+  AvatarShaderGlobalIllumination gi = GetGlobalIllumination(giInput, smoothness, metallic, occlusion, albedo, normal); \
+  \
+  /* realtime lighting: call lighting function */ \
+  c += LightingFuncName(o, worldViewDir, gi); \
+  \
+  UNITY_APPLY_FOG(_unity_fogCoord, c); /* apply fog */ \
+  c.rgb = OverideColorWithDebug(c.rgb, IN); \
+  UNITY_OPAQUE_ALPHA(c.a); \
+  return c;
+
+#define GENERATE_AVATAR_UNITY_LIGHTING_FRAGMENT_PROGRAM(ProgName, SurfFunc, SurfOutputType, LightingFunc) \
+  fixed4 ProgName(v2f IN) : SV_Target { \
+    AvatarShaderFragInit(IN); \
+    \
+    float3 worldPos = AvatarShaderFragGetWorldPos(IN); \
+    fixed3 lightDir = AvatarShaderFragGetLightDir(worldPos); \
+    float3 worldViewDir = AvatarShaderFragGetWorldViewDir(worldPos); \
+    \
+    AVATAR_SHADER_FRAG_INIT_AND_CALL_SURFACE(SurfFunc, SurfOutputType) \
+    AVATAR_SHADER_FRAG_LIGHTING(LightingFunc) \
+  }
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/UnityLighting/AvatarUnityLighting.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/UnityLighting/AvatarUnityLighting.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..0dd4848f4d5f98f77fe792ab520193e4fef50815
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/UnityLighting/AvatarUnityLighting.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 49f27d46c9aff494c92306f274122f21
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/UnityLighting/AvatarUnityLightingTypes.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/UnityLighting/AvatarUnityLightingTypes.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..05fdb3eb9fb1cbf8484426f59defbe4afe2846c5
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/UnityLighting/AvatarUnityLightingTypes.cginc
@@ -0,0 +1,120 @@
+#ifndef AVATAR_UNITY_LIGHTING_TYPES_CGINC
+#define AVATAR_UNITY_LIGHTING_TYPES_CGINC
+
+#include "UnityCG.cginc"
+#include "AutoLight.cginc"
+
+#include "../../../../Scripts/ShaderUtils/AvatarCustom.cginc"
+
+// vertex input data
+struct appdata {
+  OVR_REQUIRED_VERTEX_FIELDS
+  float4 uv : OVR_FIRST_AVAILABLE_VERTEX_TEXCOORD_SEMANTIC;
+  float4 ormt : OVR_SECOND_AVAILABLE_VERTEX_TEXCOORD_SEMANTIC;
+  float4 texcoord2 : OVR_THIRD_AVAILABLE_VERTEX_TEXCOORD_SEMANTIC;
+  float4 texcoord3 : OVR_FOURTH_AVAILABLE_VERTEX_TEXCOORD_SEMANTIC;
+  fixed4 color : COLOR;
+  UNITY_VERTEX_INPUT_INSTANCE_ID
+};
+
+// vertex-to-fragment interpolation data
+// no lightmaps:
+#ifndef LIGHTMAP_ON
+// half-precision fragment shader registers:
+#ifdef UNITY_HALF_PRECISION_FRAGMENT_SHADER_REGISTERS
+#define FOG_COMBINED_WITH_WORLD_POS
+struct v2f {
+  UNITY_POSITION(pos);
+  float4 color: COLOR;
+  float4 uv: TEXCOORD0;
+  float4 propertiesMapUV : TEXCOORD1;
+  float4 effectsMapUV : TEXCOORD2;
+  float3 worldNormal : TEXCOORD3;
+  float4 worldPos : TEXCOORD4;
+#if UNITY_SHOULD_SAMPLE_SH
+  half3 sh : TEXCOORD5; // SH
+#endif
+  UNITY_LIGHTING_COORDS(6,7)
+#if SHADER_TARGET >= 30
+  float4 lmap : TEXCOORD8;
+#endif
+  float4 ormt : TEXCOORD9;
+  UNITY_VERTEX_INPUT_INSTANCE_ID
+  UNITY_VERTEX_OUTPUT_STEREO
+};
+#endif
+// high-precision fragment shader registers:
+#ifndef UNITY_HALF_PRECISION_FRAGMENT_SHADER_REGISTERS
+struct v2f {
+  UNITY_POSITION(pos);
+  float4 color: COLOR;
+  float4 uv: TEXCOORD0;
+  float4 propertiesMapUV : TEXCOORD1;
+  float4 effectsMapUV : TEXCOORD2;
+  float3 worldNormal : TEXCOORD3;
+  float3 worldPos : TEXCOORD4;
+#if UNITY_SHOULD_SAMPLE_SH
+  half3 sh : TEXCOORD5; // SH
+#endif
+  UNITY_FOG_COORDS(6)
+  UNITY_SHADOW_COORDS(7)
+#if SHADER_TARGET >= 30
+  float4 lmap : TEXCOORD8;
+#endif
+  float4 ormt : TEXCOORD9;
+  UNITY_VERTEX_INPUT_INSTANCE_ID
+  UNITY_VERTEX_OUTPUT_STEREO
+};
+#endif
+#endif
+// with lightmaps:
+#ifdef LIGHTMAP_ON
+// half-precision fragment shader registers:
+#ifdef UNITY_HALF_PRECISION_FRAGMENT_SHADER_REGISTERS
+#define FOG_COMBINED_WITH_WORLD_POS
+struct v2f {
+  UNITY_POSITION(pos);
+  float4 color: COLOR;
+  float4 uv: TEXCOORD0;
+  float4 propertiesMapUV : TEXCOORD1;
+  float4 effectsMapUV : TEXCOORD2;
+  float3 worldNormal : TEXCOORD3;
+  float4 worldPos : TEXCOORD4;
+  float4 lmap : TEXCOORD5;
+  float4 ormt : TEXCOORD9;
+  UNITY_LIGHTING_COORDS(6,7)
+  UNITY_VERTEX_INPUT_INSTANCE_ID
+  UNITY_VERTEX_OUTPUT_STEREO
+};
+#endif
+// high-precision fragment shader registers:
+#ifndef UNITY_HALF_PRECISION_FRAGMENT_SHADER_REGISTERS
+struct v2f {
+  UNITY_POSITION(pos);
+  float4 color: COLOR;
+  float4 uv: TEXCOORD0;
+  float4 propertiesMapUV : TEXCOORD1;
+  float4 effectsMapUV : TEXCOORD2;
+  float3 worldNormal : TEXCOORD3;
+  float3 worldPos : TEXCOORD4;
+  float4 lmap : TEXCOORD5;
+  UNITY_FOG_COORDS(6)
+  UNITY_SHADOW_COORDS(7)
+#ifdef DIRLIGHTMAP_COMBINED
+  float3 tSpace0 : TEXCOORD8;
+  float3 tSpace1 : TEXCOORD9;
+  float3 tSpace2 : TEXCOORD10;
+#endif
+  float4 ormt : TEXCOORD11;
+  UNITY_VERTEX_INPUT_INSTANCE_ID
+  UNITY_VERTEX_OUTPUT_STEREO
+};
+#endif
+#endif
+
+///////////////////////////////////////////////
+// End vertex to fragment interpolation data //
+///////////////////////////////////////////////
+
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/UnityLighting/AvatarUnityLightingTypes.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/UnityLighting/AvatarUnityLightingTypes.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..9fbad97a6e742271ced3bf7bbb99af48aec7f470
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/UnityLighting/AvatarUnityLightingTypes.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 9029ab33c8e0e4146ab2e24843740223
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/UnityLighting/AvatarUnitySurfaceFields.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/UnityLighting/AvatarUnitySurfaceFields.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..3ce1039f5a2c3741944e51170110fe19e146d0fa
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/UnityLighting/AvatarUnitySurfaceFields.cginc
@@ -0,0 +1,16 @@
+#ifndef AVATAR_UNITY_SURFACE_FIELDS_CGINC
+#define AVATAR_UNITY_SURFACE_FIELDS_CGINC
+
+#include "../AvatarCommon/AvatarShaderMacros.cginc"
+
+// These are all fields which are required by the Unity lighting scheme, NOT ones
+// that are required by different shaders.
+
+#define AVATAR_SHADER_UNITY_SURFACE_REQUIRED_FIELDS \
+    AVATAR_SHADER_SURFACE_SMOOTHNESS_FIELD_DECLARATION \
+    AVATAR_SHADER_SURFACE_NORMAL_FIELD_DECLARATION \
+    AVATAR_SHADER_SURFACE_ALBEDO_FIELD_DECLARATION \
+    AVATAR_SHADER_SURFACE_METALLIC_FIELD_DECLARATION \
+    AVATAR_SHADER_SURFACE_OCCLUSION_FIELD_DECLARATION
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/UnityLighting/AvatarUnitySurfaceFields.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/UnityLighting/AvatarUnitySurfaceFields.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..c4b05757c559ba56aeae210c276e4b1f5390d8bb
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/UnityLighting/AvatarUnitySurfaceFields.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 001f4f244cbba5744beddaaa419eb67a
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Unlit.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Unlit.meta
new file mode 100644
index 0000000000000000000000000000000000000000..126d5762e8ebd2f2585788339d96045fe26d6981
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Unlit.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: c4bb2a669e68d1c4fbff04178cc249ae
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Unlit/UnlitTypes.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Unlit/UnlitTypes.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..30f9c3ce2b16628a6a494c8f56bba27ce2bb8a27
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Unlit/UnlitTypes.cginc
@@ -0,0 +1,31 @@
+#ifndef AVATAR_UNLIT_TYPES_CGINC
+#define AVATAR_UNLIT_TYPES_CGINC
+
+#include "UnityCG.cginc"
+
+// vertex input data
+struct appdata {
+  float4 vertex : POSITION;
+  float4 tangent : TANGENT;
+  float3 normal : NORMAL;
+  float4 uv : TEXCOORD0;
+  fixed4 color : COLOR;
+  float4 ormt : TEXCOORD1;
+  UNITY_VERTEX_INPUT_INSTANCE_ID
+};
+
+// vertex-to-fragment interpolation data
+struct v2f {
+  UNITY_POSITION(pos);
+  fixed4 color : COLOR;
+  float4 uv : TEXCOORD0;
+  float4 propertiesMapUV : TEXCOORD1;
+  float4 effectsMapUV : TEXCOORD2;
+  float3 worldNormal : TEXCOORD3;
+  float3 worldPos : TEXCOORD4;
+  float4 ormt : TEXCOORD5;
+  UNITY_VERTEX_INPUT_INSTANCE_ID
+  UNITY_VERTEX_OUTPUT_STEREO
+};
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Unlit/UnlitTypes.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Unlit/UnlitTypes.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..26db5b4e865947ff85edb165f094707496b5f019
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Unlit/UnlitTypes.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 197d560332eca4149bbdc2df23dfbdd2
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Unlit/UnlitVertFrag.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Unlit/UnlitVertFrag.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..a3afea1c543ba6492e74c1ec31a67175b6fdbd10
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Unlit/UnlitVertFrag.cginc
@@ -0,0 +1,60 @@
+#ifndef AVATAR_UNLIT_VERT_FRAG_CGINC
+#define AVATAR_UNLIT_VERT_FRAG_CGINC
+
+#include "UnlitTypes.cginc"
+
+///////////////////
+// vertex shader //
+///////////////////
+
+v2f AvatarShaderUnlitVertProgramInit(appdata v) {
+  UNITY_SETUP_INSTANCE_ID(v);
+  v2f o;
+  UNITY_INITIALIZE_OUTPUT(v2f,o);
+  UNITY_TRANSFER_INSTANCE_ID(v,o);
+  UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
+
+  return o;
+}
+
+void AvatarShaderUnlitVertProgramTransform(appdata v, inout v2f o) {
+  o.pos = UnityObjectToClipPos(v.vertex);
+  o.color = v.color;
+  o.uv = v.uv;
+
+  o.propertiesMapUV.xy = v.uv.xy;
+  o.effectsMapUV.xy = v.uv.xy;
+  o.ormt = v.ormt;
+}
+
+#define GENERATE_AVATAR_SHADER_UNLIT_DEFAULT_VERT_PROGRAM(VertProgName) \
+  v2f VertProgName(appdata v) { \
+    v2f o = AvatarShaderUnlitVertProgramInit(v); \
+    AvatarShaderUnlitVertProgramTransform(v, o); \
+    \
+    return o; \
+  }
+
+#define GENERATE_AVATAR_SHADER_UNLIT_VERT_PROGRAM(VertProgName, CustomVertFunc) \
+  v2f VertProgName(appdata v) { \
+    v2f o = AvatarShaderUnlitVertProgramInit(v); \
+    \
+    AvatarShaderUnlitVertProgramTransform(v, o); \
+    \
+    CustomVertFunc(v, o); \
+    \
+    return o; \
+  }
+
+/////////////////////
+// fragment shader //
+/////////////////////
+
+#define GENERATE_AVATAR_SHADER_UNLIT_FRAG_PROGRAM(FragProgName, ColorFunc) \
+  float4 FragProgName(v2f IN) : SV_Target { \
+    UNITY_SETUP_INSTANCE_ID(IN); \
+    \
+    return ColorFunc(IN); \
+  }
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Unlit/UnlitVertFrag.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Unlit/UnlitVertFrag.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..97e80131ec7af0722555355f4523350ad8f5f783
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Horizon/Unlit/UnlitVertFrag.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: e2afb3a4539e5cf4f9dcded02edd964f
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Human.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Human.meta
new file mode 100644
index 0000000000000000000000000000000000000000..42dc5277e329ae52d32495c6cc9588dfcc05bf6b
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Human.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 91fcf9d511fe874419cddf25d9886b65
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Human/Avatar-Human.shader b/Assets/Oculus/Avatar2/Example/Common/Shaders/Human/Avatar-Human.shader
new file mode 100644
index 0000000000000000000000000000000000000000..a40996698e4e43de545e0f0557802319a76d7d70
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Human/Avatar-Human.shader
@@ -0,0 +1,273 @@
+// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
+
+// based off metallic_roughness.frag/vert from Khronos
+
+Shader "Avatar/Human"
+{
+    Properties
+    {
+        // NOTE: This texture can be visualized in the Unity editor, just expand in inspector and manually change "Dimension" to "2D" on top line
+        u_AttributeTexture("Vertex Attribute map", 3D) = "white" {}
+
+        u_NormalSampler("Normal map", 2D) = "white" {}
+        u_NormalScale("Normal map scale", Float) = 1.0
+        u_NormalUVSet("Normal UV Set", Int) = 0
+
+        u_EmissiveSampler("Emissive map", 2D) = "black" {}
+        u_EmissiveSet("Emissive UV Set", Int) = 0
+        u_EmissiveFactor("Emissive factor", Color) = (1, 1, 1, 1)
+
+        u_OcclusionSampler("Occlusion map", 2D) = "white" {}
+        u_OcclusionSet("Occlusion UV Set", Int) = 0
+        u_OcclusionStrength("Occlusion scale", Float) = 1.0
+
+        u_BaseColorSampler("Base Color", 2D) = "white" {}
+        u_BaseColorUVSet("Base Color UV Set", Int) = 0
+
+        u_BaseColorFactor("Base Color factor", Color) = (1, 1, 1, 1)
+
+        u_MetallicRoughnessSampler("Metallic Roughness", 2D) = "white" {}
+        u_MetallicRoughnessUVSet("Metallic Roughness UV Set", Int) = 0
+
+        u_MetallicFactor("Metallic Factor", Range(0, 2)) = 1.0
+        u_RoughnessFactor("Roughness Factor", Range(0, 2)) = 1.0
+        u_F0Factor("F0 Factor", Range(0, 2)) = 1.0
+        u_OcclusionStrength("Occlusion Strength", Range(0, 2)) = 1.0
+        u_ThicknessFactor("Thickness Factor", Range(0, 2)) = 1.0
+
+        u_SubsurfaceColor("Sub-Surface Color", Color) = (0, 0, 0, 1)
+        u_SkinORMFactor("Skin-only ORM Factor", Vector) = (1, 1, 1)
+
+// AVATAR SDK BEGIN
+            u_EyeGlintFactor("Eye Glint Factor", Range(0, 2.0)) = 1.0
+            u_EyeGlintColorFactor("Eye Glint Color Factor", Range(0, 2.0)) = 1.0
+
+            u_DiffuseSmoothingFactor("Diffuse Smoothing Factor", Range(0, 8)) = 1.0
+        // this factor allows for smooth diffuse light falloff similar to Babylon.js.
+        // It is not originally in the Khronos shader.
+// AVATAR SDK END
+
+
+        [ShowIfKeyword(_PALETTIZATION_SINGLE_RAMP, _PALETTIZATION_TWO_RAMP)]
+        _ColorRamp0("Color Ramp 1", 2D) = "white" {}
+        [ShowIfKeyword(_PALETTIZATION_TWO_RAMP)]
+        _ColorRamp1("Color Ramp 2", 2D) = "white" {}
+
+
+        // These should not exist here, since they should be in global shader scope and handeled by an external manager:
+        //
+        //u_DiffuseEnvSampler("IBL Diffuse Cubemap Texture", Cube) = "white" {}
+        //u_MipCount("IBL Diffuse Texture Mip Count", Int) = 10
+        //u_SpecularEnvSampler ("IBL Specular Cubemap Texture", Cube) = "white" {}
+        //u_brdfLUT ("BRDF LUT Texture", 2D) = "Assets/Oculus/Avatar2/Example/Scenes/BRDF_LUT" {}
+
+        // DEBUG_MODES: Uncomment to use Debug modes, must match the multi_compile defined below
+        // [KeywordEnum(None, BaseColor, Occlusion, Roughness, Metallic, Thickness, Normal, Normal Map, Emissive, View, Punctual, Punctual Specular, Punctual Diffuse, IBL, IBL Specular, IBL Diffuse, SH, No Tone Map, SubSurface Scattering)] Debug("Debug Render", Float) = 0
+
+        // LIGHTING_MODES: Uncomment to use Lighting modes, must match the multi_compile defined below
+        // [KeywordEnum(IBL plus Punctual, SH plus Punctual, IBL Only, SH Only, Punctual Only)] Lighting_Mode("Lighting Mode", Float) = 0
+
+        // TONEMAP_MODES: Uncomment to use ToneMapping modes, must match the multi_compile defined below
+        // [KeywordEnum(None, Uncharted, HejlRichard, ACES)] ToneMap("Tone Mapping", Float) = 0
+
+        // BRDFLUT_MODES: Uncomment to use BRDF Look up Table (LUT) modes, must match the multi_compile defined below
+        [KeywordEnum(On, Off)] BRDF_LUT_Mode("BRDF LUT Mode", Float) = 0
+          
+        // MATERIAL_MODES: Uncomment to use Material modes, must match the multi_compile defined below
+        [KeywordEnum(Texture, Vertex)] Material_Mode("Material Mode", Float) = 0
+
+        // Cull mode (Off, Front, Back)
+        [Enum(UnityEngine.Rendering.CullMode)] _Cull("Cull", Float) = 2
+
+    }
+    SubShader
+    {
+        Tags { "RenderType" = "Opaque" }
+        LOD 100
+
+        Cull[_Cull]
+
+        // Old Unity Render Pipeline, Single Light
+        Pass
+        {
+            // Commenting out this lightmode because it breaks URP compilation.
+            // Confirmed that this won't break non-URP projects.
+            // Tags{"LightMode" = "ForwardBase"}
+
+            CGPROGRAM
+            #pragma vertex vert
+            #pragma fragment frag
+
+            #pragma multi_compile DIRECTIONAL POINT SPOT
+
+            /////////////////////////////////////////////////////////
+            // PRAGMAS: Pragmas cannot exist in cginc files so include them here
+
+            // DEBUG_MODES: Must match the Properties specified above
+            // #pragma multi_compile __ DEBUG_BASECOLOR DEBUG_OCCLUSION DEBUG_ROUGHNESS DEBUG_METALLIC DEBUG_THICKNESS DEBUG_NORMAL DEBUG_NORMAL_MAP DEBUG_EMISSIVE DEBUG_VIEW DEBUG_PUNCTUAL DEBUG_PUNCTUAL_SPECULAR DEBUG_PUNCTUAL_DIFFUSE DEBUG_IBL DEBUG_IBL_SPECULAR DEBUG_IBL_DIFFUSE DEBUG_SH DEBUG_NO_TONE_MAP DEBUG_SUBSURFACE_SCATTERING
+            #define DEBUG_LIGHTING (defined(DEBUG_METALLIC) || defined(DEBUG_THICKNESS) || defined(DEBUG_ROUGHNESS) || defined(DEBUG_NORMAL) || defined(DEBUG_NORMAL_MAP) || defined(DEBUG_BASECOLOR) || defined(DEBUG_OCCLUSION) || defined(DEBUG_EMISSIVE) || defined(DEBUG_F0) || defined(DEBUG_ALPHA) || defined(DEBUG_VIEW) || defined(DEBUG_PUNCTUAL) || defined(DEBUG_PUNCTUAL_SPECULAR) || defined(DEBUG_PUNCTUAL_DIFFUSE) || defined(DEBUG_IBL) || defined(DEBUG_IBL_SPECULAR) || defined(DEBUG_IBL_DIFFUSE) || defined(DEBUG_SH) || defined(DEBUG_NO_TONE_MAP) || defined(DEBUG_SUBSURFACE_SCATTERING))
+
+            // LIGHTING_MODES: this is done to match the options of the commercial GLTF viewers
+            // #pragma multi_compile LIGHTING_MODE_IBL_PLUS_PUNCTUAL LIGHTING_MODE_SH_PLUS_PUNCTUAL LIGHTING_MODE_IBL_ONLY LIGHTING_MODE_SH_ONLY LIGHTING_MODE_PUNCTUAL_ONLY
+            #define LIGHTING_MODE_IBL_PLUS_PUNCTUAL
+
+            // TONEMAP_MODES: Must match the Properties specified above
+            // #pragma multi_compile __ TONEMAP_UNCHARTED TONEMAP_HEJLRICHARD TONEMAP_ACES
+
+            // BRDF_LUT_MODES: Must match the Properties specified above
+            #pragma multi_compile BRDF_LUT_MODE_ON BRDF_LUT_MODE_OFF
+      
+            // MATERIAL_MODES: Must match the Properties specified above
+            #pragma multi_compile MATERIAL_MODE_TEXTURE MATERIAL_MODE_VERTEX
+
+            #pragma target 4.0 // necessary for use of SV_VertexID
+
+            // Palettization modes for Avatar FBX tool
+            #pragma multi_compile __ _PALETTIZATION_SINGLE_RAMP _PALETTIZATION_TWO_RAMP
+
+            // In Avatar SDK we ALWAYS use the ORM property map extension. Indicate that here:
+            #define USE_ORM_EXTENSION
+
+            // Turn on IBL only here, not for the other additive passes
+            #if defined(LIGHTING_MODE_IBL_PLUS_PUNCTUAL) || defined(LIGHTING_MODE_IBL_ONLY)
+            #define USE_IBL // IBL only gets applied here in the first base pass
+            #define USE_IBL_DIFFUSE
+            #define USE_IBL_SPECULAR
+            #endif
+            #if defined(LIGHTING_MODE_SH_PLUS_PUNCTUAL) || defined(LIGHTING_MODE_SH_ONLY)
+            // per vertex is faster than per pixel, and almost indistinguishable for our purpose
+            #define USE_SH_PER_VERTEX
+            // #define USE_SH_PER_PIXEL
+            #endif
+
+            // Include the Vertex Shader HERE
+            #include "precision.hlsl"
+            #include "common-vert.hlsl"
+
+            // Include the Pixel Shader HERE
+            #include "precision.hlsl"
+            #include "fb_sphere_map.hlsl"
+            #include "UnityLightingCommon.cginc" // for _LightColor0, replace with hlsl when we can
+            #include "common-frag.hlsl"
+
+            ENDCG
+        }
+
+        // Old Unity Render Pipeline, Up to 4 Additive Lights
+        Pass
+        {
+            Tags{"LightMode" = "ForwardAdd"}
+
+            Blend One One
+            ZWrite Off
+
+            CGPROGRAM
+            #pragma vertex vert
+            #pragma fragment frag
+
+            #pragma multi_compile DIRECTIONAL POINT SPOT
+
+            /////////////////////////////////////////////////////////
+            // PRAGMAS: Pragmas cannot exist in cginc files so include them here
+
+            // DEBUG_MODES: Must match the Properties specified above
+            // #pragma multi_compile __ DEBUG_BASECOLOR DEBUG_OCCLUSION DEBUG_ROUGHNESS DEBUG_METALLIC DEBUG_THICKNESS DEBUG_NORMAL DEBUG_NORMAL_MAP DEBUG_EMISSIVE DEBUG_VIEW DEBUG_PUNCTUAL DEBUG_PUNCTUAL_SPECULAR DEBUG_PUNCTUAL_DIFFUSE DEBUG_IBL DEBUG_IBL_SPECULAR DEBUG_IBL_DIFFUSE DEBUG_SH DEBUG_NO_TONE_MAP DEBUG_SUBSURFACE_SCATTERING
+            #define DEBUG_LIGHTING (defined(DEBUG_METALLIC) || defined(DEBUG_THICKNESS) || defined(DEBUG_ROUGHNESS) || defined(DEBUG_NORMAL) || defined(DEBUG_NORMAL_MAP) || defined(DEBUG_BASECOLOR) || defined(DEBUG_OCCLUSION) || defined(DEBUG_EMISSIVE) || defined(DEBUG_F0) || defined(DEBUG_ALPHA) || defined(DEBUG_VIEW) || defined(DEBUG_PUNCTUAL) || defined(DEBUG_PUNCTUAL_SPECULAR) || defined(DEBUG_PUNCTUAL_DIFFUSE) || defined(DEBUG_IBL) || defined(DEBUG_IBL_SPECULAR) || defined(DEBUG_IBL_DIFFUSE) || defined(DEBUG_SH) || defined(DEBUG_NO_TONE_MAP) || defined(DEBUG_SUBSURFACE_SCATTERING))
+
+            // LIGHTING_MODES: this is done to match the options of the commercial GLTF viewers
+            // #pragma multi_compile __ LIGHTING_MODE_SH_ONLY LIGHTING_MODE_IBL_ONLY
+
+            // VERTEX COLORS: activate this to transmit lo-fi model vert colors or sub mesh information in the alpha channel
+    //            #pragma shader_feature HAS_VERTEX_COLOR_float4
+            #define HAS_VERTEX_COLOR_float4
+
+            // TONEMAP_MODES: Must match the Properties specified above
+            // #pragma multi_compile __ TONEMAP_UNCHARTED TONEMAP_HEJLRICHARD TONEMAP_ACES
+
+            // BRDF_LUT_MODES: Must match the Properties specified above
+            #pragma multi_compile BRDF_LUT_MODE_ON BRDF_LUT_MODE_OFF
+          
+            // MATERIAL_MODES: Must match the Properties specified above
+            #pragma multi_compile MATERIAL_MODE_TEXTURE MATERIAL_MODE_VERTEX
+
+            #pragma target 4.0 // necessary for use of SV_VertexID
+
+            // Include the Vertex Shader HERE
+            #include "precision.hlsl"
+            #include "common-vert.hlsl"
+
+            // Include the Pixel Shader HERE
+            #include "precision.hlsl"
+            #include "fb_sphere_map.hlsl"
+            #include "UnityLightingCommon.cginc" // for _LightColor0, replace with hlsl when we can
+            #include "common-frag.hlsl"
+
+            ENDCG
+        }
+
+        // Universal Render Pipeline (URP)
+        Pass
+        {
+            Tags{"LightMode" = "UniversalForward"}
+
+            CGPROGRAM
+            #pragma vertex vert
+            #pragma fragment frag
+
+            /////////////////////////////////////////////////////////
+            // PRAGMAS: Pragmas cannot exist in cginc files so include them here
+
+            // DEBUG_MODES: Must match the Properties specified above
+            // #pragma multi_compile __ DEBUG_BASECOLOR DEBUG_OCCLUSION DEBUG_ROUGHNESS DEBUG_METALLIC DEBUG_THICKNESS DEBUG_NORMAL DEBUG_NORMAL_MAP DEBUG_EMISSIVE DEBUG_VIEW DEBUG_PUNCTUAL DEBUG_PUNCTUAL_SPECULAR DEBUG_PUNCTUAL_DIFFUSE DEBUG_IBL DEBUG_IBL_SPECULAR DEBUG_IBL_DIFFUSE DEBUG_SH DEBUG_NO_TONE_MAP DEBUG_SUBSURFACE_SCATTERING
+            #define DEBUG_LIGHTING (defined(DEBUG_METALLIC) || defined(DEBUG_THICKNESS) || defined(DEBUG_ROUGHNESS) || defined(DEBUG_NORMAL) || defined(DEBUG_NORMAL_MAP) || defined(DEBUG_BASECOLOR) || defined(DEBUG_OCCLUSION) || defined(DEBUG_EMISSIVE) || defined(DEBUG_F0) || defined(DEBUG_ALPHA) || defined(DEBUG_VIEW) || defined(DEBUG_PUNCTUAL) || defined(DEBUG_PUNCTUAL_SPECULAR) || defined(DEBUG_PUNCTUAL_DIFFUSE) || defined(DEBUG_IBL) || defined(DEBUG_IBL_SPECULAR) || defined(DEBUG_IBL_DIFFUSE) || defined(DEBUG_SH) || defined(DEBUG_NO_TONE_MAP) || defined(DEBUG_SUBSURFACE_SCATTERING))
+
+            // LIGHTING_MODES: this is done to match the options of the commercial GLTF viewers
+            // #pragma multi_compile LIGHTING_MODE_IBL_PLUS_PUNCTUAL LIGHTING_MODE_SH_PLUS_PUNCTUAL LIGHTING_MODE_IBL_ONLY LIGHTING_MODE_SH_ONLY LIGHTING_MODE_PUNCTUAL_ONLY
+            #define LIGHTING_MODE_IBL_PLUS_PUNCTUAL
+
+            // VERTEX COLORS: activate this to transmit lo-fi model vert colors or sub mesh information in the alpha channel
+    //            #pragma shader_feature HAS_VERTEX_COLOR_float4
+            #define HAS_VERTEX_COLOR_float4
+
+            // TONEMAP_MODES: Must match the Properties specified above
+            // #pragma multi_compile __ TONEMAP_UNCHARTED TONEMAP_HEJLRICHARD TONEMAP_ACES
+
+            // BRDF_LUT_MODES: Must match the Properties specified above
+            #pragma multi_compile BRDF_LUT_MODE_ON BRDF_LUT_MODE_OFF
+          
+            // MATERIAL_MODES: Must match the Properties specified above
+            #pragma multi_compile MATERIAL_MODE_TEXTURE MATERIAL_MODE_VERTEX
+
+            #pragma target 4.0 // necessary for use of SV_VertexID
+
+            // Palettization modes for Avatar FBX tool
+            #pragma multi_compile __ _PALETTIZATION_SINGLE_RAMP _PALETTIZATION_TWO_RAMP
+
+            // In Avatar SDK we ALWAYS use the ORM property map extension. Indicate that here:
+            #define USE_ORM_EXTENSION
+
+            // Turn on IBL only here, not for the other additive passes
+            #if defined(LIGHTING_MODE_IBL_PLUS_PUNCTUAL) || defined(LIGHTING_MODE_IBL_ONLY)
+            #define USE_IBL // IBL only gets applied here in the first base pass
+            #define USE_IBL_DIFFUSE
+            #define USE_IBL_SPECULAR
+            #endif
+            #if defined(LIGHTING_MODE_SH_PLUS_PUNCTUAL) || defined(LIGHTING_MODE_SH_ONLY)
+            // per vertex is faster than per pixel, and almost indistinguishable for our purpose
+            #define USE_SH_PER_VERTEX
+            #endif
+
+            // Include the Vertex Shader HERE
+            #include "precision.hlsl"
+            #include "common-vert.hlsl"
+
+            // Include the Pixel Shader HERE
+            #include "precision.hlsl"
+            #include "fb_sphere_map.hlsl"
+            #include "UnityLightingCommon.cginc" // for _LightColor0, replace with hlsl when we can
+            #include "common-frag.hlsl"
+
+            ENDCG
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Human/Avatar-Human.shader.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Human/Avatar-Human.shader.meta
new file mode 100644
index 0000000000000000000000000000000000000000..2f3ab8ebb886f86d78c448bb9dbe7ae6831cc6f6
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Human/Avatar-Human.shader.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 48f513db188320345a43a70bbb34b7fe
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Human/UnityGIAvatar.hlsl b/Assets/Oculus/Avatar2/Example/Common/Shaders/Human/UnityGIAvatar.hlsl
new file mode 100644
index 0000000000000000000000000000000000000000..53d3c612f452b626ac5c56ac785e89e1c3ae340e
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Human/UnityGIAvatar.hlsl
@@ -0,0 +1,208 @@
+#include "UnityImageBasedLighting.cginc"
+
+struct AvatarShaderLight {
+  half3 direction;
+  half3 color;
+};
+
+struct AvatarShaderIndirect {
+  half3 diffuse;
+  half3 specular;
+};
+
+struct AvatarShaderGlobalIllumination {
+  AvatarShaderLight light;
+  AvatarShaderIndirect indirect;
+};
+
+//-----------------------------------------------------------------------------------------
+// The following use UnityStandardUtils.cginc as a reference, as of 2020.3.7
+
+half3 AvatarShadeSHPerPixel (half3 normal, half3 ambient, float3 worldPos)
+{
+    half3 ambient_contrib = 0.0;
+
+    #if defined(USE_SH_PER_PIXEL)
+        // Completely per-pixel
+        ambient_contrib = SHEvalLinearL0L1(half4(normal, 1.0));
+        ambient_contrib += SHEvalLinearL2(half4(normal, 1.0));
+        ambient += max(half3(0, 0, 0), ambient_contrib);
+
+        #ifdef UNITY_COLORSPACE_GAMMA
+            ambient = LinearToGammaSpace(ambient);
+        #endif
+    #else
+        // Completely per-vertex
+        // nothing to do here. Gamma conversion on ambient from SH takes place in the vertex shader, see ShadeSHPerVertex.
+    #endif
+
+    return ambient;
+}
+
+inline float3 AvatarBoxProjectedCubemapDirection(float3 worldRefl, float3 worldPos, float4 cubemapCenter, float4 boxMin, float4 boxMax) {
+  // Do we have a valid reflection probe?
+  UNITY_BRANCH
+  if (cubemapCenter.w > 0.0) {
+    float3 nrdir = normalize(worldRefl);
+
+    float3 rbmax = (boxMax.xyz - worldPos) / nrdir;
+    float3 rbmin = (boxMin.xyz - worldPos) / nrdir;
+
+    float3 rbminmax = (nrdir > 0.0f) ? rbmax : rbmin;
+
+    float fa = min(min(rbminmax.x, rbminmax.y), rbminmax.z);
+
+    worldPos -= cubemapCenter.xyz;
+    worldRefl = worldPos + nrdir * fa;
+  }
+  return worldRefl;
+}
+
+//-----------------------------------------------------------------------------------------
+// The following use UnityGlobalIllumination.cginc as a reference, as of 2020.3.7
+
+inline void AvatarResetUnityLight(out UnityLight outLight) {
+  outLight.color = half3(0, 0, 0);
+  outLight.dir = half3(0, 1, 0); // Irrelevant direction, just not null
+  outLight.ndotl = 0; // Not used
+}
+
+inline void AvatarResetUnityGI(out UnityGI outGI) {
+  AvatarResetUnityLight(outGI.light);
+  outGI.indirect.diffuse = 0;
+  outGI.indirect.specular = 0;
+}
+
+inline UnityGI AvatarUnityGI_Base(UnityGIInput data, half occlusion, half3 normalWorld) {
+  UnityGI o_gi;
+  AvatarResetUnityGI(o_gi);
+
+  o_gi.light = data.light;
+  o_gi.light.color *= data.atten;
+
+#if defined(USE_SH_PER_VERTEX) || defined(USE_SH_PER_PIXEL)
+  o_gi.indirect.diffuse = AvatarShadeSHPerPixel(normalWorld, data.ambient, data.worldPos);
+#endif
+
+  o_gi.indirect.diffuse *= occlusion;
+  return o_gi;
+}
+
+inline half3 AvatarUnityGI_IndirectSpecular(UnityGIInput data, half occlusion, Unity_GlossyEnvironmentData glossIn)
+{
+    half3 specular;
+
+    #ifdef UNITY_SPECCUBE_BOX_PROJECTION
+        // we will tweak reflUVW in glossIn directly (as we pass it to Unity_GlossyEnvironment twice for probe0 and probe1), so keep original to pass into AvatarBoxProjectedCubemapDirection
+        half3 originalReflUVW = glossIn.reflUVW;
+        glossIn.reflUVW = AvatarBoxProjectedCubemapDirection (originalReflUVW, data.worldPos, data.probePosition[0], data.boxMin[0], data.boxMax[0]);
+    #endif
+
+    #ifdef _GLOSSYREFLECTIONS_OFF
+        specular = unity_IndirectSpecColor.rgb;
+    #else
+        half3 env0 = Unity_GlossyEnvironment (UNITY_PASS_TEXCUBE(unity_SpecCube0), data.probeHDR[0], glossIn);
+        specular = env0;
+    #endif
+
+    return specular * occlusion;
+}
+
+
+inline UnityGI AvatarUnityGlobalIllumination(
+    UnityGIInput data,
+    half occlusion,
+    half3 normalWorld,
+    Unity_GlossyEnvironmentData glossIn) {
+  UnityGI o_gi = AvatarUnityGI_Base(data, occlusion, normalWorld);
+  o_gi.indirect.specular = AvatarUnityGI_IndirectSpecular(data, occlusion, glossIn);
+  return o_gi;
+}
+
+//-----------------------------------------------------------------------------------------
+// Finally our entry points into the Unity access functions come here:
+
+UnityGIInput GetGlobalIlluminationInput(
+    interpolators IN,
+    fixed3 lightDir,
+    float3 worldPos,
+    float3 worldViewDir,
+    fixed attenuation) {
+  // Setup lighting environment
+  UnityGI gi;
+  UNITY_INITIALIZE_OUTPUT(UnityGI, gi);
+
+  gi.indirect.diffuse = 0;
+  gi.indirect.specular = 0;
+  gi.light.color = _LightColor0.rgb;
+  gi.light.dir = lightDir;
+
+  // Call GI (lightmaps/SH/reflections) lighting function
+  UnityGIInput giInput;
+  UNITY_INITIALIZE_OUTPUT(UnityGIInput, giInput);
+  giInput.light = gi.light;
+  giInput.worldPos = worldPos;
+  giInput.worldViewDir = worldViewDir;
+  giInput.atten = attenuation;
+
+#if defined(LIGHTMAP_ON) || defined(DYNAMICLIGHTMAP_ON)
+  giInput.lightmapUV = IN.lmap;
+#else
+  giInput.lightmapUV = 0.0;
+#endif
+
+#if defined(USE_SH_PER_VERTEX)
+  giInput.ambient = IN.sh;
+#else
+  giInput.ambient.rgb = 0.0;
+#endif
+
+  giInput.probeHDR[0] = unity_SpecCube0_HDR;
+  giInput.probeHDR[1] = unity_SpecCube1_HDR;
+#if defined(UNITY_SPECCUBE_BLENDING) || defined(UNITY_SPECCUBE_BOX_PROJECTION)
+  giInput.boxMin[0] = unity_SpecCube0_BoxMin; // .w holds lerp value for blending
+#endif
+
+#ifdef UNITY_SPECCUBE_BOX_PROJECTION
+  giInput.boxMax[0] = unity_SpecCube0_BoxMax;
+  giInput.probePosition[0] = unity_SpecCube0_ProbePosition;
+  giInput.boxMax[1] = unity_SpecCube1_BoxMax;
+  giInput.boxMin[1] = unity_SpecCube1_BoxMin;
+  giInput.probePosition[1] = unity_SpecCube1_ProbePosition;
+#endif
+
+  return giInput;
+}
+
+AvatarShaderGlobalIllumination GetGlobalIllumination(
+    UnityGIInput giInput,
+    half smoothness,
+    half metallic,
+    half occlusion,
+    half3 albedo,
+    half3 normal) {
+  Unity_GlossyEnvironmentData g = UnityGlossyEnvironmentSetup(
+      smoothness,
+      giInput.worldViewDir,
+      normal,
+      lerp(unity_ColorSpaceDielectricSpec.rgb, albedo, metallic));
+
+  UnityGI gi;
+  UNITY_INITIALIZE_OUTPUT(UnityGI, gi);
+  half ambientOcclusion = occlusion;
+  gi = AvatarUnityGlobalIllumination(giInput, ambientOcclusion, normal, g);
+
+  AvatarShaderLight light;
+  light.direction = gi.light.dir;
+  light.color = gi.light.color;
+
+  AvatarShaderIndirect indirect;
+  indirect.diffuse = gi.indirect.diffuse;
+  indirect.specular = gi.indirect.specular;
+
+  AvatarShaderGlobalIllumination avatarGI;
+  avatarGI.light = light;
+  avatarGI.indirect = indirect;
+
+  return avatarGI;
+}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Human/UnityGIAvatar.hlsl.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Human/UnityGIAvatar.hlsl.meta
new file mode 100644
index 0000000000000000000000000000000000000000..8a4d7b3eda8481b5bffe668ba253a166f148e1f5
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Human/UnityGIAvatar.hlsl.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: e69949a5ed0d90d4fb01afddd512e38a
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  preprocessorOverride: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Human/common-frag.hlsl b/Assets/Oculus/Avatar2/Example/Common/Shaders/Human/common-frag.hlsl
new file mode 100644
index 0000000000000000000000000000000000000000..32d60d85da86aae51e791bedb52ca966f1e6a54f
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Human/common-frag.hlsl
@@ -0,0 +1,971 @@
+/////////////////////////////////////////////////////////
+// Unity specific defines for Avatar SDK:
+
+#include "UnityCG.cginc"
+#include "UnityLightingCommon.cginc"
+#include "UnityGIAvatar.hlsl"
+#include "../../../../Scripts/ShaderUtils/AvatarSubmesh.cginc"
+
+#define MATERIAL_METALLICROUGHNESS
+#ifdef MATERIAL_MODE_TEXTURE
+#define HAS_BASE_COLOR_MAP
+#define HAS_OCCLUSION_MAP
+#define HAS_COMBINED_OCCLUSION_MAP
+#define HAS_THICKNESS_MAP
+#define HAS_COMBINED_THICKNESS_MAP
+#define HAS_METALLIC_ROUGHNESS_MAP
+#endif
+
+// this is expensive Quest so ensure these textures are present on a high quality profile
+//#define HAS_NORMAL_MAP
+//#define HAS_EMISSIVE_MAP
+
+#define HAS_NORMAL_VEC3
+// #define HAS_SPECULAR_GLOSSINESS_MAP
+// #define HAS_DIFFUSE_MAP
+// #define HAS_SPECULAR_GLOSSINESS_MAP
+
+// Turn on Punctual here
+
+#if !defined(LIGHTING_MODE_SH_ONLY) && !defined(LIGHTING_MODE_IBL_ONLY)
+#define USE_PUNCTUAL
+#endif
+
+// Turn on the Composite methods here:
+#define FB_MATERIALS_SKIN
+#define EYE_GLINTS
+#define EYE_GLINTS_BEHIND
+
+#ifdef BRDF_LUT_MODE_ON
+#define USE_IBL_BRDF_LUT
+#endif
+
+#ifdef USE_IBL
+#define FLIP_IBL_WINDING // the z axis in Unity is backwards from Khronos
+#endif
+
+#define USE_HDR
+#define USE_IBL_SPECULAR_LOD
+
+// ALPHAMODE_MASK
+#define ALPHAMODE_OPAQUE
+
+// Unity Forward additive lighting adds light in multiple passes, so one light per pass is fine
+#define LIGHT_COUNT 1
+
+//This should be moved to a common path so that more than one shader can use it.
+//#include "AvatarFbxReview.cginc"
+
+/////////////////////////////////////////////////////////
+// Original ported code from pbr.frag:
+
+#if defined(FB_MATERIALS_SKIN)
+#define SKIN 1
+#endif
+
+#if defined(FB_MATERIALS_HAIR_STRAIGHT)
+#define HAIR_STRAIGHT 1
+#endif
+
+#if defined(FB_MATERIALS_HAIR_COILY)
+#define HAIR_COILY 1
+#endif
+
+// -----------------------------------------------------------------------------------
+// Constants
+// -----------------------------------------------------------------------------------
+static const float_t GAMMA = 2.2;
+static const float_t INV_GAMMA = 1.0 / GAMMA;
+static const float_t PI = 3.141592653589793;
+static const float_t PI_2 = 2.0 * PI;
+
+// -----------------------------------------------------------------------------------
+// STRUCTURES
+// -----------------------------------------------------------------------------------
+
+// KHR_lights_punctual extension.
+// see https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_lights_punctual
+static const int LightType_Directional = 0;
+static const int LightType_Point = 1;
+static const int LightType_Spot = 2;
+
+struct Light {
+    vec3_t direction;
+    float_t range;
+    vec3_t color;
+    float_t intensity;
+    vec3_t position;
+    float_t innerConeCos;
+    float_t outerConeCos;
+    int type; // LightType_
+};
+
+// NOTE: instead use OvrDefaultAppdata
+// input to vertex shader
+//struct meshdata {
+//    vec4_t vertex;
+//    vec4_t color;
+//    vec3_t normal;
+//    vec3_t tangent;
+//    vec2_t texcoord;
+//    vec2_t texcoord1;
+//};
+
+// -----------------------------------------------------------------------------------
+// uniforms
+// -----------------------------------------------------------------------------------
+
+// camera
+uniform vec3_t u_Camera; // camera position in world.
+
+// Only used in the former vert shader.
+// Now we macros from Avatar Custom and Unity functions like UnityObjectToWorldNormal().
+// matricies
+// uniform float4x4 u_ViewProjectionMatrix;
+// uniform float4x4 u_ModelMatrix;
+// uniform float4x4 u_NormalMatrix;
+
+// Replaced by Unity lights...
+// LIGHT_COUNT +1 in case LIGHT_COUNT = 0
+//layout (std140) uniform u_LightArray {
+//    Light u_Lights[LIGHT_COUNT + 1];
+//};
+
+// Originally require ALPHAMODE_MASK to work
+// base GLTF
+uniform float_t u_AlphaCutoff;
+
+#ifdef HAS_NORMAL_MAP
+uniform sampler2D u_NormalSampler;
+#endif
+uniform float_t u_NormalScale;
+
+#ifdef HAS_EMISSIVE_MAP
+uniform sampler2D u_EmissiveSampler;
+#endif
+uniform vec3_t u_EmissiveFactor;
+
+#ifdef HAS_OCCLUSION_MAP
+uniform sampler2D u_OcclusionSampler;
+#endif
+uniform float_t u_OcclusionStrength;
+
+#ifdef HAS_THICKNESS_MAP
+uniform sampler2D u_ThicknessSampler;
+#endif
+uniform float_t u_ThicknessFactor;
+uniform vec3_t u_SubsurfaceColor;
+uniform vec3_t u_SkinORMFactor;
+
+#ifdef HAS_METALLIC_ROUGHNESS_MAP
+uniform sampler2D u_MetallicRoughnessSampler;
+#endif
+#ifdef HAS_BASE_COLOR_MAP
+uniform sampler2D u_BaseColorSampler;
+#endif
+uniform float_t u_MetallicFactor;
+uniform float_t u_RoughnessFactor;
+uniform vec4_t u_BaseColorFactor;
+
+// IBL
+uniform samplerCUBE u_GGXEnvSampler;
+uniform samplerCUBE u_LambertianEnvSampler;
+// uniform sampler2D u_GGXLUT;  // the original GGX calclulation involved filtering the result through this LUT but it is overily expensive (and noisy) in VR
+int u_MipCount; // specific to Unity, must be manually set to the mip count of the diffuse Lambertian sampler cubemap
+
+// TONE MAPPING
+uniform float_t u_Exposure;
+
+
+// hair stuff.
+#if defined(HAS_FLOW_MAP)
+uniform sampler2D u_FlowSampler;
+#endif
+
+#ifdef HAIR_STRAIGHT
+uniform vec3_t u_SpecularColorFactor;
+uniform float_t u_SpecularWhiteIntensity;
+uniform float_t u_SpecularShiftIntensity;
+#endif
+
+#ifdef HAIR_COILY
+uniform vec3_t u_SpecularColorFactor;
+uniform float_t u_SpecularIntensity;
+uniform float_t u_FresnelPower;
+uniform float_t u_FresnelOffset;
+#endif
+
+#ifdef EYE_GLINTS
+uniform float_t u_EyeGlintFactor = 1.0;
+uniform float_t u_EyeGlintColorFactor = 1.0;
+#endif
+
+// -----------------------------------------------------------------------------------
+vec3_t linearTosRGB(vec3_t color) {
+    return pow(color, vec3_t(INV_GAMMA, INV_GAMMA, INV_GAMMA));
+}
+
+// -----------------------------------------------------------------------------------
+vec3_t sRGBToLinear(vec3_t srgbIn) {
+    return pow(srgbIn, vec3_t(GAMMA, GAMMA, GAMMA));
+}
+
+// -----------------------------------------------------------------------------------
+vec4_t sRGBToLinear(vec4_t srgbIn) {
+    return vec4_t(sRGBToLinear(srgbIn.xyz), srgbIn.w);
+}
+
+// -----------------------------------------------------------------------------------
+float_t saturate(float_t x) {
+    return clamp(x, 0.0, 1.0);
+}
+
+// -----------------------------------------------------------------------------------
+vec3_t saturate(vec3_t v)  {
+    return clamp(v, 0.0, 1.0);
+}
+
+// -----------------------------------------------------------------------------------
+float_t fastPow(float_t a, float_t b) {
+    return a / ((1. - b) * a + b);
+}
+
+// -----------------------------------------------------------------------------------
+vec3_t sampleTexCube(samplerCUBE cube, vec3_t normal, float_t mip) {
+//    return textureLod(cube, normal, mip).rgb;  // might also use texCUBElod or SAMPLE_TEXTURECUBE_LOD
+    return texCUBElod(cube, vec4_t(normal, mip));
+}
+
+// -----------------------------------------------------------------------------------
+// material support
+// -----------------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------------
+vec3_t getEmissive(vec2_t uv) {
+    vec3_t e = vec3_t(0,0,0);
+#ifdef HAS_EMISSIVE_MAP
+    e = u_EmissiveFactor * tex2D(u_EmissiveSampler, uv).rgb;
+#endif
+    return e;
+}
+
+// -----------------------------------------------------------------------------------
+vec4_t getBaseColor(interpolators i) {
+
+    // Sample Base Color Map
+    vec2_t uv = i.texcoord.xy;
+    vec4_t baseColor = vec4_t(0.0f, 0.0f, 0.0f, 1.0f);
+
+#if defined(MATERIAL_METALLICROUGHNESS)
+#if defined(MATERIAL_MODE_TEXTURE) && defined(HAS_BASE_COLOR_MAP)
+    baseColor = tex2D(u_BaseColorSampler, uv);
+#else
+    // we're using the vertexColor.a as a means to transmit the sub mesh type
+    baseColor.rgb = i.color.rgb;
+#endif
+#endif
+
+#if defined(MATERIAL_SPECULARGLOSSINESS)
+    baseColor *= u_DiffuseFactor;
+#elif defined(MATERIAL_METALLICROUGHNESS)
+    baseColor *= u_BaseColorFactor;
+#endif
+
+    return baseColor;
+}
+
+
+// -----------------------------------------------------------------------------------
+// IMAGE BASED LIGHTING
+// -----------------------------------------------------------------------------------
+
+// -----------------------------------------------------------------------------------
+// NOTE: Moved from vertex shader. texCUBE is supposed to work in Unity with #pragma glsl but it doesn't work
+vec3_t getIblDiffuseSample(vec3_t n) {
+#if !defined(USE_IBL_DIFFUSE)
+    return vec3_t(0.0, 0.0, 0.0);
+#else
+
+#ifdef FLIP_IBL_WINDING
+    vec3_t diffuseCoords = vec3_t(-n.x, n.y, n.z);
+#else
+    vec3_t diffuseCoords = n;
+#endif
+
+    vec3_t result = texCUBE(u_LambertianEnvSampler, diffuseCoords).rgb;
+    // input gamma set by Unity Samplers
+    //#if !defined(USE_IBL_DIFFUSE_HDR)
+    //    result = sRGBToLinear(result);
+    //#endif
+    return result;
+#endif
+}
+
+// -----------------------------------------------------------------------------------
+vec3_t getIblSpecularSample(vec3_t reflection, float_t roughness) {
+#if !defined(USE_IBL_SPECULAR)
+    return vec3_t(0.0, 0.0, 0.0);
+#else
+// original code, not so good because they subtract 1 to account for sampling over cube face bug...
+    // skip lower mip levels they don't work well for lighting with SDR textures
+    // float_t lod = roughness * float_t(IBL_MIPCOUNT - 1);
+
+    float_t lod = clamp(roughness * float_t(u_MipCount), 0.0, float_t(u_MipCount));
+#ifdef FLIP_IBL_WINDING
+    vec3_t specularCoords = vec3_t(-reflection.x, reflection.y, reflection.z);
+#else
+    vec3_t specularCoords = reflection;
+#endif
+
+//    vec3_t result = sampleTexCube(u_GGXEnvSampler, n, lod);
+    vec3_t result = sampleTexCube(u_GGXEnvSampler, specularCoords, lod);
+
+    // input gamma set by Unity Samplers
+    //#if !defined(USE_IBL_SPECULAR_HDR)
+    //    result = sRGBToLinear(result);
+    //#endif
+    return result;
+#endif //USE_IBL_SPECULAR
+}
+
+// -----------------------------------------------------------------------------------
+// SPHERICAL HARMONIC (SH) LIGHTING
+// -----------------------------------------------------------------------------------
+
+#if defined(USE_SH_PER_VERTEX) || defined(USE_SH_PER_PIXEL) || defined(DEBUG_SH)
+void getSHContribution(out float3 diffuse, out float3 specular, UnityGIInput giInput, float3 diffuseColor, float3 specularColor, float roughness, float occlusion, float metallic, float3 n, float3 v)
+{
+    AvatarShaderGlobalIllumination gi = GetGlobalIllumination(
+        giInput,
+        1 - roughness, // smoothness
+        metallic, // metallic
+        occlusion, // occlusion
+        diffuseColor, // albedo
+        n // normal
+    );
+
+    // These values are all in Linear color space at this point:
+    diffuse = gi.indirect.diffuse.rgb; // diffuse light should already be in the linear space from SH calculations.
+    specular = gi.indirect.specular.rgb; // specular light should already be in the linear space from exr sampling.
+}
+#endif
+
+// -----------------------------------------------------------------------------------
+// PUNCTUAL LIGHTING
+// -----------------------------------------------------------------------------------
+// NOTE: Swiftshader has issues indexing into the uniform array ( in this case the light array)
+// from a function that is passed a int variable. So instead of having functions, we use the
+// pre-processor.
+
+#define LIGHT_DIRECTION(idx_) vec3_t(-u_Lights[idx_].direction)
+
+// Unity generates the light color by multiplying it with the light intensity.
+// Divide by PI is to convert radiometric value to Lambertian
+// It's typically done in the brdf diffuse and brdf specular calculations
+// but we also would need it for the soft diffuse so we just do it here
+#define LIGHT_COLOR(idx_) vec3_t(u_Lights[idx_].color * u_Lights[idx_].intensity * (1./PI))
+
+// -----------------------------------------------------------------------------------
+// TONEMAPPING
+// -----------------------------------------------------------------------------------
+// sRGB => XYZ => D65_2_D60 => AP1 => RRT_SAT
+static const float3x3 ACESInputMat = float3x3 (
+    0.59719, 0.07600, 0.02840,
+    0.35458, 0.90834, 0.13383,
+    0.04823, 0.01566, 0.83777
+);
+
+
+// ODT_SAT => XYZ => D60_2_D65 => sRGB
+static const float3x3 ACESOutputMat = float3x3 (
+    1.60475, -0.10208, -0.00327,
+    -0.53108,  1.10813, -0.07276,
+    -0.07367, -0.00605,  1.07602
+);
+
+// -----------------------------------------------------------------------------------
+// ACES tone map (faster approximation)
+// see: https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/
+vec3_t toneMapACES_Narkowicz(vec3_t color) {
+    const float_t A = 2.51;
+    const float_t B = 0.03;
+    const float_t C = 2.43;
+    const float_t D = 0.59;
+    const float_t E = 0.14;
+    return clamp((color * (A * color + B)) / (color * (C * color + D) + E), 0.0, 1.0);
+}
+
+// -----------------------------------------------------------------------------------
+// ACES filmic tone map approximation
+// see https://github.com/TheRealMJP/BakingLab/blob/master/BakingLab/ACES.hlsl
+vec3_t RRTAndODTFit(vec3_t color) {
+    vec3_t a = color * (color + 0.0245786) - 0.000090537;
+    vec3_t b = color * (0.983729 * color + 0.4329510) + 0.238081;
+    return a / b;
+}
+
+// -----------------------------------------------------------------------------------
+vec3_t toneMapACES_Hill(vec3_t color) {
+    color = mul(color, ACESInputMat);
+    color = RRTAndODTFit(color);
+    color = mul(color, ACESOutputMat);
+    color = clamp(color, 0.0, 1.0);
+    return color;
+}
+
+// -----------------------------------------------------------------------------------
+vec3_t toneMap(vec3_t color) {
+    color *= u_Exposure;
+
+#ifdef TONEMAP_ACES_NARKOWICZ
+    color = toneMapACES_Narkowicz(color);
+#endif
+
+#ifdef TONEMAP_ACES_HILL
+    color = toneMapACES_Hill(color);
+#endif
+
+#ifdef TONEMAP_ACES_HILL_EXPOSURE_BOOST
+    color /= 0.6;
+    color = toneMapACES_Hill(color);
+#endif
+
+#ifdef UNITY_COLORSPACE_GAMMA
+    return linearTosRGB(color); // IMPORTANT: Use when in Unity GAMMA rendering mode
+#else
+    return color; // IMPORTANT: Use when in Unity LINEAR rendering mode
+#endif
+}
+
+
+float_t ComputeSpecular(vec3_t worldSpaceLightDir, vec3_t worldViewDir, vec3_t shadingNormal, float_t NdotL, float_t NdotV, float_t roughPow2, float_t roughPow4, float_t invRoughPow4) {
+    vec3_t h = normalize(worldSpaceLightDir - worldViewDir);
+    float_t NdotH = saturate(dot(shadingNormal, h));
+    float_t ggx = NdotL * sqrt(NdotV * NdotV * invRoughPow4 + roughPow2) + NdotV * sqrt(NdotL * NdotL * invRoughPow4 + roughPow2);
+    ggx = ggx > 0. ? .5 / ggx : 0.;
+    // Implementation from "Average Irregularity Representation of a Roughened Surface for Ray Reflection" by T. S. Trowbridge, and K. P. Reitz
+    float_t t = 1./(1. - NdotH * NdotH * invRoughPow4);
+    return NdotL * t * t * roughPow4 * ggx;
+}
+
+// -----------------------------------------------------------------------------------
+// HAIR_STRAIGHT
+// -----------------------------------------------------------------------------------
+
+#if defined(HAIR_STRAIGHT)
+// -----------------------------------------------------------------------------------
+vec3_t ComputeHairSpecular(vec3_t lightVector, vec3_t worldViewDir, vec3_t tangent, vec3_t normal, float_t roughness, float_t metallic, vec3_t hairColor) {
+    float_t nonMetallic = 1.0 - metallic;
+    float_t smoothness = 1.0 - roughness + 0.00001;        // smoothness of 0 causes problems with pow function
+    vec3_t t1 = tangent;
+    vec3_t t2 = t1 + normal * 0.2;
+    vec3_t floatAngleVector = normalize(worldViewDir - lightVector);
+    float_t cosA1 = abs(dot(floatAngleVector, t1));
+    float_t cosA2 = abs(dot(floatAngleVector, t2));
+    float_t whiteSpecular = fastPow(max(1. - cosA2 * cosA2, 0.), 100. * smoothness) * u_SpecularWhiteIntensity;
+    vec3_t coloredSpecular = u_SpecularColorFactor * fastPow(max(1. - cosA1 * cosA1, 0.), 100. * smoothness) * metallic * hairColor;
+    return (whiteSpecular + coloredSpecular) * (smoothness + .5);
+}
+#endif
+
+// -----------------------------------------------------------------------------------
+void getORMT(in vec2_t uv, in vec4_t ormt, inout float_t occlusion, inout float_t roughness, inout float_t metallic, inout float_t thickness , in float_t subMeshType) {
+#if defined(HAS_METALLIC_ROUGHNESS_MAP) && defined(MATERIAL_MODE_TEXTURE)
+    vec4_t metRough = tex2D(u_MetallicRoughnessSampler, uv);
+    roughness =  metRough.g * u_RoughnessFactor;
+    metallic = metRough.b * u_MetallicFactor;
+#elif defined(MATERIAL_MODE_VERTEX)
+    roughness = ormt.g * u_RoughnessFactor;
+    metallic = ormt.b * u_MetallicFactor;
+#endif
+
+#if defined(HAS_COMBINED_OCCLUSION_MAP) && defined(MATERIAL_MODE_TEXTURE)
+    occlusion = metRough.r * u_OcclusionStrength;
+#elif defined(HAS_OCCLUSION_MAP) && defined(MATERIAL_MODE_TEXTURE)
+    occlusion = tex2D(u_OcclusionSampler,  uv).r;
+    occlusion *= u_OcclusionStrength;
+#elif defined(MATERIAL_MODE_VERTEX)
+    occlusion = ormt.r * u_OcclusionStrength;
+#endif
+
+    // NOTE: When we start using hair we may need to modify this ORM by hair fade value
+    //       In that case we'll have to include: defined(HAIR_STRAIGHT) || defined(HAIR_COILY)
+#if defined(SKIN)
+    bool useSkin = ((subMeshType > (SUBMESH_TYPE_BODY - SUBMESH_TYPE_BUFFER) / 255.0) && (subMeshType < (SUBMESH_TYPE_HEAD + SUBMESH_TYPE_BUFFER) / 255.0));
+    [branch]
+    if (useSkin)
+    {
+        occlusion *= u_SkinORMFactor.r;
+        roughness *= u_SkinORMFactor.g;
+        metallic *= u_SkinORMFactor.b;
+    }
+#endif
+
+#if defined(HAS_COMBINED_THICKNESS_MAP) && defined(MATERIAL_MODE_TEXTURE)
+    thickness = metRough.a * u_ThicknessFactor;
+#elif defined(HAS_THICKNESS_MAP) && defined(MATERIAL_MODE_TEXTURE)
+    thickness = tex2D(u_ThicknessSampler,  uv).a;
+    thickness *= u_ThicknessFactor;
+#elif defined(MATERIAL_MODE_VERTEX)
+    thickness = ormt.a * u_ThicknessFactor;
+#endif
+
+    // DO NOT SUBMIT, JUST A TEST
+    //bool useBody = ((subMeshType > (SUBMESH_TYPE_BODY - SUBMESH_TYPE_BUFFER) / 255.0) && (subMeshType < (SUBMESH_TYPE_BODY + SUBMESH_TYPE_BUFFER) / 255.0));
+    //if (useSkin)
+    //{
+    //    thickness = 0.25;
+    //    thickness *= u_ThicknessFactor;
+    //}
+}
+
+
+// -----------------------------------------------------------------------------------
+// Get normal, tangent and bitangent vectors.
+void getTBN(interpolators i, out vec3_t t, out vec3_t b, out vec3_t n) {
+    vec2_t UV = i.texcoord;
+    vec3_t pos = i.vertex.xyz;
+
+    // Compute geometric normal
+#ifdef HAS_NORMAL_VEC3
+    vec3_t ng = normalize(i.normal);   // should be normalized from vert shader
+#else
+    vec3_t ng = normalize(cross(ddx(pos), ddy(pos)));
+#endif
+
+    // Compute tangent & bitangent
+#ifdef HAS_TANGENT_VEC4
+    t = normalize(i.tangent);      // should be normalized from vert shader
+    b = normalize(i.bitangent);    // should be normalized from vert shader
+#else
+    vec3_t uv_dx = ddx(vec3_t(UV.x, UV.y, 0.0));
+    vec3_t uv_dy = ddy(vec3_t(UV.x, UV.y, 0.0));
+    vec3_t t_ = (uv_dy.y * ddx(pos) - uv_dx.y * ddy(pos)) /
+    (uv_dx.x * uv_dy.y - uv_dy.x * uv_dx.y);
+    t = normalize(t_ - ng * dot(ng, t_));
+    b = cross(ng, t);
+#endif
+
+    // Compute pertubed normal
+#ifdef HAS_NORMAL_MAP
+        n = tex2D(u_NormalSampler, UV).rgb * 2.0 - vec3_t(1,1,1);
+        n *= vec3_t(u_NormalScale, u_NormalScale, 1.0);
+        n = mul(float3x3(t, b, ng), normalize(n));
+#else
+        n = ng;
+#endif
+
+}
+
+// -----------------------------------------------------------------------------------
+// FRAGMENT SHADER
+// -----------------------------------------------------------------------------------
+vec4_t frag(interpolators i) : SV_Target {
+
+    float_t subMeshType = i.color.a;
+
+    // Sample BaseColor Map
+    vec4_t baseColor = getBaseColor(i);
+
+    // fast path for unlit materials
+#ifdef MATERIAL_UNLIT
+    return vec4_t(linearTosRGB(baseColor.rgb), baseColor.a);
+#endif
+
+    // Basics
+    vec3_t worldPos = i.worldPos;
+//    vec3_t worldViewDir = -normalize(i.viewDir);
+    vec3_t worldViewDir = -normalize(_WorldSpaceCameraPos - i.worldPos); // VERY IMPORTANT: Cannot use i.position here
+
+    // Sample ORMT Map
+    float_t occlusion = 1.0;
+    float_t roughness = 1.0;
+    float_t metallic = 0.0;
+    float_t thickness = 1.0;
+    getORMT(i.texcoord, i.ormt, occlusion, roughness, metallic, thickness, subMeshType);
+
+    // Tangent, Bitangent, Normal
+    vec3_t shadingTangent;
+    vec3_t shadingBitangent;
+    vec3_t shadingNormal;
+    getTBN(i, shadingTangent, shadingBitangent, shadingNormal);
+
+    // setup some constants
+    // standard dielectric values
+    const float_t f0 = .04;
+    const float_t f90 = 1.0;
+    const float_t InvExposure = 1.0 / u_Exposure;
+    float_t alpha = baseColor.a;
+    float_t nonMetallic = 1.0 - metallic;
+    float_t thinness = 1.0 - thickness + 0.00001;
+    float_t ao = lerp(1., occlusion, u_OcclusionStrength);
+    vec3_t emissive = getEmissive(i.texcoord);
+    float_t specularWeight = 1.0;
+
+    // flow map, if available
+    // DirectionAngle=r, Fade=g, Power=b, Shift=a
+#if defined(HAS_FLOW_MAP)
+    vec4_t flowMap = tex2D(u_FlowSampler, i.texcoord);
+
+    // this is how flowAngle will arrive from the pipeline, a value from [0,1]:
+    // float_t hairFlowR = atan(hairFlow.y, hairFlow.x) / PI_2;
+
+    // taking the single value and converting it back to vector:
+    float_t flowAngle = flowMap.x * PI_2;
+    vec2_t hairFlow;
+    hairFlow.x = cos(flowAngle);
+    hairFlow.y = sin(flowAngle);
+
+    float_t hairFade = flowMap.y;
+    float_t hairSpecularPower = flowMap.b;
+    float_t hairShift = flowMap.a;
+#endif
+
+    // reflection and lighting vectors
+//    vec3_t reflectionVector = worldViewDir - 2.0 * shadingNormal * dot(worldViewDir, shadingNormal);
+    vec3_t reflectionVector = normalize(reflect(worldViewDir, shadingNormal));
+
+    float_t NdotV = saturate(-dot(shadingNormal, worldViewDir));
+
+    // compute a fresnel term used to brighten specular reflections near grazing angles
+    // Schlick approximation except we use NdotV instead of VdotH so we can use Fresnel value for both IBL and punctual light
+    float_t f = (f0 + (f90 - f0) * pow(saturate(dot(shadingNormal, worldViewDir)), 5.0)) * nonMetallic + metallic;
+
+#if defined(HAIR_STRAIGHT)
+    // give hair less of fresnel affect than other surfaces
+    float_t hairFresnel2 = f * f;
+    f = lerp(f,f * .5 + .5,hairFade);
+#endif
+
+#if defined(HAIR_COILY)
+    float_t hairFresnel2 = saturate((0.5 + u_FresnelOffset) + dot(shadingNormal, worldViewDir));
+    f = fastPow(hairFresnel2, u_FresnelPower);
+#endif
+
+    // calculate lighting
+    vec3_t p_diffuse = vec3_t(0, 0, 0);
+    vec3_t p_specular = vec3_t(0, 0, 0);
+    vec3_t ambient_diffuse = vec3_t(0, 0, 0);
+    vec3_t ambient_specular = vec3_t(0, 0, 0);
+
+    // compute some brdf terms that are constant for every light
+    float_t r2 = roughness * roughness;
+    float_t r4 = r2 * r2;
+    float_t invR4 = 1. - r4;
+
+#if defined(HAIR_STRAIGHT)
+    // hair specific lighting
+    vec3_t hairTangent = shadingTangent*hairFlow.x + shadingBitangent * hairFlow.y;
+    hairTangent += shadingNormal * (shadingNormal.x * hairFlow.x + shadingNormal.y * hairFlow.y) * .5;
+    hairTangent += shadingNormal * ((hairShift - 0.5) * u_SpecularShiftIntensity);
+
+    // IBL
+#if defined(USE_IBL_DIFFUSE)
+    ambient_diffuse = getIblDiffuseSample(shadingNormal);
+#endif
+    vec3_t regularSpec = getIblSpecularSample(reflectionVector, roughness);
+    vec3_t hairSpec = ComputeHairSpecular(i.indirectSpecularVector, worldViewDir, hairTangent, shadingNormal, roughness, metallic, baseColor.rgb) * ambient_diffuse;
+#if defined(USE_IBL_SPECULAR)
+    ambient_specular = lerp(regularSpec,hairSpec,hairFade);
+#endif
+
+#if defined(USE_SH_PER_VERTEX) || defined(USE_SH_PER_PIXEL) || defined(DEBUG_SH)
+    fixed atten = LIGHT_ATTENUATION(i); // SPOT/POINT: This gets you the attenuation + shadow value.
+    UnityGIInput giInput = GetGlobalIlluminationInput(i, i.lightDir, i.worldPos, -worldViewDir, atten); // the view here is supposed to be worldViewDir
+    getSHContribution(ambient_diffuse, ambient_specular, giInput, baseColor, baseColor, roughness, occlusion, metallic, shadingNormal, worldViewDir)
+#endif
+
+#if defined(USE_PUNCTUAL)
+    // punctual
+    //for (int lightIdx = 0; lightIdx < LIGHT_COUNT; ++lightIdx) {
+    //    vec3_t worldSpaceLightDir = LIGHT_DIRECTION(lightIdx);
+    //    vec3_t directionalLightColor = LIGHT_COLOR(lightIdx) * InvExposure;
+        vec3_t worldSpaceLightDir = i.lightDir;
+        vec3_t directionalLightColor = _LightColor0 * InvExposure;
+
+        float_t NdotL = saturate(dot(worldSpaceLightDir, shadingNormal));
+
+        p_diffuse += NdotL * directionalLightColor;
+
+        vec3_t hairPunctualSpec = ComputeHairSpecular(worldSpaceLightDir, worldViewDir, hairTangent, shadingNormal, roughness, metallic, baseColor.rgb);
+        float_t punctualSpec = ComputeSpecular(worldSpaceLightDir, worldViewDir, shadingNormal, NdotL, NdotV, r2, r4, invR4);
+        p_specular += lerp(vec3_t(punctualSpec, punctualSpec, punctualSpec),hairPunctualSpec, hairFade) * directionalLightColor;
+    //}
+#endif
+
+#else
+    // non hair lighting.
+    // IBL
+#if defined(USE_IBL_DIFFUSE)
+    ambient_diffuse = getIblDiffuseSample(shadingNormal);
+#endif
+#if defined(USE_IBL_SPECULAR)
+    ambient_specular = getIblSpecularSample(reflectionVector, roughness);
+#endif
+
+#if defined(USE_SH_PER_VERTEX) || defined(USE_SH_PER_PIXEL) || defined(DEBUG_SH)
+    fixed atten = LIGHT_ATTENUATION(i); // SPOT/POINT: This gets you the attenuation + shadow value.
+    UnityGIInput giInput = GetGlobalIlluminationInput(i, i.lightDir, i.worldPos, -worldViewDir, atten); // the view here is supposed to be worldViewDir
+    vec3_t sh_diffuse = vec3_t(0, 0, 0);
+    vec3_t sh_specular = vec3_t(0, 0, 0);
+    getSHContribution(sh_diffuse, sh_specular, giInput, baseColor, baseColor, roughness, occlusion, metallic, shadingNormal, worldViewDir);
+    ambient_diffuse += sh_diffuse;
+    ambient_specular += sh_specular;
+#endif
+
+    // punctual lighting
+#if defined(USE_PUNCTUAL)
+    //for (int lightIdx = 0; lightIdx < LIGHT_COUNT; ++lightIdx) {
+    //    vec3_t worldSpaceLightDir = LIGHT_DIRECTION(lightIdx);
+    //    vec3_t directionalLightColor = LIGHT_COLOR(lightIdx) * InvExposure;
+        vec3_t worldSpaceLightDir = i.lightDir;
+        vec3_t directionalLightColor = _LightColor0 * InvExposure;
+
+        float_t NdotL = saturate(dot(worldSpaceLightDir, shadingNormal));
+
+        float_t punctualSpec = ComputeSpecular(worldSpaceLightDir, worldViewDir, shadingNormal, NdotL, NdotV, r2, r4, invR4);
+
+        // specular without the fresnel term (It gets multiplied later to total specular)
+        p_diffuse += NdotL * directionalLightColor;
+        p_specular += punctualSpec * directionalLightColor;
+    //}
+#endif
+
+#endif
+
+    vec3_t totalDiffuse = ambient_diffuse + p_diffuse;
+    vec3_t totalSpecular = ambient_specular + p_specular;
+
+#ifdef HAS_FB_SPHERE_MAP
+    vec3_t sphereMapSpecular  = getEnvironmentSphereMap(shadingNormal, worldViewDir, roughness, vec3_t(f0, f0, f0), specularWeight);
+    // ugh. the sphere map specular is being drowned out for some reason,
+    // so this hardcoded scalar is a hack to bring it back up to match V03 look.
+    totalSpecular += sphereMapSpecular * 12.0;
+#endif
+
+    // hair modifies the specular contribution.
+#if defined(HAIR_STRAIGHT)
+    totalSpecular *= lerp(1.0,hairSpecularPower,hairFade);
+#endif
+#if defined(HAIR_COILY)
+    totalSpecular *= u_SpecularColorFactor.rgb * alpha * u_SpecularIntensity;
+#endif
+
+    // cacl diffuse, specular, subsurface.
+    vec3_t diffuseColor = totalDiffuse * ao * nonMetallic * baseColor.rgb;
+    vec3_t specularColor = (nonMetallic + baseColor.rgb * metallic) * totalSpecular * f * ao;
+    vec3_t subsurfaceColor = vec3_t(0,0,0);
+
+    // only human materials should have subsurface scattering
+    // note that SSS is not fully energy conserving, but that would take the baseColor to also include
+    // the SSS contribution which is difficult for the artists to do
+
+    // this is a potentially 'more correct' calculation of diffuseColor
+    // by factoring in the light that is NOT being scattered ala ( 1.0 - u_SubsurfaceColor )
+    //diffuseColor = totalDiffuse * (1.0 - u_SubsurfaceColor * .5 * thinness) * ao * nonMetallic * baseColor.rgb;
+
+#if defined(SKIN)
+    subsurfaceColor = ambient_diffuse * u_SubsurfaceColor * thinness * saturate(ao + .4) * baseColor.rgb;
+#endif
+
+#if defined(HAIR_STRAIGHT) || defined(HAIR_COILY)
+    subsurfaceColor = lerp(vec3_t(0,0,0), ambient_diffuse * u_SubsurfaceColor * thinness * saturate(ao + .4) * baseColor.rgb, hairFade);
+#endif
+
+#if !defined(SKIN) && !defined(HAIR_STRAIGHT) && !defined(HAIR_COILY)
+// Manipulate the specular behavior to better match the v3 shader for clothing.
+// Adhoc fresnel for not hair/skin which modulates the glancing specular by the roughness.
+    float_t fc = lerp(f0, lerp(.8, .1, roughness), pow(1.0 - saturate(dot(shadingNormal, -worldViewDir)), 5.0));
+    vec3_t fcnonmetallic = nonMetallic * vec3_t(fc, fc, fc);
+    specularColor = (baseColor.rgb * metallic + fcnonmetallic) * ao * totalSpecular;
+#endif
+
+#ifdef USE_PUNCTUAL
+#ifdef EYE_GLINTS
+    bool useEyeGlint =
+        ((subMeshType > (SUBMESH_TYPE_L_EYE - SUBMESH_TYPE_BUFFER) / 255.0) &&
+         (subMeshType < (SUBMESH_TYPE_R_EYE + SUBMESH_TYPE_BUFFER) / 255.0));
+    [branch]
+    if (useEyeGlint)
+    {
+        // specularColor += vec4_t(0, 1, 1, 1); // useful to debug eye cutout
+
+#ifdef EYE_GLINTS_BEHIND
+      // create a second reflected spec light to maintain an eye glint from the backside
+        vec3_t mirroredLightDirection = vec3_t(-i.lightDir.x, i.lightDir.y, -i.lightDir.z);
+        float_t mirroredNdotL = saturate(dot(mirroredLightDirection, shadingNormal));
+
+///        materialInfo.diffuseColor = vec3_t(0, 0, 0); // attenuate all the diffuse part, glint only comes from spec
+        float_t rpunctualSpec = ComputeSpecular(mirroredLightDirection, worldViewDir, shadingNormal, mirroredNdotL, NdotV, r2, r4, invR4);
+        vec3_t rp_specular = rpunctualSpec * directionalLightColor;
+        specularColor += u_EyeGlintFactor * lerp(rpunctualSpec, rp_specular, u_EyeGlintColorFactor);
+///        materialInfo.diffuseColor = diffuseColor; // restore to original
+#endif
+
+///        materialInfo.reflectance0 *= u_EyeGlintFactor; // amplify the spec into an eye glint
+
+        specularColor += u_EyeGlintFactor * lerp(punctualSpec, p_specular, u_EyeGlintColorFactor);
+    }
+#endif
+
+///    materialInfo.reflectance0 = specularEnvironmentR0; // restore to original
+#endif
+
+    // calc final color
+    vec3_t finalColor = vec3_t(0,0,0);
+    finalColor += diffuseColor;
+    finalColor += specularColor;
+    finalColor += subsurfaceColor;
+
+#ifdef HAS_EMISSIVE_MAP
+    finalColor += emissive;
+#endif
+
+// NORMAL OUTPUT
+#if !(DEBUG_LIGHTING)
+    finalColor = toneMap(finalColor);
+// finalColor.rgb - vec3_t(1,1,0);
+
+    return vec4_t(finalColor.rgb, alpha);
+#endif
+
+// DEBUGGING OUTPUT
+    finalColor = vec3_t(0,0,0);
+#ifdef DEBUG_BASECOLOR
+    finalColor = toneMap(baseColor.rgb);
+#endif
+
+#ifdef DEBUG_ALPHA
+    finalColor = vec3_t(baseColor.aaa);
+#endif
+
+#ifdef DEBUG_OCCLUSION
+    finalColor = toneMap(vec3_t(occlusion, occlusion, occlusion));
+#endif
+
+#ifdef DEBUG_ROUGHNESS
+    finalColor = toneMap(vec3_t(roughness, roughness, roughness));
+#endif
+
+#ifdef DEBUG_METALLIC
+    finalColor = toneMap(vec3_t(metallic, metallic, metallic));
+#endif
+
+#ifdef DEBUG_THICKNESS
+    finalColor = vec3_t(thickness, thickness, thickness);
+#endif
+
+#ifdef DEBUG_EMISSIVE
+    finalColor.rgb = toneMap(emissive);
+#endif
+
+#ifdef DEBUG_NORMAL_MAP
+#ifdef HAS_NORMAL_MAP
+    finalColor = tex2D(u_NormalSampler, i.texcoord.xy).rgb;
+#else
+    finalColor = vec3_t(0.5, 0.5, 1.0);
+#endif
+#endif
+
+#ifdef DEBUG_NORMAL // raw normal
+    finalColor = i.normal;
+//    finalColor = (i.normal + 1.0) / 2.0;
+#endif
+
+#ifdef DEBUG_NORMAL_GEOMETRY  // normal with normal map
+    finalColor = shadingNormal;
+//    finalColor = (shadingNormal + 1.0) / 2.0;
+#endif
+
+#ifdef DEBUG_TANGENT
+    finalColor = shadingTangent * 0.5 + vec3_t(0.5, 0.5, 0.5);
+#endif
+
+#ifdef DEBUG_BITANGENT
+    finalColor = shadingBitangent * 0.5 + vec3_t(0.5, 0.5, 0.5);
+#endif
+
+#ifdef DEBUG_F0
+    finalColor.rgb = vec3_t(f, f, f);
+#endif
+
+#ifdef DEBUG_VIEW
+    finalColor = worldViewDir;
+#endif
+
+#ifdef DEBUG_TOTAL_DIFFUSE
+    finalColor = toneMap((totalDiffuse);
+#endif
+
+#ifdef DEBUG_TOTAL_SPECULAR
+    finalColor = toneMap((totalSpecular);
+#endif
+
+#if defined(DEBUG_IBL) || defined(DEBUG_SH)
+    finalColor = toneMap(ambient_diffuse * ao * nonMetallic * baseColor.rgb +
+                        (nonMetallic + baseColor.rgb * metallic) * ambient_specular * f * ao);
+#endif
+
+#ifdef DEBUG_IBL_DIFFUSE
+    finalColor = toneMap(ambient_diffuse * ao * nonMetallic * baseColor.rgb);
+#endif
+
+#ifdef DEBUG_IBL_SPECULAR
+    finalColor = toneMap((nonMetallic + baseColor.rgb * metallic) * ambient_specular * f * ao);
+#endif
+
+#ifdef DEBUG_PUNCTUAL
+    finalColor = toneMap(p_diffuse * ao * nonMetallic * baseColor.rgb +
+                        (nonMetallic + baseColor.rgb * metallic) * p_specular * f * ao);
+#endif
+
+#ifdef DEBUG_PUNCTUAL_DIFFUSE
+    finalColor = toneMap(p_diffuse * ao * nonMetallic * baseColor.rgb);
+#endif
+
+#ifdef DEBUG_PUNCTUAL_SPECULAR
+    finalColor = toneMap((nonMetallic + baseColor.rgb * metallic) * p_specular * f * ao);
+#endif
+
+#ifdef DEBUG_ANISOTROPY
+#if defined(HAS_FLOW_MAP)
+{
+    vec4_t flow = tex2D(u_FlowSampler, i.texcoord.xy); // flow.rg, Specular Shift.b
+    finalColor = flow.rgb;
+}
+#endif
+#endif
+
+#ifdef DEBUG_ANISOTROPY_DIRECTION
+#if defined(HAS_FLOW_MAP)
+{
+  finalColor = vec3_t(hairFlow.xy, 0.0);
+}
+#endif
+#endif
+
+#ifdef DEBUG_ANISOTROPY_TANGENT
+#if defined(HAS_FLOW_MAP)
+{
+    vec3_t hairTangent = shadingTangent*hairFlow.x - shadingBitangent * hairFlow.y;
+    finalColor = hairTangent;
+}
+#endif
+#endif
+
+#ifdef DEBUG_ANISOTROPY_BITANGENT
+#if defined(HAS_FLOW_MAP)
+{
+    vec3_t hairTangent = shadingTangent*hairFlow.x - shadingBitangent * hairFlow.y;
+    vec3_t hairBitangent = normalize(cross(hairTangent, shadingNormal));
+    finalColor = hairBitangent;
+}
+#endif
+#endif
+
+#ifdef DEBUG_SUBSURFACE_SCATTERING
+    finalColor = toneMap(subsurfaceColor);
+#endif
+
+    return vec4_t(finalColor.rgb, 1.0);
+}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Human/common-frag.hlsl.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Human/common-frag.hlsl.meta
new file mode 100644
index 0000000000000000000000000000000000000000..426abdbd1a46c2f9181b0b58a47da3fd883ce4a5
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Human/common-frag.hlsl.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 544eed3a19632424d869e53d77d52d74
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Human/common-vert.hlsl b/Assets/Oculus/Avatar2/Example/Common/Shaders/Human/common-vert.hlsl
new file mode 100644
index 0000000000000000000000000000000000000000..4379c579ef582595b0366f17e63edf1351d42ec3
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Human/common-vert.hlsl
@@ -0,0 +1,184 @@
+/////////////////////////////////////////////////////////
+// Unity specific defines for Avatar SDK:
+
+// Shader feature defines. If we want to change these via the shader manager,
+// convert them to multi_compile or shader_feature
+
+#define HAS_NORMALS
+#define OVR_VERTEX_HAS_VERTEX_ID
+
+#if defined(OVR_VERTEX_HAS_TANGENTS)
+#define HAS_TANGENTS
+#endif
+
+// Avatar SDK specific include files, shared with all the other avatar shaders:
+#define OVR_VERTEX_POSITION_FIELD_NAME vertex
+#define OVR_VERTEX_NORMAL_FIELD_NAME normal
+#define OVR_VERTEX_TANGENT_FIELD_NAME tangent
+#define OVR_VERTEX_VERT_ID_FIELD_NAME v_Id
+#define OVR_VERTEX_TEXCOORD_FIELD_NAME texcoord
+#define OVR_VERTEX_COLOR_FIELD_NAME color
+#include "UnityCG.cginc"
+#include "../../../../Scripts/ShaderUtils/AvatarCustomTypes.cginc"
+
+#include "../../../../Scripts/ShaderUtils/AvatarCustom.cginc"
+
+// This includes the required macros for shadow and attenuation values.
+#include "AutoLight.cginc"
+
+/////////////////////////////////////////////////////////
+// Original ported code from primitive.vert:
+
+// Composite interpolators structure derived from the original Khronos cginc files.
+struct interpolators {
+    float4 color : COLOR;
+//#ifdef MATERIAL_MODE_VERTEX
+    float4 ormt : TEXCOORD1; // holds ormt vertex color channel
+//#endif
+
+    float4 vertex : SV_POSITION;
+
+    float2 texcoord : TEXCOORD0;
+    float2 texcoord11 : TEXCOORD11;    // always define this to reserve for the future
+    // NOTE: There has been discussion of reserving texccord11 for the uncombined/original toxcoords.
+    // Such a feature would allow us to use the original texture maps as opposed to an atlased map.
+    // However such a gesture would immedeately double the number of texture samples, and only for
+    // one sub-mesh. So such a feature would have to be used very sparingly. Potential applications
+    // could be a custom pattern for the outfit, or an un-atlased flow map for only the hair.
+
+#ifdef HAS_NORMALS
+#ifdef HAS_TANGENTS
+    float3 tangent : TEXCOORD6;
+    float3 bitangent : TEXCOORD7;
+#endif
+    float3 normal : TEXCOORD2;
+#endif
+
+#if defined(OVR_VERTEX_HAS_VERTEX_ID)
+    UNITY_VERTEX_INPUT_INSTANCE_ID  // uint v_Id : TEXCOORD3;
+#endif
+
+    float3 worldPos : TEXCOORD3;
+
+    float3 lightDir : TEXCOORD4;
+    DECLARE_LIGHT_COORDS(5)
+
+#ifdef USE_SH_PER_VERTEX // vertex based sh
+    half3 sh : TEXCOORD8;
+#endif
+
+#if defined(HAIR_STRAIGHT)
+    float3 indirectSpecularVector : TEXCOORD8;
+#endif
+
+    // can't get IBL diffuse from the vert sahder:
+    // float3 diffuseLight : TEXCOORD9;
+    // float3 softDiffuseLight : TEXCOORD10;
+
+    UNITY_VERTEX_OUTPUT_STEREO
+};
+
+//-----------------------------------------------------------------------------------------
+// The following use UnityStandardUtils.cginc as a reference, as of 2020.3.7
+
+half3 AvatarShadeSHPerVertex(half3 normal, half3 ambient) {
+#if defined(USE_SH_PER_PIXEL)
+  // Completely per-pixel
+  // nothing to do here
+#else
+  // Completely per-vertex
+  ambient += max(half3(0, 0, 0), ShadeSH9(half4(normal, 1.0)));
+#endif
+  return ambient;
+}
+
+//-----------------------------------------------------------------------------------------
+
+interpolators vert(OvrDefaultAppdata v) {
+    OvrInitializeDefaultAppdata(v);
+
+    interpolators o;
+    UNITY_INITIALIZE_OUTPUT(interpolators, o);
+    UNITY_TRANSFER_INSTANCE_ID(v, o);
+    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
+
+    // Object -> world transformation
+//    OvrVertexData vertexData = OVR_CREATE_VERTEX_DATA(v);
+    OvrVertexData vertexData = OvrCreateVertexData(v.vertex, v.normal, v.tangent, v.v_Id);
+
+
+
+
+// This alternative to OVR_CREATE_VERTEX_DATA breaks the skinning
+    //vertexData.position = v.vertex;
+    //vertexData.normal = v.normal;
+    //vertexData.tangent = v.tangent;
+    //vertexData.vertexId = v.v_Id;
+
+    //bool hasTangents = false;
+    //bool applyOffsetAndBias = true;
+    //OvrPopulateVertexDataFromExternalTexture(hasTangents, applyOffsetAndBias, vertexData);
+
+
+
+    // Pass through clip space position, uv
+    o.vertex = UnityObjectToClipPos(vertexData.position);
+
+#ifdef MATERIAL_MODE_VERTEX
+    o.ormt = OVR_GET_VERTEX_ORMT(v);
+    o.color = OVR_GET_VERTEX_COLOR(v);
+#else
+    o.color.a = OVR_GET_VERTEX_COLOR(v).a;
+#endif
+
+#ifdef HAS_NORMALS
+#ifdef HAS_TANGENTS
+    float4 tangent = vertexData.tangent;
+
+//    float3 worldNormal = normalize(float3(u_NormalMatrix * float4(getNormal(vertexData.position, o.texcoord, o.v_UVCoord2, vertexData.normal).xyz, 0.0)));
+//    float3 worldTangent = normalize(float3(u_ModelMatrix * float4(tangent.xyz, 0.0)));
+
+    float3 worldNormal = UnityObjectToWorldNormal(vertexData.normal);
+    float3 worldTangent = UnityObjectToWorldDir(tangent.xyz);
+    float3 worldBitangent = normalize(cross(worldNormal, worldTangent) * tangent.w);
+    o.tangent = worldTangent;
+    o.bitangent = worldBitangent;
+    o.normal = worldNormal;
+#else
+    o.normal = UnityObjectToWorldNormal(vertexData.normal);
+#endif
+#endif
+
+    o.texcoord = OVR_GET_VERTEX_TEXCOORD(v);
+
+    o.worldPos = mul(unity_ObjectToWorld, vertexData.position).xyz;
+
+    o.lightDir = WorldSpaceLightDir(vertexData.position).xyz;
+
+    // taken from "AutoLight.cginc", COMPUTE_LIGHT_COORDS(), but specialized to accomodate vertexData.position
+#ifdef POINT
+    o._LightCoord = mul(unity_WorldToLight, mul(unity_ObjectToWorld, vertexData.position)).xyz;
+#endif
+#ifdef SPOT
+    o._LightCoord = mul(unity_WorldToLight, mul(unity_ObjectToWorld, vertexData.position));
+#endif
+
+#ifdef USE_SH_PER_VERTEX // vertex based sh
+    o.sh = 0;
+    o.sh = AvatarShadeSHPerVertex(o.normal, o.sh);
+#endif
+
+// can't get IBL diffuse from the vert sahder:
+//    o.diffuseLight = getIblDiffuseSample(o.normal);
+//    o.softDiffuseLight = o.diffuseLight; // WHAT!?, This might only be used when HAS_TANGENT_VEC4
+#if defined(HAIR_STRAIGHT)
+    o.indirectSpecularVector = o.normal; // WHAT!?, This might only be used when HAS_TANGENT_VEC4
+#endif
+
+    // BELOW CODE REQUIRES VALID TANGENT DATA
+#if defined(HAS_TANGENT_VEC4)
+#error // this hasn't been ported from the glsl yet...'
+#endif // HAS_TANGENT_VEC4
+
+    return o;
+}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Human/common-vert.hlsl.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Human/common-vert.hlsl.meta
new file mode 100644
index 0000000000000000000000000000000000000000..e263810135763e903b020ebc1a927173bf424400
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Human/common-vert.hlsl.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 584d8e83405abec4faa38b29655538d2
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Human/fb_sphere_map.hlsl b/Assets/Oculus/Avatar2/Example/Common/Shaders/Human/fb_sphere_map.hlsl
new file mode 100644
index 0000000000000000000000000000000000000000..372f878581121d9a1a4ee122683c9e26c39cfbed
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Human/fb_sphere_map.hlsl
@@ -0,0 +1,27 @@
+// References
+// https://www.internalfb.com/intern/wiki/Avatar_SDK/GLTF/FB_sphere_map/
+// https://en.wikipedia.org/wiki/Sphere_mapping
+// https://www.clicktorelease.com/blog/creating-spherical-environment-mapping-shader/
+
+#ifdef HAS_FB_SPHERE_MAP
+
+uniform sampler2D u_SphereMapEnvSampler;
+float3 getEnvironmentSphereMap(float3 n, float3 v, float roughness, float3 F0, float specularWeight) {
+
+  // reflection vector
+  float3 r = normalize(reflect(-v, n));
+
+  // UV calculation based on reflection vector.
+  float m = 2.0 * sqrt( pow( r.x, 2.0 ) + pow( r.y, 2.0 ) + pow( r.z + 1.0, 2.0 ) );
+  float2 uv = float2(r.x, -r.y) / m + 0.5;
+
+  // sample the sphere environment map
+  float3 sphereMapColor = texture(u_SphereMapEnvSampler, uv).rgb;
+
+  // factor in roughness.
+  float3 sphereWeight = float3(1.0 - roughness);
+
+  return sphereMapColor * sphereWeight * specularWeight;
+}
+
+#endif // HAS_FB_SPHERE_MAP
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Human/fb_sphere_map.hlsl.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Human/fb_sphere_map.hlsl.meta
new file mode 100644
index 0000000000000000000000000000000000000000..bc82e0f7535db80152fd4cf0d06900ae2706e5df
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Human/fb_sphere_map.hlsl.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 96003f3ff93d49342a983a0334b27aae
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Human/precision.hlsl b/Assets/Oculus/Avatar2/Example/Common/Shaders/Human/precision.hlsl
new file mode 100644
index 0000000000000000000000000000000000000000..dde7ab5dd26d762ce8958af4f7f3946bb3bb4892
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Human/precision.hlsl
@@ -0,0 +1,11 @@
+// this file attempts to emulate the glsl precision flags. Here are the original statements
+// precision highp float;
+// precision highp int;
+
+// However in hlsl this switch is not as easily done.
+// It can be overriden in the shader compiler, but that affects all variables.
+// A more controlled strategy is to use these types:
+#define float_t half
+#define vec2_t half2
+#define vec3_t half3
+#define vec4_t half4
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Human/precision.hlsl.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Human/precision.hlsl.meta
new file mode 100644
index 0000000000000000000000000000000000000000..ad4d47a7324e244613d7fcb5592bc6b8f71cef80
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Human/precision.hlsl.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 2c12a3c331232834d9f74db65a34341e
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Human/skinning.hlsl b/Assets/Oculus/Avatar2/Example/Common/Shaders/Human/skinning.hlsl
new file mode 100644
index 0000000000000000000000000000000000000000..e11393fe7ca1498b09545ca0c0b5237f7fe5896b
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Human/skinning.hlsl
@@ -0,0 +1,64 @@
+
+#ifdef USE_SKINNING
+
+in float4 a_joints_0;
+in float4 a_weights_0;
+uniform sampler2D u_JointMatrixSampler;
+uniform sampler2D u_JointNormalMatrixSampler;
+
+// ---------------------------------------------------
+// INTERNAL
+float4x4 sampleMat4(sampler2D buffer, int joint) {
+  int texel = joint * 4;
+  return float4x4(
+    texelFetch(buffer, ivec2(texel + 0, 0), 0),
+    texelFetch(buffer, ivec2(texel + 1, 0), 0),
+    texelFetch(buffer, ivec2(texel + 2, 0), 0),
+    texelFetch(buffer, ivec2(texel + 3, 0), 0)
+  );
+}
+
+// ---------------------------------------------------
+// INTERNAL
+float4x4 sampleJointBuffer(int joint) {
+  return sampleMat4(u_JointMatrixSampler, joint);
+}
+
+// ---------------------------------------------------
+// INTERNAL
+float4x4 sampleJointNormalBuffer(int joint) {
+  return sampleMat4(u_JointNormalMatrixSampler, joint);
+}
+
+// ---------------------------------------------------
+// PUBLIC
+float4x4 getSkinningMatrix() {
+  float4x4 jointX = sampleJointBuffer(int(a_joints_0.x));
+  float4x4 jointY = sampleJointBuffer(int(a_joints_0.y));
+  float4x4 jointZ = sampleJointBuffer(int(a_joints_0.z));
+  float4x4 jointW = sampleJointBuffer(int(a_joints_0.w));
+
+  float4x4 skin =  a_weights_0.x * jointX +
+          a_weights_0.y * jointY +
+          a_weights_0.z * jointZ +
+          a_weights_0.w * jointW;
+  return skin;
+}
+
+// ---------------------------------------------------
+// PUBLIC
+float4x4 getSkinningNormalMatrix() {
+  float4x4 jointX = sampleJointNormalBuffer(int(a_joints_0.x));
+  float4x4 jointY = sampleJointNormalBuffer(int(a_joints_0.y));
+  float4x4 jointZ = sampleJointNormalBuffer(int(a_joints_0.z));
+  float4x4 jointW = sampleJointNormalBuffer(int(a_joints_0.w));
+
+  float4x4 skin =  a_weights_0.x * jointX +
+          a_weights_0.y * jointY +
+          a_weights_0.z * jointZ +
+          a_weights_0.w * jointW;
+
+  return skin;
+}
+
+#endif // USE_SKINNING
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Human/skinning.hlsl.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Human/skinning.hlsl.meta
new file mode 100644
index 0000000000000000000000000000000000000000..fdfdec54ef7ec302cd9554b42e9a83e8b5fdec0c
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Human/skinning.hlsl.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: fdc63bb0f48c9cc4ab3fd0add42067d6
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos.meta
new file mode 100644
index 0000000000000000000000000000000000000000..767a82da5ad29d78c8fdaa5616d1e062d04e1eea
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 3fbf991cbbc9ee941aa726ce76a81f7b
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/Avatar-Khronos.shader b/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/Avatar-Khronos.shader
new file mode 100644
index 0000000000000000000000000000000000000000..aa0b4372f58d5589ba0c99f71fdd322ed30ed509
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/Avatar-Khronos.shader
@@ -0,0 +1,243 @@
+// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
+
+// based off metallic_roughness.frag/vert from Khronos
+
+Shader "Avatar/Khronos"
+{
+    Properties
+    {
+      // NOTE: This texture can be visualized in the Unity editor, just expand in inspector and manually change "Dimension" to "2D" on top line
+      u_AttributeTexture("Vertex Attribute map", 3D) = "white" {}
+
+      u_NormalSampler("Normal map", 2D) = "white" {}
+      u_NormalScale("Normal map scale", Float) = 1.0
+      u_NormalUVSet("Normal UV Set", Int) = 0
+
+      u_EmissiveSampler("Emissive map", 2D) = "black" {}
+      u_EmissiveSet("Emissive UV Set", Int) = 0
+      u_EmissiveFactor("Emissive factor", Color) = (1, 1, 1, 1)
+
+      u_OcclusionSampler("Occlusion map", 2D) = "white" {}
+      u_OcclusionSet("Occlusion UV Set", Int) = 0
+      u_OcclusionStrength("Occlusion scale", Float) = 1.0
+
+      u_BaseColorSampler("Base Color", 2D) = "white" {}
+      u_BaseColorUVSet("Base Color UV Set", Int) = 0
+
+      u_BaseColorFactor("Base Color factor", Color) = (1, 1, 1, 1)
+
+      u_MetallicRoughnessSampler("Metallic Roughness", 2D) = "white" {}
+      u_MetallicRoughnessUVSet("Metallic Roughness UV Set", Int) = 0
+
+      u_MetallicFactor("Metallic Factor", Range(0, 1)) = 1.0
+      u_RoughnessFactor("Roughness Factor", Range(0, 1)) = 1.0
+      u_F0Factor("F0 Factor", Range(0, 1)) = 1.0
+
+// AVATAR SDK BEGIN
+      u_EyeGlintFactor("Eye Glint Factor", Range(0, 20)) = 10.0
+      u_DiffuseSmoothingFactor("Diffuse Smoothing Factor", Range(0, 8)) = 1.0
+      // this factor allows for smooth diffuse light falloff similar to Babylon.js.
+      // It is not originally in the Khronos shader.
+// AVATAR SDK END
+
+
+      [ShowIfKeyword(_PALETTIZATION_SINGLE_RAMP, _PALETTIZATION_TWO_RAMP)]
+      _ColorRamp0 ("Color Ramp 1", 2D) = "white" {}
+      [ShowIfKeyword(_PALETTIZATION_TWO_RAMP)]
+      _ColorRamp1 ("Color Ramp 2", 2D) = "white" {}
+
+
+      // These should not exist here, since they should be in global shader scope and handeled by an external manager:
+      //
+      //u_DiffuseEnvSampler("IBL Diffuse Cubemap Texture", Cube) = "white" {}
+      //u_MipCount("IBL Diffuse Texture Mip Count", Int) = 10
+      //u_SpecularEnvSampler ("IBL Specular Cubemap Texture", Cube) = "white" {}
+      //u_brdfLUT ("BRDF LUT Texture", 2D) = "Assets/Oculus/Avatar2/Example/Scenes/BRDF_LUT" {}
+
+      // DEBUG_MODES: Uncomment to use Debug modes, must match the multi_compile defined below
+      // [KeywordEnum(None, BaseColor, Occlusion, Roughness, Metallic, Thickness, Normal, Normal Map, Emissive, View, Punctual, Punctual Specular, Punctual Diffuse, IBL, IBL Specular, IBL Diffuse, SH, No Tone Map)] Debug("Debug Render", Float) = 0
+
+      // LIGHTING_MODES: Uncomment to use Lighting modes, must match the multi_compile defined below
+      // [KeywordEnum(IBL plus Punctual, SH plus Punctual, IBL Only, SH Only, Punctual Only)] Lighting_Mode("Lighting Mode", Float) = 0
+
+      // TONEMAP_MODES: Uncomment to use ToneMapping modes, must match the multi_compile defined below
+      // [KeywordEnum(None, Uncharted, HejlRichard, ACES)] ToneMap("Tone Mapping", Float) = 0
+
+      // BRDFLUT_MODES: Uncomment to use BRDF Look up Table (LUT) modes, must match the multi_compile defined below
+      [KeywordEnum(On, Off)] BRDF_LUT_Mode("BRDF LUT Mode", Float) = 0
+      
+      // MATERIAL_MODES: Uncomment to use Material modes, must match the multi_compile defined below
+      [KeywordEnum(Texture, Vertex)] Material_Mode("Material Mode", Float) = 0
+
+      // Cull mode (Off, Front, Back)
+      [Enum(UnityEngine.Rendering.CullMode)] _Cull("Cull", Float) = 2
+
+    }
+    SubShader
+    {
+        Tags { "RenderType"="Opaque" }
+        LOD 100
+
+        Cull [_Cull]
+
+        // Old Unity Render Pipeline, Single Light
+        Pass
+        {
+            // Commenting out this lightmode because it breaks URP compilation.
+            // Confirmed that this won't break non-URP projects.
+            // Tags{"LightMode" = "ForwardBase"}
+
+            CGPROGRAM
+            #pragma vertex vert
+            #pragma fragment frag
+
+            #pragma multi_compile DIRECTIONAL POINT SPOT
+
+            /////////////////////////////////////////////////////////
+            // PRAGMAS: Pragmas cannot exist in cginc files so include them here
+
+            // DEBUG_MODES: Must match the Properties specified above
+            // #pragma multi_compile __ DEBUG_BASECOLOR DEBUG_OCCLUSION DEBUG_ROUGHNESS DEBUG_METALLIC DEBUG_THICKNESS DEBUG_NORMAL DEBUG_NORMAL_MAP DEBUG_EMISSIVE DEBUG_VIEW DEBUG_PUNCTUAL DEBUG_PUNCTUAL_SPECULAR DEBUG_PUNCTUAL_DIFFUSE DEBUG_IBL DEBUG_IBL_SPECULAR DEBUG_IBL_DIFFUSE DEBUG_SH DEBUG_NO_TONE_MAP
+            #define DEBUG_LIGHTING (defined(DEBUG_METALLIC) || defined(DEBUG_THICKNESS) || defined(DEBUG_ROUGHNESS) || defined(DEBUG_NORMAL) || defined(DEBUG_NORMAL_MAP) || defined(DEBUG_BASECOLOR) || defined(DEBUG_OCCLUSION) || defined(DEBUG_EMISSIVE) || defined(DEBUG_F0) || defined(DEBUG_ALPHA) || defined(DEBUG_VIEW) || defined(DEBUG_PUNCTUAL) || defined(DEBUG_PUNCTUAL_SPECULAR) || defined(DEBUG_PUNCTUAL_DIFFUSE) || defined(DEBUG_IBL) || defined(DEBUG_IBL_SPECULAR) || defined(DEBUG_IBL_DIFFUSE) || defined(DEBUG_SH) || defined(DEBUG_NO_TONE_MAP))
+
+            // LIGHTING_MODES: this is done to match the options of the commercial GLTF viewers
+            // #pragma multi_compile LIGHTING_MODE_IBL_PLUS_PUNCTUAL LIGHTING_MODE_SH_PLUS_PUNCTUAL LIGHTING_MODE_IBL_ONLY LIGHTING_MODE_SH_ONLY LIGHTING_MODE_PUNCTUAL_ONLY
+            #define LIGHTING_MODE_IBL_PLUS_PUNCTUAL
+
+            // TONEMAP_MODES: Must match the Properties specified above
+            // #pragma multi_compile __ TONEMAP_UNCHARTED TONEMAP_HEJLRICHARD TONEMAP_ACES
+
+            // BRDF_LUT_MODES: Must match the Properties specified above
+            #pragma multi_compile BRDF_LUT_MODE_ON BRDF_LUT_MODE_OFF
+      
+            // MATERIAL_MODES: Must match the Properties specified above
+            #pragma multi_compile MATERIAL_MODE_TEXTURE MATERIAL_MODE_VERTEX
+
+            #pragma target 3.5 // necessary for use of SV_VertexID
+
+            // Palettization modes for Avatar FBX tool
+            #pragma multi_compile __ _PALETTIZATION_SINGLE_RAMP _PALETTIZATION_TWO_RAMP
+
+            // In Avatar SDK we ALWAYS use the ORM property map extension. Indicate that here:
+            #define USE_ORM_EXTENSION
+
+            // Turn on IBL only here, not for the other additive passes
+            #if defined(LIGHTING_MODE_IBL_PLUS_PUNCTUAL) || defined(LIGHTING_MODE_IBL_ONLY)
+            #define USE_IBL // IBL only gets applied here in the first base pass
+            #endif
+            #if defined(LIGHTING_MODE_SH_PLUS_PUNCTUAL) || defined(LIGHTING_MODE_SH_ONLY)
+            // per vertex is faster than per pixel, and almost indistinguishable for our purpose
+            #define USE_SH_PER_VERTEX
+            // #define USE_SH_PER_PIXEL
+            #endif
+
+            // Include the Vertex Shader HERE
+            #include "primitive-vert.cginc"
+
+            // Include the Pixel Shader HERE
+            #include "metallic-roughness-frag.cginc"
+
+            ENDCG
+        }
+
+        // Old Unity Render Pipeline, Up to 4 Additive Lights
+        Pass
+        {
+            Tags{"LightMode" = "ForwardAdd"}
+
+            Blend One One
+            ZWrite Off
+
+            CGPROGRAM
+            #pragma vertex vert
+            #pragma fragment frag
+
+            #pragma multi_compile DIRECTIONAL POINT SPOT
+
+            /////////////////////////////////////////////////////////
+            // PRAGMAS: Pragmas cannot exist in cginc files so include them here
+
+            // DEBUG_MODES: Must match the Properties specified above
+            
+            // #pragma multi_compile __ DEBUG_BASECOLOR DEBUG_OCCLUSION DEBUG_ROUGHNESS DEBUG_METALLIC DEBUG_THICKNESS DEBUG_NORMAL DEBUG_NORMAL_MAP DEBUG_EMISSIVE DEBUG_VIEW DEBUG_PUNCTUAL DEBUG_PUNCTUAL_SPECULAR DEBUG_PUNCTUAL_DIFFUSE DEBUG_IBL DEBUG_IBL_SPECULAR DEBUG_IBL_DIFFUSE DEBUG_SH DEBUG_NO_TONE_MAP
+            #define DEBUG_LIGHTING (defined(DEBUG_METALLIC) || defined(DEBUG_THICKNESS) || defined(DEBUG_ROUGHNESS) || defined(DEBUG_NORMAL) || defined(DEBUG_NORMAL_MAP) || defined(DEBUG_BASECOLOR) || defined(DEBUG_OCCLUSION) || defined(DEBUG_EMISSIVE) || defined(DEBUG_F0) || defined(DEBUG_ALPHA) || defined(DEBUG_VIEW) || defined(DEBUG_PUNCTUAL) || defined(DEBUG_PUNCTUAL_SPECULAR) || defined(DEBUG_PUNCTUAL_DIFFUSE) || defined(DEBUG_IBL) || defined(DEBUG_IBL_SPECULAR) || defined(DEBUG_IBL_DIFFUSE) || defined(DEBUG_SH) || defined(DEBUG_NO_TONE_MAP))
+
+            // LIGHTING_MODES: this is done to match the options of the commercial GLTF viewers
+            // #pragma multi_compile __ LIGHTING_MODE_SH_ONLY LIGHTING_MODE_IBL_ONLY
+
+            // TONEMAP_MODES: Must match the Properties specified above
+            // #pragma multi_compile __ TONEMAP_UNCHARTED TONEMAP_HEJLRICHARD TONEMAP_ACES
+
+            // BRDF_LUT_MODES: Must match the Properties specified above
+            #pragma multi_compile BRDF_LUT_MODE_ON BRDF_LUT_MODE_OFF
+      
+            // MATERIAL_MODES: Must match the Properties specified above
+            #pragma multi_compile MATERIAL_MODE_TEXTURE MATERIAL_MODE_VERTEX
+
+            #pragma target 3.5 // necessary for use of SV_VertexID
+
+            // Include the Vertex Shader HERE
+            #include "primitive-vert.cginc"
+
+            // Include the Pixel Shader HERE
+            #include "metallic-roughness-frag.cginc"
+
+            ENDCG
+        }
+
+        // Universal Render Pipeline (URP)
+        Pass
+        {
+            Tags{"LightMode" = "UniversalForward"}
+
+            CGPROGRAM
+            #pragma vertex vert
+            #pragma fragment frag
+
+            /////////////////////////////////////////////////////////
+            // PRAGMAS: Pragmas cannot exist in cginc files so include them here
+
+            // DEBUG_MODES: Must match the Properties specified above
+            // #pragma multi_compile __ DEBUG_BASECOLOR DEBUG_OCCLUSION DEBUG_ROUGHNESS DEBUG_METALLIC DEBUG_THICKNESS DEBUG_NORMAL DEBUG_NORMAL_MAP DEBUG_EMISSIVE DEBUG_VIEW DEBUG_PUNCTUAL DEBUG_PUNCTUAL_SPECULAR DEBUG_PUNCTUAL_DIFFUSE DEBUG_IBL DEBUG_IBL_SPECULAR DEBUG_IBL_DIFFUSE DEBUG_SH DEBUG_NO_TONE_MAP
+            #define DEBUG_LIGHTING (defined(DEBUG_METALLIC) || defined(DEBUG_THICKNESS) || defined(DEBUG_ROUGHNESS) || defined(DEBUG_NORMAL) || defined(DEBUG_NORMAL_MAP) || defined(DEBUG_BASECOLOR) || defined(DEBUG_OCCLUSION) || defined(DEBUG_EMISSIVE) || defined(DEBUG_F0) || defined(DEBUG_ALPHA) || defined(DEBUG_VIEW) || defined(DEBUG_PUNCTUAL) || defined(DEBUG_PUNCTUAL_SPECULAR) || defined(DEBUG_PUNCTUAL_DIFFUSE) || defined(DEBUG_IBL) || defined(DEBUG_IBL_SPECULAR) || defined(DEBUG_IBL_DIFFUSE) || defined(DEBUG_SH) || defined(DEBUG_NO_TONE_MAP))
+
+            // LIGHTING_MODES: this is done to match the options of the commercial GLTF viewers
+            // #pragma multi_compile LIGHTING_MODE_IBL_PLUS_PUNCTUAL LIGHTING_MODE_SH_PLUS_PUNCTUAL LIGHTING_MODE_IBL_ONLY LIGHTING_MODE_SH_ONLY LIGHTING_MODE_PUNCTUAL_ONLY
+            #define LIGHTING_MODE_IBL_PLUS_PUNCTUAL
+
+            // TONEMAP_MODES: Must match the Properties specified above
+            // #pragma multi_compile __ TONEMAP_UNCHARTED TONEMAP_HEJLRICHARD TONEMAP_ACES
+
+            // BRDF_LUT_MODES: Must match the Properties specified above
+            #pragma multi_compile BRDF_LUT_MODE_ON BRDF_LUT_MODE_OFF
+          
+            // MATERIAL_MODES: Must match the Properties specified above
+            #pragma multi_compile MATERIAL_MODE_TEXTURE MATERIAL_MODE_VERTEX
+
+            #pragma target 3.5 // necessary for use of SV_VertexID
+
+            // Palettization modes for Avatar FBX tool
+            #pragma multi_compile __ _PALETTIZATION_SINGLE_RAMP _PALETTIZATION_TWO_RAMP
+
+            // In Avatar SDK we ALWAYS use the ORM property map extension. Indicate that here:
+            #define USE_ORM_EXTENSION
+
+            // Turn on IBL only here, not for the other additive passes
+            #if defined(LIGHTING_MODE_IBL_PLUS_PUNCTUAL) || defined(LIGHTING_MODE_IBL_ONLY)
+            #define USE_IBL // IBL only gets applied here in the first base pass
+            #endif
+            #if defined(LIGHTING_MODE_SH_PLUS_PUNCTUAL) || defined(LIGHTING_MODE_SH_ONLY)
+            // per vertex is faster than per pixel, and almost indistinguishable for our purpose
+            #define USE_SH_PER_VERTEX
+            #endif
+
+            // Include the Vertex Shader HERE
+            #include "primitive-vert.cginc"
+
+            // Include the Pixel Shader HERE
+            #include "metallic-roughness-frag.cginc"
+
+            ENDCG
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/Avatar-Khronos.shader.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/Avatar-Khronos.shader.meta
new file mode 100644
index 0000000000000000000000000000000000000000..e36d121af9ef4644eb0fad624941639bb86ad764
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/Avatar-Khronos.shader.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 2a6c569e922c5e34e9c05f146aaf9c78
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/AvatarFbxReview.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/AvatarFbxReview.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..7906d0c4a13970ab22ff69c146bdc132eef54904
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/AvatarFbxReview.cginc
@@ -0,0 +1,30 @@
+#ifndef AVATAR_FBX_REVIEW_CGINC
+#define AVATAR_FBX_REVIEW_CGINC
+
+// This code is built around allowing the Fbx Review tool to
+// quickly toggle color ramps being applied to Avatars various
+// parts.
+
+#if defined(_PALETTIZATION_SINGLE_RAMP)
+sampler2D _ColorRamp0;
+#elif defined(_PALETTIZATION_TWO_RAMP)
+sampler2D _ColorRamp0;
+sampler2D _ColorRamp1;
+#endif
+
+// support for greyscale -> color ramp lookup
+float4 PalettizedAlbedo(float4 mainTex) {
+#if defined(_PALETTIZATION_SINGLE_RAMP)
+  return tex2D(_ColorRamp0, mainTex.r);
+#elif defined(_PALETTIZATION_TWO_RAMP)
+  float4 ramp0 = tex2D(_ColorRamp0, mainTex.r);
+  float4 ramp1 = tex2D(_ColorRamp1, mainTex.r);
+  float alpha = ramp1.a;
+  ramp1.a = 1.0f;
+  return lerp(ramp0.rgba, ramp1.rgba, alpha);
+#else
+  return mainTex;
+#endif
+}
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/AvatarFbxReview.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/AvatarFbxReview.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..f6162243be54397db73a8ff282c55be0b9a1084d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/AvatarFbxReview.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 477b03341e3dfd34588a4d820616502e
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/UnityGIAvatar.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/UnityGIAvatar.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..c2bab8df635179c7c93d2c4e3ed0bce4e3b98c25
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/UnityGIAvatar.cginc
@@ -0,0 +1,208 @@
+#include "UnityImageBasedLighting.cginc"
+
+struct AvatarShaderLight {
+  half3 direction;
+  half3 color;
+};
+
+struct AvatarShaderIndirect {
+  half3 diffuse;
+  half3 specular;
+};
+
+struct AvatarShaderGlobalIllumination {
+  AvatarShaderLight light;
+  AvatarShaderIndirect indirect;
+};
+
+//-----------------------------------------------------------------------------------------
+// The following use UnityStandardUtils.cginc as a reference, as of 2020.3.7
+
+half3 AvatarShadeSHPerPixel (half3 normal, half3 ambient, float3 worldPos)
+{
+    half3 ambient_contrib = 0.0;
+
+    #if defined(USE_SH_PER_PIXEL)
+        // Completely per-pixel
+        ambient_contrib = SHEvalLinearL0L1(half4(normal, 1.0));
+        ambient_contrib += SHEvalLinearL2(half4(normal, 1.0));
+        ambient += max(half3(0, 0, 0), ambient_contrib);
+
+        #ifdef UNITY_COLORSPACE_GAMMA
+            ambient = LinearToGammaSpace(ambient);
+        #endif
+    #else
+        // Completely per-vertex
+        // nothing to do here. Gamma conversion on ambient from SH takes place in the vertex shader, see ShadeSHPerVertex.
+    #endif
+
+    return ambient;
+}
+
+inline float3 AvatarBoxProjectedCubemapDirection(float3 worldRefl, float3 worldPos, float4 cubemapCenter, float4 boxMin, float4 boxMax) {
+  // Do we have a valid reflection probe?
+  UNITY_BRANCH
+  if (cubemapCenter.w > 0.0) {
+    float3 nrdir = normalize(worldRefl);
+
+    float3 rbmax = (boxMax.xyz - worldPos) / nrdir;
+    float3 rbmin = (boxMin.xyz - worldPos) / nrdir;
+
+    float3 rbminmax = (nrdir > 0.0f) ? rbmax : rbmin;
+
+    float fa = min(min(rbminmax.x, rbminmax.y), rbminmax.z);
+
+    worldPos -= cubemapCenter.xyz;
+    worldRefl = worldPos + nrdir * fa;
+  }
+  return worldRefl;
+}
+
+//-----------------------------------------------------------------------------------------
+// The following use UnityGlobalIllumination.cginc as a reference, as of 2020.3.7
+
+inline void AvatarResetUnityLight(out UnityLight outLight) {
+  outLight.color = half3(0, 0, 0);
+  outLight.dir = half3(0, 1, 0); // Irrelevant direction, just not null
+  outLight.ndotl = 0; // Not used
+}
+
+inline void AvatarResetUnityGI(out UnityGI outGI) {
+  AvatarResetUnityLight(outGI.light);
+  outGI.indirect.diffuse = 0;
+  outGI.indirect.specular = 0;
+}
+
+inline UnityGI AvatarUnityGI_Base(UnityGIInput data, half occlusion, half3 normalWorld) {
+  UnityGI o_gi;
+  AvatarResetUnityGI(o_gi);
+
+  o_gi.light = data.light;
+  o_gi.light.color *= data.atten;
+
+#if defined(USE_SH_PER_VERTEX) || defined(USE_SH_PER_PIXEL)
+  o_gi.indirect.diffuse = AvatarShadeSHPerPixel(normalWorld, data.ambient, data.worldPos);
+#endif
+
+  o_gi.indirect.diffuse *= occlusion;
+  return o_gi;
+}
+
+inline half3 AvatarUnityGI_IndirectSpecular(UnityGIInput data, half occlusion, Unity_GlossyEnvironmentData glossIn)
+{
+    half3 specular;
+
+    #ifdef UNITY_SPECCUBE_BOX_PROJECTION
+        // we will tweak reflUVW in glossIn directly (as we pass it to Unity_GlossyEnvironment twice for probe0 and probe1), so keep original to pass into AvatarBoxProjectedCubemapDirection
+        half3 originalReflUVW = glossIn.reflUVW;
+        glossIn.reflUVW = AvatarBoxProjectedCubemapDirection (originalReflUVW, data.worldPos, data.probePosition[0], data.boxMin[0], data.boxMax[0]);
+    #endif
+
+    #ifdef _GLOSSYREFLECTIONS_OFF
+        specular = unity_IndirectSpecColor.rgb;
+    #else
+        half3 env0 = Unity_GlossyEnvironment (UNITY_PASS_TEXCUBE(unity_SpecCube0), data.probeHDR[0], glossIn);
+        specular = env0;
+    #endif
+
+    return specular * occlusion;
+}
+
+
+inline UnityGI AvatarUnityGlobalIllumination(
+    UnityGIInput data,
+    half occlusion,
+    half3 normalWorld,
+    Unity_GlossyEnvironmentData glossIn) {
+  UnityGI o_gi = AvatarUnityGI_Base(data, occlusion, normalWorld);
+  o_gi.indirect.specular = AvatarUnityGI_IndirectSpecular(data, occlusion, glossIn);
+  return o_gi;
+}
+
+//-----------------------------------------------------------------------------------------
+// Finally our entry points into the Unity access functions come here:
+
+UnityGIInput GetGlobalIlluminationInput(
+    v2f IN,
+    fixed3 lightDir,
+    float3 worldPos,
+    float3 worldViewDir,
+    fixed attenuation) {
+  // Setup lighting environment
+  UnityGI gi;
+  UNITY_INITIALIZE_OUTPUT(UnityGI, gi);
+
+  gi.indirect.diffuse = 0;
+  gi.indirect.specular = 0;
+  gi.light.color = _LightColor0.rgb;
+  gi.light.dir = lightDir;
+
+  // Call GI (lightmaps/SH/reflections) lighting function
+  UnityGIInput giInput;
+  UNITY_INITIALIZE_OUTPUT(UnityGIInput, giInput);
+  giInput.light = gi.light;
+  giInput.worldPos = worldPos;
+  giInput.worldViewDir = worldViewDir;
+  giInput.atten = attenuation;
+
+#if defined(LIGHTMAP_ON) || defined(DYNAMICLIGHTMAP_ON)
+  giInput.lightmapUV = IN.lmap;
+#else
+  giInput.lightmapUV = 0.0;
+#endif
+
+#if defined(USE_SH_PER_VERTEX)
+  giInput.ambient = IN.sh;
+#else
+  giInput.ambient.rgb = 0.0;
+#endif
+
+  giInput.probeHDR[0] = unity_SpecCube0_HDR;
+  giInput.probeHDR[1] = unity_SpecCube1_HDR;
+#if defined(UNITY_SPECCUBE_BLENDING) || defined(UNITY_SPECCUBE_BOX_PROJECTION)
+  giInput.boxMin[0] = unity_SpecCube0_BoxMin; // .w holds lerp value for blending
+#endif
+
+#ifdef UNITY_SPECCUBE_BOX_PROJECTION
+  giInput.boxMax[0] = unity_SpecCube0_BoxMax;
+  giInput.probePosition[0] = unity_SpecCube0_ProbePosition;
+  giInput.boxMax[1] = unity_SpecCube1_BoxMax;
+  giInput.boxMin[1] = unity_SpecCube1_BoxMin;
+  giInput.probePosition[1] = unity_SpecCube1_ProbePosition;
+#endif
+
+  return giInput;
+}
+
+AvatarShaderGlobalIllumination GetGlobalIllumination(
+    UnityGIInput giInput,
+    half smoothness,
+    half metallic,
+    half occlusion,
+    half3 albedo,
+    half3 normal) {
+  Unity_GlossyEnvironmentData g = UnityGlossyEnvironmentSetup(
+      smoothness,
+      giInput.worldViewDir,
+      normal,
+      lerp(unity_ColorSpaceDielectricSpec.rgb, albedo, metallic));
+
+  UnityGI gi;
+  UNITY_INITIALIZE_OUTPUT(UnityGI, gi);
+  half ambientOcclusion = occlusion;
+  gi = AvatarUnityGlobalIllumination(giInput, ambientOcclusion, normal, g);
+
+  AvatarShaderLight light;
+  light.direction = gi.light.dir;
+  light.color = gi.light.color;
+
+  AvatarShaderIndirect indirect;
+  indirect.diffuse = gi.indirect.diffuse;
+  indirect.specular = gi.indirect.specular;
+
+  AvatarShaderGlobalIllumination avatarGI;
+  avatarGI.light = light;
+  avatarGI.indirect = indirect;
+
+  return avatarGI;
+}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/UnityGIAvatar.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/UnityGIAvatar.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..10f9353d3221f5eac3c1bd7ee77991d129a1d6e6
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/UnityGIAvatar.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 7dab7a890f34fd34d87803a5f8379930
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/functions.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/functions.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..015bba03af4657cf356c0c07f133e5f3058b37a4
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/functions.cginc
@@ -0,0 +1,106 @@
+// textures.glsl needs to be included
+
+static const float M_PI = 3.141592653589793;
+static const float c_MinReflectance = 0.04;
+
+struct AngularInfo
+{
+    float NdotL;                  // cos angle between normal and light direction
+    float NdotV;                  // cos angle between normal and view direction
+    float NdotH;                  // cos angle between normal and half vector
+    float LdotH;                  // cos angle between light direction and half vector
+
+    float VdotH;                  // cos angle between view direction and half vector
+
+    float3 padding;
+};
+
+// Find the normal for this fragment, pulling either from a predefined normal map
+// or from the interpolated mesh normal and tangent attributes.
+#ifdef HAS_TANGENTS
+float3 getNormal(float3 v_Position, float2 v_UVCoord1, float2 v_UVCoord2, float3 v_Tangent, float3 v_Bitangent, float3 v_Normal)
+#else
+float3 getNormal(float3 v_Position, float2 v_UVCoord1, float2 v_UVCoord2, float3 v_Normal)
+#endif
+{
+    float2 UV = getNormalUV(v_UVCoord1,v_UVCoord2);
+
+    // Retrieve the tangent space matrix
+#ifndef HAS_TANGENTS
+    float3 pos_dx = ddx(v_Position);
+    float3 pos_dy = ddy(v_Position);
+    float3 tex_dx = ddx(float3(UV, 0.0));
+    float3 tex_dy = ddy(float3(UV, 0.0));
+    float3 t = (tex_dy.y * pos_dx - tex_dx.y * pos_dy) / (tex_dx.x * tex_dy.y - tex_dy.x * tex_dx.y);
+
+#ifdef HAS_NORMALS
+    float3 ng = normalize(v_Normal);
+#else
+    float3 ng = cross(pos_dx, pos_dy);
+#endif
+
+    t = normalize(t - ng * dot(ng, t));
+    float3 b = normalize(cross(ng, t));
+    float3x3 tbn = float3x3(t, b, ng);
+#else // HAS_TANGENTS
+    float3x3 tbn = float3x3(v_Tangent, v_Bitangent, v_Normal);
+#endif
+
+#ifdef HAS_NORMAL_MAP
+    float3 n = texture2D(u_NormalSampler, UV).rgb;
+    n = normalize(tbn * ((2.0 * n - 1.0) * float3(u_NormalScale, u_NormalScale, 1.0)));
+#else
+    // The tbn matrix is linearly interpolated, so we need to re-normalize
+    float3 n = normalize(tbn[2].xyz);
+#endif
+
+    return n;
+}
+
+float getPerceivedBrightness(float3 vec)
+{
+    return sqrt(0.299 * vec.r * vec.r + 0.587 * vec.g * vec.g + 0.114 * vec.b * vec.b);
+}
+
+// https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness/examples/convert-between-workflows/js/three.pbrUtilities.js#L34
+float solveMetallic(float3 diffuse, float3 specular, float oneMinusSpecularStrength) {
+    float specularBrightness = getPerceivedBrightness(specular);
+
+    if (specularBrightness < c_MinReflectance) {
+        return 0.0;
+    }
+
+    float diffuseBrightness = getPerceivedBrightness(diffuse);
+
+    float a = c_MinReflectance;
+    float b = diffuseBrightness * oneMinusSpecularStrength / (1.0 - c_MinReflectance) + specularBrightness - 2.0 * c_MinReflectance;
+    float c = c_MinReflectance - specularBrightness;
+    float D = b * b - 4.0 * a * c;
+
+    return clamp((-b + sqrt(D)) / (2.0 * a), 0.0, 1.0);
+}
+
+AngularInfo getAngularInfo(float3 pointToLight, float3 normal, float3 view)
+{
+    // Standard one-letter names
+    float3 n = normalize(normal);           // Outward direction of surface point
+    float3 v = normalize(view);             // Direction from surface point to view
+    float3 l = normalize(pointToLight);     // Direction from surface point to light
+    float3 h = normalize(l + v);            // Direction of the vector between l and v
+
+    float NdotL = clamp(dot(n, l), 0.0, 1.0);
+    float NdotV = clamp(dot(n, v), 0.0, 1.0);
+    float NdotH = clamp(dot(n, h), 0.0, 1.0);
+    float LdotH = clamp(dot(l, h), 0.0, 1.0);
+    float VdotH = clamp(dot(v, h), 0.0, 1.0);
+
+    AngularInfo returnval;
+    returnval.NdotL = NdotL;
+    returnval.NdotV = NdotV;
+    returnval.NdotH = NdotH;
+    returnval.LdotH = LdotH;
+    returnval.VdotH = VdotH;
+    returnval.padding = float3(0, 0, 0);
+
+    return returnval;
+}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/functions.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/functions.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..9f19eb0b6f3b6f49410c0ee10913ab82532bd38e
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/functions.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 61013490478f6104a96c52ce930ba8e4
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/metallic-roughness-frag.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/metallic-roughness-frag.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..e38521102a0ef00cf2cd38e0672350e411ccbba2
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/metallic-roughness-frag.cginc
@@ -0,0 +1,787 @@
+// Upgrade NOTE: replaced '_LightMatrix0' with 'unity_WorldToLight'
+
+//
+// This fragment shader defines a reference implementation for Physically Based Shading of
+// a microfacet surface material defined by a glTF model.
+//
+// References:
+// [1] Real Shading in Unreal Engine 4
+//     http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf
+// [2] Physically Based Shading at Disney
+//     http://blog.selfshadow.com/publications/s2012-shading-course/burley/s2012_pbs_disney_brdf_notes_v3.pdf
+// [3] README.md - Environment Maps
+//     https://github.com/KhronosGroup/glTF-WebGL-PBR/#environment-maps
+// [4] "An Inexpensive BRDF Model for Physically based Rendering" by Christophe Schlick
+//     https://www.cs.virginia.edu/~jdl/bib/appearance/analytic%20models/schlick94b.pdf
+
+/////////////////////////////////////////////////////////
+// Unity specific defines for Avatar SDK:
+
+#include "UnityCG.cginc"
+#include "UnityLightingCommon.cginc"
+
+#define MATERIAL_METALLICROUGHNESS
+#ifdef MATERIAL_MODE_TEXTURE
+#define HAS_BASE_COLOR_MAP
+#define HAS_EMISSIVE_MAP
+#define HAS_OCCLUSION_MAP
+#define HAS_METALLIC_ROUGHNESS_MAP
+#endif
+
+// #define HAS_SPECULAR_GLOSSINESS_MAP
+// #define HAS_DIFFUSE_MAP
+// #define HAS_SPECULAR_GLOSSINESS_MAP
+
+// Turn on Punctual here
+#if !defined(LIGHTING_MODE_SH_ONLY) && !defined(LIGHTING_MODE_IBL_ONLY)
+#define USE_PUNCTUAL
+#define EYE_GLINTS
+#define EYE_GLINTS_BEHIND
+#endif
+
+#ifdef BRDF_LUT_MODE_ON
+#define USE_IBL_BRDF_LUT
+#endif
+
+#ifdef USE_IBL
+#define FLIP_IBL_WINDING // the z axis in Unity is backwards from Khronos
+#endif
+
+#define USE_HDR
+#define USE_IBL_SPECULAR_LOD
+
+// ALPHAMODE_MASK
+#define ALPHAMODE_OPAQUE
+
+// Unity Forward additive lighting adds light in multiple passes, so one light per pass is fine
+#define LIGHT_COUNT 1
+
+#include "AvatarFbxReview.cginc"
+
+/////////////////////////////////////////////////////////
+// Original ported code from metallic-roughness.frag:
+
+#include "tonemapping.cginc"
+#include "textures.cginc"
+#include "functions.cginc"
+#include "../../../../Scripts/ShaderUtils/AvatarSubmesh.cginc"
+#include "UnityGIAvatar.cginc"
+
+struct Light {
+  float3 direction;
+  float range;
+
+  float3 color;
+  float intensity;
+
+  float3 position;
+  float innerConeCos;
+
+  float outerConeCos;
+  int type;
+
+  float2 padding;
+};
+
+static const int LightType_Directional = 0;
+static const int LightType_Point = 1;
+static const int LightType_Spot = 2;
+
+#if defined(MATERIAL_SPECULARGLOSSINESS) || defined(MATERIAL_METALLICROUGHNESS)
+uniform float u_MetallicFactor;
+uniform float u_RoughnessFactor;
+uniform float4 u_BaseColorFactor;
+
+// AVATAR-SDK BEGIN
+uniform float u_F0Factor;   // this factor allows for specular attenuation similar to Babylon.js. It is not originally in the Khronos shader.
+#endif
+
+uniform float u_EyeGlintFactor = 10.0;
+
+uniform float u_DiffuseSmoothingFactor = 1.0;   // this factor allows for smooth diffuse light falloff similar to Babylon.js. It is not originally in the Khronos shader.
+// AVATAR-SDK END
+
+#ifdef MATERIAL_SPECULARGLOSSINESS
+uniform float3 u_SpecularFactor;
+uniform float4 u_DiffuseFactor;
+uniform float u_GlossinessFactor;
+#endif
+
+int u_MipCount;
+
+struct MaterialInfo {
+  float perceptualRoughness; // roughness value, as authored by the model creator (input
+                             // to shader)
+  float3 reflectance0; // full reflectance color (normal incidence angle)
+
+  float alphaRoughness; // roughness mapped to a more linear change in the roughness
+                        // (proposed by [2])
+  float3 diffuseColor; // color contribution from diffuse lighting
+
+  float3 reflectance90; // reflectance color at grazing angle
+  float3 specularColor; // color contribution from specular lighting
+};
+
+// Calculation of the lighting contribution from an optional Image Based Light source.
+// Precomputed Environment Maps are required uniform inputs and are computed as outlined in [1].
+// See our README.md on Environment Maps [3] for additional discussion.
+#if defined(USE_IBL) || defined (DEBUG_IBL) || defined (DEBUG_IBL_DIFFUSE) || defined (DEBUG_IBL_SPECULAR)
+void getIBLContribution(inout float3 total, inout float3 diffuse, inout float3 specular, MaterialInfo materialInfo, float3 n, float3 v)
+{
+    float NdotV = clamp(dot(n, v), 0.0, 1.0);
+    float3 reflection = normalize(reflect(-v, n));
+
+#ifdef USE_IBL_BRDF_LUT
+    float2 brdfSamplePoint = clamp(float2(NdotV, materialInfo.perceptualRoughness), float2(0.0, 0.0), float2(1.0, 1.0));
+    // retrieve a scale and bias to F0. See [1], Figure 3
+    float2 brdf = tex2D(u_brdfLUT, brdfSamplePoint).rg;
+#endif
+
+#ifdef FLIP_IBL_WINDING
+    float3 diffuseCoords = float3(-n.x, n.y, n.z);
+    float3 specularCoords = float3(-reflection.x, reflection.y, reflection.z);
+#else
+    float3 diffuseCoords = n;
+    float3 specularCoords = reflection;
+#endif
+
+    float4 diffuseSample = texCUBE(u_DiffuseEnvSampler, diffuseCoords);
+
+#ifdef USE_IBL_SPECULAR_LOD
+    float lod = clamp(materialInfo.perceptualRoughness * float(u_MipCount), 0.0, float(u_MipCount));
+    float4 specularSample = texCUBElod(u_SpecularEnvSampler, float4(specularCoords, lod));
+#else
+    float4 specularSample = texCUBE(u_SpecularEnvSampler, specularCoords);
+#endif
+
+    // We must make sure these values are all in Linear color space at this point:
+#ifdef USE_HDR
+    float3 diffuseLight = diffuseSample.rgb;
+    float3 specularLight = specularSample.rgb;
+#else
+    float3 diffuseLight = SRGBtoLINEAR(diffuseSample).rgb;
+    float3 specularLight = SRGBtoLINEAR(specularSample).rgb;
+#endif
+
+    diffuse = diffuseLight * materialInfo.diffuseColor;
+#ifdef USE_IBL_BRDF_LUT
+    specular = specularLight * (materialInfo.specularColor * brdf.x + brdf.y);
+#else
+    specular = specularLight * (materialInfo.specularColor);
+#endif
+    total = diffuse + specular;
+}
+#endif
+
+#if defined(USE_SH_PER_VERTEX) || defined(USE_SH_PER_PIXEL) || defined(DEBUG_SH)
+float3 getSHContribution(UnityGIInput giInput, MaterialInfo materialInfo, float occlusion, float metallic, float3 n, float3 v)
+{
+    AvatarShaderGlobalIllumination gi = GetGlobalIllumination(
+      giInput,
+      1 - materialInfo.perceptualRoughness, // smoothness
+      metallic, // metallic
+      occlusion, // occlusion
+      materialInfo.diffuseColor, // albedo
+      n // normal
+    );
+
+    // These values are all in Linear color space at this point:
+    float3 diffuseLight = gi.indirect.diffuse.rgb; // diffuse light should already be in the linear space from SH calculations.
+    float3 specularLight = gi.indirect.specular.rgb; // specular light should already be in the linear space from exr sampling.
+
+    float3 diffuse = diffuseLight * materialInfo.diffuseColor;
+    float3 specular = specularLight * materialInfo.specularColor;
+    return diffuse + specular;
+}
+#endif
+
+// Lambert lighting
+// see https://seblagarde.wordpress.com/2012/01/08/pi-or-not-to-pi-in-game-lighting-equation/
+float3 diffuse(MaterialInfo materialInfo)
+{
+    return materialInfo.diffuseColor / M_PI;
+}
+
+// The following equation models the Fresnel reflectance term of the spec equation (aka F())
+// Implementation of fresnel from [4], Equation 15
+float3 specularReflection(MaterialInfo materialInfo, AngularInfo angularInfo)
+{
+    return materialInfo.reflectance0 + (materialInfo.reflectance90 - materialInfo.reflectance0) * pow(clamp(1.0 - angularInfo.VdotH, 0.0, 1.0), 5.0);
+}
+
+// Smith Joint GGX
+// Note: Vis = G / (4 * NdotL * NdotV)
+// see Eric Heitz. 2014. Understanding the Masking-Shadowing Function in Microfacet-Based BRDFs. Journal of Computer Graphics Techniques, 3
+// see Real-Time Rendering. Page 331 to 336.
+// see https://google.github.io/filament/Filament.md.html#materialsystem/specularbrdf/geometricshadowing(specularg)
+float visibilityOcclusion(MaterialInfo materialInfo, AngularInfo angularInfo)
+{
+    float NdotL = angularInfo.NdotL;
+    float NdotV = angularInfo.NdotV;
+    float alphaRoughnessSq = materialInfo.alphaRoughness * materialInfo.alphaRoughness;
+
+    float GGXV = NdotL * sqrt(NdotV * NdotV * (1.0 - alphaRoughnessSq) + alphaRoughnessSq);
+    float GGXL = NdotV * sqrt(NdotL * NdotL * (1.0 - alphaRoughnessSq) + alphaRoughnessSq);
+
+    float GGX = GGXV + GGXL;
+    if (GGX > 0.0)
+    {
+        return 0.5 / GGX;
+    }
+    return 0.0;
+}
+
+// The following equation(s) model the distribution of microfacet normals across the area being drawn (aka D())
+// Implementation from "Average Irregularity Representation of a Roughened Surface for Ray Reflection" by T. S. Trowbridge, and K. P. Reitz
+// Follows the distribution function recommended in the SIGGRAPH 2013 course notes from EPIC Games [1], Equation 3.
+float microfacetDistribution(MaterialInfo materialInfo, AngularInfo angularInfo)
+{
+    float alphaRoughnessSq = materialInfo.alphaRoughness * materialInfo.alphaRoughness;
+    float f = (angularInfo.NdotH * alphaRoughnessSq - angularInfo.NdotH) * angularInfo.NdotH + 1.0;
+    return alphaRoughnessSq / (M_PI * f * f);
+}
+
+float3 getPointShade(float3 pointToLight, MaterialInfo materialInfo, float3 normal, float3 view)
+{
+    AngularInfo angularInfo = getAngularInfo(pointToLight, normal, view);
+
+    if (angularInfo.NdotL > 0.0 || angularInfo.NdotV > 0.0)
+    {
+        // Calculate the shading terms for the microfacet specular shading model
+        float3 F = specularReflection(materialInfo, angularInfo);
+        float Vis = visibilityOcclusion(materialInfo, angularInfo);
+        float D = microfacetDistribution(materialInfo, angularInfo);
+
+        // Calculation of analytical lighting contribution
+        float3 diffuseContrib = (1.0 - F) * diffuse(materialInfo);
+        float3 specContrib = F * Vis * D;
+
+        float3 sumContrib = float3(0.0, 0.0, 0.0);
+#ifndef DEBUG_PUNCTUAL_DIFFUSE
+        sumContrib += specContrib;
+#endif
+#ifndef DEBUG_PUNCTUAL_SPECULAR
+        sumContrib += diffuseContrib;
+#endif
+
+        // Obtain final intensity as reflectance (BRDF) scaled by the energy of the light (cosine law)
+        // AVATAR SDK BEGIN
+        // return angularInfo.NdotL * (sumContrib);
+        return pow(angularInfo.NdotL, u_DiffuseSmoothingFactor) * (sumContrib);
+        // AVATAR SDK END
+    }
+
+    return float3(0.0, 0.0, 0.0);
+}
+
+// https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_lights_punctual/README.md#range-property
+float getRangeAttenuation(float range, float distance)
+{
+    if (range <= 0.0)
+    {
+        // negative range means unlimited
+        return 1.0;
+    }
+    return max(min(1.0 - pow(distance / range, 4.0), 1.0), 0.0) / pow(distance, 2.0);
+}
+
+// https://github.com/KhronosGroup/glTF/blob/master/extensions/2.0/Khronos/KHR_lights_punctual/README.md#inner-and-outer-cone-angles
+float getSpotAttenuation(float3 pointToLight, float3 spotDirection, float outerConeCos, float innerConeCos)
+{
+    float actualCos = dot(normalize(spotDirection), normalize(-pointToLight));
+    if (actualCos > outerConeCos)
+    {
+        if (actualCos < innerConeCos)
+        {
+            return smoothstep(outerConeCos, innerConeCos, actualCos);
+        }
+        return 1.0;
+    }
+    return 0.0;
+}
+
+float3 applyDirectionalLight(Light light, MaterialInfo materialInfo, float3 normal, float3 view)
+{
+    float3 pointToLight = -light.direction;
+    float3 shade = getPointShade(pointToLight, materialInfo, normal, view);
+    return light.intensity * light.color * shade;
+}
+
+float3 applyPointLight(Light light, MaterialInfo materialInfo, float3 normal, float3 view, float3 v_Position)
+{
+    float3 pointToLight = light.position - v_Position;
+    float distance = length(pointToLight);
+    float attenuation = getRangeAttenuation(light.range, distance);
+    float3 shade = getPointShade(pointToLight, materialInfo, normal, view);
+    return attenuation * light.intensity * light.color * shade;
+}
+
+float3 applySpotLight(Light light, MaterialInfo materialInfo, float3 normal, float3 view, float3 v_Position)
+{
+    float3 pointToLight = light.position - v_Position;
+    float distance = length(pointToLight);
+    float rangeAttenuation = getRangeAttenuation(light.range, distance);
+    float spotAttenuation = getSpotAttenuation(pointToLight, light.direction, light.outerConeCos, light.innerConeCos);
+    float3 shade = getPointShade(pointToLight, materialInfo, normal, view);
+    return rangeAttenuation * spotAttenuation * light.intensity * light.color * shade;
+}
+
+Light GetUnityLight(float3 worldPos, float3 lightDir, fixed atten) {
+    Light l;
+        float3 pos = _WorldSpaceLightPos0;
+        float3 toLight = _WorldSpaceLightPos0.xyz - worldPos;
+        float3 pointdir = normalize(toLight);
+        float attenuation = 1 / (1 + dot(pointdir, pointdir));
+
+        // instead of using the original attentuation algorithm we utilize the common Unity pattern with vertex shader calculation
+        // l.type = _WorldSpaceLightPos0.w < 0.5 ? LightType_Directional : LightType_Point;
+        l.type = LightType_Directional;
+
+#if defined(DIRECTIONAL)
+        l.direction = -lightDir;
+        l.range = -1.0;
+        l.color = _LightColor0; // includes both the color and amplitude, not normalized
+        l.intensity = 5.0;  // 1.0 in Unity is 5klux, also 0.2 intensity in Unity = 1.0 intensity in GLTF, this will be modulated by the color anyways
+        l.position = pos;
+        l.innerConeCos = 1.0;
+        l.outerConeCos = 0.5;
+#else
+        float3 lightCoord = mul(unity_WorldToLight, float4(worldPos, 1)).xyz;
+        float range = length(toLight) / length(lightCoord);
+
+        l.direction = -lightDir;
+        l.range = range;
+        l.color = _LightColor0 * atten; // includes both the color and amplitude, not normalized
+        l.intensity = 5.0;  // 1.0 in Unity is 5klux, also 0.2 intensity in Unity = 1.0 intensity in GLTF, this will be modulated by the color anyways
+        l.position = pos;
+        l.innerConeCos = 1.0;
+        l.outerConeCos = 0.5;
+#endif
+
+    return l;
+}
+
+// Calculation of punctual lights (directional, point, and spotlights)
+// This code is moved here from the original Khronos shader for bookkeeping and
+// due to the fact that some of our shader compliers do not support for loops on multiple lights
+#ifdef USE_PUNCTUAL
+float3 getPunctualContribution(MaterialInfo materialInfo, float3 normal, float3 view, float3 v_Position, float3 worldPos, float3 lightDir, fixed atten)
+{
+    float3 color = float3(0.0, 0.0, 0.0);
+
+// Unity Forward additive lighting adds light in multiple passes, so one light per pass is fine
+// for (int li = 0; li < LIGHT_COUNT; ++li) {
+
+    Light light = GetUnityLight(worldPos, lightDir, atten); // see above for this custom Unity light function, instead of u_Lights[li];
+    if (light.type == LightType_Directional) {
+        color += applyDirectionalLight(light, materialInfo, normal, view);
+    } else if (light.type == LightType_Point) {
+        color += applyPointLight(light, materialInfo, normal, view, v_Position);
+    } else if (light.type == LightType_Spot) {
+        color += applySpotLight(light, materialInfo, normal, view, v_Position);
+    }
+
+// }
+    return color;
+}
+#endif
+
+fixed4 frag (v2f i) : SV_Target
+{
+    UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i);
+
+    // Metallic and Roughness material properties are packed together
+    // In glTF, these factors can be specified by fixed scalar values
+    // or from a metallic-roughness map
+    float perceptualRoughness = 0.0;
+    float metallic = 0.0;
+    float4 baseColor = float4(0.0, 0.0, 0.0, 1.0);
+    float3 diffuseColor = float3(0,0,0);
+    float3 specularColor = float3(0,0,0);
+    float3 f0 = float3(0.04, 0.04, 0.04);
+
+#ifdef MATERIAL_SPECULARGLOSSINESS
+
+#ifdef HAS_SPECULAR_GLOSSINESS_MAP
+    float4 sgSample =
+        SRGBtoLINEAR(tex2D(u_SpecularGlossinessSampler, getSpecularGlossinessUV(i.v_UVCoord1, i.v_UVCoord2)));
+    perceptualRoughness =
+        (1.0 - sgSample.a * u_GlossinessFactor); // glossiness to roughness
+    f0 = sgSample.rgb * u_SpecularFactor; // specular
+#else
+    f0 = u_SpecularFactor;
+    perceptualRoughness = 1.0 - u_GlossinessFactor;
+#endif // ! HAS_SPECULAR_GLOSSINESS_MAP
+
+// AVATAR SDK BEGIN
+    // IMPORTANT: Not originally part of the shader but used for tuning in viewers like Babylon
+    f0 *= u_F0Factor;
+// AVATAR SDK END
+
+    // f0 = specular
+    specularColor = f0;
+    float oneMinusSpecularStrength = 1.0 - max(max(f0.r, f0.g), f0.b);
+    diffuseColor = baseColor.rgb * oneMinusSpecularStrength;
+
+#ifdef DEBUG_METALLIC
+    // do conversion between metallic M-R and S-G metallic
+    metallic = solveMetallic(baseColor.rgb, specularColor, oneMinusSpecularStrength);
+#endif // ! DEBUG_METALLIC
+
+#endif // ! MATERIAL_SPECULARGLOSSINESS
+
+#ifdef MATERIAL_METALLICROUGHNESS
+
+#ifdef HAS_METALLIC_ROUGHNESS_MAP
+    // Roughness is stored in the 'g' channel, metallic is stored in the 'b' channel.
+    // This layout intentionally reserves the 'r' channel for (optional) occlusion map
+    // data
+#ifdef MATERIAL_MODE_TEXTURE
+    float4 ormSample = tex2D(u_MetallicRoughnessSampler, getMetallicRoughnessUV(i.v_UVCoord1, i.v_UVCoord2));
+    perceptualRoughness = ormSample.g * u_RoughnessFactor;
+    metallic = ormSample.b * u_MetallicFactor;
+#else
+    perceptualRoughness = i.v_ORMT.g * u_RoughnessFactor;
+    metallic = i.v_ORMT.b * u_MetallicFactor;
+#endif
+#else
+    metallic = u_MetallicFactor;
+    perceptualRoughness = u_RoughnessFactor;
+#endif
+
+// AVATAR BEGIN
+// In order to match Babylon, Roughness must be squared
+//    perceptualRoughness = perceptualRoughness* perceptualRoughness;
+// AVATAR END
+
+    // The albedo may be defined from a base texture or a flat color
+#if defined(HAS_BASE_COLOR_MAP) && defined(MATERIAL_MODE_TEXTURE)
+    baseColor = tex2D(u_BaseColorSampler, getBaseColorUV(i.v_UVCoord1, i.v_UVCoord2)) *
+        u_BaseColorFactor; // NOTE: for ease of use of the Unity editor, I moved the u_BaseColorFactor into the SRGBtoLinear conversion.
+#else
+    baseColor.rgb = i.v_Color.rgb * u_BaseColorFactor.rgb;
+    baseColor.a = u_BaseColorFactor.a;
+#endif
+
+    // Avatar Fbx Review Tool
+#ifdef UNITY_COLORSPACE_GAMMA
+    baseColor = SRGBtoLINEAR(PalettizedAlbedo(baseColor));  // this is needed because it seems the standalone integration does not load the basecolor as SRGB as it should be
+#else
+    baseColor = PalettizedAlbedo(baseColor);
+#endif
+
+    // IMPORTANT: Not originally part of the shader but used for tuning in viewers like Babylon
+    f0 *= u_F0Factor;
+
+    diffuseColor = baseColor.rgb * (float3(1.0,1.0,1.0) - f0) * (1.0 - metallic);
+
+    specularColor = lerp(f0, baseColor.rgb, metallic);
+
+#endif // ! MATERIAL_METALLICROUGHNESS
+
+#ifdef ALPHAMODE_MASK
+    if (baseColor.a < u_AlphaCutoff) {
+        discard;
+    }
+    baseColor.a = 1.0;
+#endif
+
+#ifdef ALPHAMODE_OPAQUE
+    baseColor.a = 1.0;
+#endif
+
+#ifdef MATERIAL_UNLIT
+    gl_FragColor = float4(LINEARtoSRGB(baseColor.rgb), baseColor.a);
+    return;
+#endif
+
+    perceptualRoughness = clamp(perceptualRoughness, 0.0, 1.0);
+    metallic = clamp(metallic, 0.0, 1.0);
+
+    // Roughness is authored as perceptual roughness; as is convention,
+    // convert to material roughness by squaring the perceptual roughness [2].
+    float alphaRoughness = perceptualRoughness * perceptualRoughness;
+
+    // Compute reflectance.
+    float reflectance = max(max(specularColor.r, specularColor.g), specularColor.b);
+
+    float3 specularEnvironmentR0 = specularColor.rgb;
+    // Anything less than 2% is physically impossible and is instead considered to be
+    // shadowing. Compare to "Real-Time-Rendering" 4th editon on page 325.
+    float reflectanceclamp = clamp(reflectance * 50.0, 0.0, 1.0);
+    float3 specularEnvironmentR90 = float3(reflectanceclamp, reflectanceclamp,reflectanceclamp);
+// AVATAR BEGIN
+// In order for this shader to even come close to Babylon and other off the shelf viewers, the
+// specularEnvironmentR90 has to be affected by the specular color. If not, all sort of greyish
+// desaturation colors occur.
+    specularEnvironmentR90 *= specularColor.rgb;
+// AVATAR END
+
+    MaterialInfo materialInfo;
+    materialInfo.perceptualRoughness = perceptualRoughness;
+    materialInfo.reflectance0 = specularEnvironmentR0;
+    materialInfo.alphaRoughness = alphaRoughness;
+    materialInfo.diffuseColor = diffuseColor;
+    materialInfo.reflectance90 = specularEnvironmentR90;
+    materialInfo.specularColor = specularColor;
+
+    // LIGHTING
+    float3 color = float3(0.0, 0.0, 0.0);
+
+#ifdef HAS_TANGENTS
+    float3 normal = getNormal(i.v_Position, i.v_UVCoord1, i.v_UVCoord2, i.v_Tangent, i.v_Bitangent, i.v_Normal);
+#else
+    float3 normal = getNormal(i.v_Position, i.v_UVCoord1, i.v_UVCoord2, i.v_Normal);
+#endif
+
+    // NOTE: (jsepulveda, 4/3/21)
+    // This is the most difficult part to port - the original GLSL code used u_Camera.
+    // u_Camera must be in local space, and be calculated from _WorldSpaceCameraPos as:
+    float3 localCameraPos = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos, 1.0));
+    float3 view = normalize(_WorldSpaceCameraPos - i.worldPos); // VERY IMPORTANT: Cannot use i.v_Position here
+
+#if defined(USE_PUNCTUAL) || defined(USE_SH_PER_VERTEX) || defined(USE_SH_PER_PIXEL) || defined(DEBUG_SH)
+    fixed atten = LIGHT_ATTENUATION(i); // SPOT/POINT: This gets you the attenuation + shadow value.
+#endif
+
+#ifdef USE_PUNCTUAL
+#ifdef EYE_GLINTS
+    bool useEyeGlint =
+        ((i.v_Color.a > (SUBMESH_TYPE_L_EYE - SUBMESH_TYPE_BUFFER) / 255.0) &&
+         (i.v_Color.a < (SUBMESH_TYPE_R_EYE + SUBMESH_TYPE_BUFFER) / 255.0));
+    [branch]
+    if (useEyeGlint)
+    {
+      // color = float4(0, 1, 1, 1); // useful to debug eye cutout
+
+#ifdef EYE_GLINTS_BEHIND
+      // create a second reflected spec light to maintain an eye glint from the backside
+      float3 mirroredLightDirection = float3(-i.lightDir.x, i.lightDir.y, -i.lightDir.z);
+      materialInfo.diffuseColor = float3(0,0,0); // attenuate all the diffuse part, glint only comes from spec
+      color += getPunctualContribution(materialInfo, normal, view, i.v_Position, i.worldPos, mirroredLightDirection, atten);
+      materialInfo.diffuseColor = diffuseColor; // restore to original
+#endif
+
+      materialInfo.reflectance0 *= u_EyeGlintFactor; // amplify the spec into an eye glint
+    }
+#endif
+
+   color += getPunctualContribution(materialInfo, normal, view, i.v_Position, i.worldPos, i.lightDir, atten);
+
+    materialInfo.reflectance0 = specularEnvironmentR0; // restore to original
+#endif
+
+    // Calculate lighting contribution from image based lighting source (IBL)
+#if defined(USE_IBL) || defined (DEBUG_IBL) || defined (DEBUG_IBL_DIFFUSE) || defined (DEBUG_IBL_SPECULAR)
+    float3 totalIbl;
+    float3 diffuseIbl;
+    float3 specularIbl;
+    getIBLContribution(totalIbl, diffuseIbl, specularIbl, materialInfo, normal, view);
+    color += totalIbl;
+#endif
+
+#if defined(USE_SH_PER_VERTEX) || defined(USE_SH_PER_PIXEL) || defined(DEBUG_SH)
+    UnityGIInput giInput = GetGlobalIlluminationInput(i, i.lightDir, i.worldPos, view, atten); // the view here is supposed to be worldViewDir
+#endif
+
+#if defined(USE_SH_PER_VERTEX) || defined(USE_SH_PER_PIXEL)
+    // in what follows, set occlusion to 1 since it is calculated later using our own method
+    color += getSHContribution(giInput, materialInfo, 1.0, metallic, normal, view);
+#endif
+
+    float ao = 1.0;
+    // Apply optional PBR terms for additional (optional) shading
+#ifdef HAS_OCCLUSION_MAP
+#ifdef MATERIAL_MODE_TEXTURE
+#ifdef USE_ORM_EXTENSION
+    ao = ormSample.r;
+#else
+    ao = tex2D(u_OcclusionSampler, getOcclusionUV(i.v_UVCoord1, i.v_UVCoord2)).r;
+#endif
+#else
+    ao = i.v_ORMT.r;
+#endif
+    color = lerp(color, color * ao, u_OcclusionStrength);
+#endif
+
+    float3 emissive = float3(0,0,0);
+#ifdef HAS_EMISSIVE_MAP
+    emissive = SRGBtoLINEAR(tex2D(u_EmissiveSampler, getEmissiveUV(i.v_UVCoord1, i.v_UVCoord2))).rgb *
+        u_EmissiveFactor;
+    color += emissive;
+#endif
+
+    float4 gl_FragColor;    // local variable for compatibility with source glsl shader
+
+#if DEBUG_DISABLE_FOR_ADDITIVE
+    gl_FragColor = float4(0, 0, 0, baseColor.a);
+
+#else
+
+#if !(DEBUG_LIGHTING)
+
+    // regular shading
+    gl_FragColor = float4(toneMap(color), baseColor.a);
+
+#else // debug output
+
+#ifdef DEBUG_METALLIC
+#ifdef UNITY_COLORSPACE_GAMMA
+  gl_FragColor.rgb = float3(metallic, metallic, metallic);
+#else
+  gl_FragColor.rgb = SRGBtoLINEAR(float3(metallic, metallic, metallic));
+#endif
+#endif
+
+#ifdef DEBUG_ROUGHNESS
+#ifdef UNITY_COLORSPACE_GAMMA
+    gl_FragColor.rgb = float3(perceptualRoughness, perceptualRoughness,perceptualRoughness);
+#else
+    gl_FragColor.rgb = SRGBtoLINEAR(float3(perceptualRoughness, perceptualRoughness,perceptualRoughness));
+#endif
+#endif
+
+#ifdef DEBUG_NORMAL
+#ifdef HAS_TANGENTS
+    gl_FragColor.rgb = getNormal(i.v_Position, i.v_UVCoord1, i.v_UVCoord2, i.v_Tangent, i.v_Bitangent, i.v_Normal);
+#else
+    gl_FragColor.rgb = getNormal(i.v_Position, i.v_UVCoord1, i.v_UVCoord2, i.v_Normal);
+#endif
+#endif
+
+#ifdef DEBUG_NORMAL_MAP
+#ifdef HAS_NORMAL_MAP
+    gl_FragColor.rgb = tex2D(u_NormalSampler, getNormalUV(i.v_UVCoord1,i.v_UVCoord2)).rgb;
+#else
+    gl_FragColor.rgb = float3(0.5, 0.5, 1.0);
+#endif
+#endif
+
+#ifdef DEBUG_BASECOLOR
+#ifdef UNITY_COLORSPACE_GAMMA
+    gl_FragColor.rgb = baseColor.rgb;
+#else
+    gl_FragColor.rgb = LINEARtoSRGB(baseColor.rgb);
+#endif
+#endif
+
+#ifdef DEBUG_OCCLUSION
+#ifdef UNITY_COLORSPACE_GAMMA
+    gl_FragColor.rgb = float3(ao, ao, ao);
+#else
+    gl_FragColor.rgb = SRGBtoLINEAR(float3(ao, ao, ao));
+#endif
+#endif
+
+#ifdef DEBUG_EMISSIVE
+    gl_FragColor.rgb = LINEARtoSRGB(emissive);
+#endif
+
+#ifdef DEBUG_F0
+    gl_FragColor.rgb = f0;
+#endif
+
+#ifdef DEBUG_ALPHA
+    gl_FragColor.rgb = float3(baseColor.a, baseColor.a, baseColor.a);
+#endif
+
+#ifdef DEBUG_VIEW
+    gl_FragColor.rgb = view;
+#endif
+
+#ifdef DEBUG_PUNCTUAL
+#ifdef USE_PUNCTUAL
+    gl_FragColor.rgb = getPunctualContribution(materialInfo, normal, view, i.v_Position, i.worldPos, i.lightDir, atten);
+    gl_FragColor = float4(toneMap(gl_FragColor.rgb), gl_FragColor.a);
+#endif
+#endif
+
+#ifdef DEBUG_PUNCTUAL_SPECULAR
+#ifdef USE_PUNCTUAL
+    gl_FragColor.rgb = getPunctualContribution(materialInfo, normal, view, i.v_Position, i.worldPos, i.lightDir, atten);
+    gl_FragColor = float4(toneMap(gl_FragColor.rgb), gl_FragColor.a);
+#endif
+#endif
+
+#ifdef DEBUG_PUNCTUAL_DIFFUSE
+#ifdef USE_PUNCTUAL
+    gl_FragColor.rgb = getPunctualContribution(materialInfo, normal, view, i.v_Position, i.worldPos, i.lightDir, atten);
+    gl_FragColor = float4(toneMap(gl_FragColor.rgb), gl_FragColor.a);
+#endif
+#endif
+
+#ifdef DEBUG_IBL
+    gl_FragColor.rgb = totalIbl;
+    gl_FragColor = float4(toneMap(gl_FragColor.rgb), gl_FragColor.a);
+#endif
+
+#ifdef DEBUG_IBL_DIFFUSE
+    gl_FragColor.rgb = diffuseIbl;
+    gl_FragColor = float4(toneMap(gl_FragColor.rgb), gl_FragColor.a);
+#endif
+
+#ifdef DEBUG_IBL_SPECULAR
+    gl_FragColor.rgb = specularIbl;
+float3 reflection = normalize(reflect(-view, normal));
+float3 specularCoords = float3(-reflection.x, reflection.y, reflection.z);
+float lod = clamp(materialInfo.perceptualRoughness * float(u_MipCount), 0.0, float(u_MipCount));
+float4 specularSample = texCUBElod(u_SpecularEnvSampler, float4(specularCoords, lod));
+gl_FragColor.rgb = specularSample * materialInfo.specularColor; // lerp(f0, baseColor.rgb, metallic);
+
+    gl_FragColor = float4(toneMap(gl_FragColor.rgb), gl_FragColor.a);
+#endif
+
+#ifdef DEBUG_SH
+    gl_FragColor.rgb = getSHContribution(giInput, materialInfo, 1.0, metallic, normal, view);
+    gl_FragColor = float4(toneMap(gl_FragColor.rgb), gl_FragColor.a);
+#endif
+
+#ifdef DEBUG_NO_TONE_MAP
+    gl_FragColor = float4(color, baseColor.a);
+#endif
+
+#ifdef DEBUG_SUBMESHES
+    float sudmeshId = i.v_Color.a + (0.5/255.0);
+    if (sudmeshId < 1024.0 / 255.0) {
+        gl_FragColor.rgb = vec3(1.0, 1.0, 1.0); // earrings       // 10
+    }
+    if (sudmeshId < 512.0 / 255.0) {
+        gl_FragColor.rgb = vec3(0.1, 0.1, 0.1);  // headwear     // 9
+    }
+    if (sudmeshId < 256.0 / 255.0) {
+        gl_FragColor.rgb = vec3(0.2, 0.1, 0.05);  // Facial Hair // 8
+    }
+    if (sudmeshId < 128.0 / 255.0) {
+        gl_FragColor.rgb = vec3(0.5, 0.0, 0.0);  // Lashes       // 7
+    }
+    if (sudmeshId < 64.0 / 255.0) {
+        gl_FragColor.rgb = vec3(0.0, 1.0, 0.0);  // R eye        // 6
+    }
+    if (sudmeshId < 32.0 / 255.0) {
+        gl_FragColor.rgb = vec3(0.0, 0.0, 1.0);  // L eye        // 5
+    }
+    if (sudmeshId < 16.0 / 255.0) {
+        gl_FragColor.rgb = vec3(0.24, 0.19, 0.08);  // brow      // 4
+    }
+    if (sudmeshId < 8.0 / 255.0) {
+        gl_FragColor.rgb = vec3(0.345, 0.27, 0.11);  // hair     // 3
+    }
+    if (sudmeshId < 4.0 / 255.0) {
+        gl_FragColor.rgb = vec3(0.77, 0.65, 0.65); // head       // 2
+    }
+    if (sudmeshId < 2.0 / 255.0) {
+        gl_FragColor.rgb = vec3(0.77, 0.65, 0.65); // body       // 1
+    }
+    if (sudmeshId < 1.0 / 255.0) {
+        gl_FragColor.rgb = vec3(0.2, 0.2, 0.2); // outfit        // 0
+    }
+
+#endif
+
+    gl_FragColor.a = 1.0;
+
+#endif // DEBUG_LIGHTING
+#endif // DEBUG_DISABLE_FOR_ADDITIVE
+
+    return gl_FragColor;
+}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/metallic-roughness-frag.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/metallic-roughness-frag.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..f321a526e59dc10b33bb98305610fc071dc0bde7
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/metallic-roughness-frag.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 59b2bcd872471bf4283221e84aa5caa7
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/primitive-vert.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/primitive-vert.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..0252af7a00dbdae850d8470ad4c99d16df4c4266
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/primitive-vert.cginc
@@ -0,0 +1,154 @@
+/////////////////////////////////////////////////////////
+// Unity specific defines for Avatar SDK:
+
+// Shader feature defines. If we want to change these via the shader manager,
+// convert them to multi_compile or shader_feature
+
+// #define HAS_VERTEX_COLOR_float3
+// #define HAS_VERTEX_COLOR_float4
+#define HAS_NORMALS
+#define OVR_VERTEX_HAS_VERTEX_ID
+// #define HAS_SECOND_UV
+
+#if defined(OVR_VERTEX_HAS_TANGENTS)
+#define HAS_TANGENTS
+#endif
+
+// Avatar SDK specific include files, shared with all the other avatar shaders:
+#define OVR_VERTEX_POSITION_FIELD_NAME v_Position
+#define OVR_VERTEX_NORMAL_FIELD_NAME v_Normal
+#define OVR_VERTEX_TANGENT_FIELD_NAME v_TBN
+#define OVR_VERTEX_VERT_ID_FIELD_NAME v_Id
+#define OVR_VERTEX_TEXCOORD_FIELD_NAME v_UVCoord1
+#define OVR_VERTEX_COLOR_FIELD_NAME v_Color
+#define OVR_VERTEX_ORMT_FIELD_NAME v_ORMT
+#include "UnityCG.cginc"
+#include "../../../../Scripts/ShaderUtils/AvatarCustomTypes.cginc"
+
+#include "../../../../Scripts/ShaderUtils/AvatarCustom.cginc"
+
+// This includes the required macros for shadow and attenuation values.
+#include "AutoLight.cginc"
+
+/////////////////////////////////////////////////////////
+// Original ported code from primitive.vert:
+
+// Composite v2f structure derived from the original Khronos cginc files.
+struct v2f
+{
+    float4 v_Color : COLOR;
+ #ifdef MATERIAL_MODE_VERTEX
+    float4 v_ORMT : TEXCOORD1; // holds ormt vertex color channel
+ #endif
+
+    float4 v_Position : SV_POSITION;
+
+    float2 v_UVCoord1 : TEXCOORD0;
+    float2 v_UVCoord2 : TEXCOORD11;    // always define this to simplify functions
+
+#ifdef HAS_NORMALS
+#ifdef HAS_TANGENTS
+    float3 v_Tangent : TEXCOORD6;
+    float3 v_Bitangent : TEXCOORD7;
+#endif
+    float3 v_Normal : TEXCOORD2;
+#endif
+
+#if defined(OVR_VERTEX_HAS_VERTEX_ID)
+    UNITY_VERTEX_INPUT_INSTANCE_ID  // uint v_Id : TEXCOORD3;
+#endif
+
+    float3 worldPos : TEXCOORD3;
+
+    float3 lightDir : TEXCOORD4;
+    DECLARE_LIGHT_COORDS(5)
+
+#ifdef USE_SH_PER_VERTEX // vertex based sh
+    half3 sh : TEXCOORD8;
+#endif
+
+    UNITY_VERTEX_OUTPUT_STEREO
+};
+
+//-----------------------------------------------------------------------------------------
+// The following use UnityStandardUtils.cginc as a reference, as of 2020.3.7
+
+half3 AvatarShadeSHPerVertex(half3 normal, half3 ambient) {
+#if defined(USE_SH_PER_PIXEL)
+  // Completely per-pixel
+  // nothing to do here
+#else
+  // Completely per-vertex
+  ambient += max(half3(0, 0, 0), ShadeSH9(half4(normal, 1.0)));
+#endif
+  return ambient;
+}
+
+//-----------------------------------------------------------------------------------------
+
+v2f vert(OvrDefaultAppdata v) {
+    OvrInitializeDefaultAppdata(v);
+
+    v2f o;
+    UNITY_INITIALIZE_OUTPUT(v2f, o);
+    UNITY_TRANSFER_INSTANCE_ID(v, o);
+    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
+
+    // Object -> world transformation
+    OvrVertexData vertexData = OVR_CREATE_VERTEX_DATA(v);
+
+    // Pass through clip space position, uv
+    o.v_Position = UnityObjectToClipPos(vertexData.position);
+
+#ifdef HAS_SECOND_UV
+    o.v_UVCoord2 = o.v_UVCoord2;
+#else
+    o.v_UVCoord2 = o.v_UVCoord1;
+#endif
+
+#ifdef MATERIAL_MODE_VERTEX
+    o.v_ORMT = OVR_GET_VERTEX_ORMT(v);
+    o.v_Color = OVR_GET_VERTEX_COLOR(v);
+#else
+    o.v_Color.a = OVR_GET_VERTEX_COLOR(v).a;
+#endif
+
+#ifdef HAS_NORMALS
+#ifdef HAS_TANGENTS
+    float4 tangent = vertexData.tangent;
+
+//    float3 normalW = normalize(float3(u_NormalMatrix * float4(getNormal(vertexData.position, o.v_UVCoord1, o.v_UVCoord2, vertexData.normal).xyz, 0.0)));
+//    float3 tangentW = normalize(float3(u_ModelMatrix * float4(tangent.xyz, 0.0)));
+
+    float3 normalW = UnityObjectToWorldNormal(vertexData.normal);
+    float3 tangentW = UnityObjectToWorldDir(tangent.xyz);
+    float3 bitangentW = cross(normalW, tangentW) * tangent.w;
+    o.v_Tangent = tangentW;
+    o.v_Bitangent = bitangentW;
+    o.v_Normal =  normalW;
+#else
+    o.v_Normal = UnityObjectToWorldNormal(vertexData.normal);
+#endif
+#endif
+
+    o.v_UVCoord1 = OVR_GET_VERTEX_TEXCOORD(v);
+
+    o.worldPos = mul(unity_ObjectToWorld, vertexData.position).xyz;
+
+    o.lightDir = WorldSpaceLightDir(vertexData.position).xyz;
+
+    // taken from "AutoLight.cginc", COMPUTE_LIGHT_COORDS(), but specialized to accomodate vertexData.position
+#ifdef POINT
+    o._LightCoord = mul(unity_WorldToLight, mul(unity_ObjectToWorld, vertexData.position)).xyz;
+#endif
+#ifdef SPOT
+    o._LightCoord = mul(unity_WorldToLight, mul(unity_ObjectToWorld, vertexData.position));
+#endif
+
+#ifdef USE_SH_PER_VERTEX // vertex based sh
+    o.sh = 0;
+    o.sh = AvatarShadeSHPerVertex(o.v_Normal, o.sh);
+#endif
+
+    return o;
+}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/primitive-vert.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/primitive-vert.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..0caa8a87b3fccf96085036c44296695887cb8246
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/primitive-vert.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: ebf38c92d2ee45944bc6a265934d1e07
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/textures.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/textures.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..eeb311a52c14dcdbc4e0ec381b5865a8de0eabd1
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/textures.cginc
@@ -0,0 +1,152 @@
+// TODO: Replace all float3x3 UV transforms with the Unity ST format if needed...
+
+// General Material
+#ifdef HAS_NORMAL_MAP
+uniform sampler2D u_NormalSampler;
+uniform float u_NormalScale;
+uniform int u_NormalUVSet;
+//uniform float3x3 u_NormalUVTransform;
+#endif
+
+#ifdef HAS_EMISSIVE_MAP
+uniform sampler2D u_EmissiveSampler;
+uniform int u_EmissiveUVSet;
+uniform float3 u_EmissiveFactor;
+//uniform float3x3 u_EmissiveUVTransform;
+#endif
+
+#ifdef HAS_OCCLUSION_MAP
+#ifndef USE_ORM_EXTENSION
+uniform sampler2D u_OcclusionSampler;
+uniform int u_OcclusionUVSet;
+#endif
+uniform float u_OcclusionStrength;
+//uniform float3x3 u_OcclusionUVTransform;
+#endif
+
+// Metallic Roughness Material
+#ifdef HAS_BASE_COLOR_MAP
+uniform sampler2D u_BaseColorSampler;
+uniform int u_BaseColorUVSet;
+//uniform float3x3 u_BaseColorUVTransform;
+#endif
+
+#ifdef HAS_METALLIC_ROUGHNESS_MAP
+uniform sampler2D u_MetallicRoughnessSampler;
+uniform int u_MetallicRoughnessUVSet;
+//uniform float3x3 u_MetallicRoughnessUVTransform;
+#endif
+
+// Specular Glossiness Material
+#ifdef HAS_DIFFUSE_MAP
+uniform sampler2D u_DiffuseSampler;
+uniform int u_DiffuseUVSet;
+//uniform float3x3 u_DiffuseUVTransform;
+#endif
+
+#ifdef HAS_SPECULAR_GLOSSINESS_MAP
+uniform sampler2D u_SpecularGlossinessSampler;
+uniform int u_SpecularGlossinessUVSet;
+//uniform float3x3 u_SpecularGlossinessUVTransform;
+#endif
+
+// IBL
+#if defined(USE_IBL) || defined(DEBUG_IBL)
+uniform samplerCUBE u_DiffuseEnvSampler;
+uniform samplerCUBE u_SpecularEnvSampler;
+uniform sampler2D u_brdfLUT;
+#endif
+
+float2 getNormalUV(float2 v_UVCoord1, float2 v_UVCoord2) 
+{
+    float3 uv = float3(v_UVCoord1, 1.0);
+#ifdef HAS_NORMAL_MAP
+    uv.xy = u_NormalUVSet < 1 ? v_UVCoord1 : v_UVCoord2;
+    // TODO: Replace this with the Unity ST format if needed...
+    //#ifdef HAS_NORMAL_UV_TRANSFORM
+    //uv *= u_NormalUVTransform;
+    //#endif
+#endif
+    return uv.xy;
+}
+
+float2 getEmissiveUV(float2 v_UVCoord1, float2 v_UVCoord2)
+{
+    float3 uv = float3(v_UVCoord1, 1.0);
+#ifdef HAS_EMISSIVE_MAP
+    uv.xy = u_EmissiveUVSet < 1 ? v_UVCoord1 : v_UVCoord2;
+    // TODO: Replace this with the Unity ST format if needed...
+    //#ifdef HAS_EMISSIVE_UV_TRANSFORM
+    //uv *= u_EmissiveUVTransform;
+    //#endif
+#endif
+
+    return uv.xy;
+}
+
+#ifndef USE_ORM_EXTENSION
+float2 getOcclusionUV(float2 v_UVCoord1, float2 v_UVCoord2) 
+{
+    float3 uv = float3(v_UVCoord1, 1.0);
+#ifdef HAS_OCCLUSION_MAP
+    uv.xy = u_OcclusionUVSet < 1 ? v_UVCoord1 : v_UVCoord2;
+    // TODO: Replace this with the Unity ST format if needed...
+    //#ifdef HAS_OCCLSION_UV_TRANSFORM
+    //uv *= u_OcclusionUVTransform;
+    //#endif
+#endif
+    return uv.xy;
+}
+#endif
+
+float2 getBaseColorUV(float2 v_UVCoord1, float2 v_UVCoord2)
+{
+    float3 uv = float3(v_UVCoord1, 1.0);
+#ifdef HAS_BASE_COLOR_MAP
+    uv.xy = u_BaseColorUVSet < 1 ? v_UVCoord1 : v_UVCoord2;
+    // TODO: Replace this with the Unity ST format if needed...
+    //#ifdef HAS_BASECOLOR_UV_TRANSFORM
+    //uv *= u_BaseColorUVTransform;
+    //#endif
+#endif
+    return uv.xy;
+}
+
+float2 getMetallicRoughnessUV(float2 v_UVCoord1, float2 v_UVCoord2)
+{
+    float3 uv = float3(v_UVCoord1, 1.0);
+#ifdef HAS_METALLIC_ROUGHNESS_MAP
+    uv.xy = u_MetallicRoughnessUVSet < 1 ? v_UVCoord1 : v_UVCoord2;
+    // TODO: Replace this with the Unity ST format if needed...
+    //#ifdef HAS_METALLICROUGHNESS_UV_TRANSFORM
+    //uv *= u_MetallicRoughnessUVTransform;
+    //#endif
+#endif
+    return uv.xy;
+}
+
+float2 getSpecularGlossinessUV(float2 v_UVCoord1, float2 v_UVCoord2)
+{
+    float3 uv = float3(v_UVCoord1, 1.0);
+#ifdef HAS_SPECULAR_GLOSSINESS_MAP
+    uv.xy = u_SpecularGlossinessUVSet < 1 ? v_UVCoord1 : v_UVCoord2;
+    // TODO: Replace this with the Unity ST format if needed...
+    //#ifdef HAS_SPECULARGLOSSINESS_UV_TRANSFORM
+    //uv *= u_SpecularGlossinessUVTransform;
+    //#endif
+#endif
+    return uv.xy;
+}
+
+float2 getDiffuseUV(float2 v_UVCoord1, float2 v_UVCoord2)
+{
+    float3 uv = float3(v_UVCoord1, 1.0);
+#ifdef HAS_DIFFUSE_MAP
+    uv.xy = u_DiffuseUVSet < 1 ? v_UVCoord1 : v_UVCoord2;
+    // TODO: Replace this with the Unity ST format if needed...
+    //#ifdef HAS_DIFFUSE_UV_TRANSFORM
+    //uv *= u_DiffuseUVTransform;
+    //#endif
+#endif
+    return uv.xy;
+}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/textures.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/textures.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..cc91240c6ca6377d254931cea062d9154dccfab1
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/textures.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: ece7436934ba4424a89e1166f9eb52c5
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/tonemapping.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/tonemapping.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..795f6aa8835e2c7a4e4d4ecd1562414b900f44bc
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/tonemapping.cginc
@@ -0,0 +1,90 @@
+uniform float u_Exposure;
+
+static const float GAMMA = 2.2;
+static const float INV_GAMMA = 1.0 / 2.2;
+
+// linear to sRGB approximation
+// see http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html
+float3 LINEARtoSRGB(float3 color)
+{
+  return pow(color, float3(INV_GAMMA, INV_GAMMA, INV_GAMMA));
+}
+
+float4 LINEARtoSRGB(float4 color) {
+  return float4(pow(color.xyz, float3(INV_GAMMA, INV_GAMMA, INV_GAMMA)), color.w);
+}
+
+// sRGB to linear approximation
+// see http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html
+float4 SRGBtoLINEAR(float4 srgbIn)
+{
+  return float4(pow(srgbIn.xyz, float3(GAMMA, GAMMA, GAMMA)), srgbIn.w);
+}
+
+float3 SRGBtoLINEAR(float3 srgbIn) {
+  return pow(srgbIn.xyz, float3(GAMMA, GAMMA, GAMMA));
+}
+
+// Uncharted 2 tone map
+// see: http://filmicworlds.com/blog/filmic-tonemapping-operators/
+float3 toneMapUncharted2Impl(float3 color)
+{
+    static const float UA = 0.15;
+    static const float UB = 0.50;
+    static const float UC = 0.10;
+    static const float UD = 0.20;
+    static const float UE = 0.02;
+    static const float UF = 0.30;
+    return ((color*(UA*color+UC*UB)+UD*UE)/(color*(UA*color+UB)+UD*UF))-UE/UF;
+}
+
+float3 toneMapUncharted(float3 color)
+{
+    static const float W = 11.2;
+    color = toneMapUncharted2Impl(color * 2.0);
+    float3 whiteScale = 1.0 / toneMapUncharted2Impl(float3(W, W, W));
+    return LINEARtoSRGB(color * whiteScale);
+}
+
+// Hejl Richard tone map
+// see: http://filmicworlds.com/blog/filmic-tonemapping-operators/
+float3 toneMapHejlRichard(float3 color)
+{
+    color = max(float3(0.0, 0.0, 0.0), color - float3(0.004, 0.004, 0.004));
+    return (color*(6.2*color+.5))/(color*(6.2*color+1.7)+0.06);
+}
+
+// ACES tone map
+// see: https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/
+float3 toneMapACES(float3 color)
+{
+    static const float A = 2.51;
+    static const float B = 0.03;
+    static const float C = 2.43;
+    static const float D = 0.59;
+    static const float E = 0.14;
+    return LINEARtoSRGB(clamp((color * (A * color + B)) / (color * (C * color + D) + E), 0.0, 1.0));
+}
+
+float3 toneMap(float3 color)
+{
+    color *= u_Exposure;
+
+#ifdef TONEMAP_UNCHARTED
+    return toneMapUncharted(color);
+#endif
+
+#ifdef TONEMAP_HEJLRICHARD
+    return toneMapHejlRichard(color);
+#endif
+
+#ifdef TONEMAP_ACES
+    return toneMapACES(color);
+#endif
+
+#ifdef UNITY_COLORSPACE_GAMMA
+    return LINEARtoSRGB(color); // IMPORTANT: Use when in Unity GAMMA rendering mode
+#else
+    return color; // IMPORTANT: Use when in Unity LINEAR rendering mode
+#endif
+}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/tonemapping.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/tonemapping.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..1fe51ee84bbbee078467ce5cac157b6255fb1abe
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Khronos/tonemapping.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 5b30801fa0cbb9947bfe87bafcbae44b
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Library.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library.meta
new file mode 100644
index 0000000000000000000000000000000000000000..7f3a2b1212cdf1685be0e44a61eb0bc3bae663f4
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 1102ee7f60cba17498bcdb14ecba57f9
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/Avatar-Library.shader b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/Avatar-Library.shader
new file mode 100644
index 0000000000000000000000000000000000000000..a07bd1e2c4555638046aa394302ff56824830dbd
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/Avatar-Library.shader
@@ -0,0 +1,346 @@
+// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'
+
+// based off metallic_roughness.frag/vert from Khronos
+
+Shader "Avatar/Library"
+{
+    Properties
+    {
+        // NOTE: This texture can be visualized in the Unity editor, just expand in inspector and manually change "Dimension" to "2D" on top line
+        u_AttributeTexture("Vertex Attribute map", 3D) = "white" {}
+
+        [NoScaleOffset] u_NormalSampler("Normal map", 2D) = "white" {}
+        u_NormalScale("Normal map scale", Float) = 1.0
+        u_NormalUVSet("Normal UV Set", Int) = 0
+
+        [NoScaleOffset] u_EmissiveSampler("Emissive map", 2D) = "black" {}
+        u_EmissiveSet("Emissive UV Set", Int) = 0
+        u_EmissiveFactor("Emissive factor", Color) = (1, 1, 1, 1)
+
+        [NoScaleOffset] u_OcclusionSampler("Occlusion map", 2D) = "white" {}
+        u_OcclusionSet("Occlusion UV Set", Int) = 0
+        u_OcclusionStrength("Occlusion scale", Float) = 1.0
+
+        [NoScaleOffset] u_BaseColorSampler("Base Color", 2D) = "white" {}
+        u_BaseColorUVSet("Base Color UV Set", Int) = 0
+
+        u_BaseColorFactor("Base Color factor", Color) = (1, 1, 1, 1)
+
+        [NoScaleOffset]  u_MetallicRoughnessSampler("Metallic Roughness", 2D) = "white" {}
+        u_MetallicRoughnessUVSet("Metallic Roughness UV Set", Int) = 0
+
+        u_MetallicFactor("Metallic Factor", Range(0, 2)) = 1.0
+        u_RoughnessFactor("Roughness Factor", Range(0, 2)) = 1.0
+        u_F0Factor("F0 Factor", Range(0, 2)) = 1.0
+        u_OcclusionStrength("Occlusion Strength", Range(0, 2)) = 1.0
+        u_ThicknessFactor("Thickness Factor", Range(0, 2)) = 1.0
+
+        u_Exposure("Material Exposure", Range(0, 2)) = 1.0
+
+        [ShowIfKeyword(SKIN_ON)]
+        u_SubsurfaceColor("Sub-Surface Color", Color) = (0, 0, 0, 1)
+        [ShowIfKeyword(SKIN_ON)]
+        u_SkinORMFactor("Skin-only ORM Factor", Vector) = (1, 1, 1)
+
+        [ShowIfKeyword(ENABLE_HAIR_ON)]
+        u_HairSpecularColorFactor("Hair Specular Color Factor", Color) = (1,1,1)
+        [ShowIfKeyword(ENABLE_HAIR_ON)]
+        u_HairScatterIntensity("Hair Scatter intensity", Range(0, 1)) = 1.
+        [ShowIfKeyword(ENABLE_HAIR_ON)]
+        u_HairSpecularShiftIntensity("Hair Specular Shift Intensity", Range(-1,1)) = .2
+        [ShowIfKeyword(ENABLE_HAIR_ON)]
+        u_HairSpecularWhiteIntensity("Hair Specular White Intensity", Range(0,10)) = .2
+        [ShowIfKeyword(ENABLE_HAIR_ON)]
+        u_HairSpecularColorIntensity("Hair Specular Color Intensity", Range(0,10)) = .2
+        [ShowIfKeyword(ENABLE_HAIR_ON)]
+        u_HairSpecularColorOffset("Hair Specular Color Offset", Range(-1,1)) = .2
+        [ShowIfKeyword(ENABLE_HAIR_ON)]
+        u_HairRoughness("Hair Roughness", Range(0,1)) = .2
+        [ShowIfKeyword(ENABLE_HAIR_ON)]
+        u_HairColorRoughness("Hair Color Roughness", Range(0,1)) = .4
+        [ShowIfKeyword(ENABLE_HAIR_ON)]
+        u_HairAnisotropicIntensity("Hair Anistropic Intensity", Range(-1,1)) = .5
+        [ShowIfKeyword(ENABLE_HAIR_ON)]
+        u_HairSpecularNormalIntensity("Hair Specular Normal Intensity", Range(0,1)) = 1.
+        [ShowIfKeyword(ENABLE_HAIR_ON)]
+        u_HairSpecularGlint("HairSpecularGlint", Range(0,1)) = .1
+        [ShowIfKeyword(ENABLE_HAIR_ON)]
+        u_HairDiffusedIntensity("Hair Difuse Intensity", Range(0,10)) = .25
+
+        [ShowIfKeyword(ENABLE_RIM_LIGHT_ON)]
+        u_RimLightIntensity ("Rim Light Intensity", Range(0,1)) = 0.6
+        [ShowIfKeyword(ENABLE_RIM_LIGHT_ON)]
+        u_RimLightBias("Rim Light Bias", Range(0.0,1.0)) = 0.5
+        [ShowIfKeyword(ENABLE_RIM_LIGHT_ON)]
+        u_RimLightColor ("Rim Light Color", Color) = (1,1,1)
+        [ShowIfKeyword(ENABLE_RIM_LIGHT_ON)]
+        u_RimLightTransition ("Rim Light Transition", Range(0,0.2)) = 0.0000001
+        [ShowIfKeyword(ENABLE_RIM_LIGHT_ON)]
+        u_RimLightStartPosition ("Rim Light Start Position", Range(0,1)) = 0.1
+        [ShowIfKeyword(ENABLE_RIM_LIGHT_ON)]
+        u_RimLightEndPosition ("Rim Light End Position", Range(0,1)) = 0.5
+
+        [ShowIfKeyword(EYE_GLINTS_ON)]
+        u_EyeGlintFactor("Eye Glint Factor", Range(0, 4.0)) = 2.0
+        [ShowIfKeyword(EYE_GLINTS_ON)]
+        u_EyeGlintColorFactor("Eye Glint Color Factor", Range(0, 1.0)) = 0.5
+
+
+        [ShowIfKeyword(_PALETTIZATION_SINGLE_RAMP, _PALETTIZATION_TWO_RAMP)]
+        _ColorRamp0("Color Ramp 1", 2D) = "white" {}
+        [ShowIfKeyword(_PALETTIZATION_TWO_RAMP)]
+        _ColorRamp1("Color Ramp 2", 2D) = "white" {}
+
+        u_ColorGradientSampler("Color Gradient Sampler", 2D) = "white" {}
+        u_RampSelector("Ramp Selector",  Range(1,45)) = 1
+
+
+        // These should not exist here, since they should be in global shader scope and handeled by an external manager:
+        //
+        //u_DiffuseEnvSampler("IBL Diffuse Cubemap Texture", Cube) = "white" {}
+        //u_MipCount("IBL Diffuse Texture Mip Count", Int) = 10
+        //u_SpecularEnvSampler ("IBL Specular Cubemap Texture", Cube) = "white" {}
+        //u_brdfLUT ("BRDF LUT Texture", 2D) = "Assets/Oculus/Avatar2/Example/Scenes/BRDF_LUT" {}
+
+        // SHADER OPTIONS: These must match the options specified in options_common.hlsl
+        [Toggle] HAS_NORMAL_MAP("Has Normal Map", Float) = 0
+        [Toggle] SKIN("Skin", Float) = 1
+        [Toggle] EYE_GLINTS("Eye Glints", Float) = 1
+        [Toggle] ENABLE_HAIR("Enable Hair", Float) = 0
+        [Toggle] ENABLE_RIM_LIGHT("Enable Rim Light", Float) = 0
+        [Toggle] ENABLE_PREVIEW_COLOR_RAMP("Enable Preview Color Ramp", Float) = 0
+        [Toggle] ENABLE_DEBUG_RENDER("Enable Debug Render", Float) = 0
+
+        // DEBUG_MODES: Uncomment to use Debug modes, do not create a multi_compile for this, as it takes up permutations and memory. Instead static branch on DEBUG_NONE and the value of floating point uniform "Debug".
+        [KeywordEnum(None, BaseColor, Occlusion, Roughness, Metallic, Thickness, Normal, Normal Map, Emissive, View, Punctual, Punctual Specular, Punctual Diffuse, Ambient, Ambient Specular, Ambient Diffuse, No Tone Map, SubSurface Scattering, Submeshes)] Debug("Debug Render", Float) = 0
+
+        // MATERIAL_MODES: Uncomment to use Material modes, must match the multi_compile defined below
+        [KeywordEnum(Texture, Vertex)] Material_Mode("Material Mode", Float) = 0
+
+        // Cull mode (Off, Front, Back)
+        [Enum(UnityEngine.Rendering.CullMode)] _Cull("Cull", Float) = 2
+
+    }
+    SubShader
+    {
+        // Universal Render Pipeline (URP)
+        Tags { "RenderPipeline" = "UniversalPipeline" "RenderType" = "Opaque" }
+        Pass
+        {
+            PackageRequirements
+            {
+              "com.unity.render-pipelines.universal" : "10.1.0"
+            }
+            Tags{"LightMode" = "UniversalForward"}
+
+            HLSLPROGRAM
+            #pragma vertex Vertex_main
+            #pragma fragment Fragment_main
+            #pragma multi_compile_instancing
+            #pragma instancing_options procedural : setup
+
+            /////////////////////////////////////////////////////////
+            // PRAGMAS: Pragmas cannot exist in cginc files so include them here
+
+            // VERTEX COLORS: activate this to transmit lo-fi model vert colors or sub mesh information in the alpha channel
+            #define HAS_VERTEX_COLOR_float4
+
+            // SHADER OPTIONS: For the Shader Library System
+            #pragma multi_compile _ HAS_NORMAL_MAP_ON
+            #pragma multi_compile _ SKIN_ON
+            #pragma multi_compile _ EYE_GLINTS_ON
+            #pragma multi_compile _ ENABLE_HAIR_ON
+            #pragma multi_compile _ ENABLE_RIM_LIGHT_ON
+            #pragma multi_compile _ ENABLE_PREVIEW_COLOR_RAMP_ON
+            #pragma multi_compile _ ENABLE_DEBUG_RENDER_ON
+            // NOTE: To reduce permutations in the final product, hard code these as shown in this example:
+            //// #define HAS_NORMAL_MAP_ON
+            // #define SKIN_ON
+            // #define EYE_GLINTS_ON
+            //// #define ENABLE_HAIR_ON
+            // #define ENABLE_RIM_LIGHT_ON
+
+            // MATERIAL_MODES: Must match the Properties specified above
+            #pragma multi_compile MATERIAL_MODE_TEXTURE MATERIAL_MODE_VERTEX
+
+            // 5.0 required for SV_Coverage, 3.5 required for SV_VertexID
+            #pragma target 5.0
+
+            // Palettization modes for Avatar FBX tool
+            #pragma multi_compile __ _PALETTIZATION_SINGLE_RAMP _PALETTIZATION_TWO_RAMP
+
+            // In Avatar SDK we ALWAYS use the ORM property map extension. Indicate that here:
+            #define USE_ORM_EXTENSION
+
+            // per vertex is faster than per pixel, and almost indistinguishable for our purpose
+            #define USE_SH_PER_VERTEX
+
+            // This is the URP pass so set this define so that the OvrUnityGlobalIllumination headers can activate,
+            #define USING_URP
+
+            // Include Horizon Specific dependencies HERE
+            #include "app_specific/app_declarations.hlsl"
+
+            // Include the Vertex Shader HERE
+            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
+            #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/UnityInstancing.hlsl"
+            #include "../../../../Scripts/ShaderUtils/OvrUnityLightsURP.hlsl"
+            #include "../../../../Scripts/ShaderUtils/OvrUnityGlobalIlluminationURP.hlsl"
+            #include "../../../../Scripts/ShaderUtils/AvatarCustom.cginc"
+            #include "replacement/options_common.hlsl"
+            #include "replacement/structs_vert.hlsl"
+            #include "replacement/platform_vert.hlsl"
+            #include "export/pbr_vert.unity.hlsl"
+
+            // Include the Pixel Shader HERE
+            #include "replacement/platform_frag.hlsl"
+            #include "export/pbr_frag.unity.hlsl"
+            #include "app_specific/app_functions.hlsl"
+
+            ENDHLSL
+        }
+    }
+    SubShader
+    {
+        Tags { "RenderPipeline" = "" "RenderType" = "Opaque" }
+        LOD 100
+
+        Cull[_Cull]
+
+        // Old Unity Render Pipeline, Single Light
+        Pass
+        {
+
+            Tags{"LightMode" = "ForwardBase"}
+
+            CGPROGRAM
+            #pragma vertex Vertex_main
+            #pragma fragment Fragment_main
+
+            #pragma multi_compile DIRECTIONAL POINT SPOT
+
+            /////////////////////////////////////////////////////////
+            // PRAGMAS: Pragmas cannot exist in cginc files so include them here
+
+            // VERTEX COLORS: activate this to transmit lo-fi model vert colors or sub mesh information in the alpha channel
+            #define HAS_VERTEX_COLOR_float4
+
+            // SHADER OPTIONS: For the Shader Library System
+            #pragma multi_compile _ HAS_NORMAL_MAP_ON
+            #pragma multi_compile _ SKIN_ON
+            #pragma multi_compile _ EYE_GLINTS_ON
+            #pragma multi_compile _ ENABLE_HAIR_ON
+            #pragma multi_compile _ ENABLE_RIM_LIGHT_ON
+            #pragma multi_compile _ ENABLE_PREVIEW_COLOR_RAMP_ON
+            #pragma multi_compile _ ENABLE_DEBUG_RENDER_ON
+            // NOTE: To reduce permutations in the final product, hard code these as shown in this example:
+            //// #define HAS_NORMAL_MAP_ON
+            // #define SKIN_ON
+            // #define EYE_GLINTS_ON
+            //// #define ENABLE_HAIR_ON
+            // #define ENABLE_RIM_LIGHT_ON
+
+            // MATERIAL_MODES: Must match the Properties specified above
+            #pragma multi_compile MATERIAL_MODE_TEXTURE MATERIAL_MODE_VERTEX
+
+            // 5.0 required for SV_Coverage, 3.5 required for SV_VertexID
+            #pragma target 5.0
+
+            // Palettization modes for Avatar FBX tool
+            #pragma multi_compile __ _PALETTIZATION_SINGLE_RAMP _PALETTIZATION_TWO_RAMP
+
+            // In Avatar SDK we ALWAYS use the ORM property map extension. Indicate that here:
+            #define USE_ORM_EXTENSION
+
+            // per vertex is faster than per pixel, and almost indistinguishable for our purpose
+            #define LIGHTPROBE_SH 1
+
+            // Include Horizon Specific dependencies HERE
+            #include "app_specific/app_declarations.hlsl"
+
+            // Include the Vertex Shader HERE
+            #include "../../../../Scripts/ShaderUtils/AvatarCustom.cginc"
+            #include "UnityCG.cginc"
+            #include "UnityGIAvatar.hlsl"
+            #include "UnityLightingCommon.cginc"
+            #include "UnityStandardInput.cginc"
+            #include "replacement/options_common.hlsl"
+            #include "replacement/structs_vert.hlsl"
+            #include "replacement/platform_vert.hlsl"
+            #include "../../../../Scripts/ShaderUtils/OvrUnityGlobalIlluminationBuiltIn.hlsl"
+            #include "export/pbr_vert.unity.hlsl"
+
+            // Include the Pixel Shader HERE
+            #include "replacement/platform_frag.hlsl"
+            #include "export/pbr_frag.unity.hlsl"
+            #include "app_specific/app_functions.hlsl"
+
+            ENDCG
+        }
+
+        // Old Unity Render Pipeline, Up to 4 Additive Lights
+        Pass
+        {
+            Tags{"LightMode" = "ForwardAdd"}
+
+            Blend One One
+            ZWrite Off
+
+            CGPROGRAM
+            #pragma vertex Vertex_main
+            #pragma fragment Fragment_main
+
+            #pragma multi_compile DIRECTIONAL POINT SPOT
+
+            /////////////////////////////////////////////////////////
+            // PRAGMAS: Pragmas cannot exist in cginc files so include them here
+
+            // VERTEX COLORS: activate this to transmit lo-fi model vert colors or sub mesh information in the alpha channel
+            #define HAS_VERTEX_COLOR_float4
+
+            // SHADER OPTIONS: For the Shader Library System
+            #pragma multi_compile _ HAS_NORMAL_MAP_ON
+            #pragma multi_compile _ SKIN_ON
+            #pragma multi_compile _ EYE_GLINTS_ON
+            #pragma multi_compile _ ENABLE_HAIR_ON
+            #pragma multi_compile _ ENABLE_RIM_LIGHT_ON
+            #pragma multi_compile _ ENABLE_PREVIEW_COLOR_RAMP_ON
+            #pragma multi_compile _ ENABLE_DEBUG_RENDER_ON
+            // NOTE: To reduce permutations in the final product, hard code these as shown in this example:
+            //// #define HAS_NORMAL_MAP_ON
+            // #define SKIN_ON
+            // #define EYE_GLINTS_ON
+            //// #define ENABLE_HAIR_ON
+            // #define ENABLE_RIM_LIGHT_ON
+
+            // MATERIAL_MODES: Must match the Properties specified above
+            #pragma multi_compile MATERIAL_MODE_TEXTURE MATERIAL_MODE_VERTEX
+
+            // 5.0 required for SV_Coverage, 3.5 required for SV_VertexID
+            #pragma target 5.0
+
+            // Include Horizon Specific dependencies HERE
+            #include "app_specific/app_declarations.hlsl"
+
+            // Include the Vertex Shader HERE
+            #include "../../../../Scripts/ShaderUtils/AvatarCustom.cginc"
+            #include "UnityCG.cginc"
+            #include "UnityGIAvatar.hlsl"
+            #include "UnityLightingCommon.cginc"
+            #include "UnityStandardInput.cginc"
+            #include "replacement/options_common.hlsl"
+            #include "replacement/structs_vert.hlsl"
+            #include "../../../../Scripts/ShaderUtils/OvrUnityGlobalIlluminationBuiltIn.hlsl"
+            #include "replacement/platform_vert.hlsl"
+            #include "export/pbr_vert.unity.hlsl"
+
+            // Include the Pixel Shader HERE
+            #include "replacement/platform_frag.hlsl"
+            #include "export/pbr_frag.unity.hlsl"
+            #include "app_specific/app_functions.hlsl"
+
+            ENDCG
+        }
+    }
+
+}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/Avatar-Library.shader.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/Avatar-Library.shader.meta
new file mode 100644
index 0000000000000000000000000000000000000000..4fc964b2565bce031e9ad8b48fae8dc57ccb6c14
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/Avatar-Library.shader.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 3d4ea02cfcf80e344b03f3d685b3aaee
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/UnityGIAvatar.hlsl b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/UnityGIAvatar.hlsl
new file mode 100644
index 0000000000000000000000000000000000000000..1f621fc20828f123c195196b9a882b2555504dc3
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/UnityGIAvatar.hlsl
@@ -0,0 +1,197 @@
+#include "UnityImageBasedLighting.cginc"
+#include "../../../../Scripts/ShaderUtils/OvrLightTypes.hlsl"
+
+struct AvatarShaderLight {
+  half3 direction;
+  half3 color;
+};
+
+struct AvatarShaderIndirect {
+  half3 diffuse;
+  half3 specular;
+};
+
+struct AvatarShaderGlobalIllumination {
+  AvatarShaderLight light;
+  AvatarShaderIndirect indirect;
+};
+
+//-----------------------------------------------------------------------------------------
+// The following use UnityStandardUtils.cginc as a reference, as of 2020.3.7
+
+half3 AvatarShadeSHPerPixel (half3 normal, half3 ambient, float3 worldPos)
+{
+    half3 ambient_contrib = 0.0;
+
+        // Completely per-pixel
+        ambient_contrib = SHEvalLinearL0L1(half4(normal, 1.0));
+        ambient_contrib += SHEvalLinearL2(half4(normal, 1.0));
+        ambient += max(half3(0, 0, 0), ambient_contrib);
+
+        #ifdef UNITY_COLORSPACE_GAMMA
+            ambient = LinearToGammaSpace(ambient);
+        #endif
+
+
+    return ambient;
+}
+
+inline float3 AvatarBoxProjectedCubemapDirection(float3 worldRefl, float3 worldPos, float4 cubemapCenter, float4 boxMin, float4 boxMax) {
+  // Do we have a valid reflection probe?
+  UNITY_BRANCH
+  if (cubemapCenter.w > 0.0) {
+    float3 nrdir = normalize(worldRefl);
+
+    float3 rbmax = (boxMax.xyz - worldPos) / nrdir;
+    float3 rbmin = (boxMin.xyz - worldPos) / nrdir;
+
+    float3 rbminmax = (nrdir > 0.0f) ? rbmax : rbmin;
+
+    float fa = min(min(rbminmax.x, rbminmax.y), rbminmax.z);
+
+    worldPos -= cubemapCenter.xyz;
+    worldRefl = worldPos + nrdir * fa;
+  }
+  return worldRefl;
+}
+
+//-----------------------------------------------------------------------------------------
+// The following use UnityGlobalIllumination.cginc as a reference, as of 2020.3.7
+
+inline void AvatarResetUnityLight(out UnityLight outLight) {
+  outLight.color = half3(0, 0, 0);
+  outLight.dir = half3(0, 1, 0); // Irrelevant direction, just not null
+  outLight.ndotl = 0; // Not used
+}
+
+inline void AvatarResetUnityGI(out UnityGI outGI) {
+  AvatarResetUnityLight(outGI.light);
+  outGI.indirect.diffuse = 0;
+  outGI.indirect.specular = 0;
+}
+
+inline UnityGI AvatarUnityGI_Base(UnityGIInput data, half occlusion, half3 normalWorld) {
+  UnityGI o_gi;
+  AvatarResetUnityGI(o_gi);
+
+  o_gi.light = data.light;
+  o_gi.light.color *= data.atten;
+
+  o_gi.indirect.diffuse = AvatarShadeSHPerPixel(normalWorld, data.ambient, data.worldPos);
+
+  o_gi.indirect.diffuse *= occlusion;
+  return o_gi;
+}
+
+inline half3 AvatarUnityGI_IndirectSpecular(UnityGIInput data, half occlusion, Unity_GlossyEnvironmentData glossIn)
+{
+    half3 specular;
+
+    #ifdef UNITY_SPECCUBE_BOX_PROJECTION
+        // we will tweak reflUVW in glossIn directly (as we pass it to Unity_GlossyEnvironment twice for probe0 and probe1), so keep original to pass into AvatarBoxProjectedCubemapDirection
+        half3 originalReflUVW = glossIn.reflUVW;
+        glossIn.reflUVW = AvatarBoxProjectedCubemapDirection (originalReflUVW, data.worldPos, data.probePosition[0], data.boxMin[0], data.boxMax[0]);
+    #endif
+
+    #ifdef _GLOSSYREFLECTIONS_OFF
+        specular = unity_IndirectSpecColor.rgb;
+    #else
+        half3 env0 = Unity_GlossyEnvironment (UNITY_PASS_TEXCUBE(unity_SpecCube0), data.probeHDR[0], glossIn);
+        specular = env0;
+    #endif
+
+    return specular * occlusion;
+}
+
+
+inline UnityGI AvatarUnityGlobalIllumination(
+    UnityGIInput data,
+    half occlusion,
+    half3 normalWorld,
+    Unity_GlossyEnvironmentData glossIn) {
+  UnityGI o_gi = AvatarUnityGI_Base(data, occlusion, normalWorld);
+  o_gi.indirect.specular = AvatarUnityGI_IndirectSpecular(data, occlusion, glossIn);
+  return o_gi;
+}
+
+//-----------------------------------------------------------------------------------------
+// Finally our entry points into the Unity access functions come here:
+
+
+AvatarShaderGlobalIllumination GetGlobalIllumination(
+    UnityGIInput giInput,
+    half smoothness,
+    half metallic,
+    half occlusion,
+    half3 albedo,
+    half3 normal) {
+  Unity_GlossyEnvironmentData g = UnityGlossyEnvironmentSetup(
+      smoothness,
+      giInput.worldViewDir,
+      normal,
+      lerp(unity_ColorSpaceDielectricSpec.rgb, albedo, metallic));
+
+  UnityGI gi;
+  UNITY_INITIALIZE_OUTPUT(UnityGI, gi);
+  half ambientOcclusion = occlusion;
+  gi = AvatarUnityGlobalIllumination(giInput, ambientOcclusion, normal, g);
+
+  AvatarShaderLight light;
+  light.direction = gi.light.dir;
+  light.color = gi.light.color;
+
+  AvatarShaderIndirect indirect;
+  indirect.diffuse = gi.indirect.diffuse;
+  indirect.specular = gi.indirect.specular;
+
+  AvatarShaderGlobalIllumination avatarGI;
+  avatarGI.light = light;
+  avatarGI.indirect = indirect;
+
+  return avatarGI;
+}
+
+void getSHContribution(OvrLight light, float3 worldPos, float3 worldViewDir,
+fixed attenuation, half3 ambient, half smoothness, half metallic, half occlusion,
+half3 albedo, half3 normal, out float3 diffuse, out float3 specular)
+    {
+
+    UnityGIInput giInput;
+    UNITY_INITIALIZE_OUTPUT(UnityGIInput, giInput);
+    giInput.light.color = _LightColor0.rgb;
+    giInput.light.dir = light.direction;
+
+    giInput.worldPos = worldPos;
+    giInput.worldViewDir = worldViewDir;
+    giInput.atten = attenuation;
+    giInput.lightmapUV = 0.0; // we don't have a light map right?
+    giInput.ambient = ambient;
+
+    giInput.probeHDR[0] = unity_SpecCube0_HDR;
+    giInput.probeHDR[1] = unity_SpecCube1_HDR;
+
+  #if defined(UNITY_SPECCUBE_BLENDING) || defined(UNITY_SPECCUBE_BOX_PROJECTION)
+    giInput.boxMin[0] = unity_SpecCube0_BoxMin; // .w holds lerp value for blending
+  #endif
+
+  #ifdef UNITY_SPECCUBE_BOX_PROJECTION
+    giInput.boxMax[0] = unity_SpecCube0_BoxMax;
+    giInput.probePosition[0] = unity_SpecCube0_ProbePosition;
+    giInput.boxMax[1] = unity_SpecCube1_BoxMax;
+    giInput.boxMin[1] = unity_SpecCube1_BoxMin;
+    giInput.probePosition[1] = unity_SpecCube1_ProbePosition;
+  #endif
+
+
+    AvatarShaderGlobalIllumination gi = GetGlobalIllumination(
+        giInput,
+        smoothness, // smoothness
+        metallic, // metallic
+        occlusion, // occlusion
+        albedo, // albedo
+        normal // normal
+    );
+      diffuse = gi.indirect.diffuse.rgb; // diffuse light should already be in the linear space from SH calculations.
+      specular = gi.indirect.specular.rgb; // specular light should already be in the linear space from exr sampling.
+
+}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/UnityGIAvatar.hlsl.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/UnityGIAvatar.hlsl.meta
new file mode 100644
index 0000000000000000000000000000000000000000..17590bb5deca8421d53d87ca37d767473ee7022f
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/UnityGIAvatar.hlsl.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: cd3692ec627f9405fbfe1871501dd515
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  preprocessorOverride: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/app_specific.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/app_specific.meta
new file mode 100644
index 0000000000000000000000000000000000000000..4c5f2f29d4da1d3c079b2b865ce5d361acbf8efa
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/app_specific.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 6768c0ea426cde7449f969f74316055c
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/app_specific/app_declarations.hlsl b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/app_specific/app_declarations.hlsl
new file mode 100644
index 0000000000000000000000000000000000000000..4c38014fa7ef68cbc8adecfe7c46bba37ed9d093
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/app_specific/app_declarations.hlsl
@@ -0,0 +1,7 @@
+// App specific declarations that have to happen before the exported Library shader
+// If your app needs its own declarations, rename this file with your app's name and place them here.
+// This file should persist across succesive integrations.
+
+// Keyword Definitions
+
+// Scoped Variables (Uniforms)
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/app_specific/app_declarations.hlsl.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/app_specific/app_declarations.hlsl.meta
new file mode 100644
index 0000000000000000000000000000000000000000..239287cf77e0b76d373c6e530c9c802791c2afee
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/app_specific/app_declarations.hlsl.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 267a6b2d4f5287c459d6e4274d2896a1
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  preprocessorOverride: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/app_specific/app_functions.hlsl b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/app_specific/app_functions.hlsl
new file mode 100644
index 0000000000000000000000000000000000000000..2a65abd4e6ce2977b65a4d25ee9251dc4f875a39
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/app_specific/app_functions.hlsl
@@ -0,0 +1,13 @@
+// App specific functions that can be called from the exported Library shader
+// If your app needs its own declarations, rename this file with your app's name and place them here.
+// This file should persist across succesive integrations.
+
+// This function allows for app specific operations at the beginning of the fragment shader
+void AppSpecificPreManipulation(inout avatar_FragmentInput i) {
+    // Call app specific functions from here.
+}
+
+// This function allows for app specific operations at the beginning of the fragment shader
+void AppSpecificPostManipulation(avatar_FragmentInput i, inout avatar_FragmentOutput o) {
+    // Call app specific functions from here.
+}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/app_specific/app_functions.hlsl.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/app_specific/app_functions.hlsl.meta
new file mode 100644
index 0000000000000000000000000000000000000000..a7103aff250ff25d6f5ca937c285417fdf7136a0
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/app_specific/app_functions.hlsl.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 984244c9113211a4ab69f182950dd3b6
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  preprocessorOverride: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/export.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/export.meta
new file mode 100644
index 0000000000000000000000000000000000000000..71932fd7a8afb825955f8db5a559342fbc7c4316
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/export.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: b70445cf4b11f47408dbab4c2d143013
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/export/pbr_frag.unity.hlsl b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/export/pbr_frag.unity.hlsl
new file mode 100644
index 0000000000000000000000000000000000000000..250c6f9c964eab1cb173e9ba4eef05d01f3eb78b
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/export/pbr_frag.unity.hlsl
@@ -0,0 +1,1134 @@
+// Generated by AvatarShaderLibrary 242d0d331a49
+
+#ifdef ENABLE_enableEnvironmentMap
+  static const bool enableEnvironmentMap = true;
+#else
+  static const bool enableEnvironmentMap = false;
+#endif
+
+
+struct avatar_Light
+{
+    float3 direction;
+    float range;
+    float3 color;
+    float intensity;
+    float3 position;
+    float innerConeCos;
+    float outerConeCos;
+    float shadowTerm;
+    int type;
+};
+
+struct avatar_FragOptions
+{
+    bool enableNormalMapping;
+    bool enableAlphaToCoverage;
+    bool enableEnvironmentMap;
+    bool enableRimLight;
+    bool enablePreviewColorRamp;
+    bool enableDebugRender;
+    bool enableSkin;
+    bool enableEyeGlint;
+    bool enableHair;
+    bool enableShadows;
+};
+
+struct avatar_SkinMaterial
+{
+    float3 subsurface_color;
+    float3 skin_ORM_factor;
+};
+
+struct avatar_HairMaterial
+{
+    float3 subsurface_color;
+    float scatter_intensity;
+    float3 specular_color_factor;
+    float specular_shift_intensity;
+    float specular_white_intensity;
+    float specular_white_roughness;
+    float specular_color_intensity;
+    float specular_color_offset;
+    float specular_color_roughness;
+    float anisotropic_intensity;
+    float diffused_intensity;
+    float normal_intensity;
+    float specular_glint;
+    float ao_intensity;
+    float flow_angle;
+    float shift;
+    float blend;
+    float aniso_blend;
+};
+
+struct avatar_RimLightMaterial
+{
+    float intensity;
+    float bias;
+    float3 color;
+    float transition;
+    float start;
+    float end;
+};
+
+struct avatar_Material
+{
+    float3 base_color;
+    float alpha;
+    float exposure;
+    float metallic;
+    float occlusion;
+    float roughness;
+    float thickness;
+    float ambient_diffuse_factor;
+    float ambient_specular_factor;
+    float eye_glint_factor;
+    float eye_glint_color_factor;
+    avatar_SkinMaterial skin;
+    avatar_HairMaterial hair_material;
+    avatar_RimLightMaterial rim_light_material;
+    float ramp_selector;
+    int color_selector_lod;
+};
+
+struct avatar_Matrices
+{
+    float4x4 objectToWorld;
+    float3x3 worldToObject;
+    float4x4 viewProjection;
+};
+
+struct avatar_TangentSpace
+{
+    float3 normal;
+    float3 tangent;
+    float3 bitangent;
+};
+
+struct avatar_Geometry
+{
+    float3 camera;
+    float3 positionInClipSpace;
+    float3 positionInWorldSpace;
+    float3 normal;
+    avatar_TangentSpace tangentSpace;
+    float2 texcoord_0;
+    float2 texcoord_1;
+    float4 color;
+    float4 ormt;
+    float normalScale;
+    float3 worldViewDir;
+    float lod;
+};
+
+struct avatar_AmbientLighting
+{
+    float3 diffuse;
+    float3 specular;
+};
+
+struct avatar_FragmentInput
+{
+    avatar_FragOptions options;
+    avatar_Matrices matrices;
+    avatar_Geometry geometry;
+    avatar_Material material;
+    int lightsCount;
+    avatar_Light lights[8];
+    float3 ambient_color;
+    int debugMode;
+};
+
+struct avatar_FragmentOutput
+{
+    float4 color;
+    float3 p_specular;
+    float3 p_diffuse;
+    float3 a_specular;
+    float3 a_diffuse;
+    float3 subSurfaceColor;
+    uint alphaCoverage;
+};
+
+struct avatar_HemisphereNormalOffsets
+{
+    float3 nn;
+    float3 bb;
+    float3 tt;
+    float3 lv1;
+    float3 lv2;
+    float3 lv3;
+};
+
+struct avatar_SpecularData
+{
+    float3 directional_light_color;
+    float range_attentuation;
+    float spot_attenuation;
+    float _distance;
+    float3 point_to_light;
+    float actualCos;
+    float3 l;
+    float3 h;
+    float roughPow2;
+    float roughPow4;
+    float invRoughPow4;
+    float NdotV;
+};
+
+
+
+uniform sampler2D u_BaseColorSampler;
+uniform sampler2D u_MetallicRoughnessSampler;
+uniform sampler2D u_NormalSampler;
+uniform float u_NormalScale;
+uniform int u_MipCount;
+uniform int Debug;
+uniform float u_Exposure;
+uniform float u_MetallicFactor;
+uniform float u_OcclusionStrength;
+uniform float u_RoughnessFactor;
+uniform float u_ThicknessFactor;
+uniform float3 u_SubsurfaceColor;
+uniform float3 u_SkinORMFactor;
+uniform float u_EyeGlintFactor;
+uniform float u_EyeGlintColorFactor;
+uniform float3 u_HairSubsurfaceColor;
+uniform float u_HairScatterIntensity;
+uniform float3 u_HairSpecularColorFactor;
+uniform float u_HairSpecularShiftIntensity;
+uniform float u_HairSpecularWhiteIntensity;
+uniform float u_HairSpecularColorIntensity;
+uniform float u_HairSpecularColorOffset;
+uniform float u_HairRoughness;
+uniform float u_HairColorRoughness;
+uniform float u_HairAnisotropicIntensity;
+uniform float u_HairSpecularNormalIntensity;
+uniform float u_HairDiffusedIntensity;
+uniform float u_HairSpecularGlint;
+uniform float u_RimLightIntensity;
+uniform float u_RimLightBias;
+uniform float3 u_RimLightColor;
+uniform float u_RimLightTransition;
+uniform float u_RimLightStartPosition;
+uniform float u_RimLightEndPosition;
+uniform sampler2D u_ColorGradientSampler;
+uniform int u_RampSelector;
+
+static float4 _21;
+
+
+
+struct FragmentOutput
+{
+    float4 _21 : COLOR0;
+};
+
+
+avatar_Geometry avatar_zeroGeometry()
+{
+    avatar_Geometry geometry;
+    geometry.camera = 0.0f.xxx;
+    geometry.positionInWorldSpace = 0.0f.xxx;
+    geometry.positionInClipSpace = 0.0f.xxx;
+    geometry.texcoord_0 = 0.0f.xx;
+    geometry.texcoord_1 = 0.0f.xx;
+    geometry.color = 0.0f.xxxx;
+    geometry.normalScale = 0.0f;
+    geometry.tangentSpace.normal = 0.0f.xxx;
+    geometry.tangentSpace.tangent = 0.0f.xxx;
+    geometry.tangentSpace.bitangent = 0.0f.xxx;
+    geometry.normal = 0.0f.xxx;
+    return geometry;
+}
+
+avatar_HairMaterial avatar_zeroHairMaterial()
+{
+    avatar_HairMaterial material;
+    material.subsurface_color = 0.0f.xxx;
+    material.scatter_intensity = 0.0f;
+    material.specular_color_factor = 0.0f.xxx;
+    material.specular_shift_intensity = 0.0f;
+    material.specular_white_intensity = 0.0f;
+    material.specular_white_roughness = 0.0f;
+    material.specular_color_intensity = 0.0f;
+    material.specular_color_offset = 0.0f;
+    material.specular_color_roughness = 0.0f;
+    material.anisotropic_intensity = 0.0f;
+    material.diffused_intensity = 0.0f;
+    material.normal_intensity = 0.0f;
+    material.specular_glint = 0.0f;
+    material.ao_intensity = 0.0f;
+    material.flow_angle = 0.0f;
+    material.shift = 0.0f;
+    material.blend = 0.0f;
+    material.aniso_blend = 0.0f;
+    return material;
+}
+
+avatar_Material avatar_zeroMaterial()
+{
+    avatar_Material material0;
+    material0.base_color = 0.0f.xxx;
+    material0.alpha = 0.0f;
+    material0.exposure = 0.0f;
+    material0.metallic = 0.0f;
+    material0.occlusion = 0.0f;
+    material0.roughness = 0.0f;
+    material0.thickness = 0.0f;
+    material0.skin.subsurface_color = 0.0f.xxx;
+    material0.skin.skin_ORM_factor = 0.0f.xxx;
+    material0.eye_glint_factor = 0.0f;
+    material0.eye_glint_color_factor = 0.0f;
+    material0.hair_material = avatar_zeroHairMaterial();
+    material0.ramp_selector = 0.0f;
+    material0.color_selector_lod = 0;
+    return material0;
+}
+
+avatar_Matrices avatar_zeroMatrices()
+{
+    avatar_Matrices matrices;
+    matrices.objectToWorld = float4x4(0.0f.xxxx, 0.0f.xxxx, 0.0f.xxxx, 0.0f.xxxx);
+    matrices.worldToObject = float3x3(0.0f.xxx, 0.0f.xxx, 0.0f.xxx);
+    matrices.viewProjection = float4x4(0.0f.xxxx, 0.0f.xxxx, 0.0f.xxxx, 0.0f.xxxx);
+    return matrices;
+}
+
+avatar_FragOptions avatar_zeroOptions()
+{
+    avatar_FragOptions options;
+    options.enableNormalMapping = false;
+    options.enableAlphaToCoverage = false;
+    options.enableEnvironmentMap = false;
+    options.enableRimLight = false;
+    options.enableSkin = false;
+    options.enableEyeGlint = false;
+    options.enableHair = false;
+    options.enableShadows = false;
+    return options;
+}
+
+avatar_Light avatar_zeroLight()
+{
+    avatar_Light light;
+    light.direction = 0.0f.xxx;
+    light.range = 0.0f;
+    light.color = 0.0f.xxx;
+    light.intensity = 0.0f;
+    light.position = 0.0f.xxx;
+    light.innerConeCos = 0.0f;
+    light.outerConeCos = 0.0f;
+    light.shadowTerm = 1.0f;
+    light.type = 0;
+    return light;
+}
+
+avatar_FragmentInput avatar_zeroFragmentInput()
+{
+    avatar_FragmentInput i;
+    i.ambient_color = 0.0f.xxx;
+    i.geometry = avatar_zeroGeometry();
+    i.material = avatar_zeroMaterial();
+    i.matrices = avatar_zeroMatrices();
+    i.debugMode = 0;
+    i.options = avatar_zeroOptions();
+    for (int idx = 0; idx < 8; idx++)
+    {
+        i.lights[idx] = avatar_zeroLight();
+    }
+    return i;
+}
+
+bool avatar_WithinRange(float idChannel, float lowBound, float highBound)
+{
+    return (idChannel > (lowBound - 0.001953125f)) && (idChannel < (highBound + 0.001953125f));
+}
+
+bool avatar_UseSkin(float idChannel)
+{
+    return avatar_WithinRange(idChannel, 0.0078125f, 0.015625f);
+}
+
+bool avatar_UseEyeGlint(float idChannel)
+{
+    return avatar_WithinRange(idChannel, 0.125f, 0.25f);
+}
+
+bool avatar_IsOfType(float idChannel, float subMeshType)
+{
+    return avatar_WithinRange(idChannel, subMeshType, subMeshType);
+}
+
+bool avatar_UseHair(float idChannel)
+{
+    return avatar_IsOfType(idChannel, 0.03125f);
+}
+
+avatar_FragOptions getFragOptions(float subMeshIdChannel)
+{
+    avatar_FragOptions options = avatar_zeroOptions();
+    options.enableNormalMapping = enableNormalMapping;
+    options.enableAlphaToCoverage = enableAlphaToCoverage;
+    options.enableEnvironmentMap = enableEnvironmentMap;
+    options.enableRimLight = enableRimLight;
+    if (true)
+    {
+        options.enableSkin = enableSkin && avatar_UseSkin(subMeshIdChannel);
+        options.enableEyeGlint = enableEyeGlint && avatar_UseEyeGlint(subMeshIdChannel);
+        options.enableHair = enableHair && avatar_UseHair(subMeshIdChannel);
+    }
+    else
+    {
+        options.enableSkin = enableSkin;
+        options.enableEyeGlint = enableEyeGlint;
+        options.enableHair = enableHair;
+    }
+    options.enablePreviewColorRamp = enablePreviewColorRamp;
+    options.enableDebugRender = enableDebugRender;
+    return options;
+}
+
+float3 calculateBitangent(avatar_TangentSpace tangentSpace)
+{
+    return normalize(cross(tangentSpace.normal, tangentSpace.tangent));
+}
+
+float LINEARtoSRGB(float color)
+{
+    return pow(color, 0.4545454680919647216796875f);
+}
+
+float3 computeViewDir(float3 camera, float3 position)
+{
+    return normalize(position - camera);
+}
+
+void AppSpecificPreManipulation(inout avatar_FragmentInput i);
+
+avatar_FragmentOutput avatar_zeroFragmentOutput()
+{
+    avatar_FragmentOutput o;
+    o.color = 0.0f.xxxx;
+    o.p_specular = 0.0f.xxx;
+    o.p_diffuse = 0.0f.xxx;
+    o.a_specular = 0.0f.xxx;
+    o.a_diffuse = 0.0f.xxx;
+    o.subSurfaceColor = 0.0f.xxx;
+    o.alphaCoverage = 255u;
+    return o;
+}
+
+float saturate(float x)
+{
+    return clamp(x, 0.0f, 1.0f);
+}
+
+float computeNdotV(float3 normal, float3 world_view_dir)
+{
+    return saturate(-dot(normal, world_view_dir));
+}
+
+avatar_SpecularData avatar_fillInSpecularData(avatar_Geometry geometry, avatar_Light light, avatar_Material material, avatar_FragOptions options)
+{
+    avatar_SpecularData sd;
+    sd.directional_light_color = ((light.color * (1.0f / material.exposure)) * 0.3183098733425140380859375f) * light.intensity;
+    sd.point_to_light = -light.direction;
+    sd.range_attentuation = 0.0f;
+    sd.spot_attenuation = 0.0f;
+    sd._distance = length(sd.point_to_light);
+    if (light.type != 0)
+    {
+        sd.point_to_light = light.position - geometry.positionInWorldSpace;
+        if (light.range <= 0.0f)
+        {
+            sd.range_attentuation = 1.0f / pow(sd._distance, 2.0f);
+        }
+        else
+        {
+            sd.range_attentuation = max(min(1.0f - pow(sd._distance / light.range, 4.0f), 1.0f), 0.0f) / pow(sd._distance, 2.0f);
+        }
+    }
+    if (light.type != 2)
+    {
+        sd.actualCos = dot(normalize(light.direction), normalize(-sd.point_to_light));
+        if (sd.actualCos > light.outerConeCos)
+        {
+            if (sd.actualCos < light.innerConeCos)
+            {
+                sd.spot_attenuation = smoothstep(light.outerConeCos, light.innerConeCos, sd.actualCos);
+            }
+        }
+    }
+    sd.l = normalize(sd.point_to_light);
+    sd.h = normalize(sd.l - geometry.worldViewDir);
+    if (light.range <= 0.0f)
+    {
+        sd.range_attentuation = 1.0f / pow(sd._distance, 2.0f);
+    }
+    else
+    {
+        sd.range_attentuation = max(min(1.0f - pow(sd._distance / light.range, 4.0f), 1.0f), 0.0f) / pow(sd._distance, 2.0f);
+    }
+    sd.roughPow2 = material.roughness * material.roughness;
+    sd.roughPow4 = sd.roughPow2 * sd.roughPow2;
+    sd.invRoughPow4 = 1.0f - sd.roughPow4;
+    sd.NdotV = computeNdotV(geometry.normal, geometry.worldViewDir);
+    return sd;
+}
+
+float3 avatar_computeSpecular(avatar_Geometry geometry, avatar_Light light, avatar_Material material, avatar_FragOptions options)
+{
+    float LIGHTSIZE = 0.5f;
+    float sumAreaLight = 0.0f;
+    avatar_SpecularData sd = avatar_fillInSpecularData(geometry, light, material, options);
+    if (material.roughness < 0.20000000298023223876953125f)
+    {
+        LIGHTSIZE = lerp(0.0500000007450580596923828125f, 0.5f, saturate((material.roughness - 0.100000001490116119384765625f) * 10.0f));
+    }
+    float stepSize = LIGHTSIZE / 5.0f;
+    float _3522 = sd.NdotV;
+    float _3527 = sd.invRoughPow4;
+    float _3530 = sd.roughPow2;
+    float ggxCommon = sqrt(((_3522 * _3522) * _3527) + _3530);
+    float t;
+    for (int i = 0; i < 5; i++)
+    {
+        for (int j = 0; j < 5; j++)
+        {
+            float3 worldSpaceLightDir = sd.l;
+            worldSpaceLightDir.x += ((float(i) - 2.5f) * stepSize);
+            worldSpaceLightDir.y += ((float(j) - 2.5f) * stepSize);
+            float3 h = normalize(worldSpaceLightDir - geometry.worldViewDir);
+            float NdotL = saturate(dot(worldSpaceLightDir, geometry.normal));
+            float NdotH = saturate(dot(geometry.normal, h));
+            float ggx = (NdotL * ggxCommon) + (_3522 * sqrt(((NdotL * NdotL) * _3527) + _3530));
+            if (ggx > 0.0f)
+            {
+                t = 0.5f / ggx;
+            }
+            else
+            {
+                t = 0.0f;
+            }
+            ggx = t;
+            float t0 = 1.0f / (1.0f - ((NdotH * NdotH) * _3527));
+            sumAreaLight += ((((NdotL * t0) * t0) * sd.roughPow4) * ggx);
+        }
+    }
+    return (sd.directional_light_color * (sumAreaLight / 25.0f)) * 1.5f;
+}
+
+float3 avatar_computeDiffuse(avatar_Geometry geometry, avatar_Light light, avatar_Material material, out float3 diffuseWrap)
+{
+    float3 directional_light_color = ((light.color * (1.0f / material.exposure)) * 0.3183098733425140380859375f) * light.intensity;
+    float NdotL = dot(geometry.normal, normalize(-light.direction));
+    diffuseWrap = directional_light_color * saturate(smoothstep(-0.64999997615814208984375f, 1.0f, NdotL));
+    return directional_light_color * saturate(smoothstep(-0.1500000059604644775390625f, 1.0f, NdotL));
+}
+
+float4 mod289(float4 x)
+{
+    return x - (floor(x * 0.00346020772121846675872802734375f) * 289.0f);
+}
+
+float4 perm(float4 x)
+{
+    return mod289(((x * 34.0f) + 1.0f.xxxx) * x);
+}
+
+float _noise(float3 p)
+{
+    float3 a = floor(p);
+    float3 d = p - a;
+    float3 d_1 = (d * d) * (3.0f.xxx - (d * 2.0f));
+    float4 b = a.xxyy + float4(0.0f, 1.0f, 0.0f, 1.0f);
+    float4 c = perm(perm(b.xyxy).xyxy + b.zzww) + a.zzzz;
+    float _2028 = d_1.z;
+    float4 o3 = (frac(perm(c + 1.0f.xxxx) * 0.024390242993831634521484375f) * _2028) + (frac(perm(c) * 0.024390242993831634521484375f) * (1.0f - _2028));
+    float _2040 = d_1.x;
+    float2 o4 = (o3.yw * _2040) + (o3.xz * (1.0f - _2040));
+    float _2052 = d_1.y;
+    return (o4.y * _2052) + (o4.x * (1.0f - _2052));
+}
+
+float3 computeSpecularHighlight(float anisotropicIntensity, float roughness, float3 L, float3 E, float3 t, float3 tangent, float3 bitangent, float offset)
+{
+    float3 H = 0.0f.xxx;
+    float kspec = 0.0f;
+    float kexp = 0.0f;
+    float3 spec = 0.0f.xxx;
+    float3 t_1 = normalize(t + (bitangent * offset));
+    if (anisotropicIntensity > 0.0f)
+    {
+        float3 isotropicH = normalize(E + L);
+        H = normalize((E + L) - (tangent * dot(E + L, tangent)));
+        H = normalize(lerp(isotropicH, H, anisotropicIntensity.xxx));
+        kspec = dot(H, t_1);
+        kexp = 1.0f / ((roughness * roughness) + 0.001000000047497451305389404296875f);
+    }
+    else
+    {
+        H = normalize(E + L);
+        kspec = dot(t_1, H);
+        kexp = 3.0f / ((roughness * roughness) + 0.001000000047497451305389404296875f);
+    }
+    if (kspec > (-0.001000000047497451305389404296875f))
+    {
+        float specular = pow(max(0.0f, kspec), kexp);
+        spec = 0.0f.xxx + specular.xxx;
+    }
+    return spec;
+}
+
+float3 computeHairSpecular(avatar_FragmentInput i, float3 lightVector, float3x3 hairCoordinateSystem, float anisotropicBlend)
+{
+    float3 E = -i.geometry.worldViewDir;
+    float3 L = normalize(-lightVector);
+    float anisotropy = lerp(0.0f, i.material.hair_material.anisotropic_intensity, anisotropicBlend);
+    float localOffset = ((i.material.hair_material.specular_shift_intensity * (i.material.hair_material.shift - 0.5f)) * anisotropicBlend) + (((_noise(float3(i.geometry.texcoord_0.x, i.geometry.texcoord_0.y, float2(0.0f, 1.0f).x) * 2000.0f.xxx) * 2.0f) - 1.0f) * 0.00999999977648258209228515625f);
+    return ((computeSpecularHighlight(anisotropy, i.material.hair_material.specular_white_roughness, L, E, hairCoordinateSystem[0], hairCoordinateSystem[2], hairCoordinateSystem[1], localOffset) * i.material.hair_material.specular_white_intensity) + ((computeSpecularHighlight(anisotropy, i.material.hair_material.specular_color_roughness, L, E, hairCoordinateSystem[0], hairCoordinateSystem[2], hairCoordinateSystem[1], i.material.hair_material.specular_color_offset + localOffset) * i.material.hair_material.specular_color_factor) * i.material.hair_material.specular_color_intensity)) * i.material.hair_material.ao_intensity;
+}
+
+float3 blendPunctualSpecularWithHair(float3 punctualSpec, float3 hairPunctualSpec, float hairBlend)
+{
+    return lerp(punctualSpec, hairPunctualSpec, hairBlend.xxx);
+}
+
+float3 blendSubSurfaceColorWithHair(float3 hairSubsurfaceColor, float3 skinSubsurfaceColor, float hairBlend)
+{
+    return lerp(hairSubsurfaceColor, skinSubsurfaceColor, hairBlend.xxx);
+}
+
+avatar_HemisphereNormalOffsets avatar_computeHemisphereNormalOffsets(avatar_FragmentInput i)
+{
+    avatar_HemisphereNormalOffsets hno;
+    hno.nn = i.geometry.tangentSpace.normal * 0.707099974155426025390625f;
+    hno.tt = i.geometry.tangentSpace.tangent * 0.3535499870777130126953125f;
+    hno.bb = i.geometry.tangentSpace.bitangent * 0.612348616123199462890625f;
+    hno.lv1 = hno.nn + (hno.tt * 2.0f);
+    hno.lv2 = (hno.nn + hno.bb) - hno.tt;
+    hno.lv3 = (hno.nn - hno.bb) - hno.tt;
+    return hno;
+}
+
+float3 saturate(float3 v)
+{
+    return clamp(v, 0.0f.xxx, 1.0f.xxx);
+}
+
+float3 avatar_addRimLight(avatar_Geometry geometry, avatar_Material t)
+{
+    float multiplier = 1.0f;
+    avatar_Material t_1 = t;
+    float gradient = (atan2(dot(geometry.normal, float3(1.0f, 0.0f, 0.0f)), dot(geometry.normal, float3(0.0f, 1.0f, 0.0f))) / 6.283185482025146484375f) + 0.5f;
+    if (gradient < t.rim_light_material.start)
+    {
+        multiplier = 0.0f;
+    }
+    if ((gradient >= t.rim_light_material.start) && (gradient <= t.rim_light_material.end))
+    {
+        multiplier = smoothstep(t.rim_light_material.start, t.rim_light_material.start + t.rim_light_material.transition, gradient);
+        multiplier *= smoothstep(t.rim_light_material.end, t.rim_light_material.end - t.rim_light_material.transition, gradient);
+    }
+    if (gradient > t.rim_light_material.end)
+    {
+        multiplier = 0.0f;
+    }
+    if (t.rim_light_material.bias == 0.0f)
+    {
+        t_1.rim_light_material.bias = 0.001000000047497451305389404296875f;
+    }
+    return (t_1.rim_light_material.color * t_1.rim_light_material.intensity) * (pow(clamp(1.0f - computeNdotV(geometry.normal, geometry.worldViewDir), 0.0f, 1.0f), 1.0f / t_1.rim_light_material.bias) * multiplier);
+}
+
+float dither17b(float2 svPosition, float frameIndexMod4)
+{
+    return frac(dot(float3(svPosition, frameIndexMod4), float3(0.117647059261798858642578125f, 0.4117647111415863037109375f, 1.35294115543365478515625f)));
+}
+
+uint alphaToCoverage(float alpha)
+{
+    uint Coverage = 0u;
+    if (alpha > 0.0f)
+    {
+        Coverage = 136u;
+    }
+    if (alpha > 0.25f)
+    {
+        Coverage = 153u;
+    }
+    if (alpha > 0.5f)
+    {
+        Coverage = 221u;
+    }
+    if (alpha > 0.75f)
+    {
+        Coverage = 255u;
+    }
+    return Coverage;
+}
+
+uint coverageFromMaskMSAA4(float t, float2 svPos, bool dither)
+{
+    float t_1 = t;
+    if (dither)
+    {
+        t_1 = t - (dither17b(svPos, 0.0f) / 4.0f);
+    }
+    else
+    {
+        t_1 -= 0.125f;
+    }
+    return alphaToCoverage(t_1);
+}
+
+uint avatar_calculateAlphaCoverage(avatar_FragmentInput i, float3 positionInScreenSpace)
+{
+    bool _return = false;
+    uint _returnValue;
+    if (!i.options.enableAlphaToCoverage)
+    {
+        _return = true;
+        _returnValue = 255u;
+    }
+    if (!_return)
+    {
+        _return = true;
+        _returnValue = coverageFromMaskMSAA4(i.material.alpha, positionInScreenSpace.xy, true);
+    }
+    return _returnValue;
+}
+
+void AppSpecificPostManipulation(avatar_FragmentInput i, inout avatar_FragmentOutput o);
+
+float3 SRGBtoLINEAR(float3 srgbIn)
+{
+    return pow(srgbIn, 2.2000000476837158203125f.xxx);
+}
+
+float4 avatar_finalOutputColor(avatar_FragmentInput i, avatar_FragmentOutput o)
+{
+    float4 color = o.color;
+    if (i.debugMode == 1)
+    {
+        color = float4(i.material.base_color.x, i.material.base_color.y, i.material.base_color.z, float2(0.0f, 1.0f).y);
+    }
+    if (i.debugMode == 2)
+    {
+        float3 _4337 = i.material.occlusion.xxx;
+        color = float4(_4337.x, _4337.y, _4337.z, color.w);
+    }
+    if (i.debugMode == 3)
+    {
+        float3 _4348 = ConvertOutputColorSpaceFromSRGB(i.material.roughness.xxx);
+        color = float4(_4348.x, _4348.y, _4348.z, color.w);
+    }
+    if (i.debugMode == 4)
+    {
+        float3 _4359 = ConvertOutputColorSpaceFromSRGB(i.material.metallic.xxx);
+        color = float4(_4359.x, _4359.y, _4359.z, color.w);
+    }
+    if (i.debugMode == 5)
+    {
+        float3 _4369 = i.material.thickness.xxx;
+        color = float4(_4369.x, _4369.y, _4369.z, color.w);
+    }
+    bool _4374 = i.debugMode == 6;
+    if (_4374)
+    {
+        color = float4(i.geometry.normal.x, i.geometry.normal.y, i.geometry.normal.z, color.w);
+    }
+    if (_4374)
+    {
+        float3 _4389 = (i.geometry.normal * 0.5f) + 0.5f.xxx;
+        color = float4(_4389.x, _4389.y, _4389.z, color.w);
+    }
+    if (i.debugMode == 19)
+    {
+        float3 _4400 = (i.geometry.tangentSpace.tangent * 0.5f) + 0.5f.xxx;
+        color = float4(_4400.x, _4400.y, _4400.z, color.w);
+    }
+    if (i.debugMode == 20)
+    {
+        float3 _4411 = (i.geometry.tangentSpace.bitangent * 0.5f) + 0.5f.xxx;
+        color = float4(_4411.x, _4411.y, _4411.z, color.w);
+    }
+    if (i.debugMode == 7)
+    {
+    }
+    if (i.debugMode == 8)
+    {
+    }
+    if (i.debugMode == 9)
+    {
+        float3 world_view_dir = computeViewDir(i.geometry.camera, i.geometry.positionInWorldSpace);
+        color = float4(world_view_dir.x, world_view_dir.y, world_view_dir.z, float2(0.0f, 1.0f).y);
+    }
+    if (i.debugMode == 10)
+    {
+        float3 _4443 = o.p_specular + o.p_diffuse;
+        color = float4(_4443.x, _4443.y, _4443.z, color.w);
+    }
+    if (i.debugMode == 11)
+    {
+        color = float4(o.p_specular.x, o.p_specular.y, o.p_specular.z, color.w);
+    }
+    if (i.debugMode == 12)
+    {
+        color = float4(o.p_diffuse.x, o.p_diffuse.y, o.p_diffuse.z, color.w);
+    }
+    if (i.debugMode == 13)
+    {
+        float3 _4469 = o.a_specular + o.a_diffuse;
+        color = float4(_4469.x, _4469.y, _4469.z, color.w);
+    }
+    if (i.debugMode == 14)
+    {
+        color = float4(o.a_specular.x, o.a_specular.y, o.a_specular.z, color.w);
+    }
+    if (i.debugMode == 15)
+    {
+        color = float4(o.a_diffuse.x, o.a_diffuse.y, o.a_diffuse.z, color.w);
+    }
+    if (i.debugMode == 16)
+    {
+    }
+    if (i.debugMode == 17)
+    {
+        color = float4(o.subSurfaceColor.x, o.subSurfaceColor.y, o.subSurfaceColor.z, color.w);
+    }
+    if (i.debugMode == 18)
+    {
+        float subMeshId = i.material.alpha;
+        color = float4(0.0f.xxx.x, 0.0f.xxx.y, 0.0f.xxx.z, color.w);
+        if (avatar_IsOfType(subMeshId, 0.00390625f))
+        {
+            color = float4(0.20000000298023223876953125f.xxx.x, 0.20000000298023223876953125f.xxx.y, 0.20000000298023223876953125f.xxx.z, color.w);
+        }
+        if (avatar_IsOfType(subMeshId, 0.0078125f))
+        {
+            color = float4(float3(0.769999980926513671875f, 0.64999997615814208984375f, 0.64999997615814208984375f).x, float3(0.769999980926513671875f, 0.64999997615814208984375f, 0.64999997615814208984375f).y, float3(0.769999980926513671875f, 0.64999997615814208984375f, 0.64999997615814208984375f).z, color.w);
+        }
+        if (avatar_IsOfType(subMeshId, 0.015625f))
+        {
+            color = float4(float3(0.769999980926513671875f, 0.64999997615814208984375f, 0.64999997615814208984375f).x, float3(0.769999980926513671875f, 0.64999997615814208984375f, 0.64999997615814208984375f).y, float3(0.769999980926513671875f, 0.64999997615814208984375f, 0.64999997615814208984375f).z, color.w);
+        }
+        if (avatar_IsOfType(subMeshId, 0.03125f))
+        {
+            color = float4(float3(0.3449999988079071044921875f, 0.2700000107288360595703125f, 0.10999999940395355224609375f).x, float3(0.3449999988079071044921875f, 0.2700000107288360595703125f, 0.10999999940395355224609375f).y, float3(0.3449999988079071044921875f, 0.2700000107288360595703125f, 0.10999999940395355224609375f).z, color.w);
+        }
+        if (avatar_IsOfType(subMeshId, 0.0625f))
+        {
+            color = float4(float3(0.23999999463558197021484375f, 0.189999997615814208984375f, 0.07999999821186065673828125f).x, float3(0.23999999463558197021484375f, 0.189999997615814208984375f, 0.07999999821186065673828125f).y, float3(0.23999999463558197021484375f, 0.189999997615814208984375f, 0.07999999821186065673828125f).z, color.w);
+        }
+        if (avatar_IsOfType(subMeshId, 0.125f))
+        {
+            color = float4(float3(0.0f, 0.0f, 1.0f).x, float3(0.0f, 0.0f, 1.0f).y, float3(0.0f, 0.0f, 1.0f).z, color.w);
+        }
+        if (avatar_IsOfType(subMeshId, 0.25f))
+        {
+            color = float4(float3(0.0f, 1.0f, 0.0f).x, float3(0.0f, 1.0f, 0.0f).y, float3(0.0f, 1.0f, 0.0f).z, color.w);
+        }
+        if (avatar_IsOfType(subMeshId, 0.5f))
+        {
+            color = float4(float3(0.5f, 0.0f, 0.0f).x, float3(0.5f, 0.0f, 0.0f).y, float3(0.5f, 0.0f, 0.0f).z, color.w);
+        }
+        if (avatar_IsOfType(subMeshId, 1.0f))
+        {
+            color = float4(float3(0.20000000298023223876953125f, 0.100000001490116119384765625f, 0.0500000007450580596923828125f).x, float3(0.20000000298023223876953125f, 0.100000001490116119384765625f, 0.0500000007450580596923828125f).y, float3(0.20000000298023223876953125f, 0.100000001490116119384765625f, 0.0500000007450580596923828125f).z, color.w);
+        }
+        if (avatar_IsOfType(subMeshId, 2.0f))
+        {
+            color = float4(0.100000001490116119384765625f.xxx.x, 0.100000001490116119384765625f.xxx.y, 0.100000001490116119384765625f.xxx.z, color.w);
+        }
+        if (avatar_IsOfType(subMeshId, 4.0f))
+        {
+            color = float4(1.0f.xxx.x, 1.0f.xxx.y, 1.0f.xxx.z, color.w);
+        }
+    }
+    return color;
+}
+
+void frag_Fragment_main()
+{
+    avatar_FragmentInput i = avatar_zeroFragmentInput();
+    i.options = getFragOptions(v_Color.w);
+    i.debugMode = Debug;
+    int lightCount = getLightCount();
+    i.lightsCount = lightCount;
+    i.lights[0].intensity = 1.0f;
+    i.lights[0].direction = getLightDirection();
+    i.lights[0].color = getLightColor();
+    i.lights[0].position = getLightPosition();
+    i.lights[0].type = 0;
+    i.lights[0].range = 1000.0f;
+    i.lights[0].innerConeCos = 0.0f;
+    i.lights[0].outerConeCos = 0.0f;
+    avatar_Light avatarLight;
+    for (int idx = 1; idx < 8; idx++)
+    {
+        if (idx < lightCount)
+        {
+            OvrLight ovrLight = getAdditionalLight(idx, v_WorldPos);
+            avatarLight.direction = -ovrLight.direction;
+            avatarLight.intensity = 1.0f;
+            avatarLight.color = ovrLight.color;
+            avatarLight.position = ovrLight.direction;
+            avatarLight.type = 0;
+            avatarLight.range = 100.0f;
+            avatarLight.innerConeCos = 0.0f;
+            avatarLight.outerConeCos = 0.0f;
+            i.lights[idx] = avatarLight;
+        }
+    }
+    i.geometry.camera = _WorldSpaceCameraPos;
+    i.geometry.positionInWorldSpace = v_WorldPos;
+    i.geometry.positionInClipSpace = v_Vertex.xyz / v_Vertex.w.xxx;
+    i.geometry.texcoord_0 = v_UVCoord1;
+    i.geometry.texcoord_1 = v_UVCoord2;
+    i.geometry.normalScale = u_NormalScale;
+    i.geometry.tangentSpace.normal = normalize(v_Normal);
+    i.geometry.tangentSpace.tangent = normalize(v_Tangent.xyz);
+    i.geometry.tangentSpace.bitangent = calculateBitangent(i.geometry.tangentSpace);
+    i.geometry.normal = i.geometry.tangentSpace.normal;
+    bool _return = false;
+    float3 _returnValue;
+    if (!i.options.enableNormalMapping)
+    {
+        _return = true;
+        _returnValue = i.geometry.normal;
+    }
+    if (!_return)
+    {
+        float3 shadingNormal = float3(((tex2D(u_NormalSampler, i.geometry.texcoord_0) * 2.0f) - 1.0f.xxxx).xy, 1.0f);
+        shadingNormal *= float3(i.geometry.normalScale, i.geometry.normalScale, 1.0f);
+        _return = true;
+        _returnValue = mul(normalize(shadingNormal), float3x3(i.geometry.tangentSpace.tangent, i.geometry.tangentSpace.bitangent, i.geometry.tangentSpace.normal));
+    }
+    i.geometry.normal = _returnValue;
+    i.geometry.color = v_Color;
+    i.geometry.ormt = v_ORMT;
+    i.material.base_color = StaticSelectMaterialModeColor(u_BaseColorSampler, i.geometry.texcoord_0, float4(i.geometry.color.x, i.geometry.color.y, i.geometry.color.z, float2(0.0f, 1.0f).y)).xyz;
+    i.material.ramp_selector = 1.0f - (float(u_RampSelector) / 45.0f);
+    i.material.color_selector_lod = 0;
+    bool _return_1 = false;
+    float3 _returnValue_1;
+    if (i.options.enablePreviewColorRamp)
+    {
+        float2 rampCoord = float2(LINEARtoSRGB(i.material.base_color.x), i.material.ramp_selector);
+        _return_1 = true;
+        _returnValue_1 = tex2Dlod(u_ColorGradientSampler, float4(rampCoord, 0.0, float(i.material.color_selector_lod))).xyz;
+    }
+    if (!_return_1)
+    {
+        _return_1 = true;
+        _returnValue_1 = i.material.base_color;
+    }
+    i.material.base_color = _returnValue_1;
+    i.material.alpha = i.geometry.color.w;
+    float4 ormt = StaticSelectMaterialModeColor(u_MetallicRoughnessSampler, i.geometry.texcoord_0, i.geometry.ormt);
+    float _5058 = ormt.x;
+    i.material.occlusion = lerp(1.0f, _5058, u_OcclusionStrength);
+    float _5063 = ormt.y;
+    i.material.roughness = _5063 * u_RoughnessFactor;
+    float _5068 = ormt.z;
+    i.material.metallic = _5068 * u_MetallicFactor;
+    i.material.ambient_diffuse_factor = 1.0f;
+    i.material.ambient_specular_factor = 1.0f;
+    i.ambient_color = v_SH;
+    float _5077 = ormt.w;
+    i.material.thickness = _5077 * u_ThicknessFactor;
+    i.material.thickness = u_ThicknessFactor;
+    if (i.options.enableSkin)
+    {
+        i.material.occlusion *= u_SkinORMFactor.x;
+        i.material.roughness *= u_SkinORMFactor.y;
+        i.material.metallic *= u_SkinORMFactor.z;
+    }
+    i.material.hair_material.subsurface_color = u_HairSubsurfaceColor;
+    i.material.hair_material.scatter_intensity = u_HairScatterIntensity;
+    i.material.hair_material.specular_color_factor = u_HairSpecularColorFactor;
+    i.material.hair_material.specular_shift_intensity = u_HairSpecularShiftIntensity;
+    i.material.hair_material.specular_white_intensity = u_HairSpecularWhiteIntensity;
+    i.material.hair_material.specular_white_roughness = u_HairRoughness;
+    i.material.hair_material.specular_color_intensity = u_HairSpecularColorIntensity;
+    i.material.hair_material.specular_color_offset = u_HairSpecularColorOffset;
+    i.material.hair_material.specular_color_roughness = u_HairColorRoughness;
+    i.material.hair_material.anisotropic_intensity = u_HairAnisotropicIntensity;
+    i.material.hair_material.diffused_intensity = u_HairDiffusedIntensity;
+    i.material.hair_material.normal_intensity = u_HairSpecularNormalIntensity;
+    i.material.hair_material.specular_glint = u_HairSpecularGlint;
+    i.material.hair_material.ao_intensity = _5058;
+    i.material.hair_material.flow_angle = (_5063 - 0.5f) * 6.283185482025146484375f;
+    i.material.hair_material.shift = _5068;
+    i.material.hair_material.blend = _5077;
+    i.material.rim_light_material.intensity = u_RimLightIntensity;
+    i.material.rim_light_material.bias = u_RimLightBias;
+    i.material.rim_light_material.color = u_RimLightColor;
+    i.material.rim_light_material.transition = u_RimLightTransition;
+    i.material.rim_light_material.start = u_RimLightStartPosition;
+    i.material.rim_light_material.end = u_RimLightEndPosition;
+    i.material.exposure = u_Exposure;
+    i.material.skin.subsurface_color = u_SubsurfaceColor;
+    i.material.skin.skin_ORM_factor = u_SkinORMFactor;
+    i.material.eye_glint_factor = u_EyeGlintFactor;
+    i.material.eye_glint_color_factor = u_EyeGlintColorFactor;
+    i.matrices.objectToWorld = unity_ObjectToWorld;
+    i.matrices.worldToObject = float3x3(unity_WorldToObject[0].xyz, unity_WorldToObject[1].xyz, unity_WorldToObject[2].xyz);
+    i.geometry.worldViewDir = computeViewDir(i.geometry.camera, i.geometry.positionInWorldSpace);
+    float mipCount = 1.0f * float(u_MipCount);
+    i.geometry.lod = clamp(i.material.roughness * mipCount, 0.0f, mipCount);
+    AppSpecificPreManipulation(i);
+    float3 punctualDiffuseWrap = 0.0f.xxx;
+    float3 rimLight = 0.0f.xxx;
+    avatar_FragmentOutput o_1 = avatar_zeroFragmentOutput();
+    for (int lightIdx = 0; lightIdx < 8; lightIdx++)
+    {
+        if (lightIdx < i.lightsCount)
+        {
+            avatar_Light light = i.lights[lightIdx];
+            if (!i.options.enableEyeGlint)
+            {
+                o_1.p_specular += avatar_computeSpecular(i.geometry, light, i.material, i.options);
+            }
+            float3 diffuseWrap = 0.0f.xxx;
+            float3 _5389 = avatar_computeDiffuse(i.geometry, light, i.material, diffuseWrap);
+            o_1.p_diffuse += _5389;
+            punctualDiffuseWrap += diffuseWrap;
+        }
+        if (i.options.enableShadows)
+        {
+            o_1.p_diffuse *= i.lights[0].shadowTerm;
+            o_1.p_specular *= i.lights[0].shadowTerm;
+            punctualDiffuseWrap *= i.lights[0].shadowTerm;
+        }
+    }
+    if ((i.options.enableEyeGlint && (!i.options.enableSkin)) && (!i.options.enableHair))
+    {
+        o_1.p_specular += 0.0f.xxx;
+    }
+    float3 diffuse = 0.0f.xxx;
+    float3 _5511 = -i.geometry.worldViewDir;
+    float _5516 = 1.0f - i.material.roughness;
+    OvrGetUnityDiffuseGlobalIllumination(i.lights[0].color, i.lights[0].direction, i.geometry.positionInWorldSpace, _5511, 1.0f, i.ambient_color, _5516, i.material.metallic, i.material.occlusion, i.material.base_color, i.geometry.normal, 0.0f.xxx, diffuse);
+    avatar_AmbientLighting ambient;
+    ambient.diffuse = diffuse;
+    float3 diffuse0 = 0.0f.xxx;
+    float3 specular = 0.0f.xxx;
+    OvrGetUnityGlobalIllumination(i.lights[0].color, i.lights[0].direction, i.geometry.positionInWorldSpace, _5511, 1.0f, i.ambient_color, _5516, i.material.metallic, i.material.occlusion, i.material.base_color, i.geometry.normal, o_1.p_specular, diffuse0, specular);
+    ambient.specular = specular;
+    float hairBlend = 0.0f;
+    float3 totalhairPunctualSpec = 0.0f.xxx;
+    float3 specularFactor = lerp((0.039999999105930328369140625f + ((lerp(1.0f, 0.039999999105930328369140625f, sqrt(i.material.roughness)) - 0.039999999105930328369140625f) * saturate(pow(1.0f - computeNdotV(i.geometry.normal, i.geometry.worldViewDir), 5.0f)))).xxx, i.material.base_color, i.material.metallic.xxx) * i.material.occlusion;
+    if (!i.options.enableEyeGlint)
+    {
+        o_1.p_specular *= specularFactor;
+    }
+    o_1.a_specular = (specularFactor * i.material.ambient_specular_factor) * ambient.specular;
+    if (i.options.enableHair)
+    {
+        hairBlend = i.material.hair_material.blend;
+        avatar_HairMaterial hair_mat = i.material.hair_material;
+        float2 flow = float2(cos(hair_mat.flow_angle), sin(hair_mat.flow_angle));
+        float3 hairTangent = (i.geometry.tangentSpace.tangent * flow.x) + (i.geometry.tangentSpace.bitangent * flow.y);
+        float3 hairTangent2 = normalize(hairTangent);
+        float3 hairNormal = lerp(i.geometry.tangentSpace.normal, i.geometry.normal, i.material.hair_material.normal_intensity.xxx);
+        float3x3 hairCoordinateSystem = float3x3(hairNormal, hairTangent2, normalize(cross(hairNormal, hairTangent2)));
+        for (int lightIdx_1 = 0; lightIdx_1 < 8; lightIdx_1++)
+        {
+            if (lightIdx_1 < i.lightsCount)
+            {
+                avatar_Light light_1 = i.lights[lightIdx_1];
+                float3 directional_light_color = ((light_1.color * (1.0f / i.material.exposure)) * 0.3183098733425140380859375f) * light_1.intensity;
+                totalhairPunctualSpec += ((computeHairSpecular(i, light_1.direction, hairCoordinateSystem, i.material.hair_material.aniso_blend) * directional_light_color) * 0.3183098733425140380859375f);
+            }
+        }
+        o_1.p_specular = blendPunctualSpecularWithHair(o_1.p_specular, totalhairPunctualSpec, hairBlend);
+        o_1.subSurfaceColor = blendSubSurfaceColorWithHair(i.material.hair_material.subsurface_color, float3(1.0f, 0.300000011920928955078125f, 0.20000000298023223876953125f), hairBlend);
+        o_1.a_specular *= (1.0f - hairBlend);
+        o_1.p_diffuse *= lerp(1.0f, i.material.hair_material.diffused_intensity, hairBlend);
+    }
+    avatar_FragmentOutput _5442 = o_1;
+    if (i.options.enableSkin)
+    {
+        o_1.p_diffuse = (punctualDiffuseWrap * float3(1.0f, 0.300000011920928955078125f, 0.20000000298023223876953125f)) + (_5442.p_diffuse * (1.0f.xxx - float3(1.0f, 0.300000011920928955078125f, 0.20000000298023223876953125f)));
+        float3 subsurfaceColor = 0.0f.xxx;
+        float3 diffuse_1 = 0.0f.xxx;
+        float3 accumulatedDiffuseColor = 0.0f.xxx;
+        avatar_HemisphereNormalOffsets hemisphereNormalOffsets = avatar_computeHemisphereNormalOffsets(i);
+        float3 diffuse_2 = 0.0f.xxx;
+        OvrGetUnityDiffuseGlobalIllumination(i.lights[0].color, i.lights[0].direction, i.geometry.positionInWorldSpace, -i.geometry.worldViewDir, 1.0f, i.ambient_color, 1.0f - i.material.roughness, i.material.metallic, i.material.occlusion, i.material.base_color, hemisphereNormalOffsets.lv1, 0.0f.xxx, diffuse_2);
+        float3 ibl_diffuse1 = saturate(diffuse_2);
+        float3 diffuse_3 = 0.0f.xxx;
+        OvrGetUnityDiffuseGlobalIllumination(i.lights[0].color, i.lights[0].direction, i.geometry.positionInWorldSpace, -i.geometry.worldViewDir, 1.0f, i.ambient_color, 1.0f - i.material.roughness, i.material.metallic, i.material.occlusion, i.material.base_color, hemisphereNormalOffsets.lv2, 0.0f.xxx, diffuse_3);
+        float3 ibl_diffuse2 = saturate(diffuse_3);
+        float3 diffuse_4 = 0.0f.xxx;
+        OvrGetUnityDiffuseGlobalIllumination(i.lights[0].color, i.lights[0].direction, i.geometry.positionInWorldSpace, -i.geometry.worldViewDir, 1.0f, i.ambient_color, 1.0f - i.material.roughness, i.material.metallic, i.material.occlusion, i.material.base_color, hemisphereNormalOffsets.lv3, 0.0f.xxx, diffuse_4);
+        float3 ibl_diffuse3 = saturate(diffuse_4);
+        float3 diffuse_5 = 0.0f.xxx;
+        OvrGetUnityDiffuseGlobalIllumination(i.lights[0].color, i.lights[0].direction, i.geometry.positionInWorldSpace, -i.geometry.worldViewDir, 1.0f, i.ambient_color, 1.0f - i.material.roughness, i.material.metallic, i.material.occlusion, i.material.base_color, hemisphereNormalOffsets.nn, 0.0f.xxx, diffuse_5);
+        float3 ibl_diffuseN = saturate(diffuse_5);
+        for (int lightIdx_2 = 0; lightIdx_2 < 8; lightIdx_2++)
+        {
+            if (lightIdx_2 < i.lightsCount)
+            {
+                float3 worldSpaceLightDir = -i.lights[lightIdx_2].direction;
+                float3 lightColor = (i.lights[lightIdx_2].color * i.lights[lightIdx_2].intensity) * (1.0f / 3.1415927410125732421875f);
+                float3 directionalLightColor = lightColor * (1.0f / i.material.exposure);
+                float directionalSoftDiffuseValue = (saturate(dot(worldSpaceLightDir, hemisphereNormalOffsets.lv1)) + saturate(dot(worldSpaceLightDir, hemisphereNormalOffsets.lv2))) + saturate(dot(worldSpaceLightDir, hemisphereNormalOffsets.lv3));
+                diffuse_1 += (directionalSoftDiffuseValue.xxx * directionalLightColor);
+                accumulatedDiffuseColor += (directionalLightColor * saturate(dot(worldSpaceLightDir, hemisphereNormalOffsets.nn)));
+            }
+        }
+        float3 softDiffuseLight = (((ibl_diffuse1 + ibl_diffuse2) + ibl_diffuse3) + diffuse_1) * 0.3333333432674407958984375f;
+        subsurfaceColor = ((softDiffuseLight * float3(1.0f, 0.300000011920928955078125f, 0.20000000298023223876953125f)) * saturate(i.material.occlusion + 0.4000000059604644775390625f)) + (((accumulatedDiffuseColor + ibl_diffuseN) * (1.0f.xxx - float3(1.0f, 0.300000011920928955078125f, 0.20000000298023223876953125f))) * i.material.occlusion);
+        o_1.subSurfaceColor = subsurfaceColor;
+        o_1.subSurfaceColor -= (o_1.p_diffuse * i.material.occlusion);
+        o_1.subSurfaceColor = saturate(o_1.subSurfaceColor);
+    }
+    float3 diffuseFactor = i.material.base_color * (i.material.occlusion * (1.0f - i.material.metallic));
+    o_1.p_diffuse = diffuseFactor * o_1.p_diffuse;
+    o_1.a_diffuse = (diffuseFactor * i.material.ambient_diffuse_factor) * ambient.diffuse;
+    o_1.subSurfaceColor *= i.material.base_color;
+    if (i.options.enableRimLight)
+    {
+        rimLight = 0.0f.xxx + avatar_addRimLight(i.geometry, i.material);
+    }
+    float3 finalColor_1 = ((((0.0f.xxx + (o_1.p_diffuse + o_1.a_diffuse)) + rimLight) + (_5442.p_specular + _5442.a_specular)) + o_1.subSurfaceColor) * i.material.exposure;
+    o_1.color = float4(finalColor_1.x, finalColor_1.y, finalColor_1.z, o_1.color.w);
+    o_1.color.w = 1.0f;
+    avatar_FragmentOutput o = o_1;
+    o.color = float4(o_1.color.xyz.x, o_1.color.xyz.y, o_1.color.xyz.z, o.color.w);
+    o.alphaCoverage = avatar_calculateAlphaCoverage(i, i.geometry.positionInClipSpace);
+    AppSpecificPostManipulation(i, o);
+    float4 finalColor = float4(o.color.x, o.color.y, o.color.z, float2(0.0f, 1.0f).y);
+    if (i.options.enableDebugRender)
+    {
+        finalColor = avatar_finalOutputColor(i, o);
+    }
+    float4 _5191 = finalColor;
+    finalColor = float4(_5191.xyz.x, _5191.xyz.y, _5191.xyz.z, _5191.w);
+    _21 = float4(_5191.xyz.x, _5191.xyz.y, _5191.xyz.z, _5191.w);
+}
+
+FragmentOutput Fragment_main(VertexToFragment stage_input)
+{
+    v_Vertex = stage_input.v_Vertex;
+    v_WorldPos = stage_input.v_WorldPos;
+    v_Normal = stage_input.v_Normal;
+    v_Tangent = stage_input.v_Tangent;
+    v_UVCoord1 = stage_input.v_UVCoord1;
+    v_UVCoord2 = stage_input.v_UVCoord2;
+    v_Color = stage_input.v_Color;
+    v_ORMT = stage_input.v_ORMT;
+    v_SH = stage_input.v_SH;
+    frag_Fragment_main();
+    FragmentOutput stage_output;
+    stage_output._21 = float4(_21);
+    return stage_output;
+}
+
+// Generated by AvatarShaderLibrary 242d0d331a49
+
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/export/pbr_frag.unity.hlsl.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/export/pbr_frag.unity.hlsl.meta
new file mode 100644
index 0000000000000000000000000000000000000000..0dda0ac2732e291fc130d3d69d087b46c85fb1fc
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/export/pbr_frag.unity.hlsl.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 17ba2b93e3c1b704dab01e5266a61b8b
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  preprocessorOverride: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/export/pbr_vert.unity.hlsl b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/export/pbr_vert.unity.hlsl
new file mode 100644
index 0000000000000000000000000000000000000000..cbacbdc3c2efbf436dcea1249ecc0b310713fac2
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/export/pbr_vert.unity.hlsl
@@ -0,0 +1,121 @@
+// Generated by AvatarShaderLibrary 242d0d331a49
+
+
+
+struct avatar_VertOptions
+{
+    bool enableNormalMapping;
+    bool enableAlphaToCoverage;
+    bool enableSkin;
+    bool enableEyeGlint;
+};
+
+struct avatar_AvatarVertexInput
+{
+    float4 position;
+    float3 normal;
+    float3 tangent;
+    float2 texcoord0;
+    float2 texcoord1;
+    float4 color;
+    float4 ormt;
+    float3 ambient;
+    avatar_VertOptions options;
+};
+
+
+
+
+
+static float4 a_Position;
+static float4 a_Normal;
+static float4 a_Tangent;
+static uint a_instanceID;
+static uint a_vertexID;
+static float2 a_UV1;
+static float2 a_UV2;
+static float4 a_Color;
+static float4 a_ORMT;
+static float4 v_Vertex;
+static float3 v_WorldPos;
+static float3 v_Normal;
+static float4 v_Tangent;
+static uint v_InstanceID;
+static float2 v_UVCoord1;
+static float2 v_UVCoord2;
+static float4 v_Color;
+static float4 v_ORMT;
+static float3 v_SH;
+
+
+
+
+
+
+avatar_VertOptions getVertOptions()
+{
+    avatar_VertOptions options;
+    options.enableNormalMapping = enableNormalMapping;
+    options.enableAlphaToCoverage = enableAlphaToCoverage;
+    options.enableSkin = enableSkin;
+    options.enableEyeGlint = enableEyeGlint;
+    return options;
+}
+
+void vert_Vertex_main()
+{
+    OvrVertexData ovrData = OvrCreateVertexData(float4(a_Position.xyz, 1.0f), a_Normal.xyz, a_Tangent, a_vertexID);
+    avatar_AvatarVertexInput vin;
+    vin.position = ovrData.position;
+    vin.normal = ovrData.normal;
+    vin.tangent = ovrData.tangent.xyz;
+    VertexInput vertexInput;
+    vertexInput.uv0 = a_UV1;
+    vertexInput.uv0 = a_UV2;
+    vin.ambient = VertexGIForward(vertexInput, vin.position.xyz, vin.normal).xyz;
+    vin.texcoord0 = a_UV1;
+    vin.texcoord1 = a_UV2;
+    vin.color = a_Color;
+    vin.ormt = a_ORMT;
+    vin.options = getVertOptions();
+    v_UVCoord1 = vin.texcoord0;
+    v_UVCoord2 = vin.texcoord1;
+    v_Color = vin.color;
+    v_ORMT = vin.ormt;
+    v_SH = vin.ambient;
+    v_Vertex = getVertexInClipSpace(vin.position.xyz / vin.position.w.xxx);
+    float4 worldPos = mul(unity_ObjectToWorld, vin.position);
+    v_WorldPos = worldPos.xyz / worldPos.w.xxx;
+    v_Normal = normalize(mul(float4(vin.normal, 0.0f), unity_WorldToObject).xyz);
+    v_Tangent = normalize(mul(float4(vin.tangent, 0.0f), unity_WorldToObject));
+    v_InstanceID = a_instanceID;
+}
+
+VertexToFragment Vertex_main(AvatarVertexInput stage_input)
+{
+    a_Position = stage_input.a_Position;
+    a_Normal = stage_input.a_Normal;
+    a_Tangent = stage_input.a_Tangent;
+    a_instanceID = stage_input.a_instanceID;
+    a_vertexID = stage_input.a_vertexID;
+    a_UV1 = stage_input.a_UV1;
+    a_UV2 = stage_input.a_UV2;
+    a_Color = stage_input.a_Color;
+    a_ORMT = stage_input.a_ORMT;
+    vert_Vertex_main();
+    VertexToFragment stage_output;
+    stage_output.v_Vertex = v_Vertex;
+    stage_output.v_WorldPos = v_WorldPos;
+    stage_output.v_Normal = v_Normal;
+    stage_output.v_Tangent = v_Tangent;
+    stage_output.v_InstanceID = v_InstanceID;
+    stage_output.v_UVCoord1 = v_UVCoord1;
+    stage_output.v_UVCoord2 = v_UVCoord2;
+    stage_output.v_Color = v_Color;
+    stage_output.v_ORMT = v_ORMT;
+    stage_output.v_SH = v_SH;
+    return stage_output;
+}
+
+// Generated by AvatarShaderLibrary 242d0d331a49
+
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/export/pbr_vert.unity.hlsl.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/export/pbr_vert.unity.hlsl.meta
new file mode 100644
index 0000000000000000000000000000000000000000..f29a7dd60dce710772fdb08a3a68a0a5a888ab41
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/export/pbr_vert.unity.hlsl.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 749f63198726fa14c8708c0514af554c
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  preprocessorOverride: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/replacement.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/replacement.meta
new file mode 100644
index 0000000000000000000000000000000000000000..a1ad1d98c6046489ebb5894b1d861704736a8fc7
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/replacement.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: c5f17e6464fe46940ab47d14ef74628f
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/replacement/options_common.hlsl b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/replacement/options_common.hlsl
new file mode 100644
index 0000000000000000000000000000000000000000..e7a6335a491611ea668c742048f2efe1625c7709
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/replacement/options_common.hlsl
@@ -0,0 +1,48 @@
+
+#ifdef HAS_NORMAL_MAP_ON
+static const bool enableNormalMapping = true;
+#else
+static const bool enableNormalMapping = false;
+#endif
+
+#ifdef SKIN_ON
+static const bool enableSkin = true;
+#else
+static const bool enableSkin = false;
+#endif
+
+#ifdef EYE_GLINTS_ON
+static const bool enableEyeGlint = true;
+#else
+static const bool enableEyeGlint = false;
+#endif
+
+#ifdef ENABLE_HAIR_ON
+static const bool enableHair = true;
+#else
+static const bool enableHair = false;
+#endif
+
+#ifdef ENABLE_RIM_LIGHT_ON
+static const bool enableRimLight = true;
+#else
+static const bool enableRimLight = false;
+#endif
+
+#ifdef ENABLE_ALPHA_TO_COVERAGE
+static const bool enableAlphaToCoverage = true;
+#else
+static const bool enableAlphaToCoverage = false;
+#endif
+
+#ifdef ENABLE_PREVIEW_COLOR_RAMP_ON
+static const bool enablePreviewColorRamp = true;
+#else
+static const bool enablePreviewColorRamp = false;
+#endif
+
+#if ENABLE_DEBUG_RENDER_ON
+static const bool enableDebugRender = true;
+#else
+static const bool enableDebugRender = false;
+#endif
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/replacement/options_common.hlsl.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/replacement/options_common.hlsl.meta
new file mode 100644
index 0000000000000000000000000000000000000000..3388b7f18bc4bffefd24e47836e31a672e31f0da
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/replacement/options_common.hlsl.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: e6669f1ed9a8b134a93a75edf6d2a68c
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  preprocessorOverride: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/replacement/platform_frag.hlsl b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/replacement/platform_frag.hlsl
new file mode 100644
index 0000000000000000000000000000000000000000..a400b6663873d782969dda418adc1b6b53b89f3d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/replacement/platform_frag.hlsl
@@ -0,0 +1,71 @@
+
+float3 SRGBtoLINEAR(float3 srgbIn);
+
+float3 ConvertOutputColorSpaceFromSRGB(float3 srgbInput)
+{
+#ifdef UNITY_COLORSPACE_GAMMA
+  return srgbInput;
+#else
+  return SRGBtoLINEAR(srgbInput);
+#endif
+}
+
+float3 LINEARtoSRGB(float3 color);
+
+float3 ConvertOutputColorSpaceFromLinear(float3 linearInput)
+{
+#ifdef UNITY_COLORSPACE_GAMMA
+  return LINEARtoSRGB(linearInput);
+#else
+  return linearInput;
+#endif
+}
+
+float4 StaticSelectMaterialModeColor(sampler2D texSampler, float2 texCoords, float4 vertexColor) {
+#if defined(MATERIAL_MODE_VERTEX)
+  return vertexColor;
+#else
+  return tex2D(texSampler, texCoords);
+#endif
+}
+
+int getLightCount() {
+#ifdef USING_URP
+    return GetAdditionalLightsCount() + 1;
+#else
+    return 1;
+#endif
+}
+
+float3 getLightDirection() {
+  #ifdef USING_URP
+    return -GetMainLight().direction;
+  #else
+      return -_WorldSpaceLightPos0;
+  #endif
+}
+
+float3 getLightColor() {
+#ifdef USING_URP
+    return _MainLightColor;
+#else
+    return _LightColor0;
+#endif
+}
+
+float3 getLightPosition() {
+#ifdef USING_URP
+    return _MainLightPosition;
+#else
+    return _WorldSpaceLightPos0;
+#endif
+}
+
+OvrLight getAdditionalLight(int idx, float3 worldPos){
+#ifdef USING_URP
+    return OvrGetAdditionalLight(idx, worldPos);
+#else
+    OvrLight dummy;
+    return dummy;
+#endif
+}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/replacement/platform_frag.hlsl.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/replacement/platform_frag.hlsl.meta
new file mode 100644
index 0000000000000000000000000000000000000000..d086185bf87cb7f4ccf4a31c779e5e0c64ae1353
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/replacement/platform_frag.hlsl.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 644b605420b639b4ea442ca5e2ffcc6a
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  preprocessorOverride: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/replacement/platform_vert.hlsl b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/replacement/platform_vert.hlsl
new file mode 100644
index 0000000000000000000000000000000000000000..5581887c44ec31cc795b6e15fda1ec63c5cfe864
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/replacement/platform_vert.hlsl
@@ -0,0 +1,22 @@
+
+
+float4 getVertexInClipSpace(float3 pos) {
+    #ifdef USING_URP
+        return mul (UNITY_MATRIX_VP, mul (UNITY_MATRIX_M, float4 (pos,1.0)));
+    #else
+        return UnityObjectToClipPos(pos);
+    #endif
+}
+#ifdef USING_URP
+
+struct VertexInput {
+    float2 uv0;
+    float2 uv1;
+};
+
+float4 VertexGIForward(VertexInput v, float3 posWorld, float3 normalWorld){
+      return float4(SampleSHVertex(normalWorld), 1.0);
+}
+
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/replacement/platform_vert.hlsl.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/replacement/platform_vert.hlsl.meta
new file mode 100644
index 0000000000000000000000000000000000000000..4ddfec703f2552de9e01bc6c76265424a7275625
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/replacement/platform_vert.hlsl.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: abf345b44e63d4ac5aab742ff85981d2
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  preprocessorOverride: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/replacement/structs_vert.hlsl b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/replacement/structs_vert.hlsl
new file mode 100644
index 0000000000000000000000000000000000000000..ea638c5ebb99b547e3afdab56ef9d5070bde8709
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/replacement/structs_vert.hlsl
@@ -0,0 +1,27 @@
+struct AvatarVertexInput
+{
+    float4 a_Color : COLOR;
+    float4 a_ORMT : TEXCOORD3;
+    float4 a_Normal : NORMAL;
+    float4 a_Position : POSITION;
+    float4 a_Tangent : TANGENT;
+    float2 a_UV1 : TEXCOORD0;
+    float2 a_UV2 : TEXCOORD1;
+    uint a_instanceID : SV_InstanceID;
+    uint a_vertexID : SV_VertexID;
+};
+
+struct VertexToFragment
+{
+    float4 v_Color : COLOR;
+    float4 v_ORMT : TEXCOORD5;
+    uint v_InstanceID : TEXCOORD8;
+    float3 v_Normal : TEXCOORD2;
+    float4 v_Tangent : TEXCOORD3;
+    float2 v_UVCoord1 : TEXCOORD0;
+    float2 v_UVCoord2 : TEXCOORD1;
+    float4 v_Vertex : SV_POSITION;
+    float3 v_WorldPos : TEXCOORD4;
+    float3 v_SH : TEXCOORD6;
+    //    float3 v_LightDir : TEXCOORD6;
+};
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/replacement/structs_vert.hlsl.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/replacement/structs_vert.hlsl.meta
new file mode 100644
index 0000000000000000000000000000000000000000..e9b00ce856edaaba856a9a05df24e08f641494b4
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Library/replacement/structs_vert.hlsl.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 44fc87e1a89a0f94096ac2cbd0893fc4
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  preprocessorOverride: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/LibraryVertexGI.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/LibraryVertexGI.meta
new file mode 100644
index 0000000000000000000000000000000000000000..a9c8e084ae50d507922209ca39f19b79ba16dff1
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/LibraryVertexGI.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 860af814d5896f54b85b57606027f0a5
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/LibraryVertexGI/export.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/LibraryVertexGI/export.meta
new file mode 100644
index 0000000000000000000000000000000000000000..497b1691fa247c1fbf42c174901952830178b27a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/LibraryVertexGI/export.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 00f6923528135e74fb67096f0c875420
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/LibraryVertexGI/export/pbr_frag.unity.hlsl b/Assets/Oculus/Avatar2/Example/Common/Shaders/LibraryVertexGI/export/pbr_frag.unity.hlsl
new file mode 100644
index 0000000000000000000000000000000000000000..2bceae961a2d31d0ae87b1a906d185ab75bd1669
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/LibraryVertexGI/export/pbr_frag.unity.hlsl
@@ -0,0 +1,1140 @@
+// Generated by AvatarShaderLibrary 242d0d331a49
+
+#ifdef ENABLE_enableEnvironmentMap
+  static const bool enableEnvironmentMap = true;
+#else
+  static const bool enableEnvironmentMap = false;
+#endif
+
+
+struct avatar_Light
+{
+    float3 direction;
+    float range;
+    float3 color;
+    float intensity;
+    float3 position;
+    float innerConeCos;
+    float outerConeCos;
+    float shadowTerm;
+    int type;
+};
+
+struct avatar_FragOptions
+{
+    bool enableNormalMapping;
+    bool enableAlphaToCoverage;
+    bool enableEnvironmentMap;
+    bool enableRimLight;
+    bool enablePreviewColorRamp;
+    bool enableDebugRender;
+    bool enableSkin;
+    bool enableEyeGlint;
+    bool enableHair;
+    bool enableShadows;
+};
+
+struct avatar_SkinMaterial
+{
+    float3 subsurface_color;
+    float3 skin_ORM_factor;
+};
+
+struct avatar_HairMaterial
+{
+    float3 subsurface_color;
+    float scatter_intensity;
+    float3 specular_color_factor;
+    float specular_shift_intensity;
+    float specular_white_intensity;
+    float specular_white_roughness;
+    float specular_color_intensity;
+    float specular_color_offset;
+    float specular_color_roughness;
+    float anisotropic_intensity;
+    float diffused_intensity;
+    float normal_intensity;
+    float specular_glint;
+    float ao_intensity;
+    float flow_angle;
+    float shift;
+    float blend;
+    float aniso_blend;
+};
+
+struct avatar_RimLightMaterial
+{
+    float intensity;
+    float bias;
+    float3 color;
+    float transition;
+    float start;
+    float end;
+};
+
+struct avatar_Material
+{
+    float3 base_color;
+    float alpha;
+    float exposure;
+    float metallic;
+    float occlusion;
+    float roughness;
+    float thickness;
+    float ambient_diffuse_factor;
+    float ambient_specular_factor;
+    float eye_glint_factor;
+    float eye_glint_color_factor;
+    avatar_SkinMaterial skin;
+    avatar_HairMaterial hair_material;
+    avatar_RimLightMaterial rim_light_material;
+    float ramp_selector;
+    int color_selector_lod;
+};
+
+struct avatar_Matrices
+{
+    float4x4 objectToWorld;
+    float3x3 worldToObject;
+    float4x4 viewProjection;
+};
+
+struct avatar_TangentSpace
+{
+    float3 normal;
+    float3 tangent;
+    float3 bitangent;
+};
+
+struct avatar_Geometry
+{
+    float3 camera;
+    float3 positionInClipSpace;
+    float3 positionInWorldSpace;
+    float3 normal;
+    avatar_TangentSpace tangentSpace;
+    float2 texcoord_0;
+    float2 texcoord_1;
+    float4 color;
+    float4 ormt;
+    float normalScale;
+    float3 worldViewDir;
+    float lod;
+};
+
+struct avatar_AmbientLighting
+{
+    float3 diffuse;
+    float3 specular;
+};
+
+struct avatar_FragmentInput
+{
+    avatar_FragOptions options;
+    avatar_Matrices matrices;
+    avatar_Geometry geometry;
+    avatar_Material material;
+    int lightsCount;
+    avatar_Light lights[8];
+    float3 ambient_color;
+    int debugMode;
+};
+
+struct avatar_FragmentOutput
+{
+    float4 color;
+    float3 p_specular;
+    float3 p_diffuse;
+    float3 a_specular;
+    float3 a_diffuse;
+    float3 subSurfaceColor;
+    uint alphaCoverage;
+};
+
+struct avatar_HemisphereNormalOffsets
+{
+    float3 nn;
+    float3 bb;
+    float3 tt;
+    float3 lv1;
+    float3 lv2;
+    float3 lv3;
+};
+
+struct avatar_SpecularData
+{
+    float3 directional_light_color;
+    float range_attentuation;
+    float spot_attenuation;
+    float _distance;
+    float3 point_to_light;
+    float actualCos;
+    float3 l;
+    float3 h;
+    float roughPow2;
+    float roughPow4;
+    float invRoughPow4;
+    float NdotV;
+};
+
+
+
+uniform sampler2D u_BaseColorSampler;
+uniform sampler2D u_MetallicRoughnessSampler;
+uniform sampler2D u_NormalSampler;
+uniform samplerCUBE _ReflectionCubeMap;
+uniform float u_NormalScale;
+uniform int u_MipCount;
+uniform int Debug;
+uniform float u_Exposure;
+uniform float u_MetallicFactor;
+uniform float u_OcclusionStrength;
+uniform float u_RoughnessFactor;
+uniform float u_ThicknessFactor;
+uniform float3 u_SubsurfaceColor;
+uniform float3 u_SkinORMFactor;
+uniform float u_EyeGlintFactor;
+uniform float u_EyeGlintColorFactor;
+uniform float3 u_HairSubsurfaceColor;
+uniform float u_HairScatterIntensity;
+uniform float3 u_HairSpecularColorFactor;
+uniform float u_HairSpecularShiftIntensity;
+uniform float u_HairSpecularWhiteIntensity;
+uniform float u_HairSpecularColorIntensity;
+uniform float u_HairSpecularColorOffset;
+uniform float u_HairRoughness;
+uniform float u_HairColorRoughness;
+uniform float u_HairAnisotropicIntensity;
+uniform float u_HairSpecularNormalIntensity;
+uniform float u_HairDiffusedIntensity;
+uniform float u_HairSpecularGlint;
+uniform float u_RimLightIntensity;
+uniform float u_RimLightBias;
+uniform float3 u_RimLightColor;
+uniform float u_RimLightTransition;
+uniform float u_RimLightStartPosition;
+uniform float u_RimLightEndPosition;
+uniform sampler2D u_ColorGradientSampler;
+uniform int u_RampSelector;
+
+static float4 _21;
+
+
+
+struct FragmentOutput
+{
+    float4 _21 : COLOR0;
+};
+
+
+avatar_Geometry avatar_zeroGeometry()
+{
+    avatar_Geometry geometry;
+    geometry.camera = 0.0f.xxx;
+    geometry.positionInWorldSpace = 0.0f.xxx;
+    geometry.positionInClipSpace = 0.0f.xxx;
+    geometry.texcoord_0 = 0.0f.xx;
+    geometry.texcoord_1 = 0.0f.xx;
+    geometry.color = 0.0f.xxxx;
+    geometry.normalScale = 0.0f;
+    geometry.tangentSpace.normal = 0.0f.xxx;
+    geometry.tangentSpace.tangent = 0.0f.xxx;
+    geometry.tangentSpace.bitangent = 0.0f.xxx;
+    geometry.normal = 0.0f.xxx;
+    return geometry;
+}
+
+avatar_HairMaterial avatar_zeroHairMaterial()
+{
+    avatar_HairMaterial material;
+    material.subsurface_color = 0.0f.xxx;
+    material.scatter_intensity = 0.0f;
+    material.specular_color_factor = 0.0f.xxx;
+    material.specular_shift_intensity = 0.0f;
+    material.specular_white_intensity = 0.0f;
+    material.specular_white_roughness = 0.0f;
+    material.specular_color_intensity = 0.0f;
+    material.specular_color_offset = 0.0f;
+    material.specular_color_roughness = 0.0f;
+    material.anisotropic_intensity = 0.0f;
+    material.diffused_intensity = 0.0f;
+    material.normal_intensity = 0.0f;
+    material.specular_glint = 0.0f;
+    material.ao_intensity = 0.0f;
+    material.flow_angle = 0.0f;
+    material.shift = 0.0f;
+    material.blend = 0.0f;
+    material.aniso_blend = 0.0f;
+    return material;
+}
+
+avatar_Material avatar_zeroMaterial()
+{
+    avatar_Material material0;
+    material0.base_color = 0.0f.xxx;
+    material0.alpha = 0.0f;
+    material0.exposure = 0.0f;
+    material0.metallic = 0.0f;
+    material0.occlusion = 0.0f;
+    material0.roughness = 0.0f;
+    material0.thickness = 0.0f;
+    material0.skin.subsurface_color = 0.0f.xxx;
+    material0.skin.skin_ORM_factor = 0.0f.xxx;
+    material0.eye_glint_factor = 0.0f;
+    material0.eye_glint_color_factor = 0.0f;
+    material0.hair_material = avatar_zeroHairMaterial();
+    material0.ramp_selector = 0.0f;
+    material0.color_selector_lod = 0;
+    return material0;
+}
+
+avatar_Matrices avatar_zeroMatrices()
+{
+    avatar_Matrices matrices;
+    matrices.objectToWorld = float4x4(0.0f.xxxx, 0.0f.xxxx, 0.0f.xxxx, 0.0f.xxxx);
+    matrices.worldToObject = float3x3(0.0f.xxx, 0.0f.xxx, 0.0f.xxx);
+    matrices.viewProjection = float4x4(0.0f.xxxx, 0.0f.xxxx, 0.0f.xxxx, 0.0f.xxxx);
+    return matrices;
+}
+
+avatar_FragOptions avatar_zeroOptions()
+{
+    avatar_FragOptions options;
+    options.enableNormalMapping = false;
+    options.enableAlphaToCoverage = false;
+    options.enableEnvironmentMap = false;
+    options.enableRimLight = false;
+    options.enableSkin = false;
+    options.enableEyeGlint = false;
+    options.enableHair = false;
+    options.enableShadows = false;
+    return options;
+}
+
+avatar_Light avatar_zeroLight()
+{
+    avatar_Light light;
+    light.direction = 0.0f.xxx;
+    light.range = 0.0f;
+    light.color = 0.0f.xxx;
+    light.intensity = 0.0f;
+    light.position = 0.0f.xxx;
+    light.innerConeCos = 0.0f;
+    light.outerConeCos = 0.0f;
+    light.shadowTerm = 1.0f;
+    light.type = 0;
+    return light;
+}
+
+avatar_FragmentInput avatar_zeroFragmentInput()
+{
+    avatar_FragmentInput i;
+    i.ambient_color = 0.0f.xxx;
+    i.geometry = avatar_zeroGeometry();
+    i.material = avatar_zeroMaterial();
+    i.matrices = avatar_zeroMatrices();
+    i.debugMode = 0;
+    i.options = avatar_zeroOptions();
+    for (int idx = 0; idx < 8; idx++)
+    {
+        i.lights[idx] = avatar_zeroLight();
+    }
+    return i;
+}
+
+bool avatar_WithinRange(float idChannel, float lowBound, float highBound)
+{
+    return (idChannel > (lowBound - 0.001953125f)) && (idChannel < (highBound + 0.001953125f));
+}
+
+bool avatar_UseSkin(float idChannel)
+{
+    return avatar_WithinRange(idChannel, 0.0078125f, 0.015625f);
+}
+
+bool avatar_UseEyeGlint(float idChannel)
+{
+    return avatar_WithinRange(idChannel, 0.125f, 0.25f);
+}
+
+bool avatar_IsOfType(float idChannel, float subMeshType)
+{
+    return avatar_WithinRange(idChannel, subMeshType, subMeshType);
+}
+
+bool avatar_UseHair(float idChannel)
+{
+    return avatar_IsOfType(idChannel, 0.03125f);
+}
+
+avatar_FragOptions getFragOptions(float subMeshIdChannel)
+{
+    avatar_FragOptions options = avatar_zeroOptions();
+    options.enableNormalMapping = enableNormalMapping;
+    options.enableAlphaToCoverage = enableAlphaToCoverage;
+    options.enableEnvironmentMap = enableEnvironmentMap;
+    options.enableRimLight = enableRimLight;
+    if (true)
+    {
+        options.enableSkin = enableSkin && avatar_UseSkin(subMeshIdChannel);
+        options.enableEyeGlint = enableEyeGlint && avatar_UseEyeGlint(subMeshIdChannel);
+        options.enableHair = enableHair && avatar_UseHair(subMeshIdChannel);
+    }
+    else
+    {
+        options.enableSkin = enableSkin;
+        options.enableEyeGlint = enableEyeGlint;
+        options.enableHair = enableHair;
+    }
+    options.enablePreviewColorRamp = enablePreviewColorRamp;
+    options.enableDebugRender = enableDebugRender;
+    return options;
+}
+
+float3 calculateBitangent(avatar_TangentSpace tangentSpace)
+{
+    return normalize(cross(tangentSpace.normal, tangentSpace.tangent));
+}
+
+float LINEARtoSRGB(float color)
+{
+    return pow(color, 0.4545454680919647216796875f);
+}
+
+float3 computeViewDir(float3 camera, float3 position)
+{
+    return normalize(position - camera);
+}
+
+void AppSpecificPreManipulation(inout avatar_FragmentInput i);
+
+avatar_FragmentOutput avatar_zeroFragmentOutput()
+{
+    avatar_FragmentOutput o;
+    o.color = 0.0f.xxxx;
+    o.p_specular = 0.0f.xxx;
+    o.p_diffuse = 0.0f.xxx;
+    o.a_specular = 0.0f.xxx;
+    o.a_diffuse = 0.0f.xxx;
+    o.subSurfaceColor = 0.0f.xxx;
+    o.alphaCoverage = 255u;
+    return o;
+}
+
+float saturate(float x)
+{
+    return clamp(x, 0.0f, 1.0f);
+}
+
+float computeNdotV(float3 normal, float3 world_view_dir)
+{
+    return saturate(-dot(normal, world_view_dir));
+}
+
+avatar_SpecularData avatar_fillInSpecularData(avatar_Geometry geometry, avatar_Light light, avatar_Material material, avatar_FragOptions options)
+{
+    avatar_SpecularData sd;
+    sd.directional_light_color = ((light.color * (1.0f / material.exposure)) * 0.3183098733425140380859375f) * light.intensity;
+    sd.point_to_light = -light.direction;
+    sd.range_attentuation = 0.0f;
+    sd.spot_attenuation = 0.0f;
+    sd._distance = length(sd.point_to_light);
+    if (light.type != 0)
+    {
+        sd.point_to_light = light.position - geometry.positionInWorldSpace;
+        if (light.range <= 0.0f)
+        {
+            sd.range_attentuation = 1.0f / pow(sd._distance, 2.0f);
+        }
+        else
+        {
+            sd.range_attentuation = max(min(1.0f - pow(sd._distance / light.range, 4.0f), 1.0f), 0.0f) / pow(sd._distance, 2.0f);
+        }
+    }
+    if (light.type != 2)
+    {
+        sd.actualCos = dot(normalize(light.direction), normalize(-sd.point_to_light));
+        if (sd.actualCos > light.outerConeCos)
+        {
+            if (sd.actualCos < light.innerConeCos)
+            {
+                sd.spot_attenuation = smoothstep(light.outerConeCos, light.innerConeCos, sd.actualCos);
+            }
+        }
+    }
+    sd.l = normalize(sd.point_to_light);
+    sd.h = normalize(sd.l - geometry.worldViewDir);
+    if (light.range <= 0.0f)
+    {
+        sd.range_attentuation = 1.0f / pow(sd._distance, 2.0f);
+    }
+    else
+    {
+        sd.range_attentuation = max(min(1.0f - pow(sd._distance / light.range, 4.0f), 1.0f), 0.0f) / pow(sd._distance, 2.0f);
+    }
+    sd.roughPow2 = material.roughness * material.roughness;
+    sd.roughPow4 = sd.roughPow2 * sd.roughPow2;
+    sd.invRoughPow4 = 1.0f - sd.roughPow4;
+    sd.NdotV = computeNdotV(geometry.normal, geometry.worldViewDir);
+    return sd;
+}
+
+float3 avatar_computeSpecular(avatar_Geometry geometry, avatar_Light light, avatar_Material material, avatar_FragOptions options)
+{
+    float LIGHTSIZE = 0.5f;
+    float sumAreaLight = 0.0f;
+    avatar_SpecularData sd = avatar_fillInSpecularData(geometry, light, material, options);
+    if (material.roughness < 0.20000000298023223876953125f)
+    {
+        LIGHTSIZE = lerp(0.0500000007450580596923828125f, 0.5f, saturate((material.roughness - 0.100000001490116119384765625f) * 10.0f));
+    }
+    float stepSize = LIGHTSIZE / 5.0f;
+    float _3502 = sd.NdotV;
+    float _3507 = sd.invRoughPow4;
+    float _3510 = sd.roughPow2;
+    float ggxCommon = sqrt(((_3502 * _3502) * _3507) + _3510);
+    float t;
+    for (int i = 0; i < 5; i++)
+    {
+        for (int j = 0; j < 5; j++)
+        {
+            float3 worldSpaceLightDir = sd.l;
+            worldSpaceLightDir.x += ((float(i) - 2.5f) * stepSize);
+            worldSpaceLightDir.y += ((float(j) - 2.5f) * stepSize);
+            float3 h = normalize(worldSpaceLightDir - geometry.worldViewDir);
+            float NdotL = saturate(dot(worldSpaceLightDir, geometry.normal));
+            float NdotH = saturate(dot(geometry.normal, h));
+            float ggx = (NdotL * ggxCommon) + (_3502 * sqrt(((NdotL * NdotL) * _3507) + _3510));
+            if (ggx > 0.0f)
+            {
+                t = 0.5f / ggx;
+            }
+            else
+            {
+                t = 0.0f;
+            }
+            ggx = t;
+            float t0 = 1.0f / (1.0f - ((NdotH * NdotH) * _3507));
+            sumAreaLight += ((((NdotL * t0) * t0) * sd.roughPow4) * ggx);
+        }
+    }
+    return (sd.directional_light_color * (sumAreaLight / 25.0f)) * 1.5f;
+}
+
+float3 avatar_computeDiffuse(avatar_Geometry geometry, avatar_Light light, avatar_Material material, out float3 diffuseWrap)
+{
+    float3 directional_light_color = ((light.color * (1.0f / material.exposure)) * 0.3183098733425140380859375f) * light.intensity;
+    float NdotL = dot(geometry.normal, normalize(-light.direction));
+    diffuseWrap = directional_light_color * saturate(smoothstep(-0.64999997615814208984375f, 1.0f, NdotL));
+    return directional_light_color * saturate(smoothstep(-0.1500000059604644775390625f, 1.0f, NdotL));
+}
+
+float3 reflection(float3 normal, float3 world_view_dir)
+{
+    return world_view_dir - ((normal * 2.0f) * dot(world_view_dir, normal));
+}
+
+float4 mod289(float4 x)
+{
+    return x - (floor(x * 0.00346020772121846675872802734375f) * 289.0f);
+}
+
+float4 perm(float4 x)
+{
+    return mod289(((x * 34.0f) + 1.0f.xxxx) * x);
+}
+
+float _noise(float3 p)
+{
+    float3 a = floor(p);
+    float3 d = p - a;
+    float3 d_1 = (d * d) * (3.0f.xxx - (d * 2.0f));
+    float4 b = a.xxyy + float4(0.0f, 1.0f, 0.0f, 1.0f);
+    float4 c = perm(perm(b.xyxy).xyxy + b.zzww) + a.zzzz;
+    float _2008 = d_1.z;
+    float4 o3 = (frac(perm(c + 1.0f.xxxx) * 0.024390242993831634521484375f) * _2008) + (frac(perm(c) * 0.024390242993831634521484375f) * (1.0f - _2008));
+    float _2020 = d_1.x;
+    float2 o4 = (o3.yw * _2020) + (o3.xz * (1.0f - _2020));
+    float _2032 = d_1.y;
+    return (o4.y * _2032) + (o4.x * (1.0f - _2032));
+}
+
+float3 computeSpecularHighlight(float anisotropicIntensity, float roughness, float3 L, float3 E, float3 t, float3 tangent, float3 bitangent, float offset)
+{
+    float3 H = 0.0f.xxx;
+    float kspec = 0.0f;
+    float kexp = 0.0f;
+    float3 spec = 0.0f.xxx;
+    float3 t_1 = normalize(t + (bitangent * offset));
+    if (anisotropicIntensity > 0.0f)
+    {
+        float3 isotropicH = normalize(E + L);
+        H = normalize((E + L) - (tangent * dot(E + L, tangent)));
+        H = normalize(lerp(isotropicH, H, anisotropicIntensity.xxx));
+        kspec = dot(H, t_1);
+        kexp = 1.0f / ((roughness * roughness) + 0.001000000047497451305389404296875f);
+    }
+    else
+    {
+        H = normalize(E + L);
+        kspec = dot(t_1, H);
+        kexp = 3.0f / ((roughness * roughness) + 0.001000000047497451305389404296875f);
+    }
+    if (kspec > (-0.001000000047497451305389404296875f))
+    {
+        float specular = pow(max(0.0f, kspec), kexp);
+        spec = 0.0f.xxx + specular.xxx;
+    }
+    return spec;
+}
+
+float3 computeHairSpecular(avatar_FragmentInput i, float3 lightVector, float3x3 hairCoordinateSystem, float anisotropicBlend)
+{
+    float3 E = -i.geometry.worldViewDir;
+    float3 L = normalize(-lightVector);
+    float anisotropy = lerp(0.0f, i.material.hair_material.anisotropic_intensity, anisotropicBlend);
+    float localOffset = ((i.material.hair_material.specular_shift_intensity * (i.material.hair_material.shift - 0.5f)) * anisotropicBlend) + (((_noise(float3(i.geometry.texcoord_0.x, i.geometry.texcoord_0.y, float2(0.0f, 1.0f).x) * 2000.0f.xxx) * 2.0f) - 1.0f) * 0.00999999977648258209228515625f);
+    return ((computeSpecularHighlight(anisotropy, i.material.hair_material.specular_white_roughness, L, E, hairCoordinateSystem[0], hairCoordinateSystem[2], hairCoordinateSystem[1], localOffset) * i.material.hair_material.specular_white_intensity) + ((computeSpecularHighlight(anisotropy, i.material.hair_material.specular_color_roughness, L, E, hairCoordinateSystem[0], hairCoordinateSystem[2], hairCoordinateSystem[1], i.material.hair_material.specular_color_offset + localOffset) * i.material.hair_material.specular_color_factor) * i.material.hair_material.specular_color_intensity)) * i.material.hair_material.ao_intensity;
+}
+
+float3 blendPunctualSpecularWithHair(float3 punctualSpec, float3 hairPunctualSpec, float hairBlend)
+{
+    return lerp(punctualSpec, hairPunctualSpec, hairBlend.xxx);
+}
+
+float3 blendSubSurfaceColorWithHair(float3 hairSubsurfaceColor, float3 skinSubsurfaceColor, float hairBlend)
+{
+    return lerp(hairSubsurfaceColor, skinSubsurfaceColor, hairBlend.xxx);
+}
+
+avatar_HemisphereNormalOffsets avatar_computeHemisphereNormalOffsets(avatar_FragmentInput i)
+{
+    avatar_HemisphereNormalOffsets hno;
+    hno.nn = i.geometry.tangentSpace.normal * 0.707099974155426025390625f;
+    hno.tt = i.geometry.tangentSpace.tangent * 0.3535499870777130126953125f;
+    hno.bb = i.geometry.tangentSpace.bitangent * 0.612348616123199462890625f;
+    hno.lv1 = hno.nn + (hno.tt * 2.0f);
+    hno.lv2 = (hno.nn + hno.bb) - hno.tt;
+    hno.lv3 = (hno.nn - hno.bb) - hno.tt;
+    return hno;
+}
+
+float3 saturate(float3 v)
+{
+    return clamp(v, 0.0f.xxx, 1.0f.xxx);
+}
+
+float3 avatar_addRimLight(avatar_Geometry geometry, avatar_Material t)
+{
+    float multiplier = 1.0f;
+    avatar_Material t_1 = t;
+    float gradient = (atan2(dot(geometry.normal, float3(1.0f, 0.0f, 0.0f)), dot(geometry.normal, float3(0.0f, 1.0f, 0.0f))) / 6.283185482025146484375f) + 0.5f;
+    if (gradient < t.rim_light_material.start)
+    {
+        multiplier = 0.0f;
+    }
+    if ((gradient >= t.rim_light_material.start) && (gradient <= t.rim_light_material.end))
+    {
+        multiplier = smoothstep(t.rim_light_material.start, t.rim_light_material.start + t.rim_light_material.transition, gradient);
+        multiplier *= smoothstep(t.rim_light_material.end, t.rim_light_material.end - t.rim_light_material.transition, gradient);
+    }
+    if (gradient > t.rim_light_material.end)
+    {
+        multiplier = 0.0f;
+    }
+    if (t.rim_light_material.bias == 0.0f)
+    {
+        t_1.rim_light_material.bias = 0.001000000047497451305389404296875f;
+    }
+    return (t_1.rim_light_material.color * t_1.rim_light_material.intensity) * (pow(clamp(1.0f - computeNdotV(geometry.normal, geometry.worldViewDir), 0.0f, 1.0f), 1.0f / t_1.rim_light_material.bias) * multiplier);
+}
+
+float dither17b(float2 svPosition, float frameIndexMod4)
+{
+    return frac(dot(float3(svPosition, frameIndexMod4), float3(0.117647059261798858642578125f, 0.4117647111415863037109375f, 1.35294115543365478515625f)));
+}
+
+uint alphaToCoverage(float alpha)
+{
+    uint Coverage = 0u;
+    if (alpha > 0.0f)
+    {
+        Coverage = 136u;
+    }
+    if (alpha > 0.25f)
+    {
+        Coverage = 153u;
+    }
+    if (alpha > 0.5f)
+    {
+        Coverage = 221u;
+    }
+    if (alpha > 0.75f)
+    {
+        Coverage = 255u;
+    }
+    return Coverage;
+}
+
+uint coverageFromMaskMSAA4(float t, float2 svPos, bool dither)
+{
+    float t_1 = t;
+    if (dither)
+    {
+        t_1 = t - (dither17b(svPos, 0.0f) / 4.0f);
+    }
+    else
+    {
+        t_1 -= 0.125f;
+    }
+    return alphaToCoverage(t_1);
+}
+
+uint avatar_calculateAlphaCoverage(avatar_FragmentInput i, float3 positionInScreenSpace)
+{
+    bool _return = false;
+    uint _returnValue;
+    if (!i.options.enableAlphaToCoverage)
+    {
+        _return = true;
+        _returnValue = 255u;
+    }
+    if (!_return)
+    {
+        _return = true;
+        _returnValue = coverageFromMaskMSAA4(i.material.alpha, positionInScreenSpace.xy, true);
+    }
+    return _returnValue;
+}
+
+void AppSpecificPostManipulation(avatar_FragmentInput i, inout avatar_FragmentOutput o);
+
+float3 SRGBtoLINEAR(float3 srgbIn)
+{
+    return pow(srgbIn, 2.2000000476837158203125f.xxx);
+}
+
+float4 avatar_finalOutputColor(avatar_FragmentInput i, avatar_FragmentOutput o)
+{
+    float4 color = o.color;
+    if (i.debugMode == 1)
+    {
+        color = float4(i.material.base_color.x, i.material.base_color.y, i.material.base_color.z, float2(0.0f, 1.0f).y);
+    }
+    if (i.debugMode == 2)
+    {
+        float3 _4279 = i.material.occlusion.xxx;
+        color = float4(_4279.x, _4279.y, _4279.z, color.w);
+    }
+    if (i.debugMode == 3)
+    {
+        float3 _4290 = ConvertOutputColorSpaceFromSRGB(i.material.roughness.xxx);
+        color = float4(_4290.x, _4290.y, _4290.z, color.w);
+    }
+    if (i.debugMode == 4)
+    {
+        float3 _4301 = ConvertOutputColorSpaceFromSRGB(i.material.metallic.xxx);
+        color = float4(_4301.x, _4301.y, _4301.z, color.w);
+    }
+    if (i.debugMode == 5)
+    {
+        float3 _4311 = i.material.thickness.xxx;
+        color = float4(_4311.x, _4311.y, _4311.z, color.w);
+    }
+    bool _4316 = i.debugMode == 6;
+    if (_4316)
+    {
+        color = float4(i.geometry.normal.x, i.geometry.normal.y, i.geometry.normal.z, color.w);
+    }
+    if (_4316)
+    {
+        float3 _4331 = (i.geometry.normal * 0.5f) + 0.5f.xxx;
+        color = float4(_4331.x, _4331.y, _4331.z, color.w);
+    }
+    if (i.debugMode == 19)
+    {
+        float3 _4342 = (i.geometry.tangentSpace.tangent * 0.5f) + 0.5f.xxx;
+        color = float4(_4342.x, _4342.y, _4342.z, color.w);
+    }
+    if (i.debugMode == 20)
+    {
+        float3 _4353 = (i.geometry.tangentSpace.bitangent * 0.5f) + 0.5f.xxx;
+        color = float4(_4353.x, _4353.y, _4353.z, color.w);
+    }
+    if (i.debugMode == 7)
+    {
+    }
+    if (i.debugMode == 8)
+    {
+    }
+    if (i.debugMode == 9)
+    {
+        float3 world_view_dir = computeViewDir(i.geometry.camera, i.geometry.positionInWorldSpace);
+        color = float4(world_view_dir.x, world_view_dir.y, world_view_dir.z, float2(0.0f, 1.0f).y);
+    }
+    if (i.debugMode == 10)
+    {
+        float3 _4385 = o.p_specular + o.p_diffuse;
+        color = float4(_4385.x, _4385.y, _4385.z, color.w);
+    }
+    if (i.debugMode == 11)
+    {
+        color = float4(o.p_specular.x, o.p_specular.y, o.p_specular.z, color.w);
+    }
+    if (i.debugMode == 12)
+    {
+        color = float4(o.p_diffuse.x, o.p_diffuse.y, o.p_diffuse.z, color.w);
+    }
+    if (i.debugMode == 13)
+    {
+        float3 _4411 = o.a_specular + o.a_diffuse;
+        color = float4(_4411.x, _4411.y, _4411.z, color.w);
+    }
+    if (i.debugMode == 14)
+    {
+        color = float4(o.a_specular.x, o.a_specular.y, o.a_specular.z, color.w);
+    }
+    if (i.debugMode == 15)
+    {
+        color = float4(o.a_diffuse.x, o.a_diffuse.y, o.a_diffuse.z, color.w);
+    }
+    if (i.debugMode == 16)
+    {
+    }
+    if (i.debugMode == 17)
+    {
+        color = float4(o.subSurfaceColor.x, o.subSurfaceColor.y, o.subSurfaceColor.z, color.w);
+    }
+    if (i.debugMode == 18)
+    {
+        float subMeshId = i.material.alpha;
+        color = float4(0.0f.xxx.x, 0.0f.xxx.y, 0.0f.xxx.z, color.w);
+        if (avatar_IsOfType(subMeshId, 0.00390625f))
+        {
+            color = float4(0.20000000298023223876953125f.xxx.x, 0.20000000298023223876953125f.xxx.y, 0.20000000298023223876953125f.xxx.z, color.w);
+        }
+        if (avatar_IsOfType(subMeshId, 0.0078125f))
+        {
+            color = float4(float3(0.769999980926513671875f, 0.64999997615814208984375f, 0.64999997615814208984375f).x, float3(0.769999980926513671875f, 0.64999997615814208984375f, 0.64999997615814208984375f).y, float3(0.769999980926513671875f, 0.64999997615814208984375f, 0.64999997615814208984375f).z, color.w);
+        }
+        if (avatar_IsOfType(subMeshId, 0.015625f))
+        {
+            color = float4(float3(0.769999980926513671875f, 0.64999997615814208984375f, 0.64999997615814208984375f).x, float3(0.769999980926513671875f, 0.64999997615814208984375f, 0.64999997615814208984375f).y, float3(0.769999980926513671875f, 0.64999997615814208984375f, 0.64999997615814208984375f).z, color.w);
+        }
+        if (avatar_IsOfType(subMeshId, 0.03125f))
+        {
+            color = float4(float3(0.3449999988079071044921875f, 0.2700000107288360595703125f, 0.10999999940395355224609375f).x, float3(0.3449999988079071044921875f, 0.2700000107288360595703125f, 0.10999999940395355224609375f).y, float3(0.3449999988079071044921875f, 0.2700000107288360595703125f, 0.10999999940395355224609375f).z, color.w);
+        }
+        if (avatar_IsOfType(subMeshId, 0.0625f))
+        {
+            color = float4(float3(0.23999999463558197021484375f, 0.189999997615814208984375f, 0.07999999821186065673828125f).x, float3(0.23999999463558197021484375f, 0.189999997615814208984375f, 0.07999999821186065673828125f).y, float3(0.23999999463558197021484375f, 0.189999997615814208984375f, 0.07999999821186065673828125f).z, color.w);
+        }
+        if (avatar_IsOfType(subMeshId, 0.125f))
+        {
+            color = float4(float3(0.0f, 0.0f, 1.0f).x, float3(0.0f, 0.0f, 1.0f).y, float3(0.0f, 0.0f, 1.0f).z, color.w);
+        }
+        if (avatar_IsOfType(subMeshId, 0.25f))
+        {
+            color = float4(float3(0.0f, 1.0f, 0.0f).x, float3(0.0f, 1.0f, 0.0f).y, float3(0.0f, 1.0f, 0.0f).z, color.w);
+        }
+        if (avatar_IsOfType(subMeshId, 0.5f))
+        {
+            color = float4(float3(0.5f, 0.0f, 0.0f).x, float3(0.5f, 0.0f, 0.0f).y, float3(0.5f, 0.0f, 0.0f).z, color.w);
+        }
+        if (avatar_IsOfType(subMeshId, 1.0f))
+        {
+            color = float4(float3(0.20000000298023223876953125f, 0.100000001490116119384765625f, 0.0500000007450580596923828125f).x, float3(0.20000000298023223876953125f, 0.100000001490116119384765625f, 0.0500000007450580596923828125f).y, float3(0.20000000298023223876953125f, 0.100000001490116119384765625f, 0.0500000007450580596923828125f).z, color.w);
+        }
+        if (avatar_IsOfType(subMeshId, 2.0f))
+        {
+            color = float4(0.100000001490116119384765625f.xxx.x, 0.100000001490116119384765625f.xxx.y, 0.100000001490116119384765625f.xxx.z, color.w);
+        }
+        if (avatar_IsOfType(subMeshId, 4.0f))
+        {
+            color = float4(1.0f.xxx.x, 1.0f.xxx.y, 1.0f.xxx.z, color.w);
+        }
+    }
+    return color;
+}
+
+void frag_Fragment_main()
+{
+    avatar_FragmentInput i = avatar_zeroFragmentInput();
+    i.options = getFragOptions(v_Color.w);
+    i.debugMode = Debug;
+    int lightCount = getLightCount();
+    i.lightsCount = lightCount;
+    i.lights[0].intensity = 1.0f;
+    i.lights[0].direction = getLightDirection();
+    i.lights[0].color = getLightColor();
+    i.lights[0].position = getLightPosition();
+    i.lights[0].type = 0;
+    i.lights[0].range = 1000.0f;
+    i.lights[0].innerConeCos = 0.0f;
+    i.lights[0].outerConeCos = 0.0f;
+    avatar_Light avatarLight;
+    for (int idx = 1; idx < 8; idx++)
+    {
+        if (idx < lightCount)
+        {
+            OvrLight ovrLight = getAdditionalLight(idx, v_WorldPos);
+            avatarLight.direction = -ovrLight.direction;
+            avatarLight.intensity = 1.0f;
+            avatarLight.color = ovrLight.color;
+            avatarLight.position = ovrLight.direction;
+            avatarLight.type = 0;
+            avatarLight.range = 100.0f;
+            avatarLight.innerConeCos = 0.0f;
+            avatarLight.outerConeCos = 0.0f;
+            i.lights[idx] = avatarLight;
+        }
+    }
+    i.geometry.camera = _WorldSpaceCameraPos;
+    i.geometry.positionInWorldSpace = v_WorldPos;
+    i.geometry.positionInClipSpace = v_Vertex.xyz / v_Vertex.w.xxx;
+    i.geometry.texcoord_0 = v_UVCoord1;
+    i.geometry.texcoord_1 = v_UVCoord2;
+    i.geometry.normalScale = u_NormalScale;
+    i.geometry.tangentSpace.normal = normalize(v_Normal);
+    i.geometry.tangentSpace.tangent = normalize(v_Tangent.xyz);
+    i.geometry.tangentSpace.bitangent = calculateBitangent(i.geometry.tangentSpace);
+    i.geometry.normal = i.geometry.tangentSpace.normal;
+    bool _return = false;
+    float3 _returnValue;
+    if (!i.options.enableNormalMapping)
+    {
+        _return = true;
+        _returnValue = i.geometry.normal;
+    }
+    if (!_return)
+    {
+        float3 shadingNormal = float3(((tex2D(u_NormalSampler, i.geometry.texcoord_0) * 2.0f) - 1.0f.xxxx).xy, 1.0f);
+        shadingNormal *= float3(i.geometry.normalScale, i.geometry.normalScale, 1.0f);
+        _return = true;
+        _returnValue = mul(normalize(shadingNormal), float3x3(i.geometry.tangentSpace.tangent, i.geometry.tangentSpace.bitangent, i.geometry.tangentSpace.normal));
+    }
+    i.geometry.normal = _returnValue;
+    i.geometry.color = v_Color;
+    i.geometry.ormt = v_ORMT;
+    i.material.base_color = StaticSelectMaterialModeColor(u_BaseColorSampler, i.geometry.texcoord_0, float4(i.geometry.color.x, i.geometry.color.y, i.geometry.color.z, float2(0.0f, 1.0f).y)).xyz;
+    i.material.ramp_selector = 1.0f - (float(u_RampSelector) / 45.0f);
+    i.material.color_selector_lod = 0;
+    bool _return_1 = false;
+    float3 _returnValue_1;
+    if (i.options.enablePreviewColorRamp)
+    {
+        float2 rampCoord = float2(LINEARtoSRGB(i.material.base_color.x), i.material.ramp_selector);
+        _return_1 = true;
+        _returnValue_1 = tex2Dlod(u_ColorGradientSampler, float4(rampCoord, 0.0, float(i.material.color_selector_lod))).xyz;
+    }
+    if (!_return_1)
+    {
+        _return_1 = true;
+        _returnValue_1 = i.material.base_color;
+    }
+    i.material.base_color = _returnValue_1;
+    i.material.alpha = i.geometry.color.w;
+    float4 ormt = StaticSelectMaterialModeColor(u_MetallicRoughnessSampler, i.geometry.texcoord_0, i.geometry.ormt);
+    float _5000 = ormt.x;
+    i.material.occlusion = lerp(1.0f, _5000, u_OcclusionStrength);
+    float _5005 = ormt.y;
+    i.material.roughness = _5005 * u_RoughnessFactor;
+    float _5010 = ormt.z;
+    i.material.metallic = _5010 * u_MetallicFactor;
+    i.material.ambient_diffuse_factor = 1.0f;
+    i.material.ambient_specular_factor = 1.0f;
+    i.ambient_color = v_SH;
+    float _5019 = ormt.w;
+    i.material.thickness = _5019 * u_ThicknessFactor;
+    i.material.thickness = u_ThicknessFactor;
+    if (i.options.enableSkin)
+    {
+        i.material.occlusion *= u_SkinORMFactor.x;
+        i.material.roughness *= u_SkinORMFactor.y;
+        i.material.metallic *= u_SkinORMFactor.z;
+    }
+    i.material.hair_material.subsurface_color = u_HairSubsurfaceColor;
+    i.material.hair_material.scatter_intensity = u_HairScatterIntensity;
+    i.material.hair_material.specular_color_factor = u_HairSpecularColorFactor;
+    i.material.hair_material.specular_shift_intensity = u_HairSpecularShiftIntensity;
+    i.material.hair_material.specular_white_intensity = u_HairSpecularWhiteIntensity;
+    i.material.hair_material.specular_white_roughness = u_HairRoughness;
+    i.material.hair_material.specular_color_intensity = u_HairSpecularColorIntensity;
+    i.material.hair_material.specular_color_offset = u_HairSpecularColorOffset;
+    i.material.hair_material.specular_color_roughness = u_HairColorRoughness;
+    i.material.hair_material.anisotropic_intensity = u_HairAnisotropicIntensity;
+    i.material.hair_material.diffused_intensity = u_HairDiffusedIntensity;
+    i.material.hair_material.normal_intensity = u_HairSpecularNormalIntensity;
+    i.material.hair_material.specular_glint = u_HairSpecularGlint;
+    i.material.hair_material.ao_intensity = _5000;
+    i.material.hair_material.flow_angle = (_5005 - 0.5f) * 6.283185482025146484375f;
+    i.material.hair_material.shift = _5010;
+    i.material.hair_material.blend = _5019;
+    i.material.rim_light_material.intensity = u_RimLightIntensity;
+    i.material.rim_light_material.bias = u_RimLightBias;
+    i.material.rim_light_material.color = u_RimLightColor;
+    i.material.rim_light_material.transition = u_RimLightTransition;
+    i.material.rim_light_material.start = u_RimLightStartPosition;
+    i.material.rim_light_material.end = u_RimLightEndPosition;
+    i.material.exposure = u_Exposure;
+    i.material.skin.subsurface_color = u_SubsurfaceColor;
+    i.material.skin.skin_ORM_factor = u_SkinORMFactor;
+    i.material.eye_glint_factor = u_EyeGlintFactor;
+    i.material.eye_glint_color_factor = u_EyeGlintColorFactor;
+    i.matrices.objectToWorld = unity_ObjectToWorld;
+    i.matrices.worldToObject = float3x3(unity_WorldToObject[0].xyz, unity_WorldToObject[1].xyz, unity_WorldToObject[2].xyz);
+    i.geometry.worldViewDir = computeViewDir(i.geometry.camera, i.geometry.positionInWorldSpace);
+    float mipCount = 1.0f * float(u_MipCount);
+    i.geometry.lod = clamp(i.material.roughness * mipCount, 0.0f, mipCount);
+    AppSpecificPreManipulation(i);
+    float3 punctualDiffuseWrap = 0.0f.xxx;
+    float3 rimLight = 0.0f.xxx;
+    avatar_FragmentOutput o_1 = avatar_zeroFragmentOutput();
+    for (int lightIdx = 0; lightIdx < 8; lightIdx++)
+    {
+        if (lightIdx < i.lightsCount)
+        {
+            avatar_Light light = i.lights[lightIdx];
+            if (!i.options.enableEyeGlint)
+            {
+                o_1.p_specular += avatar_computeSpecular(i.geometry, light, i.material, i.options);
+            }
+            float3 diffuseWrap = 0.0f.xxx;
+            float3 _5331 = avatar_computeDiffuse(i.geometry, light, i.material, diffuseWrap);
+            o_1.p_diffuse += _5331;
+            punctualDiffuseWrap += diffuseWrap;
+        }
+        if (i.options.enableShadows)
+        {
+            o_1.p_diffuse *= i.lights[0].shadowTerm;
+            o_1.p_specular *= i.lights[0].shadowTerm;
+            punctualDiffuseWrap *= i.lights[0].shadowTerm;
+        }
+    }
+    if ((i.options.enableEyeGlint && (!i.options.enableSkin)) && (!i.options.enableHair))
+    {
+        o_1.p_specular += 0.0f.xxx;
+    }
+    float _lightProbeGlow;
+    float3 sh_4 = vgi_ProbeRadiance(i.geometry.normal, 0.0f, _lightProbeGlow);
+    avatar_AmbientLighting ambient;
+    ambient.diffuse = sh_4 * i.material.occlusion;
+    float3 _5470 = texCUBElod(_ReflectionCubeMap, float4(reflection(i.geometry.normal, i.geometry.worldViewDir), i.geometry.lod));
+    ambient.specular = float4(_5470.x, _5470.y, _5470.z, float2(0.0f, 1.0f).x).xyz;
+    float hairBlend = 0.0f;
+    float3 totalhairPunctualSpec = 0.0f.xxx;
+    float3 specularFactor = lerp((0.039999999105930328369140625f + ((lerp(1.0f, 0.039999999105930328369140625f, sqrt(i.material.roughness)) - 0.039999999105930328369140625f) * saturate(pow(1.0f - computeNdotV(i.geometry.normal, i.geometry.worldViewDir), 5.0f)))).xxx, i.material.base_color, i.material.metallic.xxx) * i.material.occlusion;
+    if (!i.options.enableEyeGlint)
+    {
+        o_1.p_specular *= specularFactor;
+    }
+    o_1.a_specular = (specularFactor * i.material.ambient_specular_factor) * ambient.specular;
+    if (i.options.enableHair)
+    {
+        hairBlend = i.material.hair_material.blend;
+        avatar_HairMaterial hair_mat = i.material.hair_material;
+        float2 flow = float2(cos(hair_mat.flow_angle), sin(hair_mat.flow_angle));
+        float3 hairTangent = (i.geometry.tangentSpace.tangent * flow.x) + (i.geometry.tangentSpace.bitangent * flow.y);
+        float3 hairTangent2 = normalize(hairTangent);
+        float3 hairNormal = lerp(i.geometry.tangentSpace.normal, i.geometry.normal, i.material.hair_material.normal_intensity.xxx);
+        float3x3 hairCoordinateSystem = float3x3(hairNormal, hairTangent2, normalize(cross(hairNormal, hairTangent2)));
+        for (int lightIdx_1 = 0; lightIdx_1 < 8; lightIdx_1++)
+        {
+            if (lightIdx_1 < i.lightsCount)
+            {
+                avatar_Light light_1 = i.lights[lightIdx_1];
+                float3 directional_light_color = ((light_1.color * (1.0f / i.material.exposure)) * 0.3183098733425140380859375f) * light_1.intensity;
+                totalhairPunctualSpec += ((computeHairSpecular(i, light_1.direction, hairCoordinateSystem, i.material.hair_material.aniso_blend) * directional_light_color) * 0.3183098733425140380859375f);
+            }
+        }
+        o_1.p_specular = blendPunctualSpecularWithHair(o_1.p_specular, totalhairPunctualSpec, hairBlend);
+        o_1.subSurfaceColor = blendSubSurfaceColorWithHair(i.material.hair_material.subsurface_color, float3(1.0f, 0.300000011920928955078125f, 0.20000000298023223876953125f), hairBlend);
+        o_1.a_specular *= (1.0f - hairBlend);
+        o_1.p_diffuse *= lerp(1.0f, i.material.hair_material.diffused_intensity, hairBlend);
+    }
+    avatar_FragmentOutput _5384 = o_1;
+    if (i.options.enableSkin)
+    {
+        o_1.p_diffuse = (punctualDiffuseWrap * float3(1.0f, 0.300000011920928955078125f, 0.20000000298023223876953125f)) + (_5384.p_diffuse * (1.0f.xxx - float3(1.0f, 0.300000011920928955078125f, 0.20000000298023223876953125f)));
+        float3 subsurfaceColor = 0.0f.xxx;
+        float3 diffuse = 0.0f.xxx;
+        float3 accumulatedDiffuseColor = 0.0f.xxx;
+        avatar_HemisphereNormalOffsets hemisphereNormalOffsets = avatar_computeHemisphereNormalOffsets(i);
+        float _lightProbeGlow_1;
+        float3 _5832 = vgi_ProbeRadiance(hemisphereNormalOffsets.lv1, 0.0f, _lightProbeGlow_1);
+        float3 sh = _5832;
+        float3 ibl_diffuse1 = saturate(sh * i.material.occlusion);
+        float _lightProbeGlow_2;
+        float3 _5840 = vgi_ProbeRadiance(hemisphereNormalOffsets.lv2, 0.0f, _lightProbeGlow_2);
+        float3 sh_1 = _5840;
+        float3 ibl_diffuse2 = saturate(sh_1 * i.material.occlusion);
+        float _lightProbeGlow_3;
+        float3 _5848 = vgi_ProbeRadiance(hemisphereNormalOffsets.lv3, 0.0f, _lightProbeGlow_3);
+        float3 sh_2 = _5848;
+        float3 ibl_diffuse3 = saturate(sh_2 * i.material.occlusion);
+        float _lightProbeGlow_4;
+        float3 _5856 = vgi_ProbeRadiance(hemisphereNormalOffsets.nn, 0.0f, _lightProbeGlow_4);
+        float3 sh_3 = _5856;
+        float3 ibl_diffuseN = saturate(sh_3 * i.material.occlusion);
+        for (int lightIdx_2 = 0; lightIdx_2 < 8; lightIdx_2++)
+        {
+            if (lightIdx_2 < i.lightsCount)
+            {
+                float3 worldSpaceLightDir = -i.lights[lightIdx_2].direction;
+                float3 lightColor = (i.lights[lightIdx_2].color * i.lights[lightIdx_2].intensity) * (1.0f / 3.1415927410125732421875f);
+                float3 directionalLightColor = lightColor * (1.0f / i.material.exposure);
+                float directionalSoftDiffuseValue = (saturate(dot(worldSpaceLightDir, hemisphereNormalOffsets.lv1)) + saturate(dot(worldSpaceLightDir, hemisphereNormalOffsets.lv2))) + saturate(dot(worldSpaceLightDir, hemisphereNormalOffsets.lv3));
+                diffuse += (directionalSoftDiffuseValue.xxx * directionalLightColor);
+                accumulatedDiffuseColor += (directionalLightColor * saturate(dot(worldSpaceLightDir, hemisphereNormalOffsets.nn)));
+            }
+        }
+        float3 softDiffuseLight = (((ibl_diffuse1 + ibl_diffuse2) + ibl_diffuse3) + diffuse) * 0.3333333432674407958984375f;
+        subsurfaceColor = ((softDiffuseLight * float3(1.0f, 0.300000011920928955078125f, 0.20000000298023223876953125f)) * saturate(i.material.occlusion + 0.4000000059604644775390625f)) + (((accumulatedDiffuseColor + ibl_diffuseN) * (1.0f.xxx - float3(1.0f, 0.300000011920928955078125f, 0.20000000298023223876953125f))) * i.material.occlusion);
+        o_1.subSurfaceColor = subsurfaceColor;
+        o_1.subSurfaceColor -= (o_1.p_diffuse * i.material.occlusion);
+        o_1.subSurfaceColor = saturate(o_1.subSurfaceColor);
+    }
+    float3 diffuseFactor = i.material.base_color * (i.material.occlusion * (1.0f - i.material.metallic));
+    o_1.p_diffuse = diffuseFactor * o_1.p_diffuse;
+    o_1.a_diffuse = (diffuseFactor * i.material.ambient_diffuse_factor) * ambient.diffuse;
+    o_1.subSurfaceColor *= i.material.base_color;
+    if (i.options.enableRimLight)
+    {
+        rimLight = 0.0f.xxx + avatar_addRimLight(i.geometry, i.material);
+    }
+    float3 finalColor_1 = ((((0.0f.xxx + (o_1.p_diffuse + o_1.a_diffuse)) + rimLight) + (_5384.p_specular + _5384.a_specular)) + o_1.subSurfaceColor) * i.material.exposure;
+    o_1.color = float4(finalColor_1.x, finalColor_1.y, finalColor_1.z, o_1.color.w);
+    o_1.color.w = 1.0f;
+    avatar_FragmentOutput o = o_1;
+    o.color = float4(o_1.color.xyz.x, o_1.color.xyz.y, o_1.color.xyz.z, o.color.w);
+    o.alphaCoverage = avatar_calculateAlphaCoverage(i, i.geometry.positionInClipSpace);
+    AppSpecificPostManipulation(i, o);
+    float4 finalColor = float4(o.color.x, o.color.y, o.color.z, float2(0.0f, 1.0f).y);
+    if (i.options.enableDebugRender)
+    {
+        finalColor = avatar_finalOutputColor(i, o);
+    }
+    float4 _5133 = finalColor;
+    finalColor = float4(_5133.xyz.x, _5133.xyz.y, _5133.xyz.z, _5133.w);
+    _21 = float4(_5133.xyz.x, _5133.xyz.y, _5133.xyz.z, _5133.w);
+}
+
+FragmentOutput Fragment_main(VertexToFragment stage_input)
+{
+    v_Vertex = stage_input.v_Vertex;
+    v_WorldPos = stage_input.v_WorldPos;
+    v_Normal = stage_input.v_Normal;
+    v_Tangent = stage_input.v_Tangent;
+    v_UVCoord1 = stage_input.v_UVCoord1;
+    v_UVCoord2 = stage_input.v_UVCoord2;
+    v_Color = stage_input.v_Color;
+    v_ORMT = stage_input.v_ORMT;
+    v_SH = stage_input.v_SH;
+    frag_Fragment_main();
+    FragmentOutput stage_output;
+    stage_output._21 = float4(_21);
+    return stage_output;
+}
+
+// Generated by AvatarShaderLibrary 242d0d331a49
+
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/LibraryVertexGI/export/pbr_frag.unity.hlsl.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/LibraryVertexGI/export/pbr_frag.unity.hlsl.meta
new file mode 100644
index 0000000000000000000000000000000000000000..40c5328ef00130b94578b576778f31c2ee31d711
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/LibraryVertexGI/export/pbr_frag.unity.hlsl.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 18cedbf29faf0be46a076764d4d339a0
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  preprocessorOverride: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/LibraryVertexGI/export/pbr_vert.unity.hlsl b/Assets/Oculus/Avatar2/Example/Common/Shaders/LibraryVertexGI/export/pbr_vert.unity.hlsl
new file mode 100644
index 0000000000000000000000000000000000000000..cbacbdc3c2efbf436dcea1249ecc0b310713fac2
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/LibraryVertexGI/export/pbr_vert.unity.hlsl
@@ -0,0 +1,121 @@
+// Generated by AvatarShaderLibrary 242d0d331a49
+
+
+
+struct avatar_VertOptions
+{
+    bool enableNormalMapping;
+    bool enableAlphaToCoverage;
+    bool enableSkin;
+    bool enableEyeGlint;
+};
+
+struct avatar_AvatarVertexInput
+{
+    float4 position;
+    float3 normal;
+    float3 tangent;
+    float2 texcoord0;
+    float2 texcoord1;
+    float4 color;
+    float4 ormt;
+    float3 ambient;
+    avatar_VertOptions options;
+};
+
+
+
+
+
+static float4 a_Position;
+static float4 a_Normal;
+static float4 a_Tangent;
+static uint a_instanceID;
+static uint a_vertexID;
+static float2 a_UV1;
+static float2 a_UV2;
+static float4 a_Color;
+static float4 a_ORMT;
+static float4 v_Vertex;
+static float3 v_WorldPos;
+static float3 v_Normal;
+static float4 v_Tangent;
+static uint v_InstanceID;
+static float2 v_UVCoord1;
+static float2 v_UVCoord2;
+static float4 v_Color;
+static float4 v_ORMT;
+static float3 v_SH;
+
+
+
+
+
+
+avatar_VertOptions getVertOptions()
+{
+    avatar_VertOptions options;
+    options.enableNormalMapping = enableNormalMapping;
+    options.enableAlphaToCoverage = enableAlphaToCoverage;
+    options.enableSkin = enableSkin;
+    options.enableEyeGlint = enableEyeGlint;
+    return options;
+}
+
+void vert_Vertex_main()
+{
+    OvrVertexData ovrData = OvrCreateVertexData(float4(a_Position.xyz, 1.0f), a_Normal.xyz, a_Tangent, a_vertexID);
+    avatar_AvatarVertexInput vin;
+    vin.position = ovrData.position;
+    vin.normal = ovrData.normal;
+    vin.tangent = ovrData.tangent.xyz;
+    VertexInput vertexInput;
+    vertexInput.uv0 = a_UV1;
+    vertexInput.uv0 = a_UV2;
+    vin.ambient = VertexGIForward(vertexInput, vin.position.xyz, vin.normal).xyz;
+    vin.texcoord0 = a_UV1;
+    vin.texcoord1 = a_UV2;
+    vin.color = a_Color;
+    vin.ormt = a_ORMT;
+    vin.options = getVertOptions();
+    v_UVCoord1 = vin.texcoord0;
+    v_UVCoord2 = vin.texcoord1;
+    v_Color = vin.color;
+    v_ORMT = vin.ormt;
+    v_SH = vin.ambient;
+    v_Vertex = getVertexInClipSpace(vin.position.xyz / vin.position.w.xxx);
+    float4 worldPos = mul(unity_ObjectToWorld, vin.position);
+    v_WorldPos = worldPos.xyz / worldPos.w.xxx;
+    v_Normal = normalize(mul(float4(vin.normal, 0.0f), unity_WorldToObject).xyz);
+    v_Tangent = normalize(mul(float4(vin.tangent, 0.0f), unity_WorldToObject));
+    v_InstanceID = a_instanceID;
+}
+
+VertexToFragment Vertex_main(AvatarVertexInput stage_input)
+{
+    a_Position = stage_input.a_Position;
+    a_Normal = stage_input.a_Normal;
+    a_Tangent = stage_input.a_Tangent;
+    a_instanceID = stage_input.a_instanceID;
+    a_vertexID = stage_input.a_vertexID;
+    a_UV1 = stage_input.a_UV1;
+    a_UV2 = stage_input.a_UV2;
+    a_Color = stage_input.a_Color;
+    a_ORMT = stage_input.a_ORMT;
+    vert_Vertex_main();
+    VertexToFragment stage_output;
+    stage_output.v_Vertex = v_Vertex;
+    stage_output.v_WorldPos = v_WorldPos;
+    stage_output.v_Normal = v_Normal;
+    stage_output.v_Tangent = v_Tangent;
+    stage_output.v_InstanceID = v_InstanceID;
+    stage_output.v_UVCoord1 = v_UVCoord1;
+    stage_output.v_UVCoord2 = v_UVCoord2;
+    stage_output.v_Color = v_Color;
+    stage_output.v_ORMT = v_ORMT;
+    stage_output.v_SH = v_SH;
+    return stage_output;
+}
+
+// Generated by AvatarShaderLibrary 242d0d331a49
+
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/LibraryVertexGI/export/pbr_vert.unity.hlsl.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/LibraryVertexGI/export/pbr_vert.unity.hlsl.meta
new file mode 100644
index 0000000000000000000000000000000000000000..88c71f2f73eccf1379897b6860c9fb09b3e0f139
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/LibraryVertexGI/export/pbr_vert.unity.hlsl.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 213be49f1b8f48d45a601b46c0de639f
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  preprocessorOverride: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Skybox-Cubed-Reverse.shader b/Assets/Oculus/Avatar2/Example/Common/Shaders/Skybox-Cubed-Reverse.shader
new file mode 100644
index 0000000000000000000000000000000000000000..482fb09b286e213b86bb49915bb8e45b6373ab09
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Skybox-Cubed-Reverse.shader
@@ -0,0 +1,87 @@
+// Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)
+
+Shader "Skybox/Cubemap Reversed" {
+Properties {
+    _Tint ("Tint Color", Color) = (.5, .5, .5, .5)
+    [Gamma] _Exposure ("Exposure", Range(0, 8)) = 1.0
+    _Rotation ("Rotation", Range(0, 360)) = 0
+    [NoScaleOffset] _Tex ("Cubemap   (HDR)", Cube) = "grey" {}
+}
+
+SubShader {
+    Tags { "Queue"="Background" "RenderType"="Background" "PreviewType"="Skybox" }
+    Cull Off ZWrite Off
+
+    Pass {
+
+        CGPROGRAM
+        #pragma vertex vert
+        #pragma fragment frag
+        #pragma target 2.0
+
+        #include "UnityCG.cginc"
+
+        samplerCUBE _Tex;
+        half4 _Tex_HDR;
+        half4 _Tint;
+        half _Exposure;
+        float _Rotation;
+
+        static const float GAMMA = 2.2;
+        static const float INV_GAMMA = 1.0 / GAMMA;
+        float3 linearTosRGB(float3 color) {
+            return pow(color, float3(INV_GAMMA, INV_GAMMA, INV_GAMMA));
+        }
+
+        float3 RotateAroundYInDegrees (float3 vertex, float degrees)
+        {
+            float alpha = degrees * UNITY_PI / 180.0;
+            float sina, cosa;
+            sincos(alpha, sina, cosa);
+            float2x2 m = float2x2(cosa, -sina, sina, cosa);
+            return float3(mul(m, vertex.xz), vertex.y).xzy;
+        }
+
+        struct appdata_t {
+            float4 vertex : POSITION;
+            UNITY_VERTEX_INPUT_INSTANCE_ID
+        };
+
+        struct v2f {
+            float4 vertex : SV_POSITION;
+            float3 texcoord : TEXCOORD0;
+            UNITY_VERTEX_OUTPUT_STEREO
+        };
+
+        v2f vert (appdata_t v)
+        {
+            v2f o;
+            UNITY_SETUP_INSTANCE_ID(v);
+            UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
+            float3 rotated = RotateAroundYInDegrees(v.vertex, _Rotation);
+            o.vertex = UnityObjectToClipPos(rotated);
+            o.texcoord = v.vertex.xyz;
+            o.texcoord.x = -o.texcoord.x;
+            return o;
+        }
+
+        fixed4 frag (v2f i) : SV_Target
+        {
+            half4 tex = texCUBElod(_Tex, float4(i.texcoord, 0));
+            half3 c = DecodeHDR (tex, _Tex_HDR);
+            c = c * _Tint.rgb * unity_ColorSpaceDouble.rgb;
+            c *= _Exposure;
+#ifdef UNITY_COLORSPACE_GAMMA
+            return half4(linearTosRGB(c),1); // IMPORTANT: Use when in Unity GAMMA rendering mode
+#else
+            return half4(c, 1);
+#endif
+        }
+        ENDCG
+    }
+}
+
+
+Fallback Off
+
+}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/Skybox-Cubed-Reverse.shader.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/Skybox-Cubed-Reverse.shader.meta
new file mode 100644
index 0000000000000000000000000000000000000000..b244ca3461434e4d969c54e5aa113476af2138c6
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/Skybox-Cubed-Reverse.shader.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 4dda107104e02f9418ce35671143716c
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityMobile.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityMobile.meta
new file mode 100644
index 0000000000000000000000000000000000000000..306f89f3098d8f69fb23a468030852b6153c10bb
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityMobile.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 75ca1aa248bbd18448fbd0886af7536f
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityMobile/Avatar-Mobile-BumpSpec.shader b/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityMobile/Avatar-Mobile-BumpSpec.shader
new file mode 100644
index 0000000000000000000000000000000000000000..2b7cb662de1d52a069b4fe516a42f0ed28a4a19a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityMobile/Avatar-Mobile-BumpSpec.shader
@@ -0,0 +1,88 @@
+// Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)
+
+// Simplified Bumped Specular shader. Differences from regular Bumped Specular one:
+// - no Main Color nor Specular Color
+// - specular lighting directions are approximated per vertex
+// - writes zero to alpha channel
+// - Normalmap uses Tiling/Offset of the Base texture
+// - no Deferred Lighting support
+// - no Lightmap support
+// - fully supports only 1 directional light. Other lights can affect it, but it will be per-vertex/SH.
+
+Shader "Avatar/Mobile/Bumped Specular" {
+Properties {
+    [PowerSlider(5.0)] _Shininess ("Shininess", Range (0.03, 1)) = 0.078125
+    _MainTex ("Base (RGB) Gloss (A)", 2D) = "white" {}
+    [NoScaleOffset] _BumpMap ("Normalmap", 2D) = "bump" {}
+
+    // AVATAR_SDK_2 BEGIN
+    _OcclusionMetallicRoughnessMap("Occlusion Roughness Metallic (ORM)", 2D) = "white" {}
+    [PowerSlider(1.0)] _RoughnessFactor ("RoughnessFactor", Range (0.0, 1)) = 0.8
+    // AVATAR_SDK_2 END
+}
+SubShader {
+    Tags { "RenderType"="Opaque" }
+    LOD 250
+
+CGPROGRAM
+#pragma surface surf MobileBlinnPhong vertex:vert exclude_path:prepass nolightmap noforwardadd halfasview interpolateview
+
+    #pragma target 3.5 // necessary for use of SV_VertexID
+    #include "../../../../Scripts/ShaderUtils/AvatarCustom.cginc"
+
+inline fixed4 LightingMobileBlinnPhong (SurfaceOutput s, fixed3 lightDir, fixed3 halfDir, fixed atten)
+{
+    fixed diff = max (0, dot (s.Normal, lightDir));
+    fixed nh = max (0, dot (s.Normal, halfDir));
+    fixed spec = pow (nh, s.Specular*128) * s.Gloss;
+
+    fixed4 c;
+    c.rgb = (s.Albedo * _LightColor0.rgb * diff + _LightColor0.rgb * spec) * atten;
+    UNITY_OPAQUE_ALPHA(c.a);
+    return c;
+}
+
+// AVATAR_SDK_2 BEGIN
+sampler2D _OcclusionMetallicRoughnessMap;
+half _RoughnessFactor;
+// AVATAR_SDK_2 END
+
+sampler2D _MainTex;
+sampler2D _BumpMap;
+half _Shininess;
+
+struct Input {
+    float2 uv_MainTex;
+};
+
+void vert(inout OvrDefaultAppdata v) {
+  OvrInitializeDefaultAppdataAndPopulateWithVertexData(v);
+}
+
+void surf (Input IN, inout SurfaceOutput o) {
+
+    fixed4 tex = tex2D(_MainTex, IN.uv_MainTex);
+    o.Albedo = tex.rgb;
+
+    // AVATAR_SDK_2 BEGIN
+    // https: // docs.unity3d.com/Manual/SL-SurfaceShaders.html
+    // legacy shaders like this use a Spec/gloss model
+    // while standard uses the metallic/roughness model
+    fixed4 orm = tex2D(_OcclusionMetallicRoughnessMap, IN.uv_MainTex);
+    float occlusion = orm.r;
+    float roughness = orm.g;
+    float metallic = orm.b;
+    o.Gloss = (1 - roughness) * _RoughnessFactor;
+    o.Albedo *= occlusion;
+    o.Gloss *= occlusion;
+    // AVATAR_SDK_2 END
+
+    o.Alpha = tex.a;
+    o.Specular = _Shininess;
+    o.Normal = UnpackNormal (tex2D(_BumpMap, IN.uv_MainTex));
+}
+ENDCG
+}
+
+FallBack "Mobile/VertexLit"
+}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityMobile/Avatar-Mobile-BumpSpec.shader.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityMobile/Avatar-Mobile-BumpSpec.shader.meta
new file mode 100644
index 0000000000000000000000000000000000000000..286f43a885961000fb84463d8e3dadf81d34f5b3
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityMobile/Avatar-Mobile-BumpSpec.shader.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 4bbfef03382cb7e4e8ffefdd492e6fa0
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityMobile/Avatar-Mobile-Custom.shader b/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityMobile/Avatar-Mobile-Custom.shader
new file mode 100644
index 0000000000000000000000000000000000000000..cb043ae4c155d45b4678f12cc12bfa871be116ad
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityMobile/Avatar-Mobile-Custom.shader
@@ -0,0 +1,113 @@
+// Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)
+
+// Simplified Bumped Specular shader. Differences from regular Bumped Specular one:
+// - no Main Color nor Specular Color
+// - specular lighting directions are approximated per vertex
+// - writes zero to alpha channel
+// - Normalmap uses Tiling/Offset of the Base texture
+// - no Deferred Lighting support
+// - no Lightmap support
+// - fully supports only 1 directional light. Other lights can affect it, but it will be per-vertex/SH.
+
+Shader "Avatar/Mobile/Custom" {
+
+  Properties {
+    [PowerSlider(5.0)] _Shininess("Shininess", Range(0.03, 1)) = 0.078125
+
+    [NoScaleOffset] _MainTex("Base (RGB) Gloss (A)", 2D) = "white" {}
+    [ShowIfKeyword(HAS_NORMALS)]
+    [NoScaleOffset] _BumpMap("Normalmap", 2D) = "bump" {}
+
+    // AVATAR_SDK_2 BEGIN
+    _OcclusionMetallicRoughnessMap("Occlusion Roughness Metallic (ORM)", 2D) = "white" {}
+    [PowerSlider(1.0)] _RoughnessFactor ("RoughnessFactor", Range (0.0, 1)) = 0.8
+    [PowerSlider(5.0)] _Extrusion("Extrusion", Range(-0.050, 0.050)) = 0.00
+    // AVATAR_SDK_2 END
+
+    [NoScaleOffset] u_AttributeTexture("GPU Skinning Source Texture", 2DArray) = "black" {}
+  }
+
+  SubShader {
+    Tags{"RenderType" = "Opaque"}
+    LOD 250
+
+    CGPROGRAM
+#pragma surface surf MobileBlinnPhongCustom vertex:vert exclude_path:prepass nolightmap noforwardadd halfasview interpolateview
+
+    #pragma target 3.5 // necessary for use of SV_VertexID
+    #include "../../../../Scripts/ShaderUtils/AvatarCustom.cginc"
+
+    struct SurfaceOutputCustom {
+      fixed3 Albedo; // diffuse color
+      fixed3 Normal; // tangent space normal, if written
+      fixed3 Emission;
+      half Specular; // specular power in 0..1 range
+      fixed Gloss; // specular intensity
+      fixed Alpha; // alpha for transparencies
+      fixed Metal; // determines color of specular highlight
+    };
+
+    inline fixed4 LightingMobileBlinnPhongCustom(SurfaceOutputCustom s, fixed3 lightDir, fixed3 halfDir, fixed atten)
+    {
+      fixed diff = max(0, dot(s.Normal, lightDir));
+      fixed nh = max(0, dot(s.Normal, halfDir));
+      fixed spec = pow(nh, s.Specular * 128) * s.Gloss;
+
+      fixed4 c;
+      fixed3 highlightColor = _LightColor0.rgb + s.Metal * (s.Albedo - _LightColor0.rgb);
+      c.rgb = (s.Albedo * _LightColor0.rgb * diff + highlightColor * spec) * atten;
+
+      UNITY_OPAQUE_ALPHA(c.a);
+      return c;
+    }
+
+    // AVATAR_SDK_2 BEGIN
+    sampler2D _OcclusionMetallicRoughnessMap;
+    half _RoughnessFactor;
+    // AVATAR_SDK_2 END
+
+    sampler2D _MainTex;
+#if HAS_NORMALS // AVATAR_SDK_2 REMOVE
+    sampler2D _BumpMap;
+#endif
+    half _Shininess;
+
+    struct Input {
+      float2 uv_MainTex;
+    };
+
+    // AVATAR_SDK_2 BEGIN
+    void vert(inout OvrDefaultAppdata v) {
+      OvrInitializeDefaultAppdataAndPopulateWithVertexData(v);
+    }
+    // AVATAR_SDK_2 END
+
+    void surf(Input IN, inout SurfaceOutputCustom o) {
+        fixed4 tex = tex2D(_MainTex, IN.uv_MainTex);
+        o.Albedo = tex.rgb;
+
+        // AVATAR_SDK_2 BEGIN
+        // https: // docs.unity3d.com/Manual/SL-SurfaceShaders.html
+        // legacy shaders like this use a Spec/gloss model
+        // while standard uses the metallic/roughness model
+        fixed4 orm = tex2D(_OcclusionMetallicRoughnessMap, IN.uv_MainTex);
+        float occlusion = orm.r;
+        float roughness = orm.g;
+        float metallic = orm.b;
+        o.Gloss = (1 - roughness) * _RoughnessFactor;
+        o.Albedo *= occlusion;
+        o.Gloss *= occlusion;
+        o.Metal = metallic;
+        // AVATAR_SDK_2 END
+
+        o.Alpha = tex.a;
+        o.Specular = _Shininess;
+#if HAS_NORMALS // AVATAR_SDK_2 REMOVE
+        o.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_MainTex));
+#endif
+    }
+    ENDCG
+  }
+
+  FallBack "Mobile/VertexLit"
+}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityMobile/Avatar-Mobile-Custom.shader.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityMobile/Avatar-Mobile-Custom.shader.meta
new file mode 100644
index 0000000000000000000000000000000000000000..d8d3384ac7a3c868daeae224d88c68862e74d809
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityMobile/Avatar-Mobile-Custom.shader.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: fd3a9e2d35990a74487ef0d8cd8320ad
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityMobile/Avatar-Mobile-Diffuse.shader b/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityMobile/Avatar-Mobile-Diffuse.shader
new file mode 100644
index 0000000000000000000000000000000000000000..adeb2ce239652995d139398765c0c47c9c53e789
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityMobile/Avatar-Mobile-Diffuse.shader
@@ -0,0 +1,40 @@
+// Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)
+
+// Simplified Diffuse shader. Differences from regular Diffuse one:
+// - no Main Color
+// - fully supports only 1 directional light. Other lights can affect it, but it will be per-vertex/SH.
+
+Shader "Avatar/Mobile/Diffuse" {
+Properties {
+    _MainTex ("Base (RGB)", 2D) = "white" {}
+}
+SubShader {
+    Tags { "RenderType"="Opaque" }
+    LOD 150
+
+CGPROGRAM
+#pragma surface surf Lambert vertex:vert nolightmap noforwardadd
+
+    #pragma target 3.5 // necessary for use of SV_VertexID
+    #include "../../../../Scripts/ShaderUtils/AvatarCustom.cginc"
+
+sampler2D _MainTex;
+
+struct Input {
+    float2 uv_MainTex;
+};
+
+void vert(inout OvrDefaultAppdata v) {
+  OvrInitializeDefaultAppdataAndPopulateWithVertexData(v);
+}
+
+void surf (Input IN, inout SurfaceOutput o) {
+    fixed4 c = tex2D(_MainTex, IN.uv_MainTex);
+    o.Albedo = c.rgb;
+    o.Alpha = c.a;
+}
+ENDCG
+}
+
+Fallback "Mobile/Diffuse"
+}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityMobile/Avatar-Mobile-Diffuse.shader.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityMobile/Avatar-Mobile-Diffuse.shader.meta
new file mode 100644
index 0000000000000000000000000000000000000000..6b0bdccf7e94c06e3953476323c167b6b25396a8
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityMobile/Avatar-Mobile-Diffuse.shader.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: ed2dc23baab27c847a0193bc6dce1816
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityMobile/Avatar-Mobile-VertexLit.shader b/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityMobile/Avatar-Mobile-VertexLit.shader
new file mode 100644
index 0000000000000000000000000000000000000000..8b2ce25fcdbc39a6a50aa97aa0db5a0c382cd45e
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityMobile/Avatar-Mobile-VertexLit.shader
@@ -0,0 +1,150 @@
+// Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)
+
+// Simplified VertexLit shader. Differences from regular VertexLit one:
+// - no per-material color
+// - no specular
+// - no emission
+
+Shader "Avatar/Mobile/VertexLit" {
+Properties {
+    _MainTex ("Base (RGB)", 2D) = "white" {}
+}
+
+SubShader {
+    Tags { "RenderType"="Opaque" }
+    LOD 80
+
+    // Non-lightmapped
+    Pass {
+        Tags { "LightMode" = "Vertex" }
+
+        Material {
+            Diffuse (1,1,1,1)
+            Ambient (1,1,1,1)
+        }
+        Lighting On
+        SetTexture [_MainTex] {
+            constantColor (1,1,1,1)
+            Combine texture * primary DOUBLE, constant // UNITY_OPAQUE_ALPHA_FFP
+        }
+    }
+
+    // Lightmapped
+    Pass
+    {
+        Tags{ "LIGHTMODE" = "VertexLM" "RenderType" = "Opaque" }
+
+        CGPROGRAM
+
+        #pragma vertex vert
+        #pragma fragment frag
+        #pragma target 3.5 // necessary for use of SV_VertexID
+        #include "UnityCG.cginc"
+        #pragma multi_compile_fog
+        #define USING_FOG (defined(FOG_LINEAR) || defined(FOG_EXP) || defined(FOG_EXP2))
+        #include "../../../../Scripts/ShaderUtils/AvatarCustom.cginc"
+
+        float4 _MainTex_ST;
+
+        struct appdata {
+          OVR_REQUIRED_VERTEX_FIELDS
+          float4 uv0 : OVR_FIRST_AVAILABLE_VERTEX_TEXCOORD_SEMANTIC;
+          float4 uv1 : OVR_SECOND_AVAILABLE_VERTEX_TEXCOORD_SEMANTIC;
+          UNITY_VERTEX_INPUT_INSTANCE_ID
+        };
+
+        struct v2f
+        {
+            float2 uv0 : TEXCOORD0;
+            float2 uv1 : TEXCOORD1;
+        #if USING_FOG
+            fixed fog : TEXCOORD2;
+        #endif
+            float4 pos : SV_POSITION;
+
+            UNITY_VERTEX_OUTPUT_STEREO
+        };
+
+        v2f vert(appdata IN)
+        {
+            OVR_INITIALIZE_VERTEX_FIELDS(IN);
+            UNITY_SETUP_INSTANCE_ID(IN);
+            v2f o;
+            UNITY_INITIALIZE_OUTPUT(v2f,o);
+            UNITY_TRANSFER_INSTANCE_ID(IN,o);
+            UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
+
+            o.uv0 = IN.uv1.xy * unity_LightmapST.xy + unity_LightmapST.zw;
+            o.uv1 = IN.uv0.xy * _MainTex_ST.xy + _MainTex_ST.zw;
+
+            const OvrVertexData vertexData = OVR_CREATE_VERTEX_DATA(IN);
+        #if USING_FOG
+            float3 eyePos = UnityObjectToViewPos(vertexData.position);
+            float fogCoord = length(eyePos.xyz);
+            UNITY_CALC_FOG_FACTOR_RAW(fogCoord);
+            o.fog = saturate(unityFogFactor);
+        #endif
+            o.pos = UnityObjectToClipPos(vertexData.position);
+
+            return o;
+        }
+
+        sampler2D _MainTex;
+
+        fixed4 frag(v2f IN) : SV_Target
+        {
+            fixed4 col;
+            fixed4 tex = UNITY_SAMPLE_TEX2D(unity_Lightmap, IN.uv0.xy);
+            half3 bakedColor = DecodeLightmap(tex);
+
+            tex = tex2D(_MainTex, IN.uv1.xy);
+            col.rgb = tex.rgb * bakedColor;
+            col.a = 1.0f;
+
+            #if USING_FOG
+            col.rgb = lerp(unity_FogColor.rgb, col.rgb, IN.fog);
+            #endif
+
+            return col;
+        }
+
+        ENDCG
+    }
+
+    // Pass to render object as a shadow caster
+    Pass
+    {
+        Name "ShadowCaster"
+        Tags { "LightMode" = "ShadowCaster" }
+
+        ZWrite On ZTest LEqual Cull Off
+
+        CGPROGRAM
+        #pragma vertex vert
+        #pragma fragment frag
+        #pragma target 2.0
+        #pragma multi_compile_shadowcaster
+        #include "UnityCG.cginc"
+
+        struct v2f {
+            V2F_SHADOW_CASTER;
+            UNITY_VERTEX_OUTPUT_STEREO
+        };
+
+        v2f vert( appdata_base v )
+        {
+            v2f o;
+            UNITY_SETUP_INSTANCE_ID(v);
+            UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
+            TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
+            return o;
+        }
+
+        float4 frag( v2f i ) : SV_Target
+        {
+            SHADOW_CASTER_FRAGMENT(i)
+        }
+        ENDCG
+    }
+}
+}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityMobile/Avatar-Mobile-VertexLit.shader.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityMobile/Avatar-Mobile-VertexLit.shader.meta
new file mode 100644
index 0000000000000000000000000000000000000000..f4889b5358e9685f444b80519226f6b5df89745a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityMobile/Avatar-Mobile-VertexLit.shader.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 9b411f78255f62b45a36763ce7382ad1
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityStandard.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityStandard.meta
new file mode 100644
index 0000000000000000000000000000000000000000..e623a2d53f2df6d0446259a7067a54e3eb6e4933
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityStandard.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: c25a8599fd6301d428a34f15f232c522
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityStandard/Avatar-Standard.shader b/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityStandard/Avatar-Standard.shader
new file mode 100644
index 0000000000000000000000000000000000000000..a227bb62fb2c04e8f3197cd6bc0e98defcc74f19
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityStandard/Avatar-Standard.shader
@@ -0,0 +1,386 @@
+// Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)
+
+Shader "Avatar/Standard"
+{
+    Properties
+    {
+        _Color("Color", Color) = (1,1,1,1)
+        _MainTex("Albedo", 2D) = "white" {}
+
+        _Cutoff("Alpha Cutoff", Range(0.0, 1.0)) = 0.5
+
+        _Glossiness("Smoothness", Range(0.0, 1.0)) = 0.5
+        _GlossMapScale("Smoothness Scale", Range(0.0, 1.0)) = 1.0
+        [Enum(Metallic Alpha,0,Albedo Alpha,1,Avatar ORM,2)] _SmoothnessTextureChannel ("Smoothness texture channel", Float) = 2
+
+        [Gamma] _Metallic("Metallic", Range(0.0, 1.0)) = 0.0
+        _MetallicGlossMap("Metallic", 2D) = "white" {}
+
+        [ToggleOff] _SpecularHighlights("Specular Highlights", Float) = 1.0
+        [ToggleOff] _GlossyReflections("Glossy Reflections", Float) = 1.0
+
+        _BumpScale("Scale", Float) = 1.0
+        [Normal] _BumpMap("Normal Map", 2D) = "bump" {}
+
+        _Parallax ("Height Scale", Range (0.005, 0.08)) = 0.02
+        _ParallaxMap ("Height Map", 2D) = "black" {}
+
+        _OcclusionStrength("Strength", Range(0.0, 1.0)) = 1.0
+        _OcclusionMap("Occlusion", 2D) = "white" {}
+
+        _EmissionColor("Color", Color) = (0,0,0)
+        _EmissionMap("Emission", 2D) = "white" {}
+
+        _DetailMask("Detail Mask", 2D) = "white" {}
+
+        _DetailAlbedoMap("Detail Albedo x2", 2D) = "grey" {}
+        _DetailNormalMapScale("Scale", Float) = 1.0
+        [Normal] _DetailNormalMap("Normal Map", 2D) = "bump" {}
+
+        [Enum(UV0,0,UV1,1)] _UVSec ("UV Set for secondary textures", Float) = 0
+
+
+        // Blending state
+        [HideInInspector] _Mode ("__mode", Float) = 0.0
+        [HideInInspector] _SrcBlend ("__src", Float) = 1.0
+        [HideInInspector] _DstBlend ("__dst", Float) = 0.0
+        [HideInInspector] _ZWrite ("__zw", Float) = 1.0
+    }
+
+    CGINCLUDE
+        #define UNITY_SETUP_BRDF_INPUT MetallicSetup
+    ENDCG
+
+    SubShader
+    {
+        Tags { "RenderType"="Opaque" "PerformanceChecks"="False" }
+        LOD 300
+
+
+        // ------------------------------------------------------------------
+        //  Base forward pass (directional light, emission, lightmaps, ...)
+        Pass
+        {
+            Name "FORWARD"
+            Tags { "LightMode" = "ForwardBase" }
+
+            Blend [_SrcBlend] [_DstBlend]
+            ZWrite [_ZWrite]
+
+            CGPROGRAM
+            // -------------------------------------
+
+            #pragma shader_feature_local _NORMALMAP
+            #pragma shader_feature_local _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
+            #pragma shader_feature _EMISSION
+            #pragma shader_feature_local _METALLICGLOSSMAP
+            #pragma shader_feature_local _DETAIL_MULX2
+            #pragma shader_feature_local _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
+            #pragma shader_feature_local _AVATAR_ORM_CHANNEL
+            #pragma shader_feature_local _SPECULARHIGHLIGHTS_OFF
+            #pragma shader_feature_local _GLOSSYREFLECTIONS_OFF
+            #pragma shader_feature_local _PARALLAXMAP
+
+            #pragma target 3.5 // necessary for use of SV_VertexID
+            #include "UnityInstancing.cginc"
+            #include "UnityCG.cginc"
+            #include "../../../../Scripts/ShaderUtils/AvatarCustom.cginc"
+
+            #pragma multi_compile_fwdbase
+            #pragma multi_compile_fog
+            #pragma multi_compile_instancing
+            // Uncomment the following line to enable dithering LOD crossfade. Note: there are more in the file to uncomment for other passes.
+            //#pragma multi_compile _ LOD_FADE_CROSSFADE
+
+            #pragma vertex vertBase
+            #pragma fragment fragBase
+            #include "AvatarStandardCoreForward.cginc"
+
+            ENDCG
+        }
+        // ------------------------------------------------------------------
+        //  Additive forward pass (one light per pass)
+        Pass
+        {
+            Name "FORWARD_DELTA"
+            Tags { "LightMode" = "ForwardAdd" }
+            Blend [_SrcBlend] One
+            Fog { Color (0,0,0,0) } // in additive pass fog should be black
+            ZWrite Off
+            ZTest LEqual
+
+            CGPROGRAM
+            // -------------------------------------
+
+
+            #pragma shader_feature_local _NORMALMAP
+            #pragma shader_feature_local _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
+            #pragma shader_feature_local _METALLICGLOSSMAP
+            #pragma shader_feature_local _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
+            #pragma shader_feature_local _AVATAR_ORM_CHANNEL
+            #pragma shader_feature_local _SPECULARHIGHLIGHTS_OFF
+            #pragma shader_feature_local _DETAIL_MULX2
+            #pragma shader_feature_local _PARALLAXMAP
+
+            #pragma target 3.5 // necessary for use of SV_VertexID
+            #include "UnityInstancing.cginc"
+            #include "UnityCG.cginc"
+            #include "../../../../Scripts/ShaderUtils/AvatarCustom.cginc"
+
+            #pragma multi_compile_fwdadd_fullshadows
+            #pragma multi_compile_fog
+            // Uncomment the following line to enable dithering LOD crossfade. Note: there are more in the file to uncomment for other passes.
+            //#pragma multi_compile _ LOD_FADE_CROSSFADE
+
+            #pragma vertex vertAdd
+            #pragma fragment fragAdd
+            #include "AvatarStandardCoreForward.cginc"
+
+            ENDCG
+        }
+        // ------------------------------------------------------------------
+        //  Shadow rendering pass
+        Pass {
+            Name "ShadowCaster"
+            Tags { "LightMode" = "ShadowCaster" }
+
+            ZWrite On ZTest LEqual
+
+            CGPROGRAM
+            // -------------------------------------
+
+
+            #pragma shader_feature_local _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
+            #pragma shader_feature_local _METALLICGLOSSMAP
+            #pragma shader_feature_local _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
+            #pragma shader_feature_local _AVATAR_ORM_CHANNEL
+            #pragma shader_feature_local _PARALLAXMAP
+            #pragma multi_compile_shadowcaster
+            #pragma multi_compile_instancing
+            // Uncomment the following line to enable dithering LOD crossfade. Note: there are more in the file to uncomment for other passes.
+            //#pragma multi_compile _ LOD_FADE_CROSSFADE
+
+            #pragma target 3.5 // necessary for use of SV_VertexID
+            #include "UnityInstancing.cginc"
+            #include "UnityCG.cginc"
+            #include "../../../../Scripts/ShaderUtils/AvatarCustom.cginc"
+
+            #pragma vertex vertShadowCaster
+            #pragma fragment fragShadowCaster
+
+            #include "UnityStandardShadow.cginc"
+
+            ENDCG
+        }
+        // ------------------------------------------------------------------
+        //  Deferred pass
+        Pass
+        {
+            Name "DEFERRED"
+            Tags { "LightMode" = "Deferred" }
+
+            CGPROGRAM
+            #pragma exclude_renderers nomrt
+
+
+            // -------------------------------------
+
+            #pragma shader_feature_local _NORMALMAP
+            #pragma shader_feature_local _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
+            #pragma shader_feature _EMISSION
+            #pragma shader_feature_local _METALLICGLOSSMAP
+            #pragma shader_feature_local _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
+            #pragma shader_feature_local _AVATAR_ORM_CHANNEL
+            #pragma shader_feature_local _SPECULARHIGHLIGHTS_OFF
+            #pragma shader_feature_local _DETAIL_MULX2
+            #pragma shader_feature_local _PARALLAXMAP
+
+            #pragma target 3.5 // necessary for use of SV_VertexID
+            #include "UnityInstancing.cginc"
+            #include "UnityCG.cginc"
+            #include "../../../../Scripts/ShaderUtils/AvatarCustom.cginc"
+
+            #pragma multi_compile_prepassfinal
+            #pragma multi_compile_instancing
+            // Uncomment the following line to enable dithering LOD crossfade. Note: there are more in the file to uncomment for other passes.
+            //#pragma multi_compile _ LOD_FADE_CROSSFADE
+
+            #pragma vertex vertDeferred
+            #pragma fragment fragDeferred
+
+            #include "AvatarStandardCore.cginc"
+
+            ENDCG
+        }
+
+        // ------------------------------------------------------------------
+        // Extracts information for lightmapping, GI (emission, albedo, ...)
+        // This pass it not used during regular rendering.
+        Pass
+        {
+            Name "META"
+            Tags { "LightMode"="Meta" }
+
+            Cull Off
+
+            CGPROGRAM
+            #pragma vertex vert_meta
+            #pragma fragment frag_meta
+
+            #pragma shader_feature _EMISSION
+            #pragma shader_feature_local _METALLICGLOSSMAP
+            #pragma shader_feature_local _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
+            #pragma shader_feature_local _AVATAR_ORM_CHANNEL
+            #pragma shader_feature_local _DETAIL_MULX2
+            #pragma shader_feature EDITOR_VISUALIZATION
+
+            #pragma target 3.5 // necessary for use of SV_VertexID
+            #include "UnityInstancing.cginc"
+            #include "UnityCG.cginc"
+            #include "../../../../Scripts/ShaderUtils/AvatarCustom.cginc"
+
+            #include "UnityStandardMeta.cginc"
+            ENDCG
+        }
+    }
+
+    SubShader
+    {
+        Tags { "RenderType"="Opaque" "PerformanceChecks"="False" }
+        LOD 150
+
+        // ------------------------------------------------------------------
+        //  Base forward pass (directional light, emission, lightmaps, ...)
+        Pass
+        {
+            Name "FORWARD"
+            Tags { "LightMode" = "ForwardBase" }
+
+            Blend [_SrcBlend] [_DstBlend]
+            ZWrite [_ZWrite]
+
+            CGPROGRAM
+            #pragma shader_feature_local _NORMALMAP
+            #pragma shader_feature_local _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
+            #pragma shader_feature _EMISSION
+            #pragma shader_feature_local _METALLICGLOSSMAP
+            #pragma shader_feature_local _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
+            #pragma shader_feature_local _AVATAR_ORM_CHANNEL
+            #pragma shader_feature_local _SPECULARHIGHLIGHTS_OFF
+            #pragma shader_feature_local _GLOSSYREFLECTIONS_OFF
+            // SM2.0: NOT SUPPORTED shader_feature_local _DETAIL_MULX2
+            // SM2.0: NOT SUPPORTED shader_feature_local _PARALLAXMAP
+
+            #pragma target 3.5 // necessary for use of SV_VertexID
+            #include "UnityInstancing.cginc"
+            #include "UnityCG.cginc"
+            #include "../../../../Scripts/ShaderUtils/AvatarCustom.cginc"
+
+            #pragma skip_variants SHADOWS_SOFT DIRLIGHTMAP_COMBINED
+
+            #pragma multi_compile_fwdbase
+            #pragma multi_compile_fog
+
+            #pragma vertex vertBase
+            #pragma fragment fragBase
+            #include "AvatarStandardCoreForward.cginc"
+
+            ENDCG
+        }
+        // ------------------------------------------------------------------
+        //  Additive forward pass (one light per pass)
+        Pass
+        {
+            Name "FORWARD_DELTA"
+            Tags { "LightMode" = "ForwardAdd" }
+            Blend [_SrcBlend] One
+            Fog { Color (0,0,0,0) } // in additive pass fog should be black
+            ZWrite Off
+            ZTest LEqual
+
+            CGPROGRAM
+            #pragma shader_feature_local _NORMALMAP
+            #pragma shader_feature_local _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
+            #pragma shader_feature_local _METALLICGLOSSMAP
+            #pragma shader_feature_local _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
+            #pragma shader_feature_local _AVATAR_ORM_CHANNEL
+            #pragma shader_feature_local _SPECULARHIGHLIGHTS_OFF
+            #pragma shader_feature_local _DETAIL_MULX2
+            // SM2.0: NOT SUPPORTED shader_feature_local _PARALLAXMAP
+            #pragma skip_variants SHADOWS_SOFT
+
+            #pragma target 3.5 // necessary for use of SV_VertexID
+            #include "UnityInstancing.cginc"
+            #include "UnityCG.cginc"
+            #include "../../../../Scripts/ShaderUtils/AvatarCustom.cginc"
+
+            #pragma multi_compile_fwdadd_fullshadows
+            #pragma multi_compile_fog
+
+            #pragma vertex vertAdd
+            #pragma fragment fragAdd
+            #include "AvatarStandardCoreForward.cginc"
+
+            ENDCG
+        }
+        // ------------------------------------------------------------------
+        //  Shadow rendering pass
+        Pass {
+            Name "ShadowCaster"
+            Tags { "LightMode" = "ShadowCaster" }
+
+            ZWrite On ZTest LEqual
+
+            CGPROGRAM
+            #pragma shader_feature_local _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
+            #pragma shader_feature_local _METALLICGLOSSMAP
+            #pragma shader_feature_local _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
+            #pragma shader_feature_local _AVATAR_ORM_CHANNEL
+            #pragma skip_variants SHADOWS_SOFT
+            #pragma multi_compile_shadowcaster
+
+            #pragma target 3.5 // necessary for use of SV_VertexID
+            #include "../../../../Scripts/ShaderUtils/AvatarCustom.cginc"
+
+            #pragma vertex vertShadowCaster
+            #pragma fragment fragShadowCaster
+
+            #include "UnityStandardShadow.cginc"
+
+            ENDCG
+        }
+
+        // ------------------------------------------------------------------
+        // Extracts information for lightmapping, GI (emission, albedo, ...)
+        // This pass it not used during regular rendering.
+        Pass
+        {
+            Name "META"
+            Tags { "LightMode"="Meta" }
+
+            Cull Off
+
+            CGPROGRAM
+            #pragma vertex vert_meta
+            #pragma fragment frag_meta
+
+            #pragma shader_feature _EMISSION
+            #pragma shader_feature_local _METALLICGLOSSMAP
+            #pragma shader_feature_local _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
+            #pragma shader_feature_local _AVATAR_ORM_CHANNEL
+            #pragma shader_feature_local _DETAIL_MULX2
+            #pragma shader_feature EDITOR_VISUALIZATION
+
+            #pragma target 3.5 // necessary for use of SV_VertexID
+            #include "UnityCG.cginc"
+            #include "../../../../Scripts/ShaderUtils/AvatarCustom.cginc"
+
+            #include "UnityStandardMeta.cginc"
+            ENDCG
+        }
+    }
+
+
+    FallBack "VertexLit"
+    CustomEditor "AvatarShaderGUI"
+}
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityStandard/Avatar-Standard.shader.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityStandard/Avatar-Standard.shader.meta
new file mode 100644
index 0000000000000000000000000000000000000000..52ffac248145c8eff14862efa5f56bab63c2bde9
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityStandard/Avatar-Standard.shader.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: a137d245417b76a4bb089303f90043ce
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityStandard/AvatarStandardCore.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityStandard/AvatarStandardCore.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..d6b730456b00772755d2ed2063fc23fbf1ce15ec
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityStandard/AvatarStandardCore.cginc
@@ -0,0 +1,736 @@
+// Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)
+
+#ifndef AVATAR_STANDARD_CORE_INCLUDED
+#define AVATAR_STANDARD_CORE_INCLUDED
+
+#include "UnityCG.cginc"
+#include "UnityShaderVariables.cginc"
+#include "UnityStandardConfig.cginc"
+
+#include "AvatarStandardInput.cginc"
+
+#include "UnityPBSLighting.cginc"
+#include "UnityStandardUtils.cginc"
+#include "UnityGBuffer.cginc"
+#include "UnityStandardBRDF.cginc"
+
+#include "AutoLight.cginc"
+//-------------------------------------------------------------------------------------
+// counterpart for NormalizePerPixelNormal
+// skips normalization per-vertex and expects normalization to happen per-pixel
+half3 NormalizePerVertexNormal (float3 n) // takes float to avoid overflow
+{
+    #if (SHADER_TARGET < 30) || UNITY_STANDARD_SIMPLE
+        return normalize(n);
+    #else
+        return n; // will normalize per-pixel instead
+    #endif
+}
+
+float3 NormalizePerPixelNormal (float3 n)
+{
+    #if (SHADER_TARGET < 30) || UNITY_STANDARD_SIMPLE
+        return n;
+    #else
+        return normalize((float3)n); // takes float to avoid overflow
+    #endif
+}
+
+//-------------------------------------------------------------------------------------
+UnityLight MainLight ()
+{
+    UnityLight l;
+
+    l.color = _LightColor0.rgb;
+    l.dir = _WorldSpaceLightPos0.xyz;
+    return l;
+}
+
+UnityLight AdditiveLight (half3 lightDir, half atten)
+{
+    UnityLight l;
+
+    l.color = _LightColor0.rgb;
+    l.dir = lightDir;
+    #ifndef USING_DIRECTIONAL_LIGHT
+        l.dir = NormalizePerPixelNormal(l.dir);
+    #endif
+
+    // shadow the light
+    l.color *= atten;
+    return l;
+}
+
+UnityLight DummyLight ()
+{
+    UnityLight l;
+    l.color = 0;
+    l.dir = half3 (0,1,0);
+    return l;
+}
+
+UnityIndirect ZeroIndirect ()
+{
+    UnityIndirect ind;
+    ind.diffuse = 0;
+    ind.specular = 0;
+    return ind;
+}
+
+//-------------------------------------------------------------------------------------
+// Common fragment setup
+
+// deprecated
+half3 WorldNormal(half4 tan2world[3])
+{
+    return normalize(tan2world[2].xyz);
+}
+
+// deprecated
+#ifdef _TANGENT_TO_WORLD
+    half3x3 ExtractTangentToWorldPerPixel(half4 tan2world[3])
+    {
+        half3 t = tan2world[0].xyz;
+        half3 b = tan2world[1].xyz;
+        half3 n = tan2world[2].xyz;
+
+    #if UNITY_TANGENT_ORTHONORMALIZE
+        n = NormalizePerPixelNormal(n);
+
+        // ortho-normalize Tangent
+        t = normalize (t - n * dot(t, n));
+
+        // recalculate Binormal
+        half3 newB = cross(n, t);
+        b = newB * sign (dot (newB, b));
+    #endif
+
+        return half3x3(t, b, n);
+    }
+#else
+    half3x3 ExtractTangentToWorldPerPixel(half4 tan2world[3])
+    {
+        return half3x3(0,0,0,0,0,0,0,0,0);
+    }
+#endif
+
+float3 PerPixelWorldNormal(float4 i_tex, float4 tangentToWorld[3])
+{
+#ifdef _NORMALMAP
+    half3 tangent = tangentToWorld[0].xyz;
+    half3 binormal = tangentToWorld[1].xyz;
+    half3 normal = tangentToWorld[2].xyz;
+
+    #if UNITY_TANGENT_ORTHONORMALIZE
+        normal = NormalizePerPixelNormal(normal);
+
+        // ortho-normalize Tangent
+        tangent = normalize (tangent - normal * dot(tangent, normal));
+
+        // recalculate Binormal
+        half3 newB = cross(normal, tangent);
+        binormal = newB * sign (dot (newB, binormal));
+    #endif
+
+    half3 normalTangent = NormalInTangentSpace(i_tex);
+    float3 normalWorld = NormalizePerPixelNormal(tangent * normalTangent.x + binormal * normalTangent.y + normal * normalTangent.z); // @TODO: see if we can squeeze this normalize on SM2.0 as well
+#else
+    float3 normalWorld = normalize(tangentToWorld[2].xyz);
+#endif
+    return normalWorld;
+}
+
+#ifdef _PARALLAXMAP
+    #define IN_VIEWDIR4PARALLAX(i) NormalizePerPixelNormal(half3(i.tangentToWorldAndPackedData[0].w,i.tangentToWorldAndPackedData[1].w,i.tangentToWorldAndPackedData[2].w))
+    #define IN_VIEWDIR4PARALLAX_FWDADD(i) NormalizePerPixelNormal(i.viewDirForParallax.xyz)
+#else
+    #define IN_VIEWDIR4PARALLAX(i) half3(0,0,0)
+    #define IN_VIEWDIR4PARALLAX_FWDADD(i) half3(0,0,0)
+#endif
+
+#if UNITY_REQUIRE_FRAG_WORLDPOS
+    #if UNITY_PACK_WORLDPOS_WITH_TANGENT
+        #define IN_WORLDPOS(i) half3(i.tangentToWorldAndPackedData[0].w,i.tangentToWorldAndPackedData[1].w,i.tangentToWorldAndPackedData[2].w)
+    #else
+        #define IN_WORLDPOS(i) i.posWorld
+    #endif
+    #define IN_WORLDPOS_FWDADD(i) i.posWorld
+#else
+    #define IN_WORLDPOS(i) half3(0,0,0)
+    #define IN_WORLDPOS_FWDADD(i) half3(0,0,0)
+#endif
+
+#define IN_LIGHTDIR_FWDADD(i) half3(i.tangentToWorldAndLightDir[0].w, i.tangentToWorldAndLightDir[1].w, i.tangentToWorldAndLightDir[2].w)
+
+#define FRAGMENT_SETUP(x) FragmentCommonData x = \
+    FragmentSetup(i.tex, i.eyeVec.xyz, IN_VIEWDIR4PARALLAX(i), i.tangentToWorldAndPackedData, IN_WORLDPOS(i));
+
+#define FRAGMENT_SETUP_FWDADD(x) FragmentCommonData x = \
+    FragmentSetup(i.tex, i.eyeVec.xyz, IN_VIEWDIR4PARALLAX_FWDADD(i), i.tangentToWorldAndLightDir, IN_WORLDPOS_FWDADD(i));
+
+struct FragmentCommonData
+{
+    half3 diffColor, specColor;
+    // Note: smoothness & oneMinusReflectivity for optimization purposes, mostly for DX9 SM2.0 level.
+    // Most of the math is being done on these (1-x) values, and that saves a few precious ALU slots.
+    half oneMinusReflectivity, smoothness;
+    float3 normalWorld;
+    float3 eyeVec;
+    half alpha;
+    float3 posWorld;
+
+#if UNITY_STANDARD_SIMPLE
+    half3 reflUVW;
+#endif
+
+#if UNITY_STANDARD_SIMPLE
+    half3 tangentSpaceNormal;
+#endif
+};
+
+#ifndef UNITY_SETUP_BRDF_INPUT
+    #define UNITY_SETUP_BRDF_INPUT SpecularSetup
+#endif
+
+inline FragmentCommonData SpecularSetup (float4 i_tex)
+{
+    half4 specGloss = SpecularGloss(i_tex.xy);
+    half3 specColor = specGloss.rgb;
+    half smoothness = specGloss.a;
+
+    half oneMinusReflectivity;
+    half3 diffColor = EnergyConservationBetweenDiffuseAndSpecular (Albedo(i_tex), specColor, /*out*/ oneMinusReflectivity);
+
+    FragmentCommonData o = (FragmentCommonData)0;
+    o.diffColor = diffColor;
+    o.specColor = specColor;
+    o.oneMinusReflectivity = oneMinusReflectivity;
+    o.smoothness = smoothness;
+    return o;
+}
+
+inline FragmentCommonData RoughnessSetup(float4 i_tex)
+{
+    half2 metallicGloss = MetallicRough(i_tex.xy);
+    half metallic = metallicGloss.x;
+    half smoothness = metallicGloss.y; // this is 1 minus the square root of real roughness m.
+
+    half oneMinusReflectivity;
+    half3 specColor;
+    half3 diffColor = DiffuseAndSpecularFromMetallic(Albedo(i_tex), metallic, /*out*/ specColor, /*out*/ oneMinusReflectivity);
+
+    FragmentCommonData o = (FragmentCommonData)0;
+    o.diffColor = diffColor;
+    o.specColor = specColor;
+    o.oneMinusReflectivity = oneMinusReflectivity;
+    o.smoothness = smoothness;
+    return o;
+}
+
+inline FragmentCommonData MetallicSetup (float4 i_tex)
+{
+    half2 metallicGloss = MetallicGloss(i_tex.xy);
+    half metallic = metallicGloss.x;
+    half smoothness = metallicGloss.y; // this is 1 minus the square root of real roughness m.
+
+    half oneMinusReflectivity;
+    half3 specColor;
+    half3 diffColor = DiffuseAndSpecularFromMetallic (Albedo(i_tex), metallic, /*out*/ specColor, /*out*/ oneMinusReflectivity);
+
+    FragmentCommonData o = (FragmentCommonData)0;
+    o.diffColor = diffColor;
+    o.specColor = specColor;
+    o.oneMinusReflectivity = oneMinusReflectivity;
+    o.smoothness = smoothness;
+    return o;
+}
+
+// parallax transformed texcoord is used to sample occlusion
+inline FragmentCommonData FragmentSetup (inout float4 i_tex, float3 i_eyeVec, half3 i_viewDirForParallax, float4 tangentToWorld[3], float3 i_posWorld)
+{
+    i_tex = Parallax(i_tex, i_viewDirForParallax);
+
+    half alpha = Alpha(i_tex.xy);
+    #if defined(_ALPHATEST_ON)
+        clip (alpha - _Cutoff);
+    #endif
+
+    FragmentCommonData o = UNITY_SETUP_BRDF_INPUT (i_tex);
+    o.normalWorld = PerPixelWorldNormal(i_tex, tangentToWorld);
+    o.eyeVec = NormalizePerPixelNormal(i_eyeVec);
+    o.posWorld = i_posWorld;
+
+    // NOTE: shader relies on pre-multiply alpha-blend (_SrcBlend = One, _DstBlend = OneMinusSrcAlpha)
+    o.diffColor = PreMultiplyAlpha (o.diffColor, alpha, o.oneMinusReflectivity, /*out*/ o.alpha);
+    return o;
+}
+
+inline UnityGI FragmentGI (FragmentCommonData s, half occlusion, half4 i_ambientOrLightmapUV, half atten, UnityLight light, bool reflections)
+{
+    UnityGIInput d;
+    d.light = light;
+    d.worldPos = s.posWorld;
+    d.worldViewDir = -s.eyeVec;
+    d.atten = atten;
+    #if defined(LIGHTMAP_ON) || defined(DYNAMICLIGHTMAP_ON)
+        d.ambient = 0;
+        d.lightmapUV = i_ambientOrLightmapUV;
+    #else
+        d.ambient = i_ambientOrLightmapUV.rgb;
+        d.lightmapUV = 0;
+    #endif
+
+    d.probeHDR[0] = unity_SpecCube0_HDR;
+    d.probeHDR[1] = unity_SpecCube1_HDR;
+    #if defined(UNITY_SPECCUBE_BLENDING) || defined(UNITY_SPECCUBE_BOX_PROJECTION)
+      d.boxMin[0] = unity_SpecCube0_BoxMin; // .w holds lerp value for blending
+    #endif
+    #ifdef UNITY_SPECCUBE_BOX_PROJECTION
+      d.boxMax[0] = unity_SpecCube0_BoxMax;
+      d.probePosition[0] = unity_SpecCube0_ProbePosition;
+      d.boxMax[1] = unity_SpecCube1_BoxMax;
+      d.boxMin[1] = unity_SpecCube1_BoxMin;
+      d.probePosition[1] = unity_SpecCube1_ProbePosition;
+    #endif
+
+    if(reflections)
+    {
+        Unity_GlossyEnvironmentData g = UnityGlossyEnvironmentSetup(s.smoothness, -s.eyeVec, s.normalWorld, s.specColor);
+        // Replace the reflUVW if it has been compute in Vertex shader. Note: the compiler will optimize the calcul in UnityGlossyEnvironmentSetup itself
+        #if UNITY_STANDARD_SIMPLE
+            g.reflUVW = s.reflUVW;
+        #endif
+
+        return UnityGlobalIllumination (d, occlusion, s.normalWorld, g);
+    }
+    else
+    {
+        return UnityGlobalIllumination (d, occlusion, s.normalWorld);
+    }
+}
+
+inline UnityGI FragmentGI (FragmentCommonData s, half occlusion, half4 i_ambientOrLightmapUV, half atten, UnityLight light)
+{
+    return FragmentGI(s, occlusion, i_ambientOrLightmapUV, atten, light, true);
+}
+
+
+//-------------------------------------------------------------------------------------
+half4 OutputForward (half4 output, half alphaFromSurface)
+{
+    #if defined(_ALPHABLEND_ON) || defined(_ALPHAPREMULTIPLY_ON)
+        output.a = alphaFromSurface;
+    #else
+        UNITY_OPAQUE_ALPHA(output.a);
+    #endif
+    return output;
+}
+
+inline half4 VertexGIForward(VertexInput v, float3 posWorld, half3 normalWorld) {
+    half4 ambientOrLightmapUV = 0;
+    // Static lightmaps
+    #ifdef LIGHTMAP_ON
+        ambientOrLightmapUV.xy = v.uv1.xy * unity_LightmapST.xy + unity_LightmapST.zw;
+        ambientOrLightmapUV.zw = 0;
+    // Sample light probe for Dynamic objects only (no static or dynamic lightmaps)
+    #elif UNITY_SHOULD_SAMPLE_SH
+        #ifdef VERTEXLIGHT_ON
+            // Approximated illumination from non-important point lights
+            ambientOrLightmapUV.rgb = Shade4PointLights (
+                unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0,
+                unity_LightColor[0].rgb, unity_LightColor[1].rgb, unity_LightColor[2].rgb, unity_LightColor[3].rgb,
+                unity_4LightAtten0, posWorld, normalWorld);
+        #endif
+
+        ambientOrLightmapUV.rgb = ShadeSHPerVertex (normalWorld, ambientOrLightmapUV.rgb);
+    #endif
+
+    #ifdef DYNAMICLIGHTMAP_ON
+        ambientOrLightmapUV.zw = v.uv2.xy * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw;
+    #endif
+
+    return ambientOrLightmapUV;
+}
+
+// ------------------------------------------------------------------
+//  Base forward pass (directional light, emission, lightmaps, ...)
+
+struct VertexOutputForwardBase
+{
+    UNITY_POSITION(pos);
+    float4 tex                            : TEXCOORD0;
+    float4 eyeVec                         : TEXCOORD1;    // eyeVec.xyz | fogCoord
+    float4 tangentToWorldAndPackedData[3] : TEXCOORD2;    // [3x3:tangentToWorld | 1x3:viewDirForParallax or worldPos]
+    half4 ambientOrLightmapUV             : TEXCOORD5;    // SH or Lightmap UV
+    UNITY_LIGHTING_COORDS(6,7)
+
+    // next ones would not fit into SM2.0 limits, but they are always for SM3.0+
+#if UNITY_REQUIRE_FRAG_WORLDPOS && !UNITY_PACK_WORLDPOS_WITH_TANGENT
+    float3 posWorld                     : TEXCOORD8;
+#endif
+
+    UNITY_VERTEX_INPUT_INSTANCE_ID
+    UNITY_VERTEX_OUTPUT_STEREO
+};
+
+VertexOutputForwardBase vertForwardBase (VertexInput v)
+{
+    OVR_INITIALIZE_VERTEX_FIELDS(v);
+    UNITY_SETUP_INSTANCE_ID(v);
+    VertexOutputForwardBase o;
+    UNITY_INITIALIZE_OUTPUT(VertexOutputForwardBase, o);
+    UNITY_TRANSFER_INSTANCE_ID(v, o);
+    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
+
+    // debug...
+    //v.vertex.xyz += v.normal * 0.03;
+    const OvrVertexData vertexData = OVR_CREATE_VERTEX_DATA(v);
+
+    float4 posWorld = mul(unity_ObjectToWorld, vertexData.position);
+    #if UNITY_REQUIRE_FRAG_WORLDPOS
+        #if UNITY_PACK_WORLDPOS_WITH_TANGENT
+            o.tangentToWorldAndPackedData[0].w = posWorld.x;
+            o.tangentToWorldAndPackedData[1].w = posWorld.y;
+            o.tangentToWorldAndPackedData[2].w = posWorld.z;
+        #else
+            o.posWorld = posWorld.xyz;
+        #endif
+    #endif
+
+    o.pos = UnityObjectToClipPos(vertexData.position);
+
+    o.tex = TexCoords(v);
+    o.eyeVec.xyz = NormalizePerVertexNormal(posWorld.xyz - _WorldSpaceCameraPos);
+    float3 normalWorld = UnityObjectToWorldNormal(vertexData.normal);
+    #if defined(_TANGENT_TO_WORLD) && defined(OVR_VERTEX_HAS_TANGENTS)
+        float4 tangentWorld = float4(UnityObjectToWorldDir(vertexData.tangent.xyz), vertexData.tangent.w);
+
+        float3x3 tangentToWorld = CreateTangentToWorldPerVertex(normalWorld, tangentWorld.xyz, tangentWorld.w);
+        o.tangentToWorldAndPackedData[0].xyz = tangentToWorld[0];
+        o.tangentToWorldAndPackedData[1].xyz = tangentToWorld[1];
+        o.tangentToWorldAndPackedData[2].xyz = tangentToWorld[2];
+    #else
+        o.tangentToWorldAndPackedData[0].xyz = 0;
+        o.tangentToWorldAndPackedData[1].xyz = 0;
+        o.tangentToWorldAndPackedData[2].xyz = normalWorld;
+    #endif
+
+    //We need this for shadow receiving
+    UNITY_TRANSFER_LIGHTING(o, v.uv1);
+
+    o.ambientOrLightmapUV = VertexGIForward(v, posWorld, normalWorld);
+
+    #ifdef _PARALLAXMAP
+        TANGENT_SPACE_ROTATION;
+        half3 viewDirForParallax = mul (rotation, ObjSpaceViewDir(vertexData.position));
+        o.tangentToWorldAndPackedData[0].w = viewDirForParallax.x;
+        o.tangentToWorldAndPackedData[1].w = viewDirForParallax.y;
+        o.tangentToWorldAndPackedData[2].w = viewDirForParallax.z;
+    #endif
+
+    UNITY_TRANSFER_FOG_COMBINED_WITH_EYE_VEC(o,o.pos);
+    return o;
+}
+
+half4 fragForwardBaseInternal (VertexOutputForwardBase i)
+{
+    UNITY_APPLY_DITHER_CROSSFADE(i.pos.xy);
+
+    FRAGMENT_SETUP(s)
+
+    UNITY_SETUP_INSTANCE_ID(i);
+    UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i);
+
+    UnityLight mainLight = MainLight ();
+    UNITY_LIGHT_ATTENUATION(atten, i, s.posWorld);
+
+    half occlusion = Occlusion(i.tex.xy);
+    UnityGI gi = FragmentGI (s, occlusion, i.ambientOrLightmapUV, atten, mainLight);
+
+    half4 c = UNITY_BRDF_PBS (s.diffColor, s.specColor, s.oneMinusReflectivity, s.smoothness, s.normalWorld, -s.eyeVec, gi.light, gi.indirect);
+    c.rgb += Emission(i.tex.xy);
+
+    UNITY_EXTRACT_FOG_FROM_EYE_VEC(i);
+    UNITY_APPLY_FOG(_unity_fogCoord, c.rgb);
+
+    // debug...
+    //c.r = 1;
+
+    return OutputForward (c, s.alpha);
+}
+
+half4 fragForwardBase (VertexOutputForwardBase i) : SV_Target   // backward compatibility (this used to be the fragment entry function)
+{
+    return fragForwardBaseInternal(i);
+}
+
+// ------------------------------------------------------------------
+//  Additive forward pass (one light per pass)
+
+struct VertexOutputForwardAdd
+{
+    UNITY_POSITION(pos);
+    float4 tex                          : TEXCOORD0;
+    float4 eyeVec                       : TEXCOORD1;    // eyeVec.xyz | fogCoord
+    float4 tangentToWorldAndLightDir[3] : TEXCOORD2;    // [3x3:tangentToWorld | 1x3:lightDir]
+    float3 posWorld                     : TEXCOORD5;
+    UNITY_LIGHTING_COORDS(6, 7)
+
+    // next ones would not fit into SM2.0 limits, but they are always for SM3.0+
+#if defined(_PARALLAXMAP)
+    half3 viewDirForParallax            : TEXCOORD8;
+#endif
+
+    UNITY_VERTEX_OUTPUT_STEREO
+};
+
+VertexOutputForwardAdd vertForwardAdd (VertexInput v)
+{
+    OVR_INITIALIZE_VERTEX_FIELDS(v);
+    UNITY_SETUP_INSTANCE_ID(v);
+    VertexOutputForwardAdd o;
+    UNITY_INITIALIZE_OUTPUT(VertexOutputForwardAdd, o);
+    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
+
+    const OvrVertexData vertexData = OVR_CREATE_VERTEX_DATA(v);
+
+    float4 posWorld = mul(unity_ObjectToWorld, vertexData.position);
+    o.pos = UnityObjectToClipPos(vertexData.position);
+
+    o.tex = TexCoords(v);
+    o.eyeVec.xyz = NormalizePerVertexNormal(posWorld.xyz - _WorldSpaceCameraPos);
+    o.posWorld = posWorld.xyz;
+    float3 normalWorld = UnityObjectToWorldNormal(vertexData.normal);
+    #if defined(_TANGENT_TO_WORLD) && defined(OVR_VERTEX_HAS_TANGENTS)
+        float4 tangentWorld = float4(UnityObjectToWorldDir(vertexData.tangent.xyz), vertexData.tangent.w);
+
+        float3x3 tangentToWorld = CreateTangentToWorldPerVertex(normalWorld, tangentWorld.xyz, tangentWorld.w);
+        o.tangentToWorldAndLightDir[0].xyz = tangentToWorld[0];
+        o.tangentToWorldAndLightDir[1].xyz = tangentToWorld[1];
+        o.tangentToWorldAndLightDir[2].xyz = tangentToWorld[2];
+    #else
+        o.tangentToWorldAndLightDir[0].xyz = 0;
+        o.tangentToWorldAndLightDir[1].xyz = 0;
+        o.tangentToWorldAndLightDir[2].xyz = normalWorld;
+    #endif
+    //We need this for shadow receiving and lighting
+    UNITY_TRANSFER_LIGHTING(o, v.uv1);
+
+    float3 lightDir = _WorldSpaceLightPos0.xyz - posWorld.xyz * _WorldSpaceLightPos0.w;
+    #ifndef USING_DIRECTIONAL_LIGHT
+        lightDir = NormalizePerVertexNormal(lightDir);
+    #endif
+    o.tangentToWorldAndLightDir[0].w = lightDir.x;
+    o.tangentToWorldAndLightDir[1].w = lightDir.y;
+    o.tangentToWorldAndLightDir[2].w = lightDir.z;
+
+    #ifdef _PARALLAXMAP
+        TANGENT_SPACE_ROTATION;
+        o.viewDirForParallax = mul (rotation, ObjSpaceViewDir(vertexData.position));
+    #endif
+
+    UNITY_TRANSFER_FOG_COMBINED_WITH_EYE_VEC(o, o.pos);
+    return o;
+}
+
+half4 fragForwardAddInternal (VertexOutputForwardAdd i)
+{
+    UNITY_APPLY_DITHER_CROSSFADE(i.pos.xy);
+
+    UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i);
+
+    FRAGMENT_SETUP_FWDADD(s)
+
+    UNITY_LIGHT_ATTENUATION(atten, i, s.posWorld)
+    UnityLight light = AdditiveLight (IN_LIGHTDIR_FWDADD(i), atten);
+    UnityIndirect noIndirect = ZeroIndirect ();
+
+    half4 c = UNITY_BRDF_PBS (s.diffColor, s.specColor, s.oneMinusReflectivity, s.smoothness, s.normalWorld, -s.eyeVec, light, noIndirect);
+
+    UNITY_EXTRACT_FOG_FROM_EYE_VEC(i);
+    UNITY_APPLY_FOG_COLOR(_unity_fogCoord, c.rgb, half4(0,0,0,0)); // fog towards black in additive pass
+    return OutputForward (c, s.alpha);
+}
+
+half4 fragForwardAdd (VertexOutputForwardAdd i) : SV_Target     // backward compatibility (this used to be the fragment entry function)
+{
+    return fragForwardAddInternal(i);
+}
+
+// ------------------------------------------------------------------
+//  Deferred pass
+
+struct VertexOutputDeferred
+{
+    UNITY_POSITION(pos);
+    float4 tex                            : TEXCOORD0;
+    float3 eyeVec                         : TEXCOORD1;
+    float4 tangentToWorldAndPackedData[3] : TEXCOORD2;    // [3x3:tangentToWorld | 1x3:viewDirForParallax or worldPos]
+    half4 ambientOrLightmapUV             : TEXCOORD5;    // SH or Lightmap UVs
+
+    #if UNITY_REQUIRE_FRAG_WORLDPOS && !UNITY_PACK_WORLDPOS_WITH_TANGENT
+        float3 posWorld                     : TEXCOORD6;
+    #endif
+
+    UNITY_VERTEX_INPUT_INSTANCE_ID
+    UNITY_VERTEX_OUTPUT_STEREO
+};
+
+
+VertexOutputDeferred vertDeferred (VertexInput v)
+{
+    OVR_INITIALIZE_VERTEX_FIELDS(v);
+    UNITY_SETUP_INSTANCE_ID(v);
+    VertexOutputDeferred o;
+    UNITY_INITIALIZE_OUTPUT(VertexOutputDeferred, o);
+    UNITY_TRANSFER_INSTANCE_ID(v, o);
+    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
+
+    const OvrVertexData vertexData = OVR_CREATE_VERTEX_DATA(v);
+
+    float4 posWorld = mul(unity_ObjectToWorld, vertexData.position);
+    #if UNITY_REQUIRE_FRAG_WORLDPOS
+        #if UNITY_PACK_WORLDPOS_WITH_TANGENT
+            o.tangentToWorldAndPackedData[0].w = posWorld.x;
+            o.tangentToWorldAndPackedData[1].w = posWorld.y;
+            o.tangentToWorldAndPackedData[2].w = posWorld.z;
+        #else
+            o.posWorld = posWorld.xyz;
+        #endif
+    #endif
+    o.pos = UnityObjectToClipPos(vertexData.position);
+
+    o.tex = TexCoords(v);
+    o.eyeVec = NormalizePerVertexNormal(posWorld.xyz - _WorldSpaceCameraPos);
+    float3 normalWorld = UnityObjectToWorldNormal(vertexData.normal);
+    #if defined(_TANGENT_TO_WORLD) && defined(OVR_VERTEX_HAS_TANGENTS)
+        float4 tangentWorld = float4(UnityObjectToWorldDir(vertexData.tangent.xyz), vertexData.tangent.w);
+
+        float3x3 tangentToWorld = CreateTangentToWorldPerVertex(normalWorld, tangentWorld.xyz, tangentWorld.w);
+        o.tangentToWorldAndPackedData[0].xyz = tangentToWorld[0];
+        o.tangentToWorldAndPackedData[1].xyz = tangentToWorld[1];
+        o.tangentToWorldAndPackedData[2].xyz = tangentToWorld[2];
+    #else
+        o.tangentToWorldAndPackedData[0].xyz = 0;
+        o.tangentToWorldAndPackedData[1].xyz = 0;
+        o.tangentToWorldAndPackedData[2].xyz = normalWorld;
+    #endif
+
+    o.ambientOrLightmapUV = 0;
+    #ifdef LIGHTMAP_ON
+        o.ambientOrLightmapUV.xy = v.uv1.xy * unity_LightmapST.xy + unity_LightmapST.zw;
+    #elif UNITY_SHOULD_SAMPLE_SH
+        o.ambientOrLightmapUV.rgb = ShadeSHPerVertex (normalWorld, o.ambientOrLightmapUV.rgb);
+    #endif
+    #ifdef DYNAMICLIGHTMAP_ON
+        o.ambientOrLightmapUV.zw = v.uv2.xy * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw;
+    #endif
+
+    #ifdef _PARALLAXMAP
+        TANGENT_SPACE_ROTATION;
+        half3 viewDirForParallax = mul (rotation, ObjSpaceViewDir(vertexData.position));
+        o.tangentToWorldAndPackedData[0].w = viewDirForParallax.x;
+        o.tangentToWorldAndPackedData[1].w = viewDirForParallax.y;
+        o.tangentToWorldAndPackedData[2].w = viewDirForParallax.z;
+    #endif
+
+    return o;
+}
+
+void fragDeferred (
+    VertexOutputDeferred i,
+    out half4 outGBuffer0 : SV_Target0,
+    out half4 outGBuffer1 : SV_Target1,
+    out half4 outGBuffer2 : SV_Target2,
+    out half4 outEmission : SV_Target3          // RT3: emission (rgb), --unused-- (a)
+#if defined(SHADOWS_SHADOWMASK) && (UNITY_ALLOWED_MRT_COUNT > 4)
+    ,out half4 outShadowMask : SV_Target4       // RT4: shadowmask (rgba)
+#endif
+)
+{
+    #if (SHADER_TARGET < 30)
+        outGBuffer0 = 1;
+        outGBuffer1 = 1;
+        outGBuffer2 = 0;
+        outEmission = 0;
+        #if defined(SHADOWS_SHADOWMASK) && (UNITY_ALLOWED_MRT_COUNT > 4)
+            outShadowMask = 1;
+        #endif
+        return;
+    #endif
+
+    UNITY_APPLY_DITHER_CROSSFADE(i.pos.xy);
+
+    FRAGMENT_SETUP(s)
+    UNITY_SETUP_INSTANCE_ID(i);
+
+    // no analytic lights in this pass
+    UnityLight dummyLight = DummyLight ();
+    half atten = 1;
+
+    // only GI
+    half occlusion = Occlusion(i.tex.xy);
+#if UNITY_ENABLE_REFLECTION_BUFFERS
+    bool sampleReflectionsInDeferred = false;
+#else
+    bool sampleReflectionsInDeferred = true;
+#endif
+
+    UnityGI gi = FragmentGI (s, occlusion, i.ambientOrLightmapUV, atten, dummyLight, sampleReflectionsInDeferred);
+
+    half3 emissiveColor = UNITY_BRDF_PBS (s.diffColor, s.specColor, s.oneMinusReflectivity, s.smoothness, s.normalWorld, -s.eyeVec, gi.light, gi.indirect).rgb;
+
+    #ifdef _EMISSION
+        emissiveColor += Emission (i.tex.xy);
+    #endif
+
+    #ifndef UNITY_HDR_ON
+        emissiveColor.rgb = exp2(-emissiveColor.rgb);
+    #endif
+
+    UnityStandardData data;
+    data.diffuseColor   = s.diffColor;
+    data.occlusion      = occlusion;
+    data.specularColor  = s.specColor;
+    data.smoothness     = s.smoothness;
+    data.normalWorld    = s.normalWorld;
+
+    UnityStandardDataToGbuffer(data, outGBuffer0, outGBuffer1, outGBuffer2);
+
+    // Emissive lighting buffer
+    outEmission = half4(emissiveColor, 1);
+
+    // Baked direct lighting occlusion if any
+    #if defined(SHADOWS_SHADOWMASK) && (UNITY_ALLOWED_MRT_COUNT > 4)
+        outShadowMask = UnityGetRawBakedOcclusions(i.ambientOrLightmapUV.xy, IN_WORLDPOS(i));
+    #endif
+}
+
+
+//
+// Old FragmentGI signature. Kept only for backward compatibility and will be removed soon
+//
+
+inline UnityGI FragmentGI(
+    float3 posWorld,
+    half occlusion, half4 i_ambientOrLightmapUV, half atten, half smoothness, half3 normalWorld, half3 eyeVec,
+    UnityLight light,
+    bool reflections)
+{
+    // we init only fields actually used
+    FragmentCommonData s = (FragmentCommonData)0;
+    s.smoothness = smoothness;
+    s.normalWorld = normalWorld;
+    s.eyeVec = eyeVec;
+    s.posWorld = posWorld;
+    return FragmentGI(s, occlusion, i_ambientOrLightmapUV, atten, light, reflections);
+}
+inline UnityGI FragmentGI (
+    float3 posWorld,
+    half occlusion, half4 i_ambientOrLightmapUV, half atten, half smoothness, half3 normalWorld, half3 eyeVec,
+    UnityLight light)
+{
+    return FragmentGI (posWorld, occlusion, i_ambientOrLightmapUV, atten, smoothness, normalWorld, eyeVec, light, true);
+}
+
+#endif // UNITY_STANDARD_CORE_INCLUDED
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityStandard/AvatarStandardCore.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityStandard/AvatarStandardCore.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..8611d85d8305ec55c4527a64a98e250d9079f9b5
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityStandard/AvatarStandardCore.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 9277583bc5a4b194e92c9bdb23b4c140
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityStandard/AvatarStandardCoreForward.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityStandard/AvatarStandardCoreForward.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..bbed7bd9bc3cc48a11990b8f2ee5c2c3fefea99f
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityStandard/AvatarStandardCoreForward.cginc
@@ -0,0 +1,26 @@
+// Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)
+
+#ifndef AVATAR_STANDARD_CORE_FORWARD_INCLUDED
+#define AVATAR_STANDARD_CORE_FORWARD_INCLUDED
+
+#if defined(AVATAR_NO_FULL_STANDARD_SHADER)
+#   define AVATAR_STANDARD_SIMPLE 1
+#endif
+
+#include "UnityStandardConfig.cginc"
+
+#if AVATAR_STANDARD_SIMPLE
+    #include "AvatarStandardCoreForwardSimple.cginc"
+    VertexOutputBaseSimple vertBase (VertexInput v) { return vertForwardBaseSimple(v); }
+    VertexOutputForwardAddSimple vertAdd (VertexInput v) { return vertForwardAddSimple(v); }
+    half4 fragBase (VertexOutputBaseSimple i) : SV_Target { return fragForwardBaseSimpleInternal(i); }
+    half4 fragAdd (VertexOutputForwardAddSimple i) : SV_Target { return fragForwardAddSimpleInternal(i); }
+#else
+    #include "AvatarStandardCore.cginc"
+    VertexOutputForwardBase vertBase (VertexInput v) { return vertForwardBase(v); }
+    VertexOutputForwardAdd vertAdd (VertexInput v) { return vertForwardAdd(v); }
+    half4 fragBase (VertexOutputForwardBase i) : SV_Target { return fragForwardBaseInternal(i); }
+    half4 fragAdd (VertexOutputForwardAdd i) : SV_Target { return fragForwardAddInternal(i); }
+#endif
+
+#endif // AVATAR_STANDARD_CORE_FORWARD_INCLUDED
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityStandard/AvatarStandardCoreForward.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityStandard/AvatarStandardCoreForward.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..67d7869e02c8d913aa81b4efc05be149abaf0674
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityStandard/AvatarStandardCoreForward.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 4e8ba6e744aef4548a84480ba6ab67b7
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityStandard/AvatarStandardCoreForwardSimple.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityStandard/AvatarStandardCoreForwardSimple.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..717c6344104a7bc41b0c2e9bc87cd711e1101d92
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityStandard/AvatarStandardCoreForwardSimple.cginc
@@ -0,0 +1,396 @@
+// Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)
+
+#ifndef AVATAR_STANDARD_CORE_FORWARD_SIMPLE_INCLUDED
+#define AVATAR_STANDARD_CORE_FORWARD_SIMPLE_INCLUDED
+
+#include "AvatarStandardCore.cginc"
+
+//  Does not support: _PARALLAXMAP, DIRLIGHTMAP_COMBINED
+#define GLOSSMAP (defined(_SPECGLOSSMAP) || defined(_METALLICGLOSSMAP))
+
+#ifndef SPECULAR_HIGHLIGHTS
+    #define SPECULAR_HIGHLIGHTS (!defined(_SPECULAR_HIGHLIGHTS_OFF))
+#endif
+
+struct VertexOutputBaseSimple
+{
+    UNITY_POSITION(pos);
+    float4 tex                          : TEXCOORD0;
+    half4 eyeVec                        : TEXCOORD1; // w: grazingTerm
+
+    half4 ambientOrLightmapUV           : TEXCOORD2; // SH or Lightmap UV
+    SHADOW_COORDS(3)
+    UNITY_FOG_COORDS_PACKED(4, half4) // x: fogCoord, yzw: reflectVec
+
+    half4 normalWorld                   : TEXCOORD5; // w: fresnelTerm
+
+#ifdef _NORMALMAP
+    half3 tangentSpaceLightDir          : TEXCOORD6;
+    #if SPECULAR_HIGHLIGHTS
+        half3 tangentSpaceEyeVec        : TEXCOORD7;
+    #endif
+#endif
+#if UNITY_REQUIRE_FRAG_WORLDPOS
+    float3 posWorld                     : TEXCOORD8;
+#endif
+
+    UNITY_VERTEX_OUTPUT_STEREO
+};
+
+// UNIFORM_REFLECTIVITY(): workaround to get (uniform) reflecivity based on UNITY_SETUP_BRDF_INPUT
+half MetallicSetup_Reflectivity()
+{
+    return 1.0h - OneMinusReflectivityFromMetallic(_Metallic);
+}
+
+half SpecularSetup_Reflectivity()
+{
+    return SpecularStrength(_SpecColor.rgb);
+}
+
+half RoughnessSetup_Reflectivity()
+{
+    return MetallicSetup_Reflectivity();
+}
+
+#define JOIN2(a, b) a##b
+#define JOIN(a, b) JOIN2(a,b)
+#define UNIFORM_REFLECTIVITY JOIN(UNITY_SETUP_BRDF_INPUT, _Reflectivity)
+
+
+#ifdef _NORMALMAP
+
+half3 TransformToTangentSpace(half3 tangent, half3 binormal, half3 normal, half3 v)
+{
+    // Mali400 shader compiler prefers explicit dot product over using a half3x3 matrix
+    return half3(dot(tangent, v), dot(binormal, v), dot(normal, v));
+}
+
+void TangentSpaceLightingInput(half3 normalWorld, half4 vTangent, half3 lightDirWorld, half3 eyeVecWorld, out half3 tangentSpaceLightDir, out half3 tangentSpaceEyeVec)
+{
+    half3 tangentWorld = UnityObjectToWorldDir(vTangent.xyz);
+    half sign = half(vTangent.w) * half(unity_WorldTransformParams.w);
+    half3 binormalWorld = cross(normalWorld, tangentWorld) * sign;
+    tangentSpaceLightDir = TransformToTangentSpace(tangentWorld, binormalWorld, normalWorld, lightDirWorld);
+    #if SPECULAR_HIGHLIGHTS
+        tangentSpaceEyeVec = normalize(TransformToTangentSpace(tangentWorld, binormalWorld, normalWorld, eyeVecWorld));
+    #else
+        tangentSpaceEyeVec = 0;
+    #endif
+}
+
+#endif // _NORMALMAP
+
+VertexOutputBaseSimple vertForwardBaseSimple (VertexInput v)
+{
+    UNITY_SETUP_INSTANCE_ID(v);
+    VertexOutputBaseSimple o;
+    UNITY_INITIALIZE_OUTPUT(VertexOutputBaseSimple, o);
+    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
+
+    // debug...
+    //v.vertex.xyz += v.normal * 0.03;
+
+#if defined(OVR_VERTEX_FETCH_TEXTURE) || defined(OVR_VERTEX_FETCH_TEXTURE_UNORM)
+    v.vertex = AvatarGpuSkinningVertexPositions(v.vid);
+    v.normal = AvatarGpuSkinningVertexNormals(v.vid);
+    #if defined(OVR_VERTEX_HAS_TANGENTS)
+        v.tangent = AvatarGpuSkinningVertexTangents(v.vid);
+    #endif
+#endif
+
+    float4 posWorld = mul(unity_ObjectToWorld, v.vertex);
+    o.pos = UnityObjectToClipPos(v.vertex);
+    o.tex = TexCoords(v);
+
+    half3 eyeVec = normalize(posWorld.xyz - _WorldSpaceCameraPos);
+    half3 normalWorld = UnityObjectToWorldNormal(v.normal);
+
+    o.normalWorld.xyz = normalWorld;
+    o.eyeVec.xyz = eyeVec;
+
+    #ifdef _NORMALMAP
+        half3 tangentSpaceEyeVec;
+        TangentSpaceLightingInput(normalWorld, v.tangent, _WorldSpaceLightPos0.xyz, eyeVec, o.tangentSpaceLightDir, tangentSpaceEyeVec);
+        #if SPECULAR_HIGHLIGHTS
+            o.tangentSpaceEyeVec = tangentSpaceEyeVec;
+        #endif
+    #endif
+
+    //We need this for shadow receiving
+    TRANSFER_SHADOW(o);
+
+    o.ambientOrLightmapUV = VertexGIForward(v, posWorld, normalWorld);
+
+    o.fogCoord.yzw = reflect(eyeVec, normalWorld);
+
+    o.normalWorld.w = Pow4(1 - saturate(dot(normalWorld, -eyeVec))); // fresnel term
+    #if !GLOSSMAP
+        o.eyeVec.w = saturate(_Glossiness + UNIFORM_REFLECTIVITY()); // grazing term
+    #endif
+
+    UNITY_TRANSFER_FOG(o, o.pos);
+    return o;
+}
+
+
+FragmentCommonData FragmentSetupSimple(VertexOutputBaseSimple i)
+{
+    half alpha = Alpha(i.tex.xy);
+    #if defined(_ALPHATEST_ON)
+        clip (alpha - _Cutoff);
+    #endif
+
+    FragmentCommonData s = UNITY_SETUP_BRDF_INPUT (i.tex);
+
+    // NOTE: shader relies on pre-multiply alpha-blend (_SrcBlend = One, _DstBlend = OneMinusSrcAlpha)
+    s.diffColor = PreMultiplyAlpha (s.diffColor, alpha, s.oneMinusReflectivity, /*out*/ s.alpha);
+
+    s.normalWorld = i.normalWorld.xyz;
+    s.eyeVec = i.eyeVec.xyz;
+    s.posWorld = IN_WORLDPOS(i);
+    s.reflUVW = i.fogCoord.yzw;
+
+    #ifdef _NORMALMAP
+        s.tangentSpaceNormal =  NormalInTangentSpace(i.tex);
+    #else
+        s.tangentSpaceNormal =  0;
+    #endif
+
+    return s;
+}
+
+UnityLight MainLightSimple(VertexOutputBaseSimple i, FragmentCommonData s)
+{
+    UnityLight mainLight = MainLight();
+    return mainLight;
+}
+
+half PerVertexGrazingTerm(VertexOutputBaseSimple i, FragmentCommonData s)
+{
+    #if GLOSSMAP
+        return saturate(s.smoothness + (1-s.oneMinusReflectivity));
+    #else
+        return i.eyeVec.w;
+    #endif
+}
+
+half PerVertexFresnelTerm(VertexOutputBaseSimple i)
+{
+    return i.normalWorld.w;
+}
+
+#if !SPECULAR_HIGHLIGHTS
+#   define REFLECTVEC_FOR_SPECULAR(i, s) half3(0, 0, 0)
+#elif defined(_NORMALMAP)
+#   define REFLECTVEC_FOR_SPECULAR(i, s) reflect(i.tangentSpaceEyeVec, s.tangentSpaceNormal)
+#else
+#   define REFLECTVEC_FOR_SPECULAR(i, s) s.reflUVW
+#endif
+
+half3 LightDirForSpecular(VertexOutputBaseSimple i, UnityLight mainLight)
+{
+    #if SPECULAR_HIGHLIGHTS && defined(_NORMALMAP)
+        return i.tangentSpaceLightDir;
+    #else
+        return mainLight.dir;
+    #endif
+}
+
+half3 BRDF3DirectSimple(half3 diffColor, half3 specColor, half smoothness, half rl)
+{
+    #if SPECULAR_HIGHLIGHTS
+        return BRDF3_Direct(diffColor, specColor, Pow4(rl), smoothness);
+    #else
+        return diffColor;
+    #endif
+}
+
+half4 fragForwardBaseSimpleInternal (VertexOutputBaseSimple i)
+{
+    UNITY_APPLY_DITHER_CROSSFADE(i.pos.xy);
+
+    FragmentCommonData s = FragmentSetupSimple(i);
+
+    UnityLight mainLight = MainLightSimple(i, s);
+
+    #if !defined(LIGHTMAP_ON) && defined(_NORMALMAP)
+    half ndotl = saturate(dot(s.tangentSpaceNormal, i.tangentSpaceLightDir));
+    #else
+    half ndotl = saturate(dot(s.normalWorld, mainLight.dir));
+    #endif
+
+    //we can't have worldpos here (not enough interpolator on SM 2.0) so no shadow fade in that case.
+    half shadowMaskAttenuation = UnitySampleBakedOcclusion(i.ambientOrLightmapUV, 0);
+    half realtimeShadowAttenuation = SHADOW_ATTENUATION(i);
+    half atten = UnityMixRealtimeAndBakedShadows(realtimeShadowAttenuation, shadowMaskAttenuation, 0);
+
+    half occlusion = Occlusion(i.tex.xy);
+    half rl = dot(REFLECTVEC_FOR_SPECULAR(i, s), LightDirForSpecular(i, mainLight));
+
+    UnityGI gi = FragmentGI (s, occlusion, i.ambientOrLightmapUV, atten, mainLight);
+    half3 attenuatedLightColor = gi.light.color * ndotl;
+
+    half3 c = BRDF3_Indirect(s.diffColor, s.specColor, gi.indirect, PerVertexGrazingTerm(i, s), PerVertexFresnelTerm(i));
+    c += BRDF3DirectSimple(s.diffColor, s.specColor, s.smoothness, rl) * attenuatedLightColor;
+    c += Emission(i.tex.xy);
+
+    UNITY_APPLY_FOG(i.fogCoord, c);
+
+    // debug...
+    //c.g = 1;
+
+    return OutputForward (half4(c, 1), s.alpha);
+}
+
+half4 fragForwardBaseSimple (VertexOutputBaseSimple i) : SV_Target  // backward compatibility (this used to be the fragment entry function)
+{
+    return fragForwardBaseSimpleInternal(i);
+}
+
+struct VertexOutputForwardAddSimple
+{
+    UNITY_POSITION(pos);
+    float4 tex                          : TEXCOORD0;
+    float3 posWorld                     : TEXCOORD1;
+
+#if !defined(_NORMALMAP) && SPECULAR_HIGHLIGHTS
+    UNITY_FOG_COORDS_PACKED(2, half4) // x: fogCoord, yzw: reflectVec
+#else
+    UNITY_FOG_COORDS_PACKED(2, half1)
+#endif
+
+    half3 lightDir                      : TEXCOORD3;
+
+#if defined(_NORMALMAP)
+    #if SPECULAR_HIGHLIGHTS
+        half3 tangentSpaceEyeVec        : TEXCOORD4;
+    #endif
+#else
+    half3 normalWorld                   : TEXCOORD4;
+#endif
+
+    UNITY_LIGHTING_COORDS(5, 6)
+
+    UNITY_VERTEX_OUTPUT_STEREO
+};
+
+VertexOutputForwardAddSimple vertForwardAddSimple (VertexInput v)
+{
+    VertexOutputForwardAddSimple o;
+    UNITY_SETUP_INSTANCE_ID(v);
+    UNITY_INITIALIZE_OUTPUT(VertexOutputForwardAddSimple, o);
+    UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
+
+#if defined(OVR_VERTEX_FETCH_TEXTURE) || defined(OVR_VERTEX_FETCH_TEXTURE_UNORM)
+    v.vertex = AvatarGpuSkinningVertexPositions(v.vid);
+    v.normal = AvatarGpuSkinningVertexNormals(v.vid);
+    #if defined(OVR_VERTEX_HAS_TANGENTS)
+        v.tangent = AvatarGpuSkinningVertexTangents(v.vid);
+    #endif
+#endif
+
+    float4 posWorld = mul(unity_ObjectToWorld, v.vertex);
+    o.pos = UnityObjectToClipPos(v.vertex);
+    o.tex = TexCoords(v);
+    o.posWorld = posWorld.xyz;
+
+    //We need this for shadow receiving and lighting
+    UNITY_TRANSFER_LIGHTING(o, v.uv1);
+
+    half3 lightDir = _WorldSpaceLightPos0.xyz - posWorld.xyz * _WorldSpaceLightPos0.w;
+    #ifndef USING_DIRECTIONAL_LIGHT
+        lightDir = NormalizePerVertexNormal(lightDir);
+    #endif
+
+    #if SPECULAR_HIGHLIGHTS
+        half3 eyeVec = normalize(posWorld.xyz - _WorldSpaceCameraPos);
+    #endif
+
+    half3 normalWorld = UnityObjectToWorldNormal(v.normal);
+
+    #ifdef _NORMALMAP
+        #if SPECULAR_HIGHLIGHTS
+            TangentSpaceLightingInput(normalWorld, v.tangent, lightDir, eyeVec, o.lightDir, o.tangentSpaceEyeVec);
+        #else
+            half3 ignore;
+            TangentSpaceLightingInput(normalWorld, v.tangent, lightDir, 0, o.lightDir, ignore);
+        #endif
+    #else
+        o.lightDir = lightDir;
+        o.normalWorld = normalWorld;
+        #if SPECULAR_HIGHLIGHTS
+            o.fogCoord.yzw = reflect(eyeVec, normalWorld);
+        #endif
+    #endif
+
+    UNITY_TRANSFER_FOG(o,o.pos);
+    return o;
+}
+
+FragmentCommonData FragmentSetupSimpleAdd(VertexOutputForwardAddSimple i)
+{
+    half alpha = Alpha(i.tex.xy);
+    #if defined(_ALPHATEST_ON)
+        clip (alpha - _Cutoff);
+    #endif
+
+    FragmentCommonData s = UNITY_SETUP_BRDF_INPUT (i.tex);
+
+    // NOTE: shader relies on pre-multiply alpha-blend (_SrcBlend = One, _DstBlend = OneMinusSrcAlpha)
+    s.diffColor = PreMultiplyAlpha (s.diffColor, alpha, s.oneMinusReflectivity, /*out*/ s.alpha);
+
+    s.eyeVec = 0;
+    s.posWorld = i.posWorld;
+
+    #ifdef _NORMALMAP
+        s.tangentSpaceNormal = NormalInTangentSpace(i.tex);
+        s.normalWorld = 0;
+    #else
+        s.tangentSpaceNormal = 0;
+        s.normalWorld = i.normalWorld;
+    #endif
+
+    #if SPECULAR_HIGHLIGHTS && !defined(_NORMALMAP)
+        s.reflUVW = i.fogCoord.yzw;
+    #else
+        s.reflUVW = 0;
+    #endif
+
+    return s;
+}
+
+half3 LightSpaceNormal(VertexOutputForwardAddSimple i, FragmentCommonData s)
+{
+    #ifdef _NORMALMAP
+        return s.tangentSpaceNormal;
+    #else
+        return i.normalWorld;
+    #endif
+}
+
+half4 fragForwardAddSimpleInternal (VertexOutputForwardAddSimple i)
+{
+    UNITY_APPLY_DITHER_CROSSFADE(i.pos.xy);
+
+    FragmentCommonData s = FragmentSetupSimpleAdd(i);
+
+    half3 c = BRDF3DirectSimple(s.diffColor, s.specColor, s.smoothness, dot(REFLECTVEC_FOR_SPECULAR(i, s), i.lightDir));
+
+    #if SPECULAR_HIGHLIGHTS // else diffColor has premultiplied light color
+        c *= _LightColor0.rgb;
+    #endif
+
+    UNITY_LIGHT_ATTENUATION(atten, i, s.posWorld)
+    c *= atten * saturate(dot(LightSpaceNormal(i, s), i.lightDir));
+
+    UNITY_APPLY_FOG_COLOR(i.fogCoord, c.rgb, half4(0,0,0,0)); // fog towards black in additive pass
+    return OutputForward (half4(c, 1), s.alpha);
+}
+
+half4 fragForwardAddSimple (VertexOutputForwardAddSimple i) : SV_Target // backward compatibility (this used to be the fragment entry function)
+{
+    return fragForwardAddSimpleInternal(i);
+}
+
+#endif // UNITY_STANDARD_CORE_FORWARD_SIMPLE_INCLUDED
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityStandard/AvatarStandardCoreForwardSimple.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityStandard/AvatarStandardCoreForwardSimple.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..eca9f087b8eb519129c6bbd67586afe9941d4b36
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityStandard/AvatarStandardCoreForwardSimple.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 5c960efd5de278f40a4bd45a1edca8b0
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityStandard/AvatarStandardInput.cginc b/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityStandard/AvatarStandardInput.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..f12e0eb032a68ba44864d2d9cfad876bd26d3d78
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityStandard/AvatarStandardInput.cginc
@@ -0,0 +1,251 @@
+// Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)
+
+#ifndef UNITY_STANDARD_INPUT_INCLUDED
+#define UNITY_STANDARD_INPUT_INCLUDED
+
+#include "UnityCG.cginc"
+#include "UnityStandardConfig.cginc"
+#include "UnityPBSLighting.cginc" // TBD: remove
+#include "UnityStandardUtils.cginc"
+
+//---------------------------------------
+// Directional lightmaps & Parallax require tangent space too
+#if (_NORMALMAP || DIRLIGHTMAP_COMBINED || _PARALLAXMAP)
+    #define _TANGENT_TO_WORLD 1
+#endif
+
+#if (_DETAIL_MULX2 || _DETAIL_MUL || _DETAIL_ADD || _DETAIL_LERP)
+    #define _DETAIL 1
+#endif
+
+//---------------------------------------
+half4       _Color;
+half        _Cutoff;
+
+sampler2D   _MainTex;
+float4      _MainTex_ST;
+
+sampler2D   _DetailAlbedoMap;
+float4      _DetailAlbedoMap_ST;
+
+sampler2D   _BumpMap;
+half        _BumpScale;
+
+sampler2D   _DetailMask;
+sampler2D   _DetailNormalMap;
+half        _DetailNormalMapScale;
+
+sampler2D   _SpecGlossMap;
+sampler2D   _MetallicGlossMap;
+half        _Metallic;
+float       _Glossiness;
+float       _GlossMapScale;
+
+sampler2D   _OcclusionMap;
+half        _OcclusionStrength;
+
+sampler2D   _ParallaxMap;
+half        _Parallax;
+half        _UVSec;
+
+half4       _EmissionColor;
+sampler2D   _EmissionMap;
+
+//-------------------------------------------------------------------------------------
+// Input functions
+
+struct VertexInput
+{
+    OVR_REQUIRED_VERTEX_FIELDS
+    float2 uv0      : OVR_FIRST_AVAILABLE_VERTEX_TEXCOORD_SEMANTIC;
+    float2 uv1      : OVR_SECOND_AVAILABLE_VERTEX_TEXCOORD_SEMANTIC;
+#if defined(DYNAMICLIGHTMAP_ON) || defined(UNITY_PASS_META)
+    float2 uv2      : OVR_THIRD_AVAILABLE_VERTEX_TEXCOORD_SEMANTIC;
+#endif
+
+    UNITY_VERTEX_INPUT_INSTANCE_ID
+};
+
+float4 TexCoords(VertexInput v)
+{
+    float4 texcoord;
+    texcoord.xy = TRANSFORM_TEX(v.uv0, _MainTex); // Always source from uv0
+    texcoord.zw = TRANSFORM_TEX(((_UVSec == 0) ? v.uv0 : v.uv1), _DetailAlbedoMap);
+    return texcoord;
+}
+
+half DetailMask(float2 uv)
+{
+    return tex2D (_DetailMask, uv).a;
+}
+
+half3 Albedo(float4 texcoords)
+{
+    half3 albedo = _Color.rgb * tex2D (_MainTex, texcoords.xy).rgb;
+#if _DETAIL
+    #if (SHADER_TARGET < 30)
+        // SM20: instruction count limitation
+        // SM20: no detail mask
+        half mask = 1;
+    #else
+        half mask = DetailMask(texcoords.xy);
+    #endif
+    half3 detailAlbedo = tex2D (_DetailAlbedoMap, texcoords.zw).rgb;
+    #if _DETAIL_MULX2
+        albedo *= LerpWhiteTo (detailAlbedo * unity_ColorSpaceDouble.rgb, mask);
+    #elif _DETAIL_MUL
+        albedo *= LerpWhiteTo (detailAlbedo, mask);
+    #elif _DETAIL_ADD
+        albedo += detailAlbedo * mask;
+    #elif _DETAIL_LERP
+        albedo = lerp (albedo, detailAlbedo, mask);
+    #endif
+#endif
+    return albedo;
+}
+
+half Alpha(float2 uv)
+{
+#if defined(_SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A)
+    return _Color.a;
+#else
+    return tex2D(_MainTex, uv).a * _Color.a;
+#endif
+}
+
+half Occlusion(float2 uv)
+{
+#ifdef _AVATAR_ORM_CHANNEL
+    // FIXME in ORM mode we should use one texture sample
+    // of _MetallicGlossMap for occlusion, roughness and metalness
+    half occ = tex2D(_OcclusionMap, uv).r;
+    return LerpOneTo(occ, _OcclusionStrength);
+#elif (SHADER_TARGET < 30)
+    // SM20: instruction count limitation
+    // SM20: simpler occlusion
+    return tex2D(_OcclusionMap, uv).g;
+#else
+    half occ = tex2D(_OcclusionMap, uv).g;
+    return LerpOneTo (occ, _OcclusionStrength);
+#endif
+}
+
+half4 SpecularGloss(float2 uv)
+{
+    half4 sg;
+#ifdef _SPECGLOSSMAP
+    #if defined(_SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A)
+        sg.rgb = tex2D(_SpecGlossMap, uv).rgb;
+        sg.a = tex2D(_MainTex, uv).a;
+    #else
+        sg = tex2D(_SpecGlossMap, uv);
+    #endif
+    sg.a *= _GlossMapScale;
+#else
+    sg.rgb = _SpecColor.rgb;
+    #ifdef _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
+        sg.a = tex2D(_MainTex, uv).a * _GlossMapScale;
+    #else
+        sg.a = _Glossiness;
+    #endif
+#endif
+    return sg;
+}
+
+half2 MetallicGloss(float2 uv)
+{
+    half2 mg;
+
+#ifdef _METALLICGLOSSMAP
+    #ifdef _AVATAR_ORM_CHANNEL
+        mg = tex2D(_MetallicGlossMap, uv).bg;
+        // change roughness to Unity's "smoothness":
+        mg.g = 1 - sqrt(mg.g);
+    #elif _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
+        mg.r = tex2D(_MetallicGlossMap, uv).r;
+        mg.g = tex2D(_MainTex, uv).a;
+    #else
+        mg = tex2D(_MetallicGlossMap, uv).ra;
+    #endif
+    mg.g *= _GlossMapScale;
+#else
+    #ifdef _AVATAR_ORM_CHANNEL
+        mg = tex2D(_MetallicGlossMap, uv).bg;
+        // change roughness to Unity's "smoothness":
+        mg.g = 1 - sqrt(mg.g);
+    #elif _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
+        mg.r = _Metallic;
+        mg.g = tex2D(_MainTex, uv).a * _GlossMapScale;
+    #else
+        mg.r = _Metallic;
+        mg.g = _Glossiness;
+    #endif
+#endif
+    return mg;
+}
+
+half2 MetallicRough(float2 uv)
+{
+    half2 mg;
+#ifdef _METALLICGLOSSMAP
+    mg.r = tex2D(_MetallicGlossMap, uv).r;
+#else
+    mg.r = _Metallic;
+#endif
+
+#ifdef _SPECGLOSSMAP
+    mg.g = 1.0f - tex2D(_SpecGlossMap, uv).r;
+#else
+    mg.g = 1.0f - _Glossiness;
+#endif
+    return mg;
+}
+
+half3 Emission(float2 uv)
+{
+#ifndef _EMISSION
+    return 0;
+#else
+    return tex2D(_EmissionMap, uv).rgb * _EmissionColor.rgb;
+#endif
+}
+
+#ifdef _NORMALMAP
+half3 NormalInTangentSpace(float4 texcoords)
+{
+    half3 normalTangent = UnpackScaleNormal(tex2D (_BumpMap, texcoords.xy), _BumpScale);
+
+#if _DETAIL && defined(UNITY_ENABLE_DETAIL_NORMALMAP)
+    half mask = DetailMask(texcoords.xy);
+    half3 detailNormalTangent = UnpackScaleNormal(tex2D (_DetailNormalMap, texcoords.zw), _DetailNormalMapScale);
+    #if _DETAIL_LERP
+        normalTangent = lerp(
+            normalTangent,
+            detailNormalTangent,
+            mask);
+    #else
+        normalTangent = lerp(
+            normalTangent,
+            BlendNormals(normalTangent, detailNormalTangent),
+            mask);
+    #endif
+#endif
+
+    return normalTangent;
+}
+#endif
+
+float4 Parallax (float4 texcoords, half3 viewDir)
+{
+#if !defined(_PARALLAXMAP) || (SHADER_TARGET < 30)
+    // Disable parallax on pre-SM3.0 shader target models
+    return texcoords;
+#else
+    half h = tex2D (_ParallaxMap, texcoords.xy).g;
+    float2 offset = ParallaxOffset1Step (h, _Parallax, viewDir);
+    return float4(texcoords.xy + offset, texcoords.zw + offset);
+#endif
+
+}
+
+#endif // UNITY_STANDARD_INPUT_INCLUDED
diff --git a/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityStandard/AvatarStandardInput.cginc.meta b/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityStandard/AvatarStandardInput.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..63888ddba1f68cfa3fc56be155c5d010937cfe65
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Shaders/UnityStandard/AvatarStandardInput.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 41832d23a51e54c42906312d1b8733af
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Textures.meta b/Assets/Oculus/Avatar2/Example/Common/Textures.meta
new file mode 100644
index 0000000000000000000000000000000000000000..a84ddc1fe55e56baee328890249a695c55e7f0f0
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Textures.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 71b36d72b08c07a4892ae568f23b9a54
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Textures/BRDF LUT.png b/Assets/Oculus/Avatar2/Example/Common/Textures/BRDF LUT.png
new file mode 100644
index 0000000000000000000000000000000000000000..66c8aad81563bad4cda3f07e899b7cf270689f80
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Textures/BRDF LUT.png	
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b1c83074527cf031755eff2c0f7bfa1dec56771ea963a14a1f54ad2f9fc3b62a
+size 19740
diff --git a/Assets/Oculus/Avatar2/Example/Common/Textures/BRDF LUT.png.meta b/Assets/Oculus/Avatar2/Example/Common/Textures/BRDF LUT.png.meta
new file mode 100644
index 0000000000000000000000000000000000000000..dd60d474a7aa7f45c2296f0c1bbb50d0903a8090
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Textures/BRDF LUT.png.meta	
@@ -0,0 +1,115 @@
+fileFormatVersion: 2
+guid: 4386920a84c813b4895b612bab20ee15
+TextureImporter:
+  fileIDToRecycleName: {}
+  externalObjects: {}
+  serializedVersion: 9
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 1
+    sRGBTexture: 0
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 0
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: -1
+    aniso: -1
+    mipBias: -100
+    wrapU: 1
+    wrapV: 1
+    wrapW: 1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 1
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 0
+  textureShape: 1
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  platformSettings:
+  - serializedVersion: 2
+    buildTarget: DefaultTexturePlatform
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 1
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+  - serializedVersion: 3
+    buildTarget: Standalone
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 1
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  - serializedVersion: 3
+    buildTarget: Android
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 1
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Textures/Black.png b/Assets/Oculus/Avatar2/Example/Common/Textures/Black.png
new file mode 100644
index 0000000000000000000000000000000000000000..74d7c5400569e83204bbc2384fcdf3b372bae536
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Textures/Black.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:0560cc7f7e6f8b4f6ee9651a6df2ea0366e5927b14df9223bf119200ee226a49
+size 17239
diff --git a/Assets/Oculus/Avatar2/Example/Common/Textures/Black.png.meta b/Assets/Oculus/Avatar2/Example/Common/Textures/Black.png.meta
new file mode 100644
index 0000000000000000000000000000000000000000..210b02004434fc8d99720556075f07dd01aae8b2
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Textures/Black.png.meta
@@ -0,0 +1,89 @@
+fileFormatVersion: 2
+guid: 8126abc91ceccd84696395fc6d1e2ffc
+TextureImporter:
+  fileIDToRecycleName: {}
+  externalObjects: {}
+  serializedVersion: 9
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 0
+    sRGBTexture: 0
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 2
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 0
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: -1
+  maxTextureSize: 1024
+  textureSettings:
+    serializedVersion: 2
+    filterMode: 1
+    aniso: 1
+    mipBias: 0
+    wrapU: 0
+    wrapV: 0
+    wrapW: 0
+  nPOTScale: 0
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 1
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 0
+  textureShape: 1
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  platformSettings:
+  - serializedVersion: 2
+    buildTarget: DefaultTexturePlatform
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 1024
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 1
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Textures/Blue.png b/Assets/Oculus/Avatar2/Example/Common/Textures/Blue.png
new file mode 100644
index 0000000000000000000000000000000000000000..df5d84464ba0b67f6742dca1d2c608330bb8b369
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Textures/Blue.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6b33d0eb090420bb7bf0eaf14a4c45ac36df8865f9d995798b1d9dd4a386650f
+size 5024
diff --git a/Assets/Oculus/Avatar2/Example/Common/Textures/Blue.png.meta b/Assets/Oculus/Avatar2/Example/Common/Textures/Blue.png.meta
new file mode 100644
index 0000000000000000000000000000000000000000..29933effebc847d2c713d407971f0887f43cd1af
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Textures/Blue.png.meta
@@ -0,0 +1,89 @@
+fileFormatVersion: 2
+guid: 5b23e1da316236b48baa7a0b9f7a43bb
+TextureImporter:
+  fileIDToRecycleName: {}
+  externalObjects: {}
+  serializedVersion: 9
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 0
+    sRGBTexture: 0
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 0
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: -1
+    aniso: -1
+    mipBias: -100
+    wrapU: -1
+    wrapV: -1
+    wrapW: -1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 1
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 0
+  textureShape: 1
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  platformSettings:
+  - serializedVersion: 2
+    buildTarget: DefaultTexturePlatform
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 1
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Textures/Brown.png b/Assets/Oculus/Avatar2/Example/Common/Textures/Brown.png
new file mode 100644
index 0000000000000000000000000000000000000000..6a112380dbac33a789e3e9a820a97d0b6915452a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Textures/Brown.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ea3ce137351a3adf6712a59ce212669996ee098abc2d091d3a2bf0d70726dcbe
+size 5022
diff --git a/Assets/Oculus/Avatar2/Example/Common/Textures/Brown.png.meta b/Assets/Oculus/Avatar2/Example/Common/Textures/Brown.png.meta
new file mode 100644
index 0000000000000000000000000000000000000000..cb4b9bb2ea1407e741d1c6d695ab03430a49da70
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Textures/Brown.png.meta
@@ -0,0 +1,89 @@
+fileFormatVersion: 2
+guid: 54ef6b835957a5c4b80fd49438b7c185
+TextureImporter:
+  fileIDToRecycleName: {}
+  externalObjects: {}
+  serializedVersion: 9
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 1
+    sRGBTexture: 1
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 0
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: -1
+    aniso: -1
+    mipBias: -100
+    wrapU: 1
+    wrapV: 1
+    wrapW: 1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 1
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 0
+  textureShape: 1
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  platformSettings:
+  - serializedVersion: 2
+    buildTarget: DefaultTexturePlatform
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 1
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Textures/Green.png b/Assets/Oculus/Avatar2/Example/Common/Textures/Green.png
new file mode 100644
index 0000000000000000000000000000000000000000..1c140fdeb0a1d5ac7f0d71dba89b8178effd0657
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Textures/Green.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ccbbb944837ae25ad55691a335be16c7abc8a632dd018618d6525d5c711ab19c
+size 5022
diff --git a/Assets/Oculus/Avatar2/Example/Common/Textures/Green.png.meta b/Assets/Oculus/Avatar2/Example/Common/Textures/Green.png.meta
new file mode 100644
index 0000000000000000000000000000000000000000..0e1c10edc18a39ab5b3976068247a6b8e737cfa5
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Textures/Green.png.meta
@@ -0,0 +1,89 @@
+fileFormatVersion: 2
+guid: e8c9012cee8cad34384533bc82ec7f31
+TextureImporter:
+  fileIDToRecycleName: {}
+  externalObjects: {}
+  serializedVersion: 9
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 0
+    sRGBTexture: 0
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 0
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: -1
+    aniso: -1
+    mipBias: -100
+    wrapU: -1
+    wrapV: -1
+    wrapW: -1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 1
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 0
+  textureShape: 1
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  platformSettings:
+  - serializedVersion: 2
+    buildTarget: DefaultTexturePlatform
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 1
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Textures/Grey.png b/Assets/Oculus/Avatar2/Example/Common/Textures/Grey.png
new file mode 100644
index 0000000000000000000000000000000000000000..03dd77ffddbcabc26d7c4f6b0262598e4595707b
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Textures/Grey.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2d11ff1353251ae692ba208882a762e01d1389ed6a87c0be3ef895596d567e3d
+size 5024
diff --git a/Assets/Oculus/Avatar2/Example/Common/Textures/Grey.png.meta b/Assets/Oculus/Avatar2/Example/Common/Textures/Grey.png.meta
new file mode 100644
index 0000000000000000000000000000000000000000..3613d96a2e70b9f7e0076ca70a65a4aa5cbf245f
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Textures/Grey.png.meta
@@ -0,0 +1,89 @@
+fileFormatVersion: 2
+guid: 1303047a4a2d85649aebc891948037d0
+TextureImporter:
+  fileIDToRecycleName: {}
+  externalObjects: {}
+  serializedVersion: 9
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 0
+    sRGBTexture: 0
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 0
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: -1
+    aniso: -1
+    mipBias: -100
+    wrapU: -1
+    wrapV: -1
+    wrapW: -1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 1
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 0
+  textureShape: 1
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  platformSettings:
+  - serializedVersion: 2
+    buildTarget: DefaultTexturePlatform
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 1
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Textures/Orange.png b/Assets/Oculus/Avatar2/Example/Common/Textures/Orange.png
new file mode 100644
index 0000000000000000000000000000000000000000..a6dbb6cd256210cf84eed174a5114cff67a919a7
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Textures/Orange.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c7e5c8501079d95b5fdbaf9750091d8a279472ae1b931928a9400a42d714c952
+size 5022
diff --git a/Assets/Oculus/Avatar2/Example/Common/Textures/Orange.png.meta b/Assets/Oculus/Avatar2/Example/Common/Textures/Orange.png.meta
new file mode 100644
index 0000000000000000000000000000000000000000..08ebda2d2a106687293d8a3fc6bbccb87631ca94
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Textures/Orange.png.meta
@@ -0,0 +1,89 @@
+fileFormatVersion: 2
+guid: 6d2d5006f2360ec42aa42383a5aa03db
+TextureImporter:
+  fileIDToRecycleName: {}
+  externalObjects: {}
+  serializedVersion: 9
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 0
+    sRGBTexture: 0
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 0
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: 1
+    aniso: -1
+    mipBias: -100
+    wrapU: -1
+    wrapV: -1
+    wrapW: -1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 1
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 0
+  textureShape: 1
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  platformSettings:
+  - serializedVersion: 2
+    buildTarget: DefaultTexturePlatform
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 1
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Textures/Red.png b/Assets/Oculus/Avatar2/Example/Common/Textures/Red.png
new file mode 100644
index 0000000000000000000000000000000000000000..1cab50d98a4c8f72c9c2a74f1fa0cc9ed7c980aa
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Textures/Red.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:2510a6b0729be762cf39cb23347a754073e41a795e198f3fb38b4551d29145e3
+size 5021
diff --git a/Assets/Oculus/Avatar2/Example/Common/Textures/Red.png.meta b/Assets/Oculus/Avatar2/Example/Common/Textures/Red.png.meta
new file mode 100644
index 0000000000000000000000000000000000000000..042b0e15b54b4316542683208aa45e6bff211404
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Textures/Red.png.meta
@@ -0,0 +1,89 @@
+fileFormatVersion: 2
+guid: f9d9dd21ea85cf3449454675feb2eccb
+TextureImporter:
+  fileIDToRecycleName: {}
+  externalObjects: {}
+  serializedVersion: 9
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 0
+    sRGBTexture: 0
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 0
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: -1
+    aniso: -1
+    mipBias: -100
+    wrapU: -1
+    wrapV: -1
+    wrapW: -1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 1
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 0
+  textureShape: 1
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  platformSettings:
+  - serializedVersion: 2
+    buildTarget: DefaultTexturePlatform
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 1
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Textures/RedOrange.png b/Assets/Oculus/Avatar2/Example/Common/Textures/RedOrange.png
new file mode 100644
index 0000000000000000000000000000000000000000..b7feaee0ecd96a0699927c438ba7917f870c741a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Textures/RedOrange.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:33faea087770123c03b42c629e44ddb864216403a992d59f8a4eb6102ab6ff28
+size 5022
diff --git a/Assets/Oculus/Avatar2/Example/Common/Textures/RedOrange.png.meta b/Assets/Oculus/Avatar2/Example/Common/Textures/RedOrange.png.meta
new file mode 100644
index 0000000000000000000000000000000000000000..ffd6b56940ffaf1c02b24acf26a2e987b7e88376
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Textures/RedOrange.png.meta
@@ -0,0 +1,89 @@
+fileFormatVersion: 2
+guid: ed8192c8839b473429aff481e4c2467b
+TextureImporter:
+  fileIDToRecycleName: {}
+  externalObjects: {}
+  serializedVersion: 9
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 0
+    sRGBTexture: 0
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 0
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: -1
+    aniso: -1
+    mipBias: -100
+    wrapU: -1
+    wrapV: -1
+    wrapW: -1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 1
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 0
+  textureShape: 1
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  platformSettings:
+  - serializedVersion: 2
+    buildTarget: DefaultTexturePlatform
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 1
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Textures/Teal.png b/Assets/Oculus/Avatar2/Example/Common/Textures/Teal.png
new file mode 100644
index 0000000000000000000000000000000000000000..dd0454086f9abe17441fc7def6c12f4710764fab
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Textures/Teal.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:8a8c7a29aa1f6fdc6654fc2e4ec8be6647da05afe136f59a5865980e279b5b6f
+size 5024
diff --git a/Assets/Oculus/Avatar2/Example/Common/Textures/Teal.png.meta b/Assets/Oculus/Avatar2/Example/Common/Textures/Teal.png.meta
new file mode 100644
index 0000000000000000000000000000000000000000..e74b7f39f31413b096ecd941716811e13b874ff8
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Textures/Teal.png.meta
@@ -0,0 +1,89 @@
+fileFormatVersion: 2
+guid: eaa9fc5af7adb394d9886adb4010613b
+TextureImporter:
+  fileIDToRecycleName: {}
+  externalObjects: {}
+  serializedVersion: 9
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 0
+    sRGBTexture: 0
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 0
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: -1
+    aniso: -1
+    mipBias: -100
+    wrapU: -1
+    wrapV: -1
+    wrapW: -1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 1
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 0
+  textureShape: 1
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  platformSettings:
+  - serializedVersion: 2
+    buildTarget: DefaultTexturePlatform
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 1
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Textures/White.png b/Assets/Oculus/Avatar2/Example/Common/Textures/White.png
new file mode 100644
index 0000000000000000000000000000000000000000..4c8fb5b25a4db4463684d4bf097dab3873e08060
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Textures/White.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d2019567a72be580db1506c0d73c2ea95725b2b325b1512bfb11cd619d3bfb4e
+size 5025
diff --git a/Assets/Oculus/Avatar2/Example/Common/Textures/White.png.meta b/Assets/Oculus/Avatar2/Example/Common/Textures/White.png.meta
new file mode 100644
index 0000000000000000000000000000000000000000..119509581940b383a57b722843ace639bea33d53
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Textures/White.png.meta
@@ -0,0 +1,89 @@
+fileFormatVersion: 2
+guid: 325a054c45020654fb52d97d93ab3680
+TextureImporter:
+  fileIDToRecycleName: {}
+  externalObjects: {}
+  serializedVersion: 9
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 0
+    sRGBTexture: 0
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 0
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: -1
+    aniso: -1
+    mipBias: -100
+    wrapU: -1
+    wrapV: -1
+    wrapW: -1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 1
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 0
+  textureShape: 1
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  platformSettings:
+  - serializedVersion: 2
+    buildTarget: DefaultTexturePlatform
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 1
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Textures/Yellow.png b/Assets/Oculus/Avatar2/Example/Common/Textures/Yellow.png
new file mode 100644
index 0000000000000000000000000000000000000000..a8a9a62c1ce5f7258ba8076428bad4e836ac1f8d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Textures/Yellow.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d412d14d189117061b9929b36b578f320b71ce7eb9734d7b7fcd04f0eae6261c
+size 5022
diff --git a/Assets/Oculus/Avatar2/Example/Common/Textures/Yellow.png.meta b/Assets/Oculus/Avatar2/Example/Common/Textures/Yellow.png.meta
new file mode 100644
index 0000000000000000000000000000000000000000..baa1951ba80416f72e8eab3bde3006a98480be98
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Textures/Yellow.png.meta
@@ -0,0 +1,89 @@
+fileFormatVersion: 2
+guid: 1d4c9f5a667c08d4092f662f655e5abc
+TextureImporter:
+  fileIDToRecycleName: {}
+  externalObjects: {}
+  serializedVersion: 9
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 0
+    sRGBTexture: 0
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 0
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: -1
+    aniso: -1
+    mipBias: -100
+    wrapU: -1
+    wrapV: -1
+    wrapW: -1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 1
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 0
+  textureShape: 1
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  platformSettings:
+  - serializedVersion: 2
+    buildTarget: DefaultTexturePlatform
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 1
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Textures/brdf_LUTh.png b/Assets/Oculus/Avatar2/Example/Common/Textures/brdf_LUTh.png
new file mode 100644
index 0000000000000000000000000000000000000000..8e49451d76c55d0d59c5dfef6316c73b29428ecf
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Textures/brdf_LUTh.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:b9cea5a3a9078afd70f9333aff098f791175c3e33969ba39e253874c02cbcb9e
+size 10402
diff --git a/Assets/Oculus/Avatar2/Example/Common/Textures/brdf_LUTh.png.meta b/Assets/Oculus/Avatar2/Example/Common/Textures/brdf_LUTh.png.meta
new file mode 100644
index 0000000000000000000000000000000000000000..7591639c3539e91329f729ef49fac9ad33056c87
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Textures/brdf_LUTh.png.meta
@@ -0,0 +1,115 @@
+fileFormatVersion: 2
+guid: 893303938e14a8d49946289f2dfa0342
+TextureImporter:
+  fileIDToRecycleName: {}
+  externalObjects: {}
+  serializedVersion: 9
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 1
+    sRGBTexture: 0
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 0
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: -1
+    aniso: -1
+    mipBias: -100
+    wrapU: 1
+    wrapV: 1
+    wrapW: 1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 1
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 0
+  textureShape: 1
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  platformSettings:
+  - serializedVersion: 2
+    buildTarget: DefaultTexturePlatform
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 1
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+  - serializedVersion: 3
+    buildTarget: Standalone
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 1
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  - serializedVersion: 3
+    buildTarget: Android
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 1
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Common/Textures/brdf_LUTh16bpp.png b/Assets/Oculus/Avatar2/Example/Common/Textures/brdf_LUTh16bpp.png
new file mode 100644
index 0000000000000000000000000000000000000000..0f963c45ae06cdbc831cb15632e4bf878cd9b097
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Textures/brdf_LUTh16bpp.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f8ce0ea0460f7941291e14eb05f814f1d62a4cb25e0d3e3a648307012a08eb9d
+size 73470
diff --git a/Assets/Oculus/Avatar2/Example/Common/Textures/brdf_LUTh16bpp.png.meta b/Assets/Oculus/Avatar2/Example/Common/Textures/brdf_LUTh16bpp.png.meta
new file mode 100644
index 0000000000000000000000000000000000000000..59f223734b735535b8b421ce9b20b984347c3bd1
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Common/Textures/brdf_LUTh16bpp.png.meta
@@ -0,0 +1,115 @@
+fileFormatVersion: 2
+guid: 918eb5f61eea9f6479953048b19170c0
+TextureImporter:
+  fileIDToRecycleName: {}
+  externalObjects: {}
+  serializedVersion: 9
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 1
+    sRGBTexture: 0
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 0
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: -1
+    aniso: -1
+    mipBias: -100
+    wrapU: 1
+    wrapV: 1
+    wrapW: 1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 1
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 0
+  textureShape: 1
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  platformSettings:
+  - serializedVersion: 2
+    buildTarget: DefaultTexturePlatform
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 1
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+  - serializedVersion: 3
+    buildTarget: Standalone
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 1
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  - serializedVersion: 3
+    buildTarget: Android
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 1
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Editor.meta b/Assets/Oculus/Avatar2/Example/Editor.meta
new file mode 100644
index 0000000000000000000000000000000000000000..2fa4ff17f98b81616c97383a9e69f092969133b7
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Editor.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: dc508ed712602cc4fbf052c6c6a14eb6
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Editor/AvatarShaderGUI.cs b/Assets/Oculus/Avatar2/Example/Editor/AvatarShaderGUI.cs
new file mode 100644
index 0000000000000000000000000000000000000000..4bee713afa57ce41dbca165a22589300550432d2
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Editor/AvatarShaderGUI.cs
@@ -0,0 +1,434 @@
+// Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)
+
+using System;
+using UnityEngine;
+// using TargetAttributes = UnityEditor.BuildTargetDiscovery.TargetAttributes;
+
+namespace UnityEditor
+{
+    internal class AvatarShaderGUI : ShaderGUI
+    {
+        private enum WorkflowMode
+        {
+            Specular,
+            Metallic,
+            Dielectric
+        }
+
+        public enum BlendMode
+        {
+            Opaque,
+            Cutout,
+            Fade,   // Old school alpha-blending mode, fresnel does not affect amount of transparency
+            Transparent // Physically plausible transparency mode, implemented as alpha pre-multiply
+        }
+
+        public enum SmoothnessMapChannel
+        {
+            SpecularMetallicAlpha,
+            AlbedoAlpha,
+            AvatarORM
+        }
+
+        private static class Styles
+        {
+            public static GUIContent uvSetLabel = EditorGUIUtility.TrTextContent("UV Set");
+
+            public static GUIContent albedoText = EditorGUIUtility.TrTextContent("Albedo", "Albedo (RGB) and Transparency (A)");
+            public static GUIContent alphaCutoffText = EditorGUIUtility.TrTextContent("Alpha Cutoff", "Threshold for alpha cutoff");
+            public static GUIContent specularMapText = EditorGUIUtility.TrTextContent("Specular", "Specular (RGB) and Smoothness (A)");
+            public static GUIContent metallicMapText = EditorGUIUtility.TrTextContent("Metallic", "Metallic (R) and Smoothness (A)");
+            public static GUIContent smoothnessText = EditorGUIUtility.TrTextContent("Smoothness", "Smoothness value");
+            public static GUIContent smoothnessScaleText = EditorGUIUtility.TrTextContent("Smoothness", "Smoothness scale factor");
+            public static GUIContent smoothnessMapChannelText = EditorGUIUtility.TrTextContent("Source", "Smoothness texture and channel");
+            public static GUIContent highlightsText = EditorGUIUtility.TrTextContent("Specular Highlights", "Specular Highlights");
+            public static GUIContent reflectionsText = EditorGUIUtility.TrTextContent("Reflections", "Glossy Reflections");
+            public static GUIContent normalMapText = EditorGUIUtility.TrTextContent("Normal Map", "Normal Map");
+            public static GUIContent heightMapText = EditorGUIUtility.TrTextContent("Height Map", "Height Map (G)");
+            public static GUIContent occlusionText = EditorGUIUtility.TrTextContent("Occlusion", "Occlusion (G)");
+            public static GUIContent emissionText = EditorGUIUtility.TrTextContent("Color", "Emission (RGB)");
+            public static GUIContent detailMaskText = EditorGUIUtility.TrTextContent("Detail Mask", "Mask for Secondary Maps (A)");
+            public static GUIContent detailAlbedoText = EditorGUIUtility.TrTextContent("Detail Albedo x2", "Albedo (RGB) multiplied by 2");
+            public static GUIContent detailNormalMapText = EditorGUIUtility.TrTextContent("Normal Map", "Normal Map");
+
+            public static string primaryMapsText = "Main Maps";
+            public static string secondaryMapsText = "Secondary Maps";
+            public static string forwardText = "Forward Rendering Options";
+            public static string renderingMode = "Rendering Mode";
+            public static string advancedText = "Advanced Options";
+            public static readonly string[] blendNames = Enum.GetNames(typeof(BlendMode));
+        }
+
+        MaterialProperty blendMode = null;
+        MaterialProperty albedoMap = null;
+        MaterialProperty albedoColor = null;
+        MaterialProperty alphaCutoff = null;
+        MaterialProperty specularMap = null;
+        MaterialProperty specularColor = null;
+        MaterialProperty metallicMap = null;
+        MaterialProperty metallic = null;
+        MaterialProperty smoothness = null;
+        MaterialProperty smoothnessScale = null;
+        MaterialProperty smoothnessMapChannel = null;
+        MaterialProperty highlights = null;
+        MaterialProperty reflections = null;
+        MaterialProperty bumpScale = null;
+        MaterialProperty bumpMap = null;
+        MaterialProperty occlusionStrength = null;
+        MaterialProperty occlusionMap = null;
+        MaterialProperty heigtMapScale = null;
+        MaterialProperty heightMap = null;
+        MaterialProperty emissionColorForRendering = null;
+        MaterialProperty emissionMap = null;
+        MaterialProperty detailMask = null;
+        MaterialProperty detailAlbedoMap = null;
+        MaterialProperty detailNormalMapScale = null;
+        MaterialProperty detailNormalMap = null;
+        MaterialProperty uvSetSecondary = null;
+
+        MaterialEditor m_MaterialEditor;
+        WorkflowMode m_WorkflowMode = WorkflowMode.Specular;
+
+        bool m_FirstTimeApply = true;
+
+        public void FindProperties(MaterialProperty[] props)
+        {
+            blendMode = FindProperty("_Mode", props);
+            albedoMap = FindProperty("_MainTex", props);
+            albedoColor = FindProperty("_Color", props);
+            alphaCutoff = FindProperty("_Cutoff", props);
+            specularMap = FindProperty("_SpecGlossMap", props, false);
+            specularColor = FindProperty("_SpecColor", props, false);
+            metallicMap = FindProperty("_MetallicGlossMap", props, false);
+            metallic = FindProperty("_Metallic", props, false);
+            if (specularMap != null && specularColor != null)
+                m_WorkflowMode = WorkflowMode.Specular;
+            else if (metallicMap != null && metallic != null)
+                m_WorkflowMode = WorkflowMode.Metallic;
+            else
+                m_WorkflowMode = WorkflowMode.Dielectric;
+            smoothness = FindProperty("_Glossiness", props);
+            smoothnessScale = FindProperty("_GlossMapScale", props, false);
+            smoothnessMapChannel = FindProperty("_SmoothnessTextureChannel", props, false);
+            highlights = FindProperty("_SpecularHighlights", props, false);
+            reflections = FindProperty("_GlossyReflections", props, false);
+            bumpScale = FindProperty("_BumpScale", props);
+            bumpMap = FindProperty("_BumpMap", props);
+            heigtMapScale = FindProperty("_Parallax", props);
+            heightMap = FindProperty("_ParallaxMap", props);
+            occlusionStrength = FindProperty("_OcclusionStrength", props);
+            occlusionMap = FindProperty("_OcclusionMap", props);
+            emissionColorForRendering = FindProperty("_EmissionColor", props);
+            emissionMap = FindProperty("_EmissionMap", props);
+            detailMask = FindProperty("_DetailMask", props);
+            detailAlbedoMap = FindProperty("_DetailAlbedoMap", props);
+            detailNormalMapScale = FindProperty("_DetailNormalMapScale", props);
+            detailNormalMap = FindProperty("_DetailNormalMap", props);
+            uvSetSecondary = FindProperty("_UVSec", props);
+        }
+
+        public override void OnGUI(MaterialEditor materialEditor, MaterialProperty[] props)
+        {
+            FindProperties(props); // MaterialProperties can be animated so we do not cache them but fetch them every event to ensure animated values are updated correctly
+            m_MaterialEditor = materialEditor;
+            Material material = materialEditor.target as Material;
+
+            // Make sure that needed setup (ie keywords/renderqueue) are set up if we're switching some existing
+            // material to a standard shader.
+            // Do this before any GUI code has been issued to prevent layout issues in subsequent GUILayout statements (case 780071)
+            if (m_FirstTimeApply)
+            {
+                MaterialChanged(material, m_WorkflowMode);
+                m_FirstTimeApply = false;
+            }
+
+            ShaderPropertiesGUI(material);
+        }
+
+        public void ShaderPropertiesGUI(Material material)
+        {
+            // Use default labelWidth
+            EditorGUIUtility.labelWidth = 0f;
+
+            // Detect any changes to the material
+            EditorGUI.BeginChangeCheck();
+            {
+                BlendModePopup();
+
+                // Primary properties
+                GUILayout.Label(Styles.primaryMapsText, EditorStyles.boldLabel);
+                DoAlbedoArea(material);
+                DoSpecularMetallicArea();
+                DoNormalArea();
+                m_MaterialEditor.TexturePropertySingleLine(Styles.heightMapText, heightMap, heightMap.textureValue != null ? heigtMapScale : null);
+                m_MaterialEditor.TexturePropertySingleLine(Styles.occlusionText, occlusionMap, occlusionMap.textureValue != null ? occlusionStrength : null);
+                m_MaterialEditor.TexturePropertySingleLine(Styles.detailMaskText, detailMask);
+                DoEmissionArea(material);
+                EditorGUI.BeginChangeCheck();
+                m_MaterialEditor.TextureScaleOffsetProperty(albedoMap);
+                if (EditorGUI.EndChangeCheck())
+                    emissionMap.textureScaleAndOffset = albedoMap.textureScaleAndOffset; // Apply the main texture scale and offset to the emission texture as well, for Enlighten's sake
+
+                EditorGUILayout.Space();
+
+                // Secondary properties
+                GUILayout.Label(Styles.secondaryMapsText, EditorStyles.boldLabel);
+                m_MaterialEditor.TexturePropertySingleLine(Styles.detailAlbedoText, detailAlbedoMap);
+                m_MaterialEditor.TexturePropertySingleLine(Styles.detailNormalMapText, detailNormalMap, detailNormalMapScale);
+                m_MaterialEditor.TextureScaleOffsetProperty(detailAlbedoMap);
+                m_MaterialEditor.ShaderProperty(uvSetSecondary, Styles.uvSetLabel.text);
+
+                // Third properties
+                GUILayout.Label(Styles.forwardText, EditorStyles.boldLabel);
+                if (highlights != null)
+                    m_MaterialEditor.ShaderProperty(highlights, Styles.highlightsText);
+                if (reflections != null)
+                    m_MaterialEditor.ShaderProperty(reflections, Styles.reflectionsText);
+            }
+            if (EditorGUI.EndChangeCheck())
+            {
+                foreach (var obj in blendMode.targets)
+                    MaterialChanged((Material)obj, m_WorkflowMode);
+            }
+
+            EditorGUILayout.Space();
+
+            // NB renderqueue editor is not shown on purpose: we want to override it based on blend mode
+            GUILayout.Label(Styles.advancedText, EditorStyles.boldLabel);
+            m_MaterialEditor.EnableInstancingField();
+            m_MaterialEditor.DoubleSidedGIField();
+        }
+
+        internal void DetermineWorkflow(MaterialProperty[] props)
+        {
+            if (FindProperty("_SpecGlossMap", props, false) != null && FindProperty("_SpecColor", props, false) != null)
+                m_WorkflowMode = WorkflowMode.Specular;
+            else if (FindProperty("_MetallicGlossMap", props, false) != null && FindProperty("_Metallic", props, false) != null)
+                m_WorkflowMode = WorkflowMode.Metallic;
+            else
+                m_WorkflowMode = WorkflowMode.Dielectric;
+        }
+
+        public override void AssignNewShaderToMaterial(Material material, Shader oldShader, Shader newShader)
+        {
+            // _Emission property is lost after assigning Standard shader to the material
+            // thus transfer it before assigning the new shader
+            if (material.HasProperty("_Emission"))
+            {
+                material.SetColor("_EmissionColor", material.GetColor("_Emission"));
+            }
+
+            base.AssignNewShaderToMaterial(material, oldShader, newShader);
+
+            if (oldShader == null || !oldShader.name.Contains("Legacy Shaders/"))
+            {
+                SetupMaterialWithBlendMode(material, (BlendMode)material.GetFloat("_Mode"));
+                return;
+            }
+
+            BlendMode blendMode = BlendMode.Opaque;
+            if (oldShader.name.Contains("/Transparent/Cutout/"))
+            {
+                blendMode = BlendMode.Cutout;
+            }
+            else if (oldShader.name.Contains("/Transparent/"))
+            {
+                // NOTE: legacy shaders did not provide physically based transparency
+                // therefore Fade mode
+                blendMode = BlendMode.Fade;
+            }
+            material.SetFloat("_Mode", (float)blendMode);
+
+            DetermineWorkflow(MaterialEditor.GetMaterialProperties(new Material[] { material }));
+            MaterialChanged(material, m_WorkflowMode);
+        }
+
+        void BlendModePopup()
+        {
+            EditorGUI.showMixedValue = blendMode.hasMixedValue;
+            var mode = (BlendMode)blendMode.floatValue;
+
+            EditorGUI.BeginChangeCheck();
+            mode = (BlendMode)EditorGUILayout.Popup(Styles.renderingMode, (int)mode, Styles.blendNames);
+            if (EditorGUI.EndChangeCheck())
+            {
+                m_MaterialEditor.RegisterPropertyChangeUndo("Rendering Mode");
+                blendMode.floatValue = (float)mode;
+            }
+
+            EditorGUI.showMixedValue = false;
+        }
+
+        void DoNormalArea()
+        {
+            m_MaterialEditor.TexturePropertySingleLine(Styles.normalMapText, bumpMap, bumpMap.textureValue != null ? bumpScale : null);
+            if (bumpScale.floatValue != 1
+                //&& BuildTargetDiscovery.PlatformHasFlag(EditorUserBuildSettings.activeBuildTarget, TargetAttributes.HasIntegratedGPU)
+            )
+                if (m_MaterialEditor.HelpBoxWithButton(
+                    EditorGUIUtility.TrTextContent("Bump scale is not supported on mobile platforms"),
+                    EditorGUIUtility.TrTextContent("Fix Now")))
+                {
+                    bumpScale.floatValue = 1;
+                }
+        }
+
+        void DoAlbedoArea(Material material)
+        {
+            m_MaterialEditor.TexturePropertySingleLine(Styles.albedoText, albedoMap, albedoColor);
+            if (((BlendMode)material.GetFloat("_Mode") == BlendMode.Cutout))
+            {
+                m_MaterialEditor.ShaderProperty(alphaCutoff, Styles.alphaCutoffText.text, MaterialEditor.kMiniTextureFieldLabelIndentLevel + 1);
+            }
+        }
+
+        void DoEmissionArea(Material material)
+        {
+            // Emission for GI?
+            if (m_MaterialEditor.EmissionEnabledProperty())
+            {
+                bool hadEmissionTexture = emissionMap.textureValue != null;
+
+                // Texture and HDR color controls
+                m_MaterialEditor.TexturePropertyWithHDRColor(Styles.emissionText, emissionMap, emissionColorForRendering, false);
+
+                // If texture was assigned and color was black set color to white
+                float brightness = emissionColorForRendering.colorValue.maxColorComponent;
+                if (emissionMap.textureValue != null && !hadEmissionTexture && brightness <= 0f)
+                    emissionColorForRendering.colorValue = Color.white;
+
+                // change the GI flag and fix it up with emissive as black if necessary
+                m_MaterialEditor.LightmapEmissionFlagsProperty(MaterialEditor.kMiniTextureFieldLabelIndentLevel, true);
+            }
+        }
+
+        void DoSpecularMetallicArea()
+        {
+            bool hasGlossMap = false;
+            if (m_WorkflowMode == WorkflowMode.Specular)
+            {
+                hasGlossMap = specularMap.textureValue != null;
+                m_MaterialEditor.TexturePropertySingleLine(Styles.specularMapText, specularMap, hasGlossMap ? null : specularColor);
+            }
+            else if (m_WorkflowMode == WorkflowMode.Metallic)
+            {
+                hasGlossMap = metallicMap.textureValue != null;
+                m_MaterialEditor.TexturePropertySingleLine(Styles.metallicMapText, metallicMap, hasGlossMap ? null : metallic);
+            }
+
+            bool showSmoothnessScale = hasGlossMap;
+            if (smoothnessMapChannel != null)
+            {
+                int smoothnessChannel = (int)smoothnessMapChannel.floatValue;
+                if (smoothnessChannel == (int)SmoothnessMapChannel.AlbedoAlpha)
+                    showSmoothnessScale = true;
+            }
+
+            int indentation = 2; // align with labels of texture properties
+            m_MaterialEditor.ShaderProperty(showSmoothnessScale ? smoothnessScale : smoothness, showSmoothnessScale ? Styles.smoothnessScaleText : Styles.smoothnessText, indentation);
+
+            ++indentation;
+            if (smoothnessMapChannel != null)
+                m_MaterialEditor.ShaderProperty(smoothnessMapChannel, Styles.smoothnessMapChannelText, indentation);
+        }
+
+        public static void SetupMaterialWithBlendMode(Material material, BlendMode blendMode)
+        {
+            switch (blendMode)
+            {
+                case BlendMode.Opaque:
+                    material.SetOverrideTag("RenderType", "");
+                    material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
+                    material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
+                    material.SetInt("_ZWrite", 1);
+                    material.DisableKeyword("_ALPHATEST_ON");
+                    material.DisableKeyword("_ALPHABLEND_ON");
+                    material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
+                    material.renderQueue = -1;
+                    break;
+                case BlendMode.Cutout:
+                    material.SetOverrideTag("RenderType", "TransparentCutout");
+                    material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
+                    material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero);
+                    material.SetInt("_ZWrite", 1);
+                    material.EnableKeyword("_ALPHATEST_ON");
+                    material.DisableKeyword("_ALPHABLEND_ON");
+                    material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
+                    material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.AlphaTest;
+                    break;
+                case BlendMode.Fade:
+                    material.SetOverrideTag("RenderType", "Transparent");
+                    material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.SrcAlpha);
+                    material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
+                    material.SetInt("_ZWrite", 0);
+                    material.DisableKeyword("_ALPHATEST_ON");
+                    material.EnableKeyword("_ALPHABLEND_ON");
+                    material.DisableKeyword("_ALPHAPREMULTIPLY_ON");
+                    material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent;
+                    break;
+                case BlendMode.Transparent:
+                    material.SetOverrideTag("RenderType", "Transparent");
+                    material.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One);
+                    material.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha);
+                    material.SetInt("_ZWrite", 0);
+                    material.DisableKeyword("_ALPHATEST_ON");
+                    material.DisableKeyword("_ALPHABLEND_ON");
+                    material.EnableKeyword("_ALPHAPREMULTIPLY_ON");
+                    material.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent;
+                    break;
+            }
+        }
+
+        static SmoothnessMapChannel GetSmoothnessMapChannel(Material material)
+        {
+            int ch = (int)material.GetFloat("_SmoothnessTextureChannel");
+            if (ch == (int)SmoothnessMapChannel.AvatarORM)
+                return SmoothnessMapChannel.AvatarORM;
+            else if (ch == (int)SmoothnessMapChannel.AlbedoAlpha)
+                return SmoothnessMapChannel.AlbedoAlpha;
+            else
+                return SmoothnessMapChannel.SpecularMetallicAlpha;
+        }
+
+        static void SetMaterialKeywords(Material material, WorkflowMode workflowMode)
+        {
+            // Note: keywords must be based on Material value not on MaterialProperty due to multi-edit & material animation
+            // (MaterialProperty value might come from renderer material property block)
+            SetKeyword(material, "_NORMALMAP", material.GetTexture("_BumpMap") || material.GetTexture("_DetailNormalMap"));
+            if (workflowMode == WorkflowMode.Specular)
+                SetKeyword(material, "_SPECGLOSSMAP", material.GetTexture("_SpecGlossMap"));
+            else if (workflowMode == WorkflowMode.Metallic)
+                SetKeyword(material, "_METALLICGLOSSMAP", material.GetTexture("_MetallicGlossMap"));
+            SetKeyword(material, "_PARALLAXMAP", material.GetTexture("_ParallaxMap"));
+            SetKeyword(material, "_DETAIL_MULX2", material.GetTexture("_DetailAlbedoMap") || material.GetTexture("_DetailNormalMap"));
+
+            // A material's GI flag internally keeps track of whether emission is enabled at all, it's enabled but has no effect
+            // or is enabled and may be modified at runtime. This state depends on the values of the current flag and emissive color.
+            // The fixup routine makes sure that the material is in the correct state if/when changes are made to the mode or color.
+            MaterialEditor.FixupEmissiveFlag(material);
+            bool shouldEmissionBeEnabled = (material.globalIlluminationFlags & MaterialGlobalIlluminationFlags.EmissiveIsBlack) == 0;
+            SetKeyword(material, "_EMISSION", shouldEmissionBeEnabled);
+
+            if (material.HasProperty("_SmoothnessTextureChannel"))
+            {
+                SetKeyword(material, "_SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A", GetSmoothnessMapChannel(material) == SmoothnessMapChannel.AlbedoAlpha);
+                SetKeyword(material, "_AVATAR_ORM_CHANNEL", GetSmoothnessMapChannel(material) == SmoothnessMapChannel.AvatarORM);
+            }
+        }
+
+        static void MaterialChanged(Material material, WorkflowMode workflowMode)
+        {
+            SetupMaterialWithBlendMode(material, (BlendMode)material.GetFloat("_Mode"));
+
+            SetMaterialKeywords(material, workflowMode);
+        }
+
+        static void SetKeyword(Material m, string keyword, bool state)
+        {
+            if (state)
+                m.EnableKeyword(keyword);
+            else
+                m.DisableKeyword(keyword);
+        }
+    }
+} // namespace UnityEditor
diff --git a/Assets/Oculus/Avatar2/Example/Editor/AvatarShaderGUI.cs.meta b/Assets/Oculus/Avatar2/Example/Editor/AvatarShaderGUI.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..d02a8d9ced529fe2de0f5e31fb81919db01929d2
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Editor/AvatarShaderGUI.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 37b05c815d62e6d4bab2a878862488c2
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Editor/Oculus.AvatarSDK2.Example.Editor.asmdef b/Assets/Oculus/Avatar2/Example/Editor/Oculus.AvatarSDK2.Example.Editor.asmdef
new file mode 100644
index 0000000000000000000000000000000000000000..9a36b26e0a7e7e478518bce5e58f4f6ab631c1c0
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Editor/Oculus.AvatarSDK2.Example.Editor.asmdef
@@ -0,0 +1,17 @@
+{
+    "name": "Oculus.AvatarSDK2.Example.Editor",
+    "references": [
+        "Oculus.AvatarSDK2",
+        "Oculus.AvatarSDK2.Example"
+    ],
+    "optionalUnityReferences": [],
+    "includePlatforms": [
+        "Editor"
+    ],
+    "excludePlatforms": [],
+    "allowUnsafeCode": false,
+    "overrideReferences": false,
+    "precompiledReferences": [],
+    "autoReferenced": true,
+    "defineConstraints": []
+}
\ No newline at end of file
diff --git a/Assets/Oculus/Avatar2/Example/Editor/Oculus.AvatarSDK2.Example.Editor.asmdef.meta b/Assets/Oculus/Avatar2/Example/Editor/Oculus.AvatarSDK2.Example.Editor.asmdef.meta
new file mode 100644
index 0000000000000000000000000000000000000000..7dd39e8bdcb3dc51140cffb205a6788f99c51963
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Editor/Oculus.AvatarSDK2.Example.Editor.asmdef.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 2ff863622f103a741a372eda84ebb771
+AssemblyDefinitionImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Editor/OvrAvatarMaterialExtensionConfigDrawer.cs b/Assets/Oculus/Avatar2/Example/Editor/OvrAvatarMaterialExtensionConfigDrawer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..c7223780dedf67ff92d093c0934078b8aebc18ea
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Editor/OvrAvatarMaterialExtensionConfigDrawer.cs
@@ -0,0 +1,349 @@
+using UnityEditor;
+using UnityEngine;
+
+namespace Oculus.Avatar2
+{
+    [CustomPropertyDrawer(typeof(OvrAvatarMaterialExtensionConfig))]
+    public class OvrAvatarMaterialExtensionConfigDrawer : PropertyDrawer
+    {
+        private const float PIXEL_HEIGHT_BETWEEN_FIELDS = 2.0f;
+
+        private const string ADD_BUTTON_TEXT = "+";
+        private const float ADD_BUTTON_PIXEL_WIDTH = 30.0f;
+
+        private const string REMOVE_BUTTON_TEXT = "-";
+        private const float REMOVE_BUTTON_PIXEL_WIDTH = 30.0f;
+
+        private const string DEFAULT_EXTENSION_NAME = "Material Extension";
+        private const string DEFAULT_ENTRY_NAME = "Extension Entry";
+        private const string DEFAULT_REPLACEMENT_NAME = "Replacement Name";
+        private const string ENTRIES_LABEL_TEXT = "Entries:";
+
+        private static readonly float ENTRY_NAME_WIDTH = EditorGUIUtility.labelWidth;
+
+        private static readonly GUIContent ADD_EXTENSION_BUTTON_TOOLTIP = new GUIContent(ADD_BUTTON_TEXT, "Add a new extension");
+        private static readonly GUIContent ADD_ENTRY_BUTTON_TOOLTIP = new GUIContent(ADD_BUTTON_TEXT, "Add a new entry mapping for the extension");
+        private static readonly GUIContent REMOVE_ENTRY_BUTTON_TOOLTIP = new GUIContent(REMOVE_BUTTON_TEXT, "Removes the last entry mapping for the extension");
+
+        private static readonly GUIContent EXTENSION_ENTRY_FOLDOUT_CONTENT =
+            new GUIContent("Extension Name:", "Edit the extension name here.");
+
+        public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
+        {
+            float totalHeight = EditorGUIUtility.singleLineHeight; // Always at least one single line for the property label
+
+            if(property.isExpanded)
+            {
+                // Add height needed for the "add new extension" button and the padding between then
+                totalHeight += EditorGUIUtility.singleLineHeight + PIXEL_HEIGHT_BETWEEN_FIELDS;
+
+                // Add in height for each extension
+                var extNamesProp = property.FindPropertyRelative(OvrAvatarMaterialExtensionConfig.ExtensionNamesPropertyName);
+                var numEntriesPerExtension =
+                    property.FindPropertyRelative(OvrAvatarMaterialExtensionConfig.EntryNamesPropertyName);
+
+                Debug.Assert(extNamesProp != null);
+                Debug.Assert(numEntriesPerExtension != null);
+                Debug.Assert(extNamesProp.arraySize == numEntriesPerExtension.arraySize);
+
+                var numExtensions = extNamesProp.arraySize;
+                for (int i = 0; i < numExtensions; i++)
+                {
+                    // Add in padding between the property label and the extension
+                    totalHeight += GetSingleExtensionPixelHeight(extNamesProp, numEntriesPerExtension, i) + PIXEL_HEIGHT_BETWEEN_FIELDS;
+                }
+            }
+
+            return totalHeight;
+        }
+
+        public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
+            label = EditorGUI.BeginProperty(position, label, property);
+
+            var foldoutRect = new Rect(position)
+            {
+                height = EditorGUIUtility.singleLineHeight,
+            };
+
+            property.isExpanded = EditorGUI.Foldout(foldoutRect, property.isExpanded, label);
+
+            if (property.isExpanded)
+            {
+                DrawExpanded(foldoutRect, property, label);
+            }
+
+            EditorGUI.EndProperty();
+        }
+
+        private void DrawExpanded(in Rect position, SerializedProperty property, GUIContent label)
+        {
+            var extensionNamesProp =
+                property.FindPropertyRelative(OvrAvatarMaterialExtensionConfig.ExtensionNamesPropertyName);
+            var entryNamesProp = property.FindPropertyRelative(OvrAvatarMaterialExtensionConfig.EntryNamesPropertyName);
+            var replacementNamesProp =
+                property.FindPropertyRelative(OvrAvatarMaterialExtensionConfig.ReplacementNamesPropertyName);
+
+            Debug.Assert(extensionNamesProp != null);
+            Debug.Assert(entryNamesProp != null);
+            Debug.Assert(replacementNamesProp != null);
+
+            Debug.Assert(extensionNamesProp.arraySize == entryNamesProp.arraySize);
+            Debug.Assert(entryNamesProp.arraySize == replacementNamesProp.arraySize);
+
+            int lvl = EditorGUI.indentLevel;
+            EditorGUI.indentLevel = lvl + 1;
+
+            Rect r = position;
+            for (int i = 0; i < extensionNamesProp.arraySize; i++)
+            {
+                // Draw the extension name at one indent level in, it has its own foldout
+                r = DrawSingleExtension(
+                    r,
+                    extensionNamesProp,
+                    entryNamesProp,
+                    replacementNamesProp,
+                    i);
+            }
+
+            EditorGUI.indentLevel = lvl;
+
+            // Draw a + and - button for adding new extension
+            DrawAddExtensionButton(r, extensionNamesProp, entryNamesProp, replacementNamesProp);
+        }
+
+        private static Rect DrawAddExtensionButton(
+            in Rect position,
+            SerializedProperty extensionNamesProp,
+            SerializedProperty entryNamesProp,
+            SerializedProperty replacementNamesProp)
+        {
+            var r = GetNextRect(position);
+            var pRect = new Rect(r.xMin, r.yMin, ADD_BUTTON_PIXEL_WIDTH, EditorGUIUtility.singleLineHeight);
+
+            if(GUI.Button(pRect, ADD_EXTENSION_BUTTON_TOOLTIP))
+            {
+                AddNewExtension(extensionNamesProp, entryNamesProp, replacementNamesProp);
+            }
+
+            return r;
+        }
+
+        private static Rect DrawAddAndRemoveEntryButtons(
+            in Rect position,
+            SerializedProperty extensionNamesProp,
+            SerializedProperty entryNamesPerExtensionProp,
+            SerializedProperty replacementNamesPerExtensionProp,
+            SerializedProperty entryNamesForThisExtension,
+            SerializedProperty replacementNamesForThisExtension,
+            int extensionIndex)
+        {
+            // Calculate placement rectangles (right aligned)
+            var r = GetNextRect(position);
+            var pRect = new Rect(r.xMax - ADD_BUTTON_PIXEL_WIDTH - REMOVE_BUTTON_PIXEL_WIDTH, r.yMin, ADD_BUTTON_PIXEL_WIDTH, EditorGUIUtility.singleLineHeight);
+            var mRect = new Rect(r.xMax - REMOVE_BUTTON_PIXEL_WIDTH, r.yMin, REMOVE_BUTTON_PIXEL_WIDTH, EditorGUIUtility.singleLineHeight);
+
+            if(GUI.Button(pRect, ADD_ENTRY_BUTTON_TOOLTIP))
+            {
+                // If clicked, add new entry
+                AddNewEntryForExtension(entryNamesForThisExtension, replacementNamesForThisExtension);
+            }
+
+            if (GUI.Button(mRect, REMOVE_ENTRY_BUTTON_TOOLTIP))
+            {
+                RemoveLastEntryForExtension(entryNamesForThisExtension, replacementNamesForThisExtension);
+
+                if (entryNamesForThisExtension.arraySize == 0)
+                {
+                    // Empty extension, remove it
+                    RemoveExtension(extensionNamesProp, entryNamesPerExtensionProp, replacementNamesPerExtensionProp, extensionIndex);
+                }
+            }
+
+            return r;
+        }
+
+        // Returns last used rect
+        private static Rect DrawSingleExtension(
+            in Rect position,
+            SerializedProperty extensionNamesProp,
+            SerializedProperty entryNamesPerExtensionProp,
+            SerializedProperty replacementNamesPerExtensionProp,
+            int extensionIndex)
+        {
+            // Draw the extension name at one indent level in, it has it's own foldout
+            // Draw foldout to left of property label
+            var r = GetNextRect(position);
+            var prop = extensionNamesProp.GetArrayElementAtIndex(extensionIndex);
+            DrawExtensionFoldout(r, prop);
+
+            if (prop.isExpanded)
+            {
+                // Add all entries for this extension
+                var keysProp = GetNestedSerializedArrayProperty(entryNamesPerExtensionProp, extensionIndex);
+                var valuesProp = GetNestedSerializedArrayProperty(replacementNamesPerExtensionProp, extensionIndex);
+
+                // Make a label that states that the following are entries
+                r = GetNextRect(r);
+                EditorGUI.LabelField(r,  ENTRIES_LABEL_TEXT);
+
+                EditorGUI.indentLevel++;
+
+                for (int i = 0; i < keysProp.arraySize; i++)
+                {
+                    r = GetNextRect(r);
+                    var valueWidth = r.width - ENTRY_NAME_WIDTH;
+                    var keyRect = new Rect(r.xMin, r.yMin, ENTRY_NAME_WIDTH, r.height);
+                    var valueRect = new Rect(keyRect.xMax, r.yMin, valueWidth, r.height);
+
+                    var keyProp = keysProp.GetArrayElementAtIndex(i);
+                    var valueProp = valuesProp.GetArrayElementAtIndex(i);
+
+                    EditorGUI.PropertyField(keyRect, keyProp, GUIContent.none, false);
+                    EditorGUI.PropertyField(valueRect, valueProp, GUIContent.none, false);
+                }
+
+                EditorGUI.indentLevel--;
+
+                // Draw +- buttons for adding and removing entries
+                r = DrawAddAndRemoveEntryButtons(
+                    r,
+                    extensionNamesProp,
+                    entryNamesPerExtensionProp,
+                    replacementNamesPerExtensionProp,
+                    keysProp,
+                    valuesProp,
+                    extensionIndex);
+            }
+
+            return r;
+        }
+
+        private static float GetSingleExtensionPixelHeight(
+            SerializedProperty extensionNamesProp,
+            SerializedProperty entriesProp,
+            int extensionIndex)
+        {
+            // Add in height for each extension
+
+            // Extension has single line at least extension name label
+            float extensionHeight = EditorGUIUtility.singleLineHeight;
+
+            var prop = extensionNamesProp.GetArrayElementAtIndex(extensionIndex);
+            if (prop.isExpanded)
+            {
+                // Add a line (and vertical padding) for the "Entries" label
+                // Add a line (and vertical padding) for the +- buttons
+                extensionHeight += 2.0f * (EditorGUIUtility.singleLineHeight + PIXEL_HEIGHT_BETWEEN_FIELDS);
+
+                // Pull num entries out
+                prop = GetNestedSerializedArrayProperty(entriesProp, extensionIndex);
+                var numEntries = prop.arraySize;
+                if (numEntries > 0)
+                {
+                    extensionHeight += (EditorGUIUtility.singleLineHeight * numEntries) +
+                           (numEntries * PIXEL_HEIGHT_BETWEEN_FIELDS);
+                }
+            }
+
+            return extensionHeight;
+        }
+
+        private static Rect GetNextRect(in Rect position)
+        {
+            var heightBetweenRects = EditorGUIUtility.singleLineHeight + PIXEL_HEIGHT_BETWEEN_FIELDS;
+            var heightOfRect = EditorGUIUtility.singleLineHeight;
+            return new Rect(position.xMin, position.yMin + heightBetweenRects, position.width, heightOfRect);
+        }
+
+        private static void AddNewExtension(
+            SerializedProperty extNameProp,
+            SerializedProperty entryNamesProp,
+            SerializedProperty replacementNamesProp)
+        {
+            // Insert empty string as new extension name
+            var extensionIndex = extNameProp.arraySize;
+            extNameProp.InsertArrayElementAtIndex(extensionIndex);
+
+            var prop = extNameProp.GetArrayElementAtIndex(extensionIndex);
+            prop.stringValue = DEFAULT_EXTENSION_NAME;
+
+            // TODO*: Check for key uniqueness
+
+            // Insert new list/array of entries
+            entryNamesProp.InsertArrayElementAtIndex(extensionIndex); // new list of entries
+            prop = GetNestedSerializedArrayProperty(entryNamesProp, extensionIndex);
+            prop.arraySize = 0; // Unity docs say Insert inserts an undefined value, so, explicitly set here
+
+            var entryIndex = 0; // Should be new list of entries, so can assume index 0
+            prop.InsertArrayElementAtIndex(entryIndex);
+            prop = prop.GetArrayElementAtIndex(entryIndex);
+            prop.stringValue = DEFAULT_ENTRY_NAME;
+
+            // Insert a new list/array of replacement names
+            replacementNamesProp.InsertArrayElementAtIndex(extensionIndex);
+            prop = GetNestedSerializedArrayProperty(replacementNamesProp, extensionIndex);
+            prop.arraySize = 0; // Unity docs say Insert inserts an undefined value, so, explicitly set here
+
+            // Add new replacement name
+            prop.InsertArrayElementAtIndex(entryIndex);
+            prop = prop.GetArrayElementAtIndex(entryIndex);
+            prop.stringValue = DEFAULT_REPLACEMENT_NAME;
+        }
+
+        private static void AddNewEntryForExtension(
+            SerializedProperty entryNamesForThisExtension,
+            SerializedProperty replacementNamesProp)
+        {
+            int entryIndex = entryNamesForThisExtension.arraySize;
+
+            // Insert a new default entry
+            entryNamesForThisExtension.InsertArrayElementAtIndex(entryIndex);
+            var prop = entryNamesForThisExtension.GetArrayElementAtIndex(entryIndex);
+            prop.stringValue = DEFAULT_ENTRY_NAME;
+
+            // TODO* Enforce name uniqueness?
+
+            // Insert a new default mapping (empty string)
+            replacementNamesProp.InsertArrayElementAtIndex(entryIndex);
+            prop = replacementNamesProp.GetArrayElementAtIndex(entryIndex);
+            prop.stringValue = DEFAULT_REPLACEMENT_NAME;
+        }
+
+        private static void RemoveLastEntryForExtension(
+            SerializedProperty entryNamesForThisExtension,
+            SerializedProperty replacementNamesForThisExtension)
+        {
+            // Seemingly have to set data to be null first, then delete....
+            // Unity docs don't say this but forums do
+            int lastIndex = entryNamesForThisExtension.arraySize - 1;
+            if (lastIndex >= 0)
+            {
+                entryNamesForThisExtension.DeleteArrayElementAtIndex(lastIndex);
+                replacementNamesForThisExtension.DeleteArrayElementAtIndex(lastIndex);
+            }
+        }
+
+        private static void RemoveExtension(
+            SerializedProperty extensionNames,
+            SerializedProperty entryNameLists,
+            SerializedProperty replacementNameLists,
+            int extensionIndex)
+        {
+            extensionNames.DeleteArrayElementAtIndex(extensionIndex);
+            entryNameLists.DeleteArrayElementAtIndex(extensionIndex);
+            replacementNameLists.DeleteArrayElementAtIndex(extensionIndex);
+        }
+
+        private static SerializedProperty GetNestedSerializedArrayProperty(SerializedProperty property, int index)
+        {
+            var prop = property.GetArrayElementAtIndex(index);
+            return prop.FindPropertyRelative(OvrAvatarMaterialExtensionConfig.InnerListProperyName);
+        }
+
+        private static void DrawExtensionFoldout(in Rect position, SerializedProperty prop)
+        {
+            EditorGUI.PropertyField(position, prop, EXTENSION_ENTRY_FOLDOUT_CONTENT, false);
+            prop.isExpanded = EditorGUI.Foldout(position, prop.isExpanded, GUIContent.none, false);
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Example/Editor/OvrAvatarMaterialExtensionConfigDrawer.cs.meta b/Assets/Oculus/Avatar2/Example/Editor/OvrAvatarMaterialExtensionConfigDrawer.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..86331da892077d63415a2b874aa51d69ee31d075
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Editor/OvrAvatarMaterialExtensionConfigDrawer.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: b914e94705b044b4aab6c3a88a3a8195
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Environments.meta b/Assets/Oculus/Avatar2/Example/Environments.meta
new file mode 100644
index 0000000000000000000000000000000000000000..7179c0b51548a48db086f585f4d1e39487d04cf6
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Environments.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: d952621c53b3da9428baadbbd49e7e0a
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt.meta b/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt.meta
new file mode 100644
index 0000000000000000000000000000000000000000..0819e90539d45a520ce964dad4452c0554e960f7
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 4262c1c66bc301042b64657c1e553ec9
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/FootprintCourt LightRig.prefab b/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/FootprintCourt LightRig.prefab
new file mode 100644
index 0000000000000000000000000000000000000000..aa1af35eb74d267e035e1430e9846376968d9fb0
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/FootprintCourt LightRig.prefab	
@@ -0,0 +1,362 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &1855828795105027457
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 2375535671387807034}
+  - component: {fileID: 6727947450747133198}
+  m_Layer: 0
+  m_Name: Reflection Probe
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &2375535671387807034
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1855828795105027457}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 5650585432433268674}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!215 &6727947450747133198
+ReflectionProbe:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1855828795105027457}
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Type: 0
+  m_Mode: 0
+  m_RefreshMode: 0
+  m_TimeSlicingMode: 0
+  m_Resolution: 1024
+  m_UpdateFrequency: 0
+  m_BoxSize: {x: 1, y: 1, z: 1}
+  m_BoxOffset: {x: 0, y: 0, z: 0}
+  m_NearClip: 0.3
+  m_FarClip: 1000
+  m_ShadowDistance: 100
+  m_ClearFlags: 1
+  m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
+  m_CullingMask:
+    serializedVersion: 2
+    m_Bits: 4294967295
+  m_IntensityMultiplier: 1
+  m_BlendDistance: 1
+  m_HDR: 1
+  m_BoxProjection: 0
+  m_RenderDynamicObjects: 0
+  m_UseOcclusionCulling: 1
+  m_Importance: 1
+  m_CustomBakedTexture: {fileID: 0}
+--- !u!1 &6176357634634757532
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 6176357634634757534}
+  - component: {fileID: 6176357634634757535}
+  m_Layer: 0
+  m_Name: Directional Light
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &6176357634634757534
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6176357634634757532}
+  m_LocalRotation: {x: 0.20199546, y: 0.24172755, z: -0.07729064, w: 0.9459344}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 5650585432433268674}
+  m_RootOrder: 2
+  m_LocalEulerAnglesHint: {x: 24.804, y: 27.995, z: -3.067}
+--- !u!108 &6176357634634757535
+Light:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6176357634634757532}
+  m_Enabled: 1
+  serializedVersion: 10
+  m_Type: 1
+  m_Shape: 0
+  m_Color: {r: 1, g: 1, b: 1, a: 1}
+  m_Intensity: 1.1
+  m_Range: 10
+  m_SpotAngle: 30
+  m_InnerSpotAngle: 21.80208
+  m_CookieSize: 10
+  m_Shadows:
+    m_Type: 0
+    m_Resolution: -1
+    m_CustomResolution: -1
+    m_Strength: 1
+    m_Bias: 0.05
+    m_NormalBias: 0.4
+    m_NearPlane: 0.2
+    m_CullingMatrixOverride:
+      e00: 1
+      e01: 0
+      e02: 0
+      e03: 0
+      e10: 0
+      e11: 1
+      e12: 0
+      e13: 0
+      e20: 0
+      e21: 0
+      e22: 1
+      e23: 0
+      e30: 0
+      e31: 0
+      e32: 0
+      e33: 1
+    m_UseCullingMatrixOverride: 0
+  m_Cookie: {fileID: 0}
+  m_DrawHalo: 0
+  m_Flare: {fileID: 0}
+  m_RenderMode: 0
+  m_CullingMask:
+    serializedVersion: 2
+    m_Bits: 4294967295
+  m_RenderingLayerMask: 1
+  m_Lightmapping: 1
+  m_LightShadowCasterMode: 0
+  m_AreaSize: {x: 1, y: 1}
+  m_BounceIntensity: 1
+  m_ColorTemperature: 6570
+  m_UseColorTemperature: 0
+  m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
+  m_UseBoundingSphereOverride: 0
+  m_UseViewFrustumForShadowCasterCull: 1
+  m_ShadowRadius: 0
+  m_ShadowAngle: 0
+--- !u!1 &6176357635736794077
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 6176357635736794076}
+  - component: {fileID: 6176357635736794079}
+  m_Layer: 0
+  m_Name: Light Probe Group
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &6176357635736794076
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6176357635736794077}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 50, y: 50, z: 50}
+  m_Children: []
+  m_Father: {fileID: 5650585432433268674}
+  m_RootOrder: 3
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!220 &6176357635736794079
+LightProbeGroup:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6176357635736794077}
+  m_Enabled: 1
+  m_SourcePositions:
+  - {x: 0, y: 0, z: 0}
+  m_Dering: 0
+--- !u!1 &6176357636261767593
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 6176357636261767605}
+  - component: {fileID: 6176357636261767594}
+  - component: {fileID: 6176357636261767595}
+  - component: {fileID: 6176357636261767592}
+  m_Layer: 0
+  m_Name: EnvironmentCube
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &6176357636261767605
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6176357636261767593}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 100, y: 100, z: 100}
+  m_Children: []
+  m_Father: {fileID: 5650585432433268674}
+  m_RootOrder: 1
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &6176357636261767594
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6176357636261767593}
+  m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &6176357636261767595
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6176357636261767593}
+  m_Enabled: 1
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RayTracingMode: 2
+  m_RayTraceProcedural: 0
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: 35f80c2b3fdf34d4aa85db1bf46f0983, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_ReceiveGI: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+  m_AdditionalVertexStreams: {fileID: 0}
+--- !u!65 &6176357636261767592
+BoxCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6176357636261767593}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Size: {x: 1, y: 1, z: 1}
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &8423574076275988413
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 5650585432433268674}
+  - component: {fileID: 6195457370031867301}
+  m_Layer: 0
+  m_Name: FootprintCourt LightRig
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &5650585432433268674
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8423574076275988413}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 2375535671387807034}
+  - {fileID: 6176357636261767605}
+  - {fileID: 6176357634634757534}
+  - {fileID: 6176357635736794076}
+  m_Father: {fileID: 0}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &6195457370031867301
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8423574076275988413}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: cc8b2bc8875ed714f874f31c2183c84b, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  CurrentExposure: 1
+  ExposureShaderParameterName: u_Exposure
+  DiffuseEnvironmentCubeMap: {fileID: 8900000, guid: b7add3f85e19c6d4d847d7dd261e1c04,
+    type: 3}
+  DiffuseEnvironmentShaderParameterNames:
+  - u_DiffuseEnvSampler
+  - u_LambertianEnvSampler
+  SpecularEnvironmentCubeMap: {fileID: 8900000, guid: 0262e7bc80729a847a319b1de2e2bfd6,
+    type: 3}
+  SpecularEnvironmentShaderParameterNames:
+  - u_SpecularEnvSampler
+  - u_GGXEnvSampler
+  SpecularMapMipCountShaderPameterName: u_MipCount
+  BrdfLutMap: {fileID: 2800000, guid: 918eb5f61eea9f6479953048b19170c0, type: 3}
+  BrdfLutShaderParameterNames:
+  - u_brdfLUT
+  - u_GGXLUT
+  CubeMapMaterial: {fileID: 2100000, guid: 35f80c2b3fdf34d4aa85db1bf46f0983, type: 2}
+  CubeMapShaderParameterName: _Tex
diff --git a/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/FootprintCourt LightRig.prefab.meta b/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/FootprintCourt LightRig.prefab.meta
new file mode 100644
index 0000000000000000000000000000000000000000..ab055b75fd82b5044fc00756f21e04933eb97bc5
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/FootprintCourt LightRig.prefab.meta	
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 9a975a19ea46baf448b38e3a8b495351
+PrefabImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/FootprintCourt Skybox.mat b/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/FootprintCourt Skybox.mat
new file mode 100644
index 0000000000000000000000000000000000000000..ee86bab41a4cdbfd261120f088fd76540973972a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/FootprintCourt Skybox.mat	
@@ -0,0 +1,85 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: FootprintCourt Skybox
+  m_Shader: {fileID: 4800000, guid: 4dda107104e02f9418ce35671143716c, type: 3}
+  m_ShaderKeywords: 
+  m_LightmapFlags: 4
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _Tex:
+        m_Texture: {fileID: 8900000, guid: 0262e7bc80729a847a319b1de2e2bfd6, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _Exposure: 1
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _Rotation: 0
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 1, g: 1, b: 1, a: 1}
+    - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
+    - _Tint: {r: 0.5, g: 0.5, b: 0.5, a: 0.5}
+  m_BuildTextureStacks: []
diff --git a/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/FootprintCourt Skybox.mat.meta b/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/FootprintCourt Skybox.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..ee2f5460fa36d160880b8b74098cfd42384e5af4
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/FootprintCourt Skybox.mat.meta	
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 35f80c2b3fdf34d4aa85db1bf46f0983
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/FootprintCourt.meta b/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/FootprintCourt.meta
new file mode 100644
index 0000000000000000000000000000000000000000..04dfcbce0c8faa20116db36a7b337ade5a2facb1
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/FootprintCourt.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 14e502b8632f7d743a1b060d8f18e16c
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/FootprintCourt.unity b/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/FootprintCourt.unity
new file mode 100644
index 0000000000000000000000000000000000000000..2a58910172c5527a24b2e1e6fdd6f7df34fac027
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/FootprintCourt.unity
@@ -0,0 +1,193 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!29 &1
+OcclusionCullingSettings:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_OcclusionBakeSettings:
+    smallestOccluder: 5
+    smallestHole: 0.25
+    backfaceThreshold: 100
+  m_SceneGUID: 00000000000000000000000000000000
+  m_OcclusionCullingData: {fileID: 0}
+--- !u!104 &2
+RenderSettings:
+  m_ObjectHideFlags: 0
+  serializedVersion: 9
+  m_Fog: 0
+  m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
+  m_FogMode: 3
+  m_FogDensity: 0.01
+  m_LinearFogStart: 0
+  m_LinearFogEnd: 300
+  m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
+  m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
+  m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
+  m_AmbientIntensity: 1
+  m_AmbientMode: 0
+  m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
+  m_SkyboxMaterial: {fileID: 2100000, guid: 35f80c2b3fdf34d4aa85db1bf46f0983, type: 2}
+  m_HaloStrength: 0.5
+  m_FlareStrength: 1
+  m_FlareFadeSpeed: 3
+  m_HaloTexture: {fileID: 0}
+  m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
+  m_DefaultReflectionMode: 0
+  m_DefaultReflectionResolution: 128
+  m_ReflectionBounces: 1
+  m_ReflectionIntensity: 1
+  m_CustomReflection: {fileID: 0}
+  m_Sun: {fileID: 0}
+  m_IndirectSpecularColor: {r: 0.5195975, g: 0.49096292, b: 0.52903646, a: 1}
+  m_UseRadianceAmbientProbe: 0
+--- !u!157 &3
+LightmapSettings:
+  m_ObjectHideFlags: 0
+  serializedVersion: 11
+  m_GIWorkflowMode: 1
+  m_GISettings:
+    serializedVersion: 2
+    m_BounceScale: 1
+    m_IndirectOutputScale: 1
+    m_AlbedoBoost: 1
+    m_EnvironmentLightingMode: 0
+    m_EnableBakedLightmaps: 0
+    m_EnableRealtimeLightmaps: 0
+  m_LightmapEditorSettings:
+    serializedVersion: 12
+    m_Resolution: 2
+    m_BakeResolution: 40
+    m_AtlasSize: 1024
+    m_AO: 0
+    m_AOMaxDistance: 1
+    m_CompAOExponent: 1
+    m_CompAOExponentDirect: 0
+    m_ExtractAmbientOcclusion: 0
+    m_Padding: 2
+    m_LightmapParameters: {fileID: 0}
+    m_LightmapsBakeMode: 1
+    m_TextureCompression: 1
+    m_FinalGather: 0
+    m_FinalGatherFiltering: 1
+    m_FinalGatherRayCount: 256
+    m_ReflectionCompression: 2
+    m_MixedBakeMode: 2
+    m_BakeBackend: 1
+    m_PVRSampling: 1
+    m_PVRDirectSampleCount: 32
+    m_PVRSampleCount: 512
+    m_PVRBounces: 2
+    m_PVREnvironmentSampleCount: 256
+    m_PVREnvironmentReferencePointCount: 2048
+    m_PVRFilteringMode: 1
+    m_PVRDenoiserTypeDirect: 1
+    m_PVRDenoiserTypeIndirect: 1
+    m_PVRDenoiserTypeAO: 1
+    m_PVRFilterTypeDirect: 0
+    m_PVRFilterTypeIndirect: 0
+    m_PVRFilterTypeAO: 0
+    m_PVREnvironmentMIS: 1
+    m_PVRCulling: 1
+    m_PVRFilteringGaussRadiusDirect: 1
+    m_PVRFilteringGaussRadiusIndirect: 5
+    m_PVRFilteringGaussRadiusAO: 2
+    m_PVRFilteringAtrousPositionSigmaDirect: 0.5
+    m_PVRFilteringAtrousPositionSigmaIndirect: 2
+    m_PVRFilteringAtrousPositionSigmaAO: 1
+    m_ExportTrainingData: 0
+    m_TrainingDataDestination: TrainingData
+    m_LightProbeSampleCountMultiplier: 4
+  m_LightingDataAsset: {fileID: 112000000, guid: f56345b5194fca64d9e02d12b22fd689,
+    type: 2}
+  m_UseShadowmask: 1
+--- !u!196 &4
+NavMeshSettings:
+  serializedVersion: 2
+  m_ObjectHideFlags: 0
+  m_BuildSettings:
+    serializedVersion: 2
+    agentTypeID: 0
+    agentRadius: 0.5
+    agentHeight: 2
+    agentSlope: 45
+    agentClimb: 0.4
+    ledgeDropHeight: 0
+    maxJumpAcrossDistance: 0
+    minRegionArea: 2
+    manualCellSize: 0
+    cellSize: 0.16666667
+    manualTileSize: 0
+    tileSize: 256
+    accuratePlacement: 0
+    debug:
+      m_Flags: 0
+  m_NavMeshData: {fileID: 0}
+--- !u!1001 &14533275
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 0}
+    m_Modifications:
+    - target: {fileID: 5650585432433268674, guid: 9a975a19ea46baf448b38e3a8b495351,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 9a975a19ea46baf448b38e3a8b495351,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 9a975a19ea46baf448b38e3a8b495351,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 9a975a19ea46baf448b38e3a8b495351,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 9a975a19ea46baf448b38e3a8b495351,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 9a975a19ea46baf448b38e3a8b495351,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 9a975a19ea46baf448b38e3a8b495351,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 9a975a19ea46baf448b38e3a8b495351,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 9a975a19ea46baf448b38e3a8b495351,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 9a975a19ea46baf448b38e3a8b495351,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 9a975a19ea46baf448b38e3a8b495351,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 8423574076275988413, guid: 9a975a19ea46baf448b38e3a8b495351,
+        type: 3}
+      propertyPath: m_Name
+      value: FootprintCourt LightRig
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: 9a975a19ea46baf448b38e3a8b495351, type: 3}
diff --git a/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/FootprintCourt.unity.meta b/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/FootprintCourt.unity.meta
new file mode 100644
index 0000000000000000000000000000000000000000..c9f34cdccb9ef040eeff408c90881eb3c2c8b985
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/FootprintCourt.unity.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: f2431c06c4a030e4380a0ca0083ad44b
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/FootprintCourt/LightingData.asset b/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/FootprintCourt/LightingData.asset
new file mode 100644
index 0000000000000000000000000000000000000000..7c9bdd8b8408461054b86e66ddcf7a6e585a7c9c
Binary files /dev/null and b/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/FootprintCourt/LightingData.asset differ
diff --git a/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/FootprintCourt/LightingData.asset.meta b/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/FootprintCourt/LightingData.asset.meta
new file mode 100644
index 0000000000000000000000000000000000000000..4f739db22bae06148d0896517ba4c0be50f2764e
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/FootprintCourt/LightingData.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: f56345b5194fca64d9e02d12b22fd689
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 112000000
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/FootprintCourt/ReflectionProbe-0.exr b/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/FootprintCourt/ReflectionProbe-0.exr
new file mode 100644
index 0000000000000000000000000000000000000000..f7b9abbb28f0246abe9e286c96de50d6a7b9337c
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/FootprintCourt/ReflectionProbe-0.exr
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f760a564f57b3512ed73f41fc5b14c1343b7fd2f99225ea0e20358e0293dc605
+size 6757527
diff --git a/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/FootprintCourt/ReflectionProbe-0.exr.meta b/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/FootprintCourt/ReflectionProbe-0.exr.meta
new file mode 100644
index 0000000000000000000000000000000000000000..c260291633a71ba13ccf3ff35defc22afd4e92ba
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/FootprintCourt/ReflectionProbe-0.exr.meta
@@ -0,0 +1,92 @@
+fileFormatVersion: 2
+guid: 69f48b3dde209434488e256c5cf781dc
+TextureImporter:
+  internalIDToNameTable: []
+  externalObjects: {}
+  serializedVersion: 11
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 1
+    sRGBTexture: 1
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 0
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 1
+  seamlessCubemap: 1
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: 2
+    aniso: 0
+    mipBias: 0
+    wrapU: 1
+    wrapV: 1
+    wrapW: 1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 1
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 0
+  textureShape: 2
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  applyGammaDecoding: 0
+  platformSettings:
+  - serializedVersion: 3
+    buildTarget: DefaultTexturePlatform
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 1
+    compressionQuality: 100
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    internalID: 0
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+    secondaryTextures: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/FootprintCourt/ReflectionProbe-1.exr b/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/FootprintCourt/ReflectionProbe-1.exr
new file mode 100644
index 0000000000000000000000000000000000000000..e51d0ff82ab4105a5d396a561cf7e963e1fa0104
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/FootprintCourt/ReflectionProbe-1.exr
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ccc7859219f59401f186a2d14b1d134c52e058fc86d68c2407c267b705488765
+size 329145
diff --git a/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/FootprintCourt/ReflectionProbe-1.exr.meta b/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/FootprintCourt/ReflectionProbe-1.exr.meta
new file mode 100644
index 0000000000000000000000000000000000000000..d5bfe9980186874f88cd59f8729dbd2bfab90c73
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/FootprintCourt/ReflectionProbe-1.exr.meta
@@ -0,0 +1,92 @@
+fileFormatVersion: 2
+guid: cc921a0fbe1fbc447b3764aa5ae60d6e
+TextureImporter:
+  internalIDToNameTable: []
+  externalObjects: {}
+  serializedVersion: 11
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 1
+    sRGBTexture: 1
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 0
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 1
+  seamlessCubemap: 1
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: 2
+    aniso: 0
+    mipBias: 0
+    wrapU: 1
+    wrapV: 1
+    wrapW: 1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 1
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 0
+  textureShape: 2
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  applyGammaDecoding: 0
+  platformSettings:
+  - serializedVersion: 3
+    buildTarget: DefaultTexturePlatform
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 1
+    compressionQuality: 100
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    internalID: 0
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+    secondaryTextures: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/diffuse-rgba16-v04.dds b/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/diffuse-rgba16-v04.dds
new file mode 100644
index 0000000000000000000000000000000000000000..e839c0183e402022e403fbf1ec39bb1b4ea3f1cc
Binary files /dev/null and b/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/diffuse-rgba16-v04.dds differ
diff --git a/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/diffuse-rgba16-v04.dds.meta b/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/diffuse-rgba16-v04.dds.meta
new file mode 100644
index 0000000000000000000000000000000000000000..9cc8f70750300137b610edb60c5354f2a1f6a990
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/diffuse-rgba16-v04.dds.meta
@@ -0,0 +1,19 @@
+fileFormatVersion: 2
+guid: b7add3f85e19c6d4d847d7dd261e1c04
+IHVImageFormatImporter:
+  externalObjects: {}
+  textureSettings:
+    serializedVersion: 2
+    filterMode: 1
+    aniso: 1
+    mipBias: 0
+    wrapU: 0
+    wrapV: 0
+    wrapW: 0
+  isReadable: 0
+  sRGBTexture: 1
+  streamingMipmaps: 0
+  streamingMipmapsPriority: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/specular-rgba16-v04.dds b/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/specular-rgba16-v04.dds
new file mode 100644
index 0000000000000000000000000000000000000000..ccd9270969553b1e46a0d7ae506226a8b4cbfdbb
Binary files /dev/null and b/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/specular-rgba16-v04.dds differ
diff --git a/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/specular-rgba16-v04.dds.meta b/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/specular-rgba16-v04.dds.meta
new file mode 100644
index 0000000000000000000000000000000000000000..507b1142c31e7c5a9b068507df9ab79d6e7250af
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Environments/FootprintCourt/specular-rgba16-v04.dds.meta
@@ -0,0 +1,19 @@
+fileFormatVersion: 2
+guid: 0262e7bc80729a847a319b1de2e2bfd6
+IHVImageFormatImporter:
+  externalObjects: {}
+  textureSettings:
+    serializedVersion: 2
+    filterMode: 1
+    aniso: 1
+    mipBias: 0
+    wrapU: 0
+    wrapV: 0
+    wrapW: 0
+  isReadable: 0
+  sRGBTexture: 1
+  streamingMipmaps: 0
+  streamingMipmapsPriority: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Oculus.AvatarSDK2.Example.asmdef b/Assets/Oculus/Avatar2/Example/Oculus.AvatarSDK2.Example.asmdef
new file mode 100644
index 0000000000000000000000000000000000000000..cae623c74c624883007e99c9ae7e87a53be4a6cf
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Oculus.AvatarSDK2.Example.asmdef
@@ -0,0 +1,29 @@
+{
+    "name": "Oculus.AvatarSDK2.Example",
+    "rootNamespace": "",
+    "references": [
+        "Oculus.AvatarSDK2",
+        "Oculus.Platform",
+        "Oculus.VR"
+    ],
+    "includePlatforms": [],
+    "excludePlatforms": [],
+    "allowUnsafeCode": false,
+    "overrideReferences": false,
+    "precompiledReferences": [],
+    "autoReferenced": true,
+    "defineConstraints": [],
+    "versionDefines": [
+        {
+            "name": "com.unity.xr.management",
+            "expression": "",
+            "define": "USING_XR_MANAGEMENT"
+        },
+        {
+            "name": "com.unity.xr.oculus",
+            "expression": "",
+            "define": "USING_XR_SDK_OCULUS"
+        }
+    ],
+    "noEngineReferences": false
+}
\ No newline at end of file
diff --git a/Assets/Oculus/Avatar2/Example/Oculus.AvatarSDK2.Example.asmdef.meta b/Assets/Oculus/Avatar2/Example/Oculus.AvatarSDK2.Example.asmdef.meta
new file mode 100644
index 0000000000000000000000000000000000000000..72d9b7b93f800f33d973a25dda7f47927201d7fb
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Oculus.AvatarSDK2.Example.asmdef.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 10ccfe6c35e29e14798a0c711aa976a0
+AssemblyDefinitionImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes.meta b/Assets/Oculus/Avatar2/Example/Scenes.meta
new file mode 100644
index 0000000000000000000000000000000000000000..826f5caa5f81f516452cadacda5a33e144ba795b
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 7d23504980250d149bb815b82e0fa67d
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample.meta b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample.meta
new file mode 100644
index 0000000000000000000000000000000000000000..96222ee627884489c628a5a1ac1952e18ab59727
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 1c6b4e216881e304c9564564394c207f
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBox.prefab b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBox.prefab
new file mode 100644
index 0000000000000000000000000000000000000000..9c3a521d85d8a1b2d9dee99bef1cb0c698143058
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBox.prefab
@@ -0,0 +1,752 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &8990045346729467266
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 8990045346729467265}
+  - component: {fileID: 8990045346729467278}
+  - component: {fileID: 8990045346729467279}
+  - component: {fileID: 8990045346729467264}
+  m_Layer: 0
+  m_Name: LeftWall
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 4294967295
+  m_IsActive: 1
+--- !u!4 &8990045346729467265
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8990045346729467266}
+  m_LocalRotation: {x: 0, y: 0, z: 0.7071068, w: 0.7071068}
+  m_LocalPosition: {x: 5, y: 5, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 8990045347437757888}
+  m_RootOrder: 1
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 90}
+--- !u!33 &8990045346729467278
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8990045346729467266}
+  m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &8990045346729467279
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8990045346729467266}
+  m_Enabled: 1
+  m_CastShadows: 0
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RayTracingMode: 2
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: ad7ef5ae8bf013549ad371a0250d222d, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_ReceiveGI: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!64 &8990045346729467264
+MeshCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8990045346729467266}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 4
+  m_Convex: 0
+  m_CookingOptions: 30
+  m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!1 &8990045346871196336
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 8990045346871196351}
+  - component: {fileID: 8990045346871196348}
+  - component: {fileID: 8990045346871196349}
+  - component: {fileID: 8990045346871196350}
+  m_Layer: 0
+  m_Name: Ceiling
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 4294967295
+  m_IsActive: 0
+--- !u!4 &8990045346871196351
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8990045346871196336}
+  m_LocalRotation: {x: 0, y: 0, z: 1, w: 0}
+  m_LocalPosition: {x: 0, y: 10, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 8990045347437757888}
+  m_RootOrder: 5
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 180}
+--- !u!33 &8990045346871196348
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8990045346871196336}
+  m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &8990045346871196349
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8990045346871196336}
+  m_Enabled: 1
+  m_CastShadows: 0
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RayTracingMode: 2
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: 92e10dd57eef28845a928f3843d68704, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_ReceiveGI: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!64 &8990045346871196350
+MeshCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8990045346871196336}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 4
+  m_Convex: 0
+  m_CookingOptions: 30
+  m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!1 &8990045347045892772
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 8990045347045892771}
+  - component: {fileID: 8990045347045892768}
+  - component: {fileID: 8990045347045892769}
+  - component: {fileID: 8990045347045892770}
+  m_Layer: 0
+  m_Name: RightWall
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 4294967295
+  m_IsActive: 1
+--- !u!4 &8990045347045892771
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8990045347045892772}
+  m_LocalRotation: {x: 0, y: 0, z: -0.7071068, w: 0.7071068}
+  m_LocalPosition: {x: -5, y: 5, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 8990045347437757888}
+  m_RootOrder: 2
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: -90}
+--- !u!33 &8990045347045892768
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8990045347045892772}
+  m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &8990045347045892769
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8990045347045892772}
+  m_Enabled: 1
+  m_CastShadows: 0
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RayTracingMode: 2
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: 2f5e2978552a8e048b99c94c1b5628a3, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_ReceiveGI: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!64 &8990045347045892770
+MeshCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8990045347045892772}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 4
+  m_Convex: 0
+  m_CookingOptions: 30
+  m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!1 &8990045347106418516
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 8990045347106418515}
+  - component: {fileID: 8990045347106418512}
+  - component: {fileID: 8990045347106418513}
+  - component: {fileID: 8990045347106418514}
+  m_Layer: 0
+  m_Name: EmissiveSkylight
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 4294967295
+  m_IsActive: 1
+--- !u!4 &8990045347106418515
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8990045347106418516}
+  m_LocalRotation: {x: 0, y: 0, z: 1, w: 0}
+  m_LocalPosition: {x: 0, y: 11, z: 0}
+  m_LocalScale: {x: 2, y: 1, z: 2}
+  m_Children: []
+  m_Father: {fileID: 8990045347437757888}
+  m_RootOrder: 6
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 180}
+--- !u!33 &8990045347106418512
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8990045347106418516}
+  m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &8990045347106418513
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8990045347106418516}
+  m_Enabled: 1
+  m_CastShadows: 0
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RayTracingMode: 2
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: 13c4a0c48b475e1449df97949f0ce046, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_ReceiveGI: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!64 &8990045347106418514
+MeshCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8990045347106418516}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 4
+  m_Convex: 0
+  m_CookingOptions: 30
+  m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!1 &8990045347265157530
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 8990045347265157529}
+  - component: {fileID: 8990045347265157542}
+  - component: {fileID: 8990045347265157543}
+  - component: {fileID: 8990045347265157528}
+  m_Layer: 0
+  m_Name: BackWall
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 4294967295
+  m_IsActive: 1
+--- !u!4 &8990045347265157529
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8990045347265157530}
+  m_LocalRotation: {x: 0.7071068, y: 0, z: 0, w: 0.7071068}
+  m_LocalPosition: {x: 0, y: 5, z: -5}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 8990045347437757888}
+  m_RootOrder: 3
+  m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0}
+--- !u!33 &8990045347265157542
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8990045347265157530}
+  m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &8990045347265157543
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8990045347265157530}
+  m_Enabled: 1
+  m_CastShadows: 0
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RayTracingMode: 2
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: 92e10dd57eef28845a928f3843d68704, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_ReceiveGI: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!64 &8990045347265157528
+MeshCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8990045347265157530}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 4
+  m_Convex: 0
+  m_CookingOptions: 30
+  m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!1 &8990045347295799852
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 8990045347295799851}
+  - component: {fileID: 8990045347295799850}
+  m_Layer: 0
+  m_Name: Light Probe Group
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &8990045347295799851
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8990045347295799852}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 8990045347437757888}
+  m_RootOrder: 7
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!220 &8990045347295799850
+LightProbeGroup:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8990045347295799852}
+  m_Enabled: 1
+  m_SourcePositions:
+  - {x: 4.9, y: 9.9, z: 4.9}
+  - {x: 4.9, y: 9.9, z: -4.9}
+  - {x: 4.9, y: 0.1, z: 4.9}
+  - {x: 4.9, y: 0.1, z: -4.9}
+  - {x: -4.9, y: 9.9, z: 4.9}
+  - {x: -4.9, y: 9.9, z: -4.9}
+  - {x: -4.9, y: 0.1, z: 4.9}
+  - {x: -4.9, y: 0.1, z: -4.9}
+  - {x: -2, y: 1.5, z: 0}
+  - {x: -1, y: 1.5, z: 0}
+  - {x: 0, y: 1.5, z: 0}
+  - {x: 1, y: 1.5, z: 0}
+  - {x: 2, y: 1.5, z: 0}
+  m_Dering: 1
+--- !u!1 &8990045347437757889
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 8990045347437757888}
+  m_Layer: 0
+  m_Name: CornellBox
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 4294967295
+  m_IsActive: 1
+--- !u!4 &8990045347437757888
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8990045347437757889}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 8990045348184701530}
+  - {fileID: 8990045346729467265}
+  - {fileID: 8990045347045892771}
+  - {fileID: 8990045347265157529}
+  - {fileID: 8990045347759033556}
+  - {fileID: 8990045346871196351}
+  - {fileID: 8990045347106418515}
+  - {fileID: 8990045347295799851}
+  m_Father: {fileID: 0}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &8990045347759033557
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 8990045347759033556}
+  - component: {fileID: 8990045347759033553}
+  - component: {fileID: 8990045347759033554}
+  - component: {fileID: 8990045347759033555}
+  m_Layer: 0
+  m_Name: FrontWall
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 4294967295
+  m_IsActive: 1
+--- !u!4 &8990045347759033556
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8990045347759033557}
+  m_LocalRotation: {x: -0.7071068, y: 0, z: 0, w: 0.7071068}
+  m_LocalPosition: {x: 0, y: 5, z: 5}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 8990045347437757888}
+  m_RootOrder: 4
+  m_LocalEulerAnglesHint: {x: -90, y: 0, z: 0}
+--- !u!33 &8990045347759033553
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8990045347759033557}
+  m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &8990045347759033554
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8990045347759033557}
+  m_Enabled: 1
+  m_CastShadows: 0
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RayTracingMode: 2
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: 92e10dd57eef28845a928f3843d68704, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_ReceiveGI: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!64 &8990045347759033555
+MeshCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8990045347759033557}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 4
+  m_Convex: 0
+  m_CookingOptions: 30
+  m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!1 &8990045348184701531
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 8990045348184701530}
+  - component: {fileID: 8990045348184701543}
+  - component: {fileID: 8990045348184701528}
+  - component: {fileID: 8990045348184701529}
+  m_Layer: 0
+  m_Name: Floor
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 4294967295
+  m_IsActive: 1
+--- !u!4 &8990045348184701530
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8990045348184701531}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 8990045347437757888}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &8990045348184701543
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8990045348184701531}
+  m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &8990045348184701528
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8990045348184701531}
+  m_Enabled: 1
+  m_CastShadows: 0
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RayTracingMode: 2
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: 92e10dd57eef28845a928f3843d68704, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_ReceiveGI: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!64 &8990045348184701529
+MeshCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8990045348184701531}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 4
+  m_Convex: 0
+  m_CookingOptions: 30
+  m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBox.prefab.meta b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBox.prefab.meta
new file mode 100644
index 0000000000000000000000000000000000000000..895b4cbafe7a74a3bf8611dd40edc09fc1b68699
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBox.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: f063d4cd6379df242a758b346de1fdd4
+PrefabImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample.meta b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample.meta
new file mode 100644
index 0000000000000000000000000000000000000000..c2502316f314121a6a99e4515298d65b33dfb3ac
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: edd24f63c44f86549ba4cf8501a243f4
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample.unity b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample.unity
new file mode 100644
index 0000000000000000000000000000000000000000..1a4a2b4768fa30d494ebd92ecfc32a54257554a8
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample.unity
@@ -0,0 +1,1245 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!29 &1
+OcclusionCullingSettings:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_OcclusionBakeSettings:
+    smallestOccluder: 5
+    smallestHole: 0.25
+    backfaceThreshold: 100
+  m_SceneGUID: 00000000000000000000000000000000
+  m_OcclusionCullingData: {fileID: 0}
+--- !u!104 &2
+RenderSettings:
+  m_ObjectHideFlags: 0
+  serializedVersion: 9
+  m_Fog: 0
+  m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
+  m_FogMode: 3
+  m_FogDensity: 0.01
+  m_LinearFogStart: 0
+  m_LinearFogEnd: 300
+  m_AmbientSkyColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
+  m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
+  m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
+  m_AmbientIntensity: 0
+  m_AmbientMode: 3
+  m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
+  m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0}
+  m_HaloStrength: 0.5
+  m_FlareStrength: 1
+  m_FlareFadeSpeed: 3
+  m_HaloTexture: {fileID: 0}
+  m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
+  m_DefaultReflectionMode: 0
+  m_DefaultReflectionResolution: 128
+  m_ReflectionBounces: 1
+  m_ReflectionIntensity: 1
+  m_CustomReflection: {fileID: 0}
+  m_Sun: {fileID: 705507994}
+  m_IndirectSpecularColor: {r: 0.16223332, g: 0.20294926, b: 0.2825347, a: 1}
+  m_UseRadianceAmbientProbe: 0
+--- !u!157 &3
+LightmapSettings:
+  m_ObjectHideFlags: 0
+  serializedVersion: 11
+  m_GIWorkflowMode: 1
+  m_GISettings:
+    serializedVersion: 2
+    m_BounceScale: 1
+    m_IndirectOutputScale: 1
+    m_AlbedoBoost: 1
+    m_EnvironmentLightingMode: 0
+    m_EnableBakedLightmaps: 1
+    m_EnableRealtimeLightmaps: 0
+  m_LightmapEditorSettings:
+    serializedVersion: 12
+    m_Resolution: 2
+    m_BakeResolution: 40
+    m_AtlasSize: 32
+    m_AO: 0
+    m_AOMaxDistance: 1
+    m_CompAOExponent: 1
+    m_CompAOExponentDirect: 0
+    m_ExtractAmbientOcclusion: 0
+    m_Padding: 2
+    m_LightmapParameters: {fileID: 0}
+    m_LightmapsBakeMode: 1
+    m_TextureCompression: 1
+    m_FinalGather: 0
+    m_FinalGatherFiltering: 1
+    m_FinalGatherRayCount: 256
+    m_ReflectionCompression: 2
+    m_MixedBakeMode: 2
+    m_BakeBackend: 0
+    m_PVRSampling: 1
+    m_PVRDirectSampleCount: 32
+    m_PVRSampleCount: 500
+    m_PVRBounces: 2
+    m_PVREnvironmentSampleCount: 500
+    m_PVREnvironmentReferencePointCount: 2048
+    m_PVRFilteringMode: 2
+    m_PVRDenoiserTypeDirect: 0
+    m_PVRDenoiserTypeIndirect: 0
+    m_PVRDenoiserTypeAO: 0
+    m_PVRFilterTypeDirect: 0
+    m_PVRFilterTypeIndirect: 0
+    m_PVRFilterTypeAO: 0
+    m_PVREnvironmentMIS: 0
+    m_PVRCulling: 1
+    m_PVRFilteringGaussRadiusDirect: 1
+    m_PVRFilteringGaussRadiusIndirect: 5
+    m_PVRFilteringGaussRadiusAO: 2
+    m_PVRFilteringAtrousPositionSigmaDirect: 0.5
+    m_PVRFilteringAtrousPositionSigmaIndirect: 2
+    m_PVRFilteringAtrousPositionSigmaAO: 1
+    m_ExportTrainingData: 0
+    m_TrainingDataDestination: TrainingData
+    m_LightProbeSampleCountMultiplier: 4
+  m_LightingDataAsset: {fileID: 112000000, guid: 632c5b76c10844847a0cf85a0cc2cb61,
+    type: 2}
+  m_UseShadowmask: 1
+--- !u!196 &4
+NavMeshSettings:
+  serializedVersion: 2
+  m_ObjectHideFlags: 0
+  m_BuildSettings:
+    serializedVersion: 2
+    agentTypeID: 0
+    agentRadius: 0.5
+    agentHeight: 2
+    agentSlope: 45
+    agentClimb: 0.4
+    ledgeDropHeight: 0
+    maxJumpAcrossDistance: 0
+    minRegionArea: 2
+    manualCellSize: 0
+    cellSize: 0.16666667
+    manualTileSize: 0
+    tileSize: 256
+    accuratePlacement: 0
+    debug:
+      m_Flags: 0
+  m_NavMeshData: {fileID: 0}
+--- !u!1 &67984799
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 67984800}
+  m_Layer: 0
+  m_Name: AbientTestSpheres
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &67984800
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 67984799}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 541741037}
+  - {fileID: 810868983}
+  - {fileID: 1580375981}
+  - {fileID: 813087153}
+  m_Father: {fileID: 838494930}
+  m_RootOrder: 1
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1001 &214634506
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 0}
+    m_Modifications:
+    - target: {fileID: 1396242425075152212, guid: dd63b8a05dd83d44b975b3c1dece2159,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: dd63b8a05dd83d44b975b3c1dece2159,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: -2
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: dd63b8a05dd83d44b975b3c1dece2159,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: dd63b8a05dd83d44b975b3c1dece2159,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: dd63b8a05dd83d44b975b3c1dece2159,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.8660254
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: dd63b8a05dd83d44b975b3c1dece2159,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: dd63b8a05dd83d44b975b3c1dece2159,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0.5
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: dd63b8a05dd83d44b975b3c1dece2159,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: dd63b8a05dd83d44b975b3c1dece2159,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: dd63b8a05dd83d44b975b3c1dece2159,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 60
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: dd63b8a05dd83d44b975b3c1dece2159,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152213, guid: dd63b8a05dd83d44b975b3c1dece2159,
+        type: 3}
+      propertyPath: m_Name
+      value: ShowcaseAvatarsGroup3
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: dd63b8a05dd83d44b975b3c1dece2159, type: 3}
+--- !u!1 &332462890
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 332462894}
+  - component: {fileID: 332462893}
+  - component: {fileID: 332462892}
+  - component: {fileID: 332462891}
+  m_Layer: 0
+  m_Name: Base
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 0
+--- !u!136 &332462891
+CapsuleCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 332462890}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  m_Radius: 0.5000001
+  m_Height: 2
+  m_Direction: 1
+  m_Center: {x: 0.000000059604645, y: 0, z: -0.00000008940697}
+--- !u!23 &332462892
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 332462890}
+  m_Enabled: 1
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RayTracingMode: 2
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: 6500e5e25ff64254aa97291b203a9902, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_ReceiveGI: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!33 &332462893
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 332462890}
+  m_Mesh: {fileID: 10206, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!4 &332462894
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 332462890}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 10, y: 0.1, z: 10}
+  m_Children: []
+  m_Father: {fileID: 838494930}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1001 &423485114
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 0}
+    m_Modifications:
+    - target: {fileID: 5848076533691757917, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_Name
+      value: AvatarSdkManagerHorizon
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 6
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: d0377a811c2a95841ba015ae0b2c8d45, type: 3}
+--- !u!1 &541741033
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 541741037}
+  - component: {fileID: 541741036}
+  - component: {fileID: 541741035}
+  - component: {fileID: 541741034}
+  m_Layer: 0
+  m_Name: AmbientTestSphere1
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!135 &541741034
+SphereCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 541741033}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Radius: 0.5
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!23 &541741035
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 541741033}
+  m_Enabled: 1
+  m_CastShadows: 1
+  m_ReceiveShadows: 0
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RayTracingMode: 2
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: 92e10dd57eef28845a928f3843d68704, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_ReceiveGI: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!33 &541741036
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 541741033}
+  m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!4 &541741037
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 541741033}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 3, y: 1, z: -3}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 67984800}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &705507993
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 705507995}
+  - component: {fileID: 705507994}
+  m_Layer: 0
+  m_Name: Directional Light
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!108 &705507994
+Light:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 705507993}
+  m_Enabled: 1
+  serializedVersion: 10
+  m_Type: 1
+  m_Shape: 0
+  m_Color: {r: 1, g: 1, b: 1, a: 1}
+  m_Intensity: 1
+  m_Range: 10
+  m_SpotAngle: 30
+  m_InnerSpotAngle: 21.80208
+  m_CookieSize: 10
+  m_Shadows:
+    m_Type: 2
+    m_Resolution: -1
+    m_CustomResolution: -1
+    m_Strength: 1
+    m_Bias: 0.05
+    m_NormalBias: 0.4
+    m_NearPlane: 0.2
+    m_CullingMatrixOverride:
+      e00: 1
+      e01: 0
+      e02: 0
+      e03: 0
+      e10: 0
+      e11: 1
+      e12: 0
+      e13: 0
+      e20: 0
+      e21: 0
+      e22: 1
+      e23: 0
+      e30: 0
+      e31: 0
+      e32: 0
+      e33: 1
+    m_UseCullingMatrixOverride: 0
+  m_Cookie: {fileID: 0}
+  m_DrawHalo: 0
+  m_Flare: {fileID: 0}
+  m_RenderMode: 0
+  m_CullingMask:
+    serializedVersion: 2
+    m_Bits: 4294967295
+  m_RenderingLayerMask: 1
+  m_Lightmapping: 1
+  m_LightShadowCasterMode: 0
+  m_AreaSize: {x: 1, y: 1}
+  m_BounceIntensity: 1
+  m_ColorTemperature: 6570
+  m_UseColorTemperature: 0
+  m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
+  m_UseBoundingSphereOverride: 0
+  m_ShadowRadius: 0
+  m_ShadowAngle: 0
+--- !u!4 &705507995
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 705507993}
+  m_LocalRotation: {x: 0.7242991, y: -0.37069848, z: 0.28188178, w: 0.5084449}
+  m_LocalPosition: {x: 0, y: 3, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 0}
+  m_RootOrder: 3
+  m_LocalEulerAnglesHint: {x: 71, y: -185.53, z: -129.73901}
+--- !u!1 &810868982
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 810868983}
+  - component: {fileID: 810868986}
+  - component: {fileID: 810868985}
+  - component: {fileID: 810868984}
+  m_Layer: 0
+  m_Name: AmbientTestSphere2
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &810868983
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 810868982}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: -3, y: 1, z: -3}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 67984800}
+  m_RootOrder: 1
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!135 &810868984
+SphereCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 810868982}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Radius: 0.5
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!23 &810868985
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 810868982}
+  m_Enabled: 1
+  m_CastShadows: 1
+  m_ReceiveShadows: 0
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RayTracingMode: 2
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: 92e10dd57eef28845a928f3843d68704, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_ReceiveGI: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!33 &810868986
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 810868982}
+  m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!1 &813087152
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 813087153}
+  - component: {fileID: 813087156}
+  - component: {fileID: 813087155}
+  - component: {fileID: 813087154}
+  m_Layer: 0
+  m_Name: AmbientTestSphere4
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &813087153
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 813087152}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: -3, y: 1, z: 3}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 67984800}
+  m_RootOrder: 3
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!135 &813087154
+SphereCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 813087152}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Radius: 0.5
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!23 &813087155
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 813087152}
+  m_Enabled: 1
+  m_CastShadows: 1
+  m_ReceiveShadows: 0
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RayTracingMode: 2
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: 92e10dd57eef28845a928f3843d68704, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_ReceiveGI: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!33 &813087156
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 813087152}
+  m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!1 &817976727
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 817976729}
+  - component: {fileID: 817976728}
+  m_Layer: 0
+  m_Name: SceneSwitcher
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!114 &817976728
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 817976727}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: ea6e0c6cd67922c48bbb5939267828e9, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+  _nextSceneInput:
+    controllerMask: 2
+    buttonMask: 1
+  _prevSceneInput:
+    controllerMask: 1
+    buttonMask: 1
+--- !u!4 &817976729
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 817976727}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 0}
+  m_RootOrder: 5
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &838494929
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 838494930}
+  m_Layer: 0
+  m_Name: Environment
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &838494930
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 838494929}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 332462894}
+  - {fileID: 67984800}
+  - {fileID: 852371399}
+  m_Father: {fileID: 0}
+  m_RootOrder: 4
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!4 &852371399 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: 8990045347437757888, guid: f063d4cd6379df242a758b346de1fdd4,
+    type: 3}
+  m_PrefabInstance: {fileID: 8990045346660901383}
+  m_PrefabAsset: {fileID: 0}
+--- !u!1 &963194225
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 963194228}
+  - component: {fileID: 963194227}
+  - component: {fileID: 963194226}
+  - component: {fileID: 963194229}
+  m_Layer: 0
+  m_Name: Main Camera
+  m_TagString: MainCamera
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!81 &963194226
+AudioListener:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 963194225}
+  m_Enabled: 1
+--- !u!20 &963194227
+Camera:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 963194225}
+  m_Enabled: 1
+  serializedVersion: 2
+  m_ClearFlags: 1
+  m_BackGroundColor: {r: 0, g: 0, b: 0, a: 0}
+  m_projectionMatrixMode: 1
+  m_GateFitMode: 2
+  m_FOVAxisMode: 0
+  m_SensorSize: {x: 36, y: 24}
+  m_LensShift: {x: 0, y: 0}
+  m_FocalLength: 50
+  m_NormalizedViewPortRect:
+    serializedVersion: 2
+    x: 0
+    y: 0
+    width: 1
+    height: 1
+  near clip plane: 0.3
+  far clip plane: 1000
+  field of view: 60
+  orthographic: 0
+  orthographic size: 5
+  m_Depth: -1
+  m_CullingMask:
+    serializedVersion: 2
+    m_Bits: 4294967295
+  m_RenderingPath: -1
+  m_TargetTexture: {fileID: 0}
+  m_TargetDisplay: 0
+  m_TargetEye: 3
+  m_HDR: 1
+  m_AllowMSAA: 1
+  m_AllowDynamicResolution: 0
+  m_ForceIntoRT: 0
+  m_OcclusionCulling: 1
+  m_StereoConvergence: 10
+  m_StereoSeparation: 0.022
+--- !u!4 &963194228
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 963194225}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1026869889}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &963194229
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 963194225}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 5a2a9c34df4095f47b9ca8f975175f5b, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+  m_Device: 0
+  m_PoseSource: 2
+  m_PoseProviderComponent: {fileID: 0}
+  m_TrackingType: 0
+  m_UpdateType: 0
+  m_UseRelativeTransform: 0
+--- !u!1 &1026869888
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1026869889}
+  - component: {fileID: 1026869890}
+  - component: {fileID: 1026869891}
+  m_Layer: 0
+  m_Name: CameraOffset
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1026869889
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1026869888}
+  m_LocalRotation: {x: 0, y: 1, z: 0, w: 0}
+  m_LocalPosition: {x: 0, y: 0, z: 4.5}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 963194228}
+  m_Father: {fileID: 0}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!114 &1026869890
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1026869888}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 5b0eaccd645fc5641ba1cf708f6fd678, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+  mirrorMovement: 0
+  movementSpeed: 3
+--- !u!114 &1026869891
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1026869888}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 4e93961d3dfb44505a84d80b1f7fffef, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+  _mode: 1
+  _coordinateSpace: 0
+  _position: {x: 0, y: 1.5, z: 0}
+  _rotation: {x: 0, y: 0, z: 0}
+--- !u!1001 &1535922085
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 0}
+    m_Modifications:
+    - target: {fileID: 1396242425075152212, guid: e44c77800a51b2c41bb0ce66366a6866,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 2
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: e44c77800a51b2c41bb0ce66366a6866,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 2
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: e44c77800a51b2c41bb0ce66366a6866,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: e44c77800a51b2c41bb0ce66366a6866,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: e44c77800a51b2c41bb0ce66366a6866,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.8660254
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: e44c77800a51b2c41bb0ce66366a6866,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: e44c77800a51b2c41bb0ce66366a6866,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: -0.5
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: e44c77800a51b2c41bb0ce66366a6866,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: e44c77800a51b2c41bb0ce66366a6866,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: e44c77800a51b2c41bb0ce66366a6866,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: -60
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: e44c77800a51b2c41bb0ce66366a6866,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152213, guid: e44c77800a51b2c41bb0ce66366a6866,
+        type: 3}
+      propertyPath: m_Name
+      value: ShowcaseAvatarsGroup4
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: e44c77800a51b2c41bb0ce66366a6866, type: 3}
+--- !u!1 &1580375980
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1580375981}
+  - component: {fileID: 1580375984}
+  - component: {fileID: 1580375983}
+  - component: {fileID: 1580375982}
+  m_Layer: 0
+  m_Name: AmbientTestSphere3
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1580375981
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1580375980}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 3, y: 1, z: 3}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 67984800}
+  m_RootOrder: 2
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!135 &1580375982
+SphereCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1580375980}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Radius: 0.5
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!23 &1580375983
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1580375980}
+  m_Enabled: 1
+  m_CastShadows: 1
+  m_ReceiveShadows: 0
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RayTracingMode: 2
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: 92e10dd57eef28845a928f3843d68704, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_ReceiveGI: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!33 &1580375984
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1580375980}
+  m_Mesh: {fileID: 10207, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!1001 &8990045346660901383
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 838494930}
+    m_Modifications:
+    - target: {fileID: 8990045347437757888, guid: f063d4cd6379df242a758b346de1fdd4,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 2
+      objectReference: {fileID: 0}
+    - target: {fileID: 8990045347437757888, guid: f063d4cd6379df242a758b346de1fdd4,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 8990045347437757888, guid: f063d4cd6379df242a758b346de1fdd4,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 8990045347437757888, guid: f063d4cd6379df242a758b346de1fdd4,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 8990045347437757888, guid: f063d4cd6379df242a758b346de1fdd4,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 8990045347437757888, guid: f063d4cd6379df242a758b346de1fdd4,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 8990045347437757888, guid: f063d4cd6379df242a758b346de1fdd4,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 8990045347437757888, guid: f063d4cd6379df242a758b346de1fdd4,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 8990045347437757888, guid: f063d4cd6379df242a758b346de1fdd4,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 8990045347437757888, guid: f063d4cd6379df242a758b346de1fdd4,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 8990045347437757888, guid: f063d4cd6379df242a758b346de1fdd4,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 8990045347437757889, guid: f063d4cd6379df242a758b346de1fdd4,
+        type: 3}
+      propertyPath: m_Name
+      value: CornellBox
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: f063d4cd6379df242a758b346de1fdd4, type: 3}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample.unity.meta b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample.unity.meta
new file mode 100644
index 0000000000000000000000000000000000000000..e5161dfeadafa502f67e6768b24a9b25ad03f006
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample.unity.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 9498d24606a8d4e4eab962e19ff08cfb
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/LightingData.asset b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/LightingData.asset
new file mode 100644
index 0000000000000000000000000000000000000000..7f26d348d5631fdc0bb0609cefcfa1c0565224e2
Binary files /dev/null and b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/LightingData.asset differ
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/LightingData.asset.meta b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/LightingData.asset.meta
new file mode 100644
index 0000000000000000000000000000000000000000..3212cc62a1c035bd9eadcbe8fadaa0ee0202611a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/LightingData.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 632c5b76c10844847a0cf85a0cc2cb61
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 112000000
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-0_comp_dir.png b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-0_comp_dir.png
new file mode 100644
index 0000000000000000000000000000000000000000..30ce2c3af1ba33f1d1ce42adc4c34a3e83117b82
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-0_comp_dir.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:04982a034d490091710818a497a245960e7007c2ae375af623e347d2d6279e57
+size 1316
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-0_comp_dir.png.meta b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-0_comp_dir.png.meta
new file mode 100644
index 0000000000000000000000000000000000000000..dd68a5ee204482603355ca135a8137593a7203e5
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-0_comp_dir.png.meta
@@ -0,0 +1,88 @@
+fileFormatVersion: 2
+guid: 7f88e105881f6d7498b8e2da31ed149e
+TextureImporter:
+  fileIDToRecycleName: {}
+  externalObjects: {}
+  serializedVersion: 9
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 1
+    sRGBTexture: 0
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 1
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: 1
+    aniso: 3
+    mipBias: 0
+    wrapU: 1
+    wrapV: 1
+    wrapW: 1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 1
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 0
+  textureShape: 1
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  platformSettings:
+  - serializedVersion: 2
+    buildTarget: DefaultTexturePlatform
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 2
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-0_comp_light.exr b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-0_comp_light.exr
new file mode 100644
index 0000000000000000000000000000000000000000..df442b3912a5b4d3943f37dc0d56394a3bad5989
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-0_comp_light.exr
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:e02a778f54b1c28dd7bb5cf220197eb48a742a3aa35b40459087dbf168224a6f
+size 4458
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-0_comp_light.exr.meta b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-0_comp_light.exr.meta
new file mode 100644
index 0000000000000000000000000000000000000000..22ee2b09e7f8a2426655a0766ec25ced101c32ae
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-0_comp_light.exr.meta
@@ -0,0 +1,88 @@
+fileFormatVersion: 2
+guid: ae40d54ef53bc564a8a38509b033e48a
+TextureImporter:
+  fileIDToRecycleName: {}
+  externalObjects: {}
+  serializedVersion: 9
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 1
+    sRGBTexture: 1
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 1
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: 1
+    aniso: 3
+    mipBias: 0
+    wrapU: 1
+    wrapV: 1
+    wrapW: 1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 0
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 6
+  textureShape: 1
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  platformSettings:
+  - serializedVersion: 2
+    buildTarget: DefaultTexturePlatform
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 2
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-0_comp_shadowmask.png b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-0_comp_shadowmask.png
new file mode 100644
index 0000000000000000000000000000000000000000..47c0e10aa67c2fddd3ceefce7420bb589140223a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-0_comp_shadowmask.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:12a571a1a319f099f15b9610a2cc7709be4964a85a406b4a322408f119c6b023
+size 104
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-0_comp_shadowmask.png.meta b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-0_comp_shadowmask.png.meta
new file mode 100644
index 0000000000000000000000000000000000000000..fdf1578918a522e77488149732267ab7d9f1de3c
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-0_comp_shadowmask.png.meta
@@ -0,0 +1,89 @@
+fileFormatVersion: 2
+guid: 8837bb73c4aa7824ea9e5bc99c556831
+TextureImporter:
+  fileIDToRecycleName: {}
+  externalObjects: {}
+  serializedVersion: 9
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 1
+    sRGBTexture: 0
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 1
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: 1
+    aniso: 3
+    mipBias: 0
+    wrapU: 1
+    wrapV: 1
+    wrapW: 1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 1
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 0
+  textureShape: 1
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  platformSettings:
+  - serializedVersion: 2
+    buildTarget: DefaultTexturePlatform
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: 2
+    textureCompression: 0
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-1_comp_dir.png b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-1_comp_dir.png
new file mode 100644
index 0000000000000000000000000000000000000000..62f932c15c29daaf61b2307e224cd7d593d8f180
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-1_comp_dir.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:f399a259624b1c397dd66f5e98c899fa7b6ee771bb030fae3c7cf045b5cc726a
+size 1025
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-1_comp_dir.png.meta b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-1_comp_dir.png.meta
new file mode 100644
index 0000000000000000000000000000000000000000..fe5662da76da649caeb73b8fe7686626fdf0ecb3
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-1_comp_dir.png.meta
@@ -0,0 +1,88 @@
+fileFormatVersion: 2
+guid: 03da7c6bb355e7b43a26868bfa1828e8
+TextureImporter:
+  fileIDToRecycleName: {}
+  externalObjects: {}
+  serializedVersion: 9
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 1
+    sRGBTexture: 0
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 1
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: 1
+    aniso: 3
+    mipBias: 0
+    wrapU: 1
+    wrapV: 1
+    wrapW: 1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 1
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 0
+  textureShape: 1
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  platformSettings:
+  - serializedVersion: 2
+    buildTarget: DefaultTexturePlatform
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 2
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-1_comp_light.exr b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-1_comp_light.exr
new file mode 100644
index 0000000000000000000000000000000000000000..40c65453ed7fd38705e1eb2b6d0ad73171ef2bff
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-1_comp_light.exr
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:632941b85f6f23fadb4e157913c9bd1955e8548d787c60e5fc7dd529429ee0eb
+size 3788
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-1_comp_light.exr.meta b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-1_comp_light.exr.meta
new file mode 100644
index 0000000000000000000000000000000000000000..cebf83e967be30e6539ca46f36ebaeec12468181
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-1_comp_light.exr.meta
@@ -0,0 +1,88 @@
+fileFormatVersion: 2
+guid: 228054185034fe14b9bbd07a2469957d
+TextureImporter:
+  fileIDToRecycleName: {}
+  externalObjects: {}
+  serializedVersion: 9
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 1
+    sRGBTexture: 1
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 1
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: 1
+    aniso: 3
+    mipBias: 0
+    wrapU: 1
+    wrapV: 1
+    wrapW: 1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 0
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 6
+  textureShape: 1
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  platformSettings:
+  - serializedVersion: 2
+    buildTarget: DefaultTexturePlatform
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 2
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-1_comp_shadowmask.png b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-1_comp_shadowmask.png
new file mode 100644
index 0000000000000000000000000000000000000000..c9265bc7f94ba214ede5642cf25159d855985ac7
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-1_comp_shadowmask.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a447139922146637365855042a9f85581717b0b8ab0b9a735726a8715db97a58
+size 124
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-1_comp_shadowmask.png.meta b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-1_comp_shadowmask.png.meta
new file mode 100644
index 0000000000000000000000000000000000000000..61c409520be33a0f9da5980e378b2cd779c2d398
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-1_comp_shadowmask.png.meta
@@ -0,0 +1,89 @@
+fileFormatVersion: 2
+guid: 53776063731869e47a81ccc0be6c20e5
+TextureImporter:
+  fileIDToRecycleName: {}
+  externalObjects: {}
+  serializedVersion: 9
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 1
+    sRGBTexture: 0
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 1
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: 1
+    aniso: 3
+    mipBias: 0
+    wrapU: 1
+    wrapV: 1
+    wrapW: 1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 1
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 0
+  textureShape: 1
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  platformSettings:
+  - serializedVersion: 2
+    buildTarget: DefaultTexturePlatform
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: 2
+    textureCompression: 0
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-2_comp_dir.png b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-2_comp_dir.png
new file mode 100644
index 0000000000000000000000000000000000000000..a286f76f64b64fed5be9f15b5896a507d15be3bf
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-2_comp_dir.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:033e02b10518d3fc8ffc865f8b61b75d87344dc63a1fb2d10466400fac0024f3
+size 1117
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-2_comp_dir.png.meta b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-2_comp_dir.png.meta
new file mode 100644
index 0000000000000000000000000000000000000000..3a4d44496b76323b150dab893d2900d13f3b4515
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-2_comp_dir.png.meta
@@ -0,0 +1,88 @@
+fileFormatVersion: 2
+guid: c6d23c7d9af6d2047bb6214f43397901
+TextureImporter:
+  fileIDToRecycleName: {}
+  externalObjects: {}
+  serializedVersion: 9
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 1
+    sRGBTexture: 0
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 1
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: 1
+    aniso: 3
+    mipBias: 0
+    wrapU: 1
+    wrapV: 1
+    wrapW: 1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 1
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 0
+  textureShape: 1
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  platformSettings:
+  - serializedVersion: 2
+    buildTarget: DefaultTexturePlatform
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 2
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-2_comp_light.exr b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-2_comp_light.exr
new file mode 100644
index 0000000000000000000000000000000000000000..43952384bafffbdc40196562d269dbcc32ca1daa
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-2_comp_light.exr
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9b56d496b35e5e734969ef7e3becf4f1f6774973c714352ea1a8f126efc9ed31
+size 4096
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-2_comp_light.exr.meta b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-2_comp_light.exr.meta
new file mode 100644
index 0000000000000000000000000000000000000000..acb26e7801eeaf10fd6d0b529de91fba2b503c01
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-2_comp_light.exr.meta
@@ -0,0 +1,88 @@
+fileFormatVersion: 2
+guid: 9b9b8efc4fcd2384da4d5b555e19fea1
+TextureImporter:
+  fileIDToRecycleName: {}
+  externalObjects: {}
+  serializedVersion: 9
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 1
+    sRGBTexture: 1
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 1
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: 1
+    aniso: 3
+    mipBias: 0
+    wrapU: 1
+    wrapV: 1
+    wrapW: 1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 0
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 6
+  textureShape: 1
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  platformSettings:
+  - serializedVersion: 2
+    buildTarget: DefaultTexturePlatform
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 2
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-2_comp_shadowmask.png b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-2_comp_shadowmask.png
new file mode 100644
index 0000000000000000000000000000000000000000..47c0e10aa67c2fddd3ceefce7420bb589140223a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-2_comp_shadowmask.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:12a571a1a319f099f15b9610a2cc7709be4964a85a406b4a322408f119c6b023
+size 104
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-2_comp_shadowmask.png.meta b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-2_comp_shadowmask.png.meta
new file mode 100644
index 0000000000000000000000000000000000000000..9e0058a4fec8aedc3f88334521212dea6658fce9
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-2_comp_shadowmask.png.meta
@@ -0,0 +1,89 @@
+fileFormatVersion: 2
+guid: 671c8d395e8c5324ca324930f5fdd123
+TextureImporter:
+  fileIDToRecycleName: {}
+  externalObjects: {}
+  serializedVersion: 9
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 1
+    sRGBTexture: 0
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 1
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: 1
+    aniso: 3
+    mipBias: 0
+    wrapU: 1
+    wrapV: 1
+    wrapW: 1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 1
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 0
+  textureShape: 1
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  platformSettings:
+  - serializedVersion: 2
+    buildTarget: DefaultTexturePlatform
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: 2
+    textureCompression: 0
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-3_comp_dir.png b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-3_comp_dir.png
new file mode 100644
index 0000000000000000000000000000000000000000..06b3cfc94afba4206397350ae148ec3510345fb2
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-3_comp_dir.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:fca37e0739448cb292d156efab742b8ca04615edba0bfdfe5a55119eea83fcd5
+size 1061
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-3_comp_dir.png.meta b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-3_comp_dir.png.meta
new file mode 100644
index 0000000000000000000000000000000000000000..fd575c60c5ecbff2ccd3c4cd7f4484107a7125a3
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-3_comp_dir.png.meta
@@ -0,0 +1,89 @@
+fileFormatVersion: 2
+guid: 7bd4ea42a762c984ea797a066dcde3d3
+TextureImporter:
+  fileIDToRecycleName: {}
+  externalObjects: {}
+  serializedVersion: 9
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 1
+    sRGBTexture: 0
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 1
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: 1
+    aniso: 3
+    mipBias: 0
+    wrapU: 1
+    wrapV: 1
+    wrapW: 1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 1
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 0
+  textureShape: 1
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  platformSettings:
+  - serializedVersion: 2
+    buildTarget: DefaultTexturePlatform
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 2
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-3_comp_light.exr b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-3_comp_light.exr
new file mode 100644
index 0000000000000000000000000000000000000000..3a11bb698db4ad3a3918c2b9b4aca5d6f95a6373
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-3_comp_light.exr
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:615ca045dfc77a86349fcb3a9fa00227cdb66549ea29748407362a4c500b3c4c
+size 4075
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-3_comp_light.exr.meta b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-3_comp_light.exr.meta
new file mode 100644
index 0000000000000000000000000000000000000000..2bfc5009420bba910d8933e843fc71285c8a1524
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-3_comp_light.exr.meta
@@ -0,0 +1,88 @@
+fileFormatVersion: 2
+guid: bb0cfd9c2edd41c439b9c20e5c755e9f
+TextureImporter:
+  fileIDToRecycleName: {}
+  externalObjects: {}
+  serializedVersion: 9
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 1
+    sRGBTexture: 1
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 1
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: 1
+    aniso: 3
+    mipBias: 0
+    wrapU: 1
+    wrapV: 1
+    wrapW: 1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 0
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 6
+  textureShape: 1
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  platformSettings:
+  - serializedVersion: 2
+    buildTarget: DefaultTexturePlatform
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 2
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-3_comp_shadowmask.png b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-3_comp_shadowmask.png
new file mode 100644
index 0000000000000000000000000000000000000000..c9265bc7f94ba214ede5642cf25159d855985ac7
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-3_comp_shadowmask.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a447139922146637365855042a9f85581717b0b8ab0b9a735726a8715db97a58
+size 124
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-3_comp_shadowmask.png.meta b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-3_comp_shadowmask.png.meta
new file mode 100644
index 0000000000000000000000000000000000000000..67288db8983622afd4703002abd9584f17f341cf
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-3_comp_shadowmask.png.meta
@@ -0,0 +1,89 @@
+fileFormatVersion: 2
+guid: ea2c212cf561e1641beb479fb46dba6c
+TextureImporter:
+  fileIDToRecycleName: {}
+  externalObjects: {}
+  serializedVersion: 9
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 1
+    sRGBTexture: 0
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 1
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: 1
+    aniso: 3
+    mipBias: 0
+    wrapU: 1
+    wrapV: 1
+    wrapW: 1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 1
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 0
+  textureShape: 1
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  platformSettings:
+  - serializedVersion: 2
+    buildTarget: DefaultTexturePlatform
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: 2
+    textureCompression: 0
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-4_comp_dir.png b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-4_comp_dir.png
new file mode 100644
index 0000000000000000000000000000000000000000..f63285247bb284a724322dc86f2567b69c7502a6
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-4_comp_dir.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:c8dd7e75e317b3c27429236effcdf6a90bc57f226aefab6e4c2774419039c579
+size 1018
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-4_comp_dir.png.meta b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-4_comp_dir.png.meta
new file mode 100644
index 0000000000000000000000000000000000000000..4f4e34a0f3845c558c7e11b0e9c48f14a857109a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-4_comp_dir.png.meta
@@ -0,0 +1,89 @@
+fileFormatVersion: 2
+guid: 902b817bd6ca46840a169ed35ffeeddb
+TextureImporter:
+  fileIDToRecycleName: {}
+  externalObjects: {}
+  serializedVersion: 9
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 1
+    sRGBTexture: 0
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 1
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: 1
+    aniso: 3
+    mipBias: 0
+    wrapU: 1
+    wrapV: 1
+    wrapW: 1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 1
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 0
+  textureShape: 1
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  platformSettings:
+  - serializedVersion: 2
+    buildTarget: DefaultTexturePlatform
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 2
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-4_comp_light.exr b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-4_comp_light.exr
new file mode 100644
index 0000000000000000000000000000000000000000..d8400d5c5d2f3c78197eab76d0e852b284711c9e
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-4_comp_light.exr
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:97548e7abdfe06f0e1ac69d2c45f12d1a0760d0469d67e83b8a5762ba98e72bf
+size 4255
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-4_comp_light.exr.meta b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-4_comp_light.exr.meta
new file mode 100644
index 0000000000000000000000000000000000000000..b6164f0cbc3592a83d122698547add07260f7dd4
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-4_comp_light.exr.meta
@@ -0,0 +1,88 @@
+fileFormatVersion: 2
+guid: 6ace8e8fc8ba5c44daf16d80b635a63e
+TextureImporter:
+  fileIDToRecycleName: {}
+  externalObjects: {}
+  serializedVersion: 9
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 1
+    sRGBTexture: 1
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 1
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: 1
+    aniso: 3
+    mipBias: 0
+    wrapU: 1
+    wrapV: 1
+    wrapW: 1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 0
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 6
+  textureShape: 1
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  platformSettings:
+  - serializedVersion: 2
+    buildTarget: DefaultTexturePlatform
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 2
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-4_comp_shadowmask.png b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-4_comp_shadowmask.png
new file mode 100644
index 0000000000000000000000000000000000000000..c9265bc7f94ba214ede5642cf25159d855985ac7
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-4_comp_shadowmask.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a447139922146637365855042a9f85581717b0b8ab0b9a735726a8715db97a58
+size 124
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-4_comp_shadowmask.png.meta b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-4_comp_shadowmask.png.meta
new file mode 100644
index 0000000000000000000000000000000000000000..9cfa3e1991cca2b27455b2efa9d0097f7770c0da
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-4_comp_shadowmask.png.meta
@@ -0,0 +1,89 @@
+fileFormatVersion: 2
+guid: abf234b5bd0473f4ca53e2b73dec5cd0
+TextureImporter:
+  fileIDToRecycleName: {}
+  externalObjects: {}
+  serializedVersion: 9
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 1
+    sRGBTexture: 0
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 1
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: 1
+    aniso: 3
+    mipBias: 0
+    wrapU: 1
+    wrapV: 1
+    wrapW: 1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 1
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 0
+  textureShape: 1
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  platformSettings:
+  - serializedVersion: 2
+    buildTarget: DefaultTexturePlatform
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: 2
+    textureCompression: 0
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-5_comp_dir.png b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-5_comp_dir.png
new file mode 100644
index 0000000000000000000000000000000000000000..2a99582d04a076da3d97130c1e9c1753ecd844b4
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-5_comp_dir.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:21936fb5a03455bbe2057f2cc5ca3c91ab70fced761f839d2bbb4663cc9e3177
+size 1092
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-5_comp_dir.png.meta b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-5_comp_dir.png.meta
new file mode 100644
index 0000000000000000000000000000000000000000..3f9980f72b7898cd8a172524c74cbdfebb8f2d61
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-5_comp_dir.png.meta
@@ -0,0 +1,89 @@
+fileFormatVersion: 2
+guid: 7c25a97e02ff10a4b95d9f472801bea8
+TextureImporter:
+  fileIDToRecycleName: {}
+  externalObjects: {}
+  serializedVersion: 9
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 1
+    sRGBTexture: 0
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 1
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: 1
+    aniso: 3
+    mipBias: 0
+    wrapU: 1
+    wrapV: 1
+    wrapW: 1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 1
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 0
+  textureShape: 1
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  platformSettings:
+  - serializedVersion: 2
+    buildTarget: DefaultTexturePlatform
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 2
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-5_comp_light.exr b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-5_comp_light.exr
new file mode 100644
index 0000000000000000000000000000000000000000..e058cda2442e377d2233036c3941dc14fde108fd
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-5_comp_light.exr
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:56dce41393007f4d2f01f0bac67a209099a0ad56a7732ad5f87bf3851b5410f1
+size 4208
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-5_comp_light.exr.meta b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-5_comp_light.exr.meta
new file mode 100644
index 0000000000000000000000000000000000000000..828c2b1fe06ff0c8168a1b60a6b29702c4e6380c
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-5_comp_light.exr.meta
@@ -0,0 +1,88 @@
+fileFormatVersion: 2
+guid: 4622605c75b4cde42af656eee36f68fb
+TextureImporter:
+  fileIDToRecycleName: {}
+  externalObjects: {}
+  serializedVersion: 9
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 1
+    sRGBTexture: 1
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 1
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: 1
+    aniso: 3
+    mipBias: 0
+    wrapU: 1
+    wrapV: 1
+    wrapW: 1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 0
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 6
+  textureShape: 1
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  platformSettings:
+  - serializedVersion: 2
+    buildTarget: DefaultTexturePlatform
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 2
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-5_comp_shadowmask.png b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-5_comp_shadowmask.png
new file mode 100644
index 0000000000000000000000000000000000000000..47c0e10aa67c2fddd3ceefce7420bb589140223a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-5_comp_shadowmask.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:12a571a1a319f099f15b9610a2cc7709be4964a85a406b4a322408f119c6b023
+size 104
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-5_comp_shadowmask.png.meta b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-5_comp_shadowmask.png.meta
new file mode 100644
index 0000000000000000000000000000000000000000..e909f79687f6bbf8b5edd6229e3eaa43bf1c5950
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/Lightmap-5_comp_shadowmask.png.meta
@@ -0,0 +1,89 @@
+fileFormatVersion: 2
+guid: f9923faa203c1e246a7c7afac48b7a2b
+TextureImporter:
+  fileIDToRecycleName: {}
+  externalObjects: {}
+  serializedVersion: 9
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 1
+    sRGBTexture: 0
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 1
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: 1
+    aniso: 3
+    mipBias: 0
+    wrapU: 1
+    wrapV: 1
+    wrapW: 1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 1
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 0
+  textureShape: 1
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  platformSettings:
+  - serializedVersion: 2
+    buildTarget: DefaultTexturePlatform
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: 2
+    textureCompression: 0
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/ReflectionProbe-0.exr b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/ReflectionProbe-0.exr
new file mode 100644
index 0000000000000000000000000000000000000000..38716e7c19240a6186bdd1c20d2cba5ffc2cb2eb
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/ReflectionProbe-0.exr
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3eb1707d850ce8d48c345e3e8310c02166fc158c34fc2be0a1595834e85b3363
+size 120341
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/ReflectionProbe-0.exr.meta b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/ReflectionProbe-0.exr.meta
new file mode 100644
index 0000000000000000000000000000000000000000..d66faee2dd3cb637c9b88478e2be6bdc0da161ad
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CornellBoxLightingExample/CornellBoxLightingExample/ReflectionProbe-0.exr.meta
@@ -0,0 +1,89 @@
+fileFormatVersion: 2
+guid: 95a02f94ce4fe894db37b3a3733dce02
+TextureImporter:
+  fileIDToRecycleName:
+    8900000: generatedCubemap
+  externalObjects: {}
+  serializedVersion: 9
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 1
+    sRGBTexture: 1
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 0
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 1
+  seamlessCubemap: 1
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: 2
+    aniso: 0
+    mipBias: 0
+    wrapU: 1
+    wrapV: 1
+    wrapW: 1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 1
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 0
+  textureShape: 2
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  platformSettings:
+  - serializedVersion: 2
+    buildTarget: DefaultTexturePlatform
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 1
+    compressionQuality: 100
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CustomHandPoseExample.meta b/Assets/Oculus/Avatar2/Example/Scenes/CustomHandPoseExample.meta
new file mode 100644
index 0000000000000000000000000000000000000000..c01b806c136992d7ae30a3b34b2ab60ec9bcbc0d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CustomHandPoseExample.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 360e4b797a6cf964dbd02e708d7696c1
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CustomHandPoseExample/CustomHandLeftPrefab.prefab b/Assets/Oculus/Avatar2/Example/Scenes/CustomHandPoseExample/CustomHandLeftPrefab.prefab
new file mode 100644
index 0000000000000000000000000000000000000000..2f2a395679a81ca05bac4863f442788f529fb76c
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CustomHandPoseExample/CustomHandLeftPrefab.prefab
@@ -0,0 +1,1025 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &7534483590661669314
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7534483590661669315}
+  - component: {fileID: 7534483590661669312}
+  m_Layer: 0
+  m_Name: LeftHandPinkyDistal_jnt
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &7534483590661669315
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483590661669314}
+  m_LocalRotation: {x: -0.0012858327, y: -0.047311552, z: 0.02750256, w: 0.99850065}
+  m_LocalPosition: {x: -0.020311384, y: -2.842171e-16, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 7534483591560014179}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &7534483590661669312
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483590661669314}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 04a0706dc00d22946b853246bd3f1e88, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  jointType: 20
+--- !u!1 &7534483590689977635
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7534483590689977632}
+  - component: {fileID: 7534483590689977633}
+  m_Layer: 0
+  m_Name: LeftHandThumbProximal_jnt
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &7534483590689977632
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483590689977635}
+  m_LocalRotation: {x: 0.0020685187, y: 0.07442744, z: 0.08631405, w: 0.9934819}
+  m_LocalPosition: {x: -0.03251291, y: 3.1974422e-16, z: 5.684342e-16}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 7534483591011148764}
+  m_Father: {fileID: 7534483591658906422}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &7534483590689977633
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483590689977635}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 04a0706dc00d22946b853246bd3f1e88, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  jointType: 3
+--- !u!1 &7534483590725842348
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7534483590725842349}
+  - component: {fileID: 7534483590725842386}
+  m_Layer: 0
+  m_Name: LeftHandMiddleProximal_jnt
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &7534483590725842349
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483590725842348}
+  m_LocalRotation: {x: -0.00023860218, y: 0.05173803, z: -0.048005182, w: 0.9975062}
+  m_LocalPosition: {x: -0.060709894, y: 2.842171e-16, z: -6.4392934e-17}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 7534483592347472683}
+  m_Father: {fileID: 7534483592361457383}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &7534483590725842386
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483590725842348}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 04a0706dc00d22946b853246bd3f1e88, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  jointType: 10
+--- !u!1 &7534483590979559469
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7534483590979559506}
+  - component: {fileID: 7534483590979559507}
+  m_Layer: 0
+  m_Name: LeftHandIndexMeta_jnt
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &7534483590979559506
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483590979559469}
+  m_LocalRotation: {x: 0.026563136, y: -0.07177673, z: -0.044874344, w: 0.9960566}
+  m_LocalPosition: {x: -0.035417024, y: 0.0015878897, z: -0.014909195}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 7534483592706830507}
+  m_Father: {fileID: 7534483591645067927}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &7534483590979559507
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483590979559469}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 04a0706dc00d22946b853246bd3f1e88, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  jointType: 5
+--- !u!1 &7534483591011148767
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7534483591011148764}
+  - component: {fileID: 7534483591011148765}
+  m_Layer: 0
+  m_Name: LeftHandThumbDistal_jnt
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &7534483591011148764
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483591011148767}
+  m_LocalRotation: {x: -0.0000001166187, y: -0.000000118219795, z: 0.0000016075634,
+    w: 1}
+  m_LocalPosition: {x: -0.03379309, y: -2.2426505e-16, z: 5.684342e-16}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 7534483590689977632}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &7534483591011148765
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483591011148767}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 04a0706dc00d22946b853246bd3f1e88, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  jointType: 4
+--- !u!1 &7534483591198407098
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7534483591198407099}
+  - component: {fileID: 7534483591198407096}
+  m_Layer: 0
+  m_Name: LeftHandIndexDistal_jnt
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &7534483591198407099
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483591198407098}
+  m_LocalRotation: {x: -0.004131363, y: 0.03168613, z: 0.07016855, w: 0.9970232}
+  m_LocalPosition: {x: -0.024303643, y: -8.5265126e-16, z: 6.217249e-17}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 7534483591532427919}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &7534483591198407096
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483591198407098}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 04a0706dc00d22946b853246bd3f1e88, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  jointType: 8
+--- !u!1 &7534483591333830244
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7534483591333830245}
+  - component: {fileID: 7534483591333830250}
+  m_Layer: 0
+  m_Name: LeftHandThumbTrapezium_jnt
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &7534483591333830245
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483591333830244}
+  m_LocalRotation: {x: 0.54354405, y: -0.4130089, z: 0.09877131, w: 0.72403574}
+  m_LocalPosition: {x: -0.020069301, y: 0.011554099, z: -0.0104965195}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 7534483591658906422}
+  m_Father: {fileID: 7534483591645067927}
+  m_RootOrder: 4
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &7534483591333830250
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483591333830244}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 04a0706dc00d22946b853246bd3f1e88, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  jointType: 1
+--- !u!1 &7534483591441504100
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7534483591441504101}
+  - component: {fileID: 7534483591441504106}
+  m_Layer: 0
+  m_Name: LeftHandPinkyProximal_jnt
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &7534483591441504101
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483591441504100}
+  m_LocalRotation: {x: -0.0038702635, y: -0.14000061, z: -0.027361698, w: 0.98976576}
+  m_LocalPosition: {x: -0.04565054, y: -2.842171e-16, z: 7.105427e-17}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 7534483591560014179}
+  m_Father: {fileID: 7534483591715428512}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &7534483591441504106
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483591441504100}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 04a0706dc00d22946b853246bd3f1e88, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  jointType: 18
+--- !u!1 &7534483591495391306
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7534483591495391307}
+  - component: {fileID: 7534483591495391304}
+  m_Layer: 0
+  m_Name: LeftHandRingIntermediate_jnt
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &7534483591495391307
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483591495391306}
+  m_LocalRotation: {x: 0.000015345924, y: 0.0025972838, z: -0.005766683, w: 0.99998003}
+  m_LocalPosition: {x: -0.0389961, y: -1.1368684e-15, z: -3.5527136e-17}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 7534483592738953755}
+  m_Father: {fileID: 7534483591872103671}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &7534483591495391304
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483591495391306}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 04a0706dc00d22946b853246bd3f1e88, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  jointType: 15
+--- !u!1 &7534483591532427918
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7534483591532427919}
+  - component: {fileID: 7534483591532427916}
+  m_Layer: 0
+  m_Name: LeftHandIndexIntermediate_jnt
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &7534483591532427919
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483591532427918}
+  m_LocalRotation: {x: 0.000025190593, y: 0.007028435, z: -0.0034760437, w: 0.9999693}
+  m_LocalPosition: {x: -0.0379273, y: 2.842171e-16, z: 1.1546319e-16}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 7534483591198407099}
+  m_Father: {fileID: 7534483592706830507}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &7534483591532427916
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483591532427918}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 04a0706dc00d22946b853246bd3f1e88, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  jointType: 7
+--- !u!1 &7534483591560014178
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7534483591560014179}
+  - component: {fileID: 7534483591560014176}
+  m_Layer: 0
+  m_Name: LeftHandPinkyIntermediate_jnt
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &7534483591560014179
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483591560014178}
+  m_LocalRotation: {x: -0.0006031977, y: 0.043400448, z: 0.011686176, w: 0.9989892}
+  m_LocalPosition: {x: -0.030720409, y: -2.842171e-16, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 7534483590661669315}
+  m_Father: {fileID: 7534483591441504101}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &7534483591560014176
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483591560014178}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 04a0706dc00d22946b853246bd3f1e88, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  jointType: 19
+--- !u!1 &7534483591645067926
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7534483591645067927}
+  - component: {fileID: 7534483591645067925}
+  - component: {fileID: 7534483591645067924}
+  m_Layer: 0
+  m_Name: LeftHandWrist_jnt
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &7534483591645067927
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483591645067926}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 7534483590979559506}
+  - {fileID: 7534483592361457383}
+  - {fileID: 7534483591715428512}
+  - {fileID: 7534483592336711395}
+  - {fileID: 7534483591333830245}
+  - {fileID: 7534483592080915777}
+  m_Father: {fileID: 7534483591808359255}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &7534483591645067925
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483591645067926}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: e23dd79c8b6109246bfc3b35e6ac44cd, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  color: {r: 0, g: 0.9167204, b: 1, a: 1}
+  drawAxes: 1
+  axisSize: 0.01
+--- !u!114 &7534483591645067924
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483591645067926}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 04a0706dc00d22946b853246bd3f1e88, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  jointType: 0
+--- !u!1 &7534483591658906417
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7534483591658906422}
+  - component: {fileID: 7534483591658906423}
+  m_Layer: 0
+  m_Name: LeftHandThumbMeta_jnt
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &7534483591658906422
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483591658906417}
+  m_LocalRotation: {x: 0.012428552, y: 0.12783232, z: -0.11990103, w: 0.9844431}
+  m_LocalPosition: {x: -0.024852559, y: 1.0480505e-15, z: -5.684342e-16}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 7534483590689977632}
+  m_Father: {fileID: 7534483591333830245}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &7534483591658906423
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483591658906417}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 04a0706dc00d22946b853246bd3f1e88, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  jointType: 2
+--- !u!1 &7534483591715428515
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7534483591715428512}
+  - component: {fileID: 7534483591715428513}
+  m_Layer: 0
+  m_Name: LeftHandPinkyMeta_jnt
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &7534483591715428512
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483591715428515}
+  m_LocalRotation: {x: -0.117761955, y: 0.13805799, z: -0.031055925, w: 0.9829078}
+  m_LocalPosition: {x: -0.034073558, y: 0.009419835, z: 0.022998573}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 7534483591441504101}
+  m_Father: {fileID: 7534483591645067927}
+  m_RootOrder: 2
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &7534483591715428513
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483591715428515}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 04a0706dc00d22946b853246bd3f1e88, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  jointType: 17
+--- !u!1 &7534483591808359254
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7534483591808359255}
+  m_Layer: 0
+  m_Name: CustomHandLeftPrefab
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &7534483591808359255
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483591808359254}
+  m_LocalRotation: {x: 1, y: 0, z: 0, w: 0}
+  m_LocalPosition: {x: 0, y: 1, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 7534483591645067927}
+  m_Father: {fileID: 0}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 180, y: 0, z: 0}
+--- !u!1 &7534483591872103670
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7534483591872103671}
+  - component: {fileID: 7534483591872103668}
+  m_Layer: 0
+  m_Name: LeftHandRingProximal_jnt
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &7534483591872103671
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483591872103670}
+  m_LocalRotation: {x: -0.0001367788, y: -0.008993682, z: -0.015205948, w: 0.99984396}
+  m_LocalPosition: {x: -0.05478463, y: -5.684342e-16, z: 7.105427e-17}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 7534483591495391307}
+  m_Father: {fileID: 7534483592336711395}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &7534483591872103668
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483591872103670}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 04a0706dc00d22946b853246bd3f1e88, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  jointType: 14
+--- !u!1 &7534483592080915776
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7534483592080915777}
+  m_Layer: 0
+  m_Name: LeftHandTracker_tgt
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &7534483592080915777
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483592080915776}
+  m_LocalRotation: {x: 0.5695382, y: 0.48004413, z: -0.5064326, w: -0.4344076}
+  m_LocalPosition: {x: -0.10407422, y: 0.037334505, z: -0.040733628}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 7534483591645067927}
+  m_RootOrder: 5
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &7534483592336711394
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7534483592336711395}
+  - component: {fileID: 7534483592336711392}
+  m_Layer: 0
+  m_Name: LeftHandRingMeta_jnt
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &7534483592336711395
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483592336711394}
+  m_LocalRotation: {x: -0.056348428, y: 0.043691937, z: -0.030203488, w: 0.99699736}
+  m_LocalPosition: {x: -0.034218278, y: 0.0029601145, z: 0.012878803}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 7534483591872103671}
+  m_Father: {fileID: 7534483591645067927}
+  m_RootOrder: 3
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &7534483592336711392
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483592336711394}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 04a0706dc00d22946b853246bd3f1e88, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  jointType: 13
+--- !u!1 &7534483592347472682
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7534483592347472683}
+  - component: {fileID: 7534483592347472680}
+  m_Layer: 0
+  m_Name: LeftHandMiddleIntermediate_jnt
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &7534483592347472683
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483592347472682}
+  m_LocalRotation: {x: -0.000008648547, y: 0.0044007967, z: 0.0019290085, w: 0.9999885}
+  m_LocalPosition: {x: -0.04292699, y: -2.842171e-16, z: 7.9936054e-17}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 7534483592624920130}
+  m_Father: {fileID: 7534483590725842349}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &7534483592347472680
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483592347472682}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 04a0706dc00d22946b853246bd3f1e88, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  jointType: 11
+--- !u!1 &7534483592361457382
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7534483592361457383}
+  - component: {fileID: 7534483592361457380}
+  m_Layer: 0
+  m_Name: LeftHandMiddleMeta_jnt
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &7534483592361457383
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483592361457382}
+  m_LocalRotation: {x: -0.013067971, y: -0.016595608, z: -0.022907652, w: 0.9995144}
+  m_LocalPosition: {x: -0.03503387, y: -0.00021060446, z: 0.00032449898}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 7534483590725842349}
+  m_Father: {fileID: 7534483591645067927}
+  m_RootOrder: 1
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &7534483592361457380
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483592361457382}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 04a0706dc00d22946b853246bd3f1e88, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  jointType: 9
+--- !u!1 &7534483592624920157
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7534483592624920130}
+  - component: {fileID: 7534483592624920131}
+  m_Layer: 0
+  m_Name: LeftHandMiddleDistal_jnt
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &7534483592624920130
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483592624920157}
+  m_LocalRotation: {x: -0.0022474818, y: 0.009686223, z: 0.09261643, w: 0.99565226}
+  m_LocalPosition: {x: -0.027549583, y: 0, z: -1.7763568e-17}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 7534483592347472683}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &7534483592624920131
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483592624920157}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 04a0706dc00d22946b853246bd3f1e88, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  jointType: 12
+--- !u!1 &7534483592706830506
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7534483592706830507}
+  - component: {fileID: 7534483592706830504}
+  m_Layer: 0
+  m_Name: LeftHandIndexProximal_jnt
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &7534483592706830507
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483592706830506}
+  m_LocalRotation: {x: 0.00010576015, y: 0.104163654, z: -0.001009803, w: 0.99455965}
+  m_LocalPosition: {x: -0.061460007, y: -1.1368684e-15, z: 3.5527136e-17}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 7534483591532427919}
+  m_Father: {fileID: 7534483590979559506}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &7534483592706830504
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483592706830506}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 04a0706dc00d22946b853246bd3f1e88, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  jointType: 6
+--- !u!1 &7534483592738953754
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7534483592738953755}
+  - component: {fileID: 7534483592738953752}
+  m_Layer: 0
+  m_Name: LeftHandRingDistal_jnt
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &7534483592738953755
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483592738953754}
+  m_LocalRotation: {x: 0.0007043812, y: -0.027317839, z: 0.027049277, w: 0.99926054}
+  m_LocalPosition: {x: -0.026573397, y: 0, z: 3.5527136e-17}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 7534483591495391307}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &7534483592738953752
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7534483592738953754}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 04a0706dc00d22946b853246bd3f1e88, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  jointType: 16
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CustomHandPoseExample/CustomHandLeftPrefab.prefab.meta b/Assets/Oculus/Avatar2/Example/Scenes/CustomHandPoseExample/CustomHandLeftPrefab.prefab.meta
new file mode 100644
index 0000000000000000000000000000000000000000..4923a177f225474d661f15bfcac2f0d2b3a879bc
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CustomHandPoseExample/CustomHandLeftPrefab.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 8bd35c20b077959449e5f44de472b784
+PrefabImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CustomHandPoseExample/CustomHandPoseExample.unity b/Assets/Oculus/Avatar2/Example/Scenes/CustomHandPoseExample/CustomHandPoseExample.unity
new file mode 100644
index 0000000000000000000000000000000000000000..1dfe313e92c9f9c81a4d39334561a469ce96c6da
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CustomHandPoseExample/CustomHandPoseExample.unity
@@ -0,0 +1,2323 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!29 &1
+OcclusionCullingSettings:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_OcclusionBakeSettings:
+    smallestOccluder: 5
+    smallestHole: 0.25
+    backfaceThreshold: 100
+  m_SceneGUID: 00000000000000000000000000000000
+  m_OcclusionCullingData: {fileID: 0}
+--- !u!104 &2
+RenderSettings:
+  m_ObjectHideFlags: 0
+  serializedVersion: 9
+  m_Fog: 0
+  m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
+  m_FogMode: 3
+  m_FogDensity: 0.01
+  m_LinearFogStart: 0
+  m_LinearFogEnd: 300
+  m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
+  m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
+  m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
+  m_AmbientIntensity: 1
+  m_AmbientMode: 0
+  m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
+  m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0}
+  m_HaloStrength: 0.5
+  m_FlareStrength: 1
+  m_FlareFadeSpeed: 3
+  m_HaloTexture: {fileID: 0}
+  m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
+  m_DefaultReflectionMode: 0
+  m_DefaultReflectionResolution: 128
+  m_ReflectionBounces: 1
+  m_ReflectionIntensity: 1
+  m_CustomReflection: {fileID: 0}
+  m_Sun: {fileID: 0}
+  m_IndirectSpecularColor: {r: 0.23315533, g: 0.35322547, b: 0.046696816, a: 1}
+  m_UseRadianceAmbientProbe: 0
+--- !u!157 &3
+LightmapSettings:
+  m_ObjectHideFlags: 0
+  serializedVersion: 11
+  m_GIWorkflowMode: 0
+  m_GISettings:
+    serializedVersion: 2
+    m_BounceScale: 1
+    m_IndirectOutputScale: 1
+    m_AlbedoBoost: 1
+    m_EnvironmentLightingMode: 0
+    m_EnableBakedLightmaps: 1
+    m_EnableRealtimeLightmaps: 1
+  m_LightmapEditorSettings:
+    serializedVersion: 12
+    m_Resolution: 2
+    m_BakeResolution: 40
+    m_AtlasSize: 1024
+    m_AO: 0
+    m_AOMaxDistance: 1
+    m_CompAOExponent: 1
+    m_CompAOExponentDirect: 0
+    m_ExtractAmbientOcclusion: 0
+    m_Padding: 2
+    m_LightmapParameters: {fileID: 0}
+    m_LightmapsBakeMode: 1
+    m_TextureCompression: 1
+    m_FinalGather: 0
+    m_FinalGatherFiltering: 1
+    m_FinalGatherRayCount: 256
+    m_ReflectionCompression: 2
+    m_MixedBakeMode: 2
+    m_BakeBackend: 1
+    m_PVRSampling: 1
+    m_PVRDirectSampleCount: 32
+    m_PVRSampleCount: 500
+    m_PVRBounces: 2
+    m_PVREnvironmentSampleCount: 500
+    m_PVREnvironmentReferencePointCount: 2048
+    m_PVRFilteringMode: 2
+    m_PVRDenoiserTypeDirect: 0
+    m_PVRDenoiserTypeIndirect: 0
+    m_PVRDenoiserTypeAO: 0
+    m_PVRFilterTypeDirect: 0
+    m_PVRFilterTypeIndirect: 0
+    m_PVRFilterTypeAO: 0
+    m_PVREnvironmentMIS: 0
+    m_PVRCulling: 1
+    m_PVRFilteringGaussRadiusDirect: 1
+    m_PVRFilteringGaussRadiusIndirect: 5
+    m_PVRFilteringGaussRadiusAO: 2
+    m_PVRFilteringAtrousPositionSigmaDirect: 0.5
+    m_PVRFilteringAtrousPositionSigmaIndirect: 2
+    m_PVRFilteringAtrousPositionSigmaAO: 1
+    m_ExportTrainingData: 0
+    m_TrainingDataDestination: TrainingData
+    m_LightProbeSampleCountMultiplier: 4
+  m_LightingDataAsset: {fileID: 0}
+  m_UseShadowmask: 1
+--- !u!196 &4
+NavMeshSettings:
+  serializedVersion: 2
+  m_ObjectHideFlags: 0
+  m_BuildSettings:
+    serializedVersion: 2
+    agentTypeID: 0
+    agentRadius: 0.5
+    agentHeight: 2
+    agentSlope: 45
+    agentClimb: 0.4
+    ledgeDropHeight: 0
+    maxJumpAcrossDistance: 0
+    minRegionArea: 2
+    manualCellSize: 0
+    cellSize: 0.16666667
+    manualTileSize: 0
+    tileSize: 256
+    accuratePlacement: 0
+    debug:
+      m_Flags: 0
+  m_NavMeshData: {fileID: 0}
+--- !u!1001 &554161777
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 1882209500}
+    m_Modifications:
+    - target: {fileID: 4684227533121428177, guid: c3dc5412b9c44234a8ae43008b7a6979,
+        type: 3}
+      propertyPath: m_Name
+      value: TrackingTransforms
+      objectReference: {fileID: 0}
+    - target: {fileID: 5338449701420077780, guid: c3dc5412b9c44234a8ae43008b7a6979,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5338449701420077780, guid: c3dc5412b9c44234a8ae43008b7a6979,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5338449701420077780, guid: c3dc5412b9c44234a8ae43008b7a6979,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5338449701420077780, guid: c3dc5412b9c44234a8ae43008b7a6979,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5338449701420077780, guid: c3dc5412b9c44234a8ae43008b7a6979,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 5338449701420077780, guid: c3dc5412b9c44234a8ae43008b7a6979,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5338449701420077780, guid: c3dc5412b9c44234a8ae43008b7a6979,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5338449701420077780, guid: c3dc5412b9c44234a8ae43008b7a6979,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5338449701420077780, guid: c3dc5412b9c44234a8ae43008b7a6979,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5338449701420077780, guid: c3dc5412b9c44234a8ae43008b7a6979,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5338449701420077780, guid: c3dc5412b9c44234a8ae43008b7a6979,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: c3dc5412b9c44234a8ae43008b7a6979, type: 3}
+--- !u!114 &554161778 stripped
+MonoBehaviour:
+  m_CorrespondingSourceObject: {fileID: 3834270478772424751, guid: c3dc5412b9c44234a8ae43008b7a6979,
+    type: 3}
+  m_PrefabInstance: {fileID: 554161777}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 0}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 66a61802bb8f1e947aa50f674d42db85, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+--- !u!4 &554161779 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: 5338449701420077780, guid: c3dc5412b9c44234a8ae43008b7a6979,
+    type: 3}
+  m_PrefabInstance: {fileID: 554161777}
+  m_PrefabAsset: {fileID: 0}
+--- !u!1 &1200256691
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1200256694}
+  - component: {fileID: 1200256693}
+  - component: {fileID: 1200256692}
+  - component: {fileID: 1200256695}
+  m_Layer: 0
+  m_Name: Main Camera
+  m_TagString: MainCamera
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!81 &1200256692
+AudioListener:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1200256691}
+  m_Enabled: 1
+--- !u!20 &1200256693
+Camera:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1200256691}
+  m_Enabled: 1
+  serializedVersion: 2
+  m_ClearFlags: 1
+  m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
+  m_projectionMatrixMode: 1
+  m_GateFitMode: 2
+  m_FOVAxisMode: 0
+  m_SensorSize: {x: 36, y: 24}
+  m_LensShift: {x: 0, y: 0}
+  m_FocalLength: 50
+  m_NormalizedViewPortRect:
+    serializedVersion: 2
+    x: 0
+    y: 0
+    width: 1
+    height: 1
+  near clip plane: 0.3
+  far clip plane: 1000
+  field of view: 60
+  orthographic: 0
+  orthographic size: 5
+  m_Depth: -1
+  m_CullingMask:
+    serializedVersion: 2
+    m_Bits: 4294967295
+  m_RenderingPath: -1
+  m_TargetTexture: {fileID: 0}
+  m_TargetDisplay: 0
+  m_TargetEye: 3
+  m_HDR: 1
+  m_AllowMSAA: 1
+  m_AllowDynamicResolution: 0
+  m_ForceIntoRT: 0
+  m_OcclusionCulling: 1
+  m_StereoConvergence: 10
+  m_StereoSeparation: 0.022
+--- !u!4 &1200256694
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1200256691}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1387182791}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!114 &1200256695
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1200256691}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 5a2a9c34df4095f47b9ca8f975175f5b, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+  m_Device: 0
+  m_PoseSource: 2
+  m_PoseProviderComponent: {fileID: 0}
+  m_TrackingType: 0
+  m_UpdateType: 0
+  m_UseRelativeTransform: 0
+--- !u!1 &1210354711
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1210354713}
+  - component: {fileID: 1210354712}
+  m_Layer: 0
+  m_Name: SceneSwitcher
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!114 &1210354712
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1210354711}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: ea6e0c6cd67922c48bbb5939267828e9, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+  _nextSceneInput:
+    controllerMask: 2
+    buttonMask: 1
+  _prevSceneInput:
+    controllerMask: 1
+    buttonMask: 1
+--- !u!4 &1210354713
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1210354711}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 0}
+  m_RootOrder: 5
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1001 &1213207057
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 0}
+    m_Modifications:
+    - target: {fileID: 5848076533691757917, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_Name
+      value: AvatarSdkManagerHorizon
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 3
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: d0377a811c2a95841ba015ae0b2c8d45, type: 3}
+--- !u!1 &1387182790
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1387182791}
+  - component: {fileID: 1387182792}
+  m_Layer: 0
+  m_Name: CameraOffset
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1387182791
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1387182790}
+  m_LocalRotation: {x: 0, y: 1, z: 0, w: 0}
+  m_LocalPosition: {x: 0, y: 0, z: 1}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 1200256694}
+  m_Father: {fileID: 0}
+  m_RootOrder: 6
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!114 &1387182792
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1387182790}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 4e93961d3dfb44505a84d80b1f7fffef, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+  _mode: 1
+  _coordinateSpace: 1
+  _position: {x: 0, y: 1.5, z: 0}
+  _rotation: {x: 0, y: 0, z: 0}
+--- !u!4 &1868580590 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: 7534483592080915777, guid: 8bd35c20b077959449e5f44de472b784,
+    type: 3}
+  m_PrefabInstance: {fileID: 7534483592443190930}
+  m_PrefabAsset: {fileID: 0}
+--- !u!1 &1882209498
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1882209500}
+  - component: {fileID: 1882209499}
+  - component: {fileID: 1882209501}
+  - component: {fileID: 1882209502}
+  m_Layer: 0
+  m_Name: SampleAvatar
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!114 &1882209499
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1882209498}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+  _creationInfo:
+    features: 342
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+  _activeView: 2
+  _activeManifestation: 2
+  _bodyTracking: {fileID: 554161778}
+  _lipSync: {fileID: 0}
+  _criticalJointTypes:
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  _useAdvancedLodSystem: 1
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _loadUserFromCdn: 0
+  _assetSource: 0
+  _assetPaths:
+  - 2
+  _assets:
+  - source: 0
+    path: 2
+  _assetPostfixDefault: _rift.glb
+  _assetPostfixAndroid: _quest.glb
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!4 &1882209500
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1882209498}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 554161779}
+  m_Father: {fileID: 0}
+  m_RootOrder: 4
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &1882209501
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1882209498}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 32f3cd2ab93d875418350b1b6f51d33f, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+  _side: 0
+  _handSkeleton: {fileID: 7534483591808359254, guid: 8bd35c20b077959449e5f44de472b784,
+    type: 3}
+  _handSkelLocalForward: {x: 0, y: 0, z: -1}
+  _handPose: {fileID: 7534483592443190931}
+  _wristOffset: {fileID: 1868580590}
+  setHandPose: 1
+  setWristOffset: 1
+  _entity: {fileID: 1882209499}
+--- !u!114 &1882209502
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1882209498}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 32f3cd2ab93d875418350b1b6f51d33f, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+  _side: 1
+  _handSkeleton: {fileID: 9195030365049692335, guid: 92dc2374677ddee43acd479ecf441901,
+    type: 3}
+  _handSkelLocalForward: {x: 0, y: 0, z: 1}
+  _handPose: {fileID: 1978962382}
+  _wristOffset: {fileID: 5903112416553395423}
+  setHandPose: 1
+  setWristOffset: 1
+  _entity: {fileID: 1882209499}
+--- !u!1 &1978962382 stripped
+GameObject:
+  m_CorrespondingSourceObject: {fileID: 9195030365049692335, guid: 92dc2374677ddee43acd479ecf441901,
+    type: 3}
+  m_PrefabInstance: {fileID: 5903112416553395422}
+  m_PrefabAsset: {fileID: 0}
+--- !u!1 &2049977709
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 2049977711}
+  - component: {fileID: 2049977710}
+  m_Layer: 0
+  m_Name: Directional Light
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!108 &2049977710
+Light:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2049977709}
+  m_Enabled: 1
+  serializedVersion: 10
+  m_Type: 1
+  m_Shape: 0
+  m_Color: {r: 1, g: 0.95686275, b: 0.8392157, a: 1}
+  m_Intensity: 1
+  m_Range: 10
+  m_SpotAngle: 30
+  m_InnerSpotAngle: 21.80208
+  m_CookieSize: 10
+  m_Shadows:
+    m_Type: 2
+    m_Resolution: -1
+    m_CustomResolution: -1
+    m_Strength: 1
+    m_Bias: 0.05
+    m_NormalBias: 0.4
+    m_NearPlane: 0.2
+    m_CullingMatrixOverride:
+      e00: 1
+      e01: 0
+      e02: 0
+      e03: 0
+      e10: 0
+      e11: 1
+      e12: 0
+      e13: 0
+      e20: 0
+      e21: 0
+      e22: 1
+      e23: 0
+      e30: 0
+      e31: 0
+      e32: 0
+      e33: 1
+    m_UseCullingMatrixOverride: 0
+  m_Cookie: {fileID: 0}
+  m_DrawHalo: 0
+  m_Flare: {fileID: 0}
+  m_RenderMode: 0
+  m_CullingMask:
+    serializedVersion: 2
+    m_Bits: 4294967295
+  m_RenderingLayerMask: 1
+  m_Lightmapping: 4
+  m_LightShadowCasterMode: 0
+  m_AreaSize: {x: 1, y: 1}
+  m_BounceIntensity: 1
+  m_ColorTemperature: 6570
+  m_UseColorTemperature: 0
+  m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
+  m_UseBoundingSphereOverride: 0
+  m_ShadowRadius: 0
+  m_ShadowAngle: 0
+--- !u!4 &2049977711
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2049977709}
+  m_LocalRotation: {x: 0.40821788, y: -0.23456968, z: 0.10938163, w: 0.8754261}
+  m_LocalPosition: {x: 0, y: 3, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 0}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 50, y: -30, z: 0}
+--- !u!1001 &5903112416553395422
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 0}
+    m_Modifications:
+    - target: {fileID: 207547438684117380, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.6576182
+      objectReference: {fileID: 0}
+    - target: {fileID: 207547438684117380, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0.0033264318
+      objectReference: {fileID: 0}
+    - target: {fileID: 207547438684117380, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0.00288203
+      objectReference: {fileID: 0}
+    - target: {fileID: 207547438684117380, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0.7533386
+      objectReference: {fileID: 0}
+    - target: {fileID: 207547438684117380, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: -0.002
+      objectReference: {fileID: 0}
+    - target: {fileID: 207547438684117380, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0.504
+      objectReference: {fileID: 0}
+    - target: {fileID: 207547438684117380, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: -97.762
+      objectReference: {fileID: 0}
+    - target: {fileID: 449694634743358682, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.86568236
+      objectReference: {fileID: 0}
+    - target: {fileID: 449694634743358682, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0.021153068
+      objectReference: {fileID: 0}
+    - target: {fileID: 449694634743358682, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0.023955822
+      objectReference: {fileID: 0}
+    - target: {fileID: 449694634743358682, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0.49957252
+      objectReference: {fileID: 0}
+    - target: {fileID: 449694634743358682, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: -0.72700006
+      objectReference: {fileID: 0}
+    - target: {fileID: 449694634743358682, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 3.59
+      objectReference: {fileID: 0}
+    - target: {fileID: 449694634743358682, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: -60
+      objectReference: {fileID: 0}
+    - target: {fileID: 658352083534403566, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.7046609
+      objectReference: {fileID: 0}
+    - target: {fileID: 658352083534403566, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0.05531899
+      objectReference: {fileID: 0}
+    - target: {fileID: 658352083534403566, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0.05876331
+      objectReference: {fileID: 0}
+    - target: {fileID: 658352083534403566, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0.7049396
+      objectReference: {fileID: 0}
+    - target: {fileID: 658352083534403566, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0.28
+      objectReference: {fileID: 0}
+    - target: {fileID: 658352083534403566, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 9.254001
+      objectReference: {fileID: 0}
+    - target: {fileID: 658352083534403566, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: -90
+      objectReference: {fileID: 0}
+    - target: {fileID: 781891339469389529, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.6535256
+      objectReference: {fileID: 0}
+    - target: {fileID: 781891339469389529, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0.0052870205
+      objectReference: {fileID: 0}
+    - target: {fileID: 781891339469389529, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0.004630981
+      objectReference: {fileID: 0}
+    - target: {fileID: 781891339469389529, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0.75687176
+      objectReference: {fileID: 0}
+    - target: {fileID: 781891339469389529, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0.006
+      objectReference: {fileID: 0}
+    - target: {fileID: 781891339469389529, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0.80500007
+      objectReference: {fileID: 0}
+    - target: {fileID: 781891339469389529, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: -98.382
+      objectReference: {fileID: 0}
+    - target: {fileID: 957472278499989956, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.8192348
+      objectReference: {fileID: 0}
+    - target: {fileID: 957472278499989956, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0.027715836
+      objectReference: {fileID: 0}
+    - target: {fileID: 957472278499989956, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0.043687906
+      objectReference: {fileID: 0}
+    - target: {fileID: 957472278499989956, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0.5711196
+      objectReference: {fileID: 0}
+    - target: {fileID: 957472278499989956, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0.257
+      objectReference: {fileID: 0}
+    - target: {fileID: 957472278499989956, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 5.926
+      objectReference: {fileID: 0}
+    - target: {fileID: 957472278499989956, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: -69.75
+      objectReference: {fileID: 0}
+    - target: {fileID: 1333918978318374608, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.9973941
+      objectReference: {fileID: 0}
+    - target: {fileID: 1333918978318374608, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0.0000001592561
+      objectReference: {fileID: 0}
+    - target: {fileID: 1333918978318374608, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0.00000010430809
+      objectReference: {fileID: 0}
+    - target: {fileID: 1333918978318374608, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0.07214595
+      objectReference: {fileID: 0}
+    - target: {fileID: 1333918978318374608, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 8.275001
+      objectReference: {fileID: 0}
+    - target: {fileID: 1499567833331348363, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.98237836
+      objectReference: {fileID: 0}
+    - target: {fileID: 1499567833331348363, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0.008458295
+      objectReference: {fileID: 0}
+    - target: {fileID: 1499567833331348363, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0.07397111
+      objectReference: {fileID: 0}
+    - target: {fileID: 1499567833331348363, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0.17143402
+      objectReference: {fileID: 0}
+    - target: {fileID: 1499567833331348363, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 19.761002
+      objectReference: {fileID: 0}
+    - target: {fileID: 2552201152882534245, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.7046609
+      objectReference: {fileID: 0}
+    - target: {fileID: 2552201152882534245, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0.05531899
+      objectReference: {fileID: 0}
+    - target: {fileID: 2552201152882534245, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0.05876331
+      objectReference: {fileID: 0}
+    - target: {fileID: 2552201152882534245, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0.7049396
+      objectReference: {fileID: 0}
+    - target: {fileID: 2552201152882534245, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0.28
+      objectReference: {fileID: 0}
+    - target: {fileID: 2552201152882534245, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 9.254001
+      objectReference: {fileID: 0}
+    - target: {fileID: 2552201152882534245, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: -90
+      objectReference: {fileID: 0}
+    - target: {fileID: 2925678175908989896, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.86568236
+      objectReference: {fileID: 0}
+    - target: {fileID: 2925678175908989896, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0.021153068
+      objectReference: {fileID: 0}
+    - target: {fileID: 2925678175908989896, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0.023955822
+      objectReference: {fileID: 0}
+    - target: {fileID: 2925678175908989896, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0.49957252
+      objectReference: {fileID: 0}
+    - target: {fileID: 2925678175908989896, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: -0.72700006
+      objectReference: {fileID: 0}
+    - target: {fileID: 2925678175908989896, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 3.59
+      objectReference: {fileID: 0}
+    - target: {fileID: 2925678175908989896, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: -60
+      objectReference: {fileID: 0}
+    - target: {fileID: 4103403191499202394, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.86568236
+      objectReference: {fileID: 0}
+    - target: {fileID: 4103403191499202394, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0.021153068
+      objectReference: {fileID: 0}
+    - target: {fileID: 4103403191499202394, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0.023955822
+      objectReference: {fileID: 0}
+    - target: {fileID: 4103403191499202394, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0.49957252
+      objectReference: {fileID: 0}
+    - target: {fileID: 4103403191499202394, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: -0.72700006
+      objectReference: {fileID: 0}
+    - target: {fileID: 4103403191499202394, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 3.59
+      objectReference: {fileID: 0}
+    - target: {fileID: 4103403191499202394, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: -60
+      objectReference: {fileID: 0}
+    - target: {fileID: 4636527648722930655, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.7046609
+      objectReference: {fileID: 0}
+    - target: {fileID: 4636527648722930655, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0.05531899
+      objectReference: {fileID: 0}
+    - target: {fileID: 4636527648722930655, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0.05876331
+      objectReference: {fileID: 0}
+    - target: {fileID: 4636527648722930655, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0.7049396
+      objectReference: {fileID: 0}
+    - target: {fileID: 4636527648722930655, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0.28
+      objectReference: {fileID: 0}
+    - target: {fileID: 4636527648722930655, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 9.254001
+      objectReference: {fileID: 0}
+    - target: {fileID: 4636527648722930655, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: -90
+      objectReference: {fileID: 0}
+    - target: {fileID: 4740302479481466684, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.99891305
+      objectReference: {fileID: 0}
+    - target: {fileID: 4740302479481466684, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0.0142214
+      objectReference: {fileID: 0}
+    - target: {fileID: 4740302479481466684, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: -0.042385936
+      objectReference: {fileID: 0}
+    - target: {fileID: 4740302479481466684, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0.013189897
+      objectReference: {fileID: 0}
+    - target: {fileID: 4740302479481466684, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 1.58
+      objectReference: {fileID: 0}
+    - target: {fileID: 4898667081225270067, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.6643252
+      objectReference: {fileID: 0}
+    - target: {fileID: 4898667081225270067, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0.033147056
+      objectReference: {fileID: 0}
+    - target: {fileID: 4898667081225270067, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0.028023101
+      objectReference: {fileID: 0}
+    - target: {fileID: 4898667081225270067, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0.74618244
+      objectReference: {fileID: 0}
+    - target: {fileID: 4898667081225270067, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: -0.127
+      objectReference: {fileID: 0}
+    - target: {fileID: 4898667081225270067, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 4.9740005
+      objectReference: {fileID: 0}
+    - target: {fileID: 4898667081225270067, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: -96.648
+      objectReference: {fileID: 0}
+    - target: {fileID: 5740912727245468853, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.8386572
+      objectReference: {fileID: 0}
+    - target: {fileID: 5740912727245468853, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0.0046685035
+      objectReference: {fileID: 0}
+    - target: {fileID: 5740912727245468853, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: -0.0076920157
+      objectReference: {fileID: 0}
+    - target: {fileID: 5740912727245468853, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0.5445853
+      objectReference: {fileID: 0}
+    - target: {fileID: 5740912727245468853, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: -0.031000001
+      objectReference: {fileID: 0}
+    - target: {fileID: 5740912727245468853, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: -1.031
+      objectReference: {fileID: 0}
+    - target: {fileID: 5740912727245468853, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: -65.995
+      objectReference: {fileID: 0}
+    - target: {fileID: 6665313800368214509, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.86568236
+      objectReference: {fileID: 0}
+    - target: {fileID: 6665313800368214509, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0.021153068
+      objectReference: {fileID: 0}
+    - target: {fileID: 6665313800368214509, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0.023955822
+      objectReference: {fileID: 0}
+    - target: {fileID: 6665313800368214509, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0.49957252
+      objectReference: {fileID: 0}
+    - target: {fileID: 6665313800368214509, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: -0.72700006
+      objectReference: {fileID: 0}
+    - target: {fileID: 6665313800368214509, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 3.59
+      objectReference: {fileID: 0}
+    - target: {fileID: 6665313800368214509, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: -60
+      objectReference: {fileID: 0}
+    - target: {fileID: 6792254048580466834, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.86568236
+      objectReference: {fileID: 0}
+    - target: {fileID: 6792254048580466834, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0.021153068
+      objectReference: {fileID: 0}
+    - target: {fileID: 6792254048580466834, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0.023955822
+      objectReference: {fileID: 0}
+    - target: {fileID: 6792254048580466834, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0.49957252
+      objectReference: {fileID: 0}
+    - target: {fileID: 6792254048580466834, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: -0.72700006
+      objectReference: {fileID: 0}
+    - target: {fileID: 6792254048580466834, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 3.59
+      objectReference: {fileID: 0}
+    - target: {fileID: 6792254048580466834, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: -60
+      objectReference: {fileID: 0}
+    - target: {fileID: 6924825323665014433, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.8646689
+      objectReference: {fileID: 0}
+    - target: {fileID: 6924825323665014433, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0.007450359
+      objectReference: {fileID: 0}
+    - target: {fileID: 6924825323665014433, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0.0065853917
+      objectReference: {fileID: 0}
+    - target: {fileID: 6924825323665014433, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0.5022439
+      objectReference: {fileID: 0}
+    - target: {fileID: 6924825323665014433, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: -0.35900003
+      objectReference: {fileID: 0}
+    - target: {fileID: 6924825323665014433, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 1.0810001
+      objectReference: {fileID: 0}
+    - target: {fileID: 6924825323665014433, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: -60.304005
+      objectReference: {fileID: 0}
+    - target: {fileID: 7292386992484522565, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.8292106
+      objectReference: {fileID: 0}
+    - target: {fileID: 7292386992484522565, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0.026402744
+      objectReference: {fileID: 0}
+    - target: {fileID: 7292386992484522565, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: -0.039279886
+      objectReference: {fileID: 0}
+    - target: {fileID: 7292386992484522565, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0.556929
+      objectReference: {fileID: 0}
+    - target: {fileID: 7292386992484522565, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0.002
+      objectReference: {fileID: 0}
+    - target: {fileID: 7292386992484522565, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: -5.426
+      objectReference: {fileID: 0}
+    - target: {fileID: 7292386992484522565, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: -67.774
+      objectReference: {fileID: 0}
+    - target: {fileID: 7429382140072283541, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.6518052
+      objectReference: {fileID: 0}
+    - target: {fileID: 7429382140072283541, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0.0019504268
+      objectReference: {fileID: 0}
+    - target: {fileID: 7429382140072283541, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0.0017163503
+      objectReference: {fileID: 0}
+    - target: {fileID: 7429382140072283541, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0.758382
+      objectReference: {fileID: 0}
+    - target: {fileID: 7429382140072283541, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0.003
+      objectReference: {fileID: 0}
+    - target: {fileID: 7429382140072283541, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0.298
+      objectReference: {fileID: 0}
+    - target: {fileID: 7429382140072283541, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: -98.644005
+      objectReference: {fileID: 0}
+    - target: {fileID: 7610962866647427585, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.8236573
+      objectReference: {fileID: 0}
+    - target: {fileID: 7610962866647427585, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0.07117548
+      objectReference: {fileID: 0}
+    - target: {fileID: 7610962866647427585, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: -0.120624326
+      objectReference: {fileID: 0}
+    - target: {fileID: 7610962866647427585, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0.5495203
+      objectReference: {fileID: 0}
+    - target: {fileID: 7610962866647427585, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: -0.878
+      objectReference: {fileID: 0}
+    - target: {fileID: 7610962866647427585, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: -16.079
+      objectReference: {fileID: 0}
+    - target: {fileID: 7610962866647427585, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: -67.296005
+      objectReference: {fileID: 0}
+    - target: {fileID: 7779291534350089121, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.86568236
+      objectReference: {fileID: 0}
+    - target: {fileID: 7779291534350089121, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0.021153068
+      objectReference: {fileID: 0}
+    - target: {fileID: 7779291534350089121, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0.023955822
+      objectReference: {fileID: 0}
+    - target: {fileID: 7779291534350089121, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0.49957252
+      objectReference: {fileID: 0}
+    - target: {fileID: 7779291534350089121, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: -0.72700006
+      objectReference: {fileID: 0}
+    - target: {fileID: 7779291534350089121, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 3.59
+      objectReference: {fileID: 0}
+    - target: {fileID: 7779291534350089121, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: -60
+      objectReference: {fileID: 0}
+    - target: {fileID: 7797415833705833784, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.86568236
+      objectReference: {fileID: 0}
+    - target: {fileID: 7797415833705833784, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0.021153068
+      objectReference: {fileID: 0}
+    - target: {fileID: 7797415833705833784, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0.023955822
+      objectReference: {fileID: 0}
+    - target: {fileID: 7797415833705833784, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0.49957252
+      objectReference: {fileID: 0}
+    - target: {fileID: 7797415833705833784, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: -0.72700006
+      objectReference: {fileID: 0}
+    - target: {fileID: 7797415833705833784, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 3.59
+      objectReference: {fileID: 0}
+    - target: {fileID: 7797415833705833784, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: -60
+      objectReference: {fileID: 0}
+    - target: {fileID: 8295203010190031949, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.82956624
+      objectReference: {fileID: 0}
+    - target: {fileID: 8295203010190031949, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0.01642305
+      objectReference: {fileID: 0}
+    - target: {fileID: 8295203010190031949, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: -0.021841263
+      objectReference: {fileID: 0}
+    - target: {fileID: 8295203010190031949, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0.5577394
+      objectReference: {fileID: 0}
+    - target: {fileID: 8295203010190031949, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0.165
+      objectReference: {fileID: 0}
+    - target: {fileID: 8295203010190031949, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: -3.127
+      objectReference: {fileID: 0}
+    - target: {fileID: 8295203010190031949, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: -67.832
+      objectReference: {fileID: 0}
+    - target: {fileID: 8445328051013840570, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.8527615
+      objectReference: {fileID: 0}
+    - target: {fileID: 8445328051013840570, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0.021749187
+      objectReference: {fileID: 0}
+    - target: {fileID: 8445328051013840570, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0.023410395
+      objectReference: {fileID: 0}
+    - target: {fileID: 8445328051013840570, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0.5213221
+      objectReference: {fileID: 0}
+    - target: {fileID: 8445328051013840570, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: -0.72700006
+      objectReference: {fileID: 0}
+    - target: {fileID: 8445328051013840570, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 3.5900002
+      objectReference: {fileID: 0}
+    - target: {fileID: 8445328051013840570, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: -62.9
+      objectReference: {fileID: 0}
+    - target: {fileID: 8601950598458638778, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.84173125
+      objectReference: {fileID: 0}
+    - target: {fileID: 8601950598458638778, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0.055305846
+      objectReference: {fileID: 0}
+    - target: {fileID: 8601950598458638778, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0.08827351
+      objectReference: {fileID: 0}
+    - target: {fileID: 8601950598458638778, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0.5297524
+      objectReference: {fileID: 0}
+    - target: {fileID: 8601950598458638778, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0.024
+      objectReference: {fileID: 0}
+    - target: {fileID: 8601950598458638778, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 11.958
+      objectReference: {fileID: 0}
+    - target: {fileID: 8601950598458638778, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: -64.367004
+      objectReference: {fileID: 0}
+    - target: {fileID: 8688229877465529362, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.86568236
+      objectReference: {fileID: 0}
+    - target: {fileID: 8688229877465529362, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0.021153068
+      objectReference: {fileID: 0}
+    - target: {fileID: 8688229877465529362, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0.023955822
+      objectReference: {fileID: 0}
+    - target: {fileID: 8688229877465529362, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0.49957252
+      objectReference: {fileID: 0}
+    - target: {fileID: 8688229877465529362, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: -60
+      objectReference: {fileID: 0}
+    - target: {fileID: 8815391949536985120, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.7046609
+      objectReference: {fileID: 0}
+    - target: {fileID: 8815391949536985120, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0.05531899
+      objectReference: {fileID: 0}
+    - target: {fileID: 8815391949536985120, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0.05876331
+      objectReference: {fileID: 0}
+    - target: {fileID: 8815391949536985120, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0.7049396
+      objectReference: {fileID: 0}
+    - target: {fileID: 8815391949536985120, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: -90
+      objectReference: {fileID: 0}
+    - target: {fileID: 9195030365049692334, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 2
+      objectReference: {fileID: 0}
+    - target: {fileID: 9195030365049692334, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0.2418296
+      objectReference: {fileID: 0}
+    - target: {fileID: 9195030365049692334, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 1.1047595
+      objectReference: {fileID: 0}
+    - target: {fileID: 9195030365049692334, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0.12397504
+      objectReference: {fileID: 0}
+    - target: {fileID: 9195030365049692334, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.5390443
+      objectReference: {fileID: 0}
+    - target: {fileID: 9195030365049692334, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0.6486023
+      objectReference: {fileID: 0}
+    - target: {fileID: 9195030365049692334, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: -0.3434555
+      objectReference: {fileID: 0}
+    - target: {fileID: 9195030365049692334, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0.413261
+      objectReference: {fileID: 0}
+    - target: {fileID: 9195030365049692334, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: -90
+      objectReference: {fileID: 0}
+    - target: {fileID: 9195030365049692334, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: -90
+      objectReference: {fileID: 0}
+    - target: {fileID: 9195030365049692334, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 9195030365049692335, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_Name
+      value: CustomHandSkeletonRight
+      objectReference: {fileID: 0}
+    - target: {fileID: 9195030365049692335, guid: 92dc2374677ddee43acd479ecf441901,
+        type: 3}
+      propertyPath: m_IsActive
+      value: 1
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: 92dc2374677ddee43acd479ecf441901, type: 3}
+--- !u!4 &5903112416553395423 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: 7328490110973527630, guid: 92dc2374677ddee43acd479ecf441901,
+    type: 3}
+  m_PrefabInstance: {fileID: 5903112416553395422}
+  m_PrefabAsset: {fileID: 0}
+--- !u!1001 &7534483592443190930
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 0}
+    m_Modifications:
+    - target: {fileID: 7534483590661669315, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.86505514
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483590661669315, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0.023681618
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483590661669315, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: -0.040982973
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483590661669315, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0.49943888
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483590661669315, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0.002
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483590661669315, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: -5.426
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483590661669315, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: -60
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483590689977632, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.9848962
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483590689977632, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0.007320842
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483590689977632, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0.07409234
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483590689977632, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0.15632126
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483590689977632, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: -0.50100005
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483590689977632, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 8.525001
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483590689977632, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 18
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483590725842349, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.7032448
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483590725842349, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0.07350771
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483590725842349, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0.07380229
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483590725842349, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0.7032757
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483590725842349, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0.024
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483590725842349, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 11.958
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483590725842349, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: -90
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591011148764, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.9832549
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591011148764, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591011148764, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591011148764, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0.18223555
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591011148764, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 21
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591198407099, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.86568236
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591198407099, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0.021153068
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591198407099, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0.023955822
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591198407099, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0.49957252
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591198407099, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: -0.72700006
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591198407099, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 3.5900002
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591198407099, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: -60
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591333830245, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.73036575
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591333830245, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0.5832985
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591333830245, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: -0.3546533
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591333830245, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0.023448702
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591333830245, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 60.305004
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591333830245, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: -82.114006
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591333830245, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: -50
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591441504101, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.7032448
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591441504101, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0.07350771
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591441504101, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0.07380229
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591441504101, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0.7032757
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591441504101, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0.024
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591441504101, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 11.958
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591441504101, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: -90
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591495391307, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.86572176
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591495391307, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0.0148889385
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591495391307, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: -0.022909699
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591495391307, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0.4997793
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591495391307, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0.165
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591495391307, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: -3.127
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591495391307, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: -60
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591532427919, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.8660038
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591532427919, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0.0034671011
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591532427919, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0.0061098672
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591532427919, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0.499988
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591532427919, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0.006
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591532427919, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0.80500007
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591532427919, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: -60
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591560014179, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.86505514
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591560014179, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0.023681618
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591560014179, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: -0.040982973
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591560014179, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0.49943888
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591560014179, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0.002
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591560014179, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: -5.426
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591560014179, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: -60
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591658906422, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.9857293
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591658906422, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0.041383985
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591658906422, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0.12158135
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591658906422, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0.10882601
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591658906422, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 3.16
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591658906422, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 14.423
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591658906422, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 13
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591808359254, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_Name
+      value: CustomHandSkeletonLeft
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591808359254, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_IsActive
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591808359255, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591808359255, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: -0.24182963
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591808359255, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 1.1047595
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591808359255, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0.12397504
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591808359255, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.6486022
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591808359255, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0.5390444
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591808359255, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0.413261
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591808359255, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0.34345555
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591808359255, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 90
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591808359255, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 90
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591808359255, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591872103671, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.7032448
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591872103671, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0.07350771
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591872103671, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0.07380229
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591872103671, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0.7032757
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591872103671, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0.024
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591872103671, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 11.958
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483591872103671, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: -90
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483592347472683, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.86599743
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483592347472683, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0.0074297613
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483592347472683, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0.006603137
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483592347472683, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0.49994972
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483592347472683, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: -0.35900003
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483592347472683, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 1.0810001
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483592347472683, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: -60
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483592624920130, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.86599743
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483592624920130, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0.0074297613
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483592624920130, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0.006603137
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483592624920130, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0.49994972
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483592624920130, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: -0.35900003
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483592624920130, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 1.0810001
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483592624920130, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: -60
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483592706830507, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.7032448
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483592706830507, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0.07350771
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483592706830507, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0.07380229
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483592706830507, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0.7032757
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483592706830507, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0.024
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483592706830507, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 11.958
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483592706830507, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: -90
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483592738953755, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.86572176
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483592738953755, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0.0148889385
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483592738953755, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: -0.022909699
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483592738953755, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0.4997793
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483592738953755, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0.165
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483592738953755, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: -3.127
+      objectReference: {fileID: 0}
+    - target: {fileID: 7534483592738953755, guid: 8bd35c20b077959449e5f44de472b784,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: -60
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: 8bd35c20b077959449e5f44de472b784, type: 3}
+--- !u!1 &7534483592443190931 stripped
+GameObject:
+  m_CorrespondingSourceObject: {fileID: 7534483591808359254, guid: 8bd35c20b077959449e5f44de472b784,
+    type: 3}
+  m_PrefabInstance: {fileID: 7534483592443190930}
+  m_PrefabAsset: {fileID: 0}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CustomHandPoseExample/CustomHandPoseExample.unity.meta b/Assets/Oculus/Avatar2/Example/Scenes/CustomHandPoseExample/CustomHandPoseExample.unity.meta
new file mode 100644
index 0000000000000000000000000000000000000000..4dd2a39a9268b35c13bc5560d1928ffb6d696caf
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CustomHandPoseExample/CustomHandPoseExample.unity.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: d5126d6597a900c4ba11ed75b3938e33
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CustomHandPoseExample/CustomRightHandPrefab.prefab b/Assets/Oculus/Avatar2/Example/Scenes/CustomHandPoseExample/CustomRightHandPrefab.prefab
new file mode 100644
index 0000000000000000000000000000000000000000..53a705d11ec0a9298d8fa90a1839cbdd2dee5b79
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CustomHandPoseExample/CustomRightHandPrefab.prefab
@@ -0,0 +1,1025 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &364339213872791506
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 3384136876995542145}
+  - component: {fileID: 1208331682}
+  - component: {fileID: 1208331683}
+  m_Layer: 0
+  m_Name: RightHandWrist_jnt
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &3384136876995542145
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 364339213872791506}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 2218094727301053923}
+  - {fileID: 8856224555033642233}
+  - {fileID: 1386622505660052727}
+  - {fileID: 8621987109978654453}
+  - {fileID: 1497206458384270008}
+  - {fileID: 7328490110973527630}
+  m_Father: {fileID: 9195030365049692334}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &1208331682
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 364339213872791506}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 04a0706dc00d22946b853246bd3f1e88, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  jointType: 0
+--- !u!114 &1208331683
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 364339213872791506}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: e23dd79c8b6109246bfc3b35e6ac44cd, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  color: {r: 0, g: 0.9167204, b: 1, a: 1}
+  drawAxes: 1
+  axisSize: 0.01
+--- !u!1 &832784008376979695
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 3639740901892058516}
+  - component: {fileID: 1208331664}
+  m_Layer: 0
+  m_Name: RightHandThumbMeta_jnt
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &3639740901892058516
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 832784008376979695}
+  m_LocalRotation: {x: 0.012428837, y: 0.12783465, z: -0.119900994, w: 0.9844428}
+  m_LocalPosition: {x: 0.024852559, y: -4.4408918e-17, z: 8.5265126e-16}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 1563848537045437428}
+  m_Father: {fileID: 1497206458384270008}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &1208331664
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 832784008376979695}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 04a0706dc00d22946b853246bd3f1e88, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  jointType: 2
+--- !u!1 &1491667947905338413
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 207547438684117380}
+  - component: {fileID: 1208331675}
+  m_Layer: 0
+  m_Name: RightHandMiddleIntermediate_jnt
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &207547438684117380
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1491667947905338413}
+  m_LocalRotation: {x: -0.000008648528, y: 0.0044007967, z: 0.0019290085, w: 0.9999885}
+  m_LocalPosition: {x: 0.04292699, y: -5.684342e-16, z: -8.8817837e-17}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 6924825323665014433}
+  m_Father: {fileID: 957472278499989956}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &1208331675
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1491667947905338413}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 04a0706dc00d22946b853246bd3f1e88, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  jointType: 11
+--- !u!1 &1684300387575668837
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7429382140072283541}
+  - component: {fileID: 1208331667}
+  m_Layer: 0
+  m_Name: RightHandRingIntermediate_jnt
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &7429382140072283541
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1684300387575668837}
+  m_LocalRotation: {x: 0.000015345942, y: 0.0025972838, z: -0.005766683, w: 0.99998003}
+  m_LocalPosition: {x: 0.0389961, y: 2.842171e-16, z: -1.4210854e-16}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 8295203010190031949}
+  m_Father: {fileID: 5740912727245468853}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &1208331667
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1684300387575668837}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 04a0706dc00d22946b853246bd3f1e88, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  jointType: 15
+--- !u!1 &2134361757442361441
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 6924825323665014433}
+  - component: {fileID: 1208331674}
+  m_Layer: 0
+  m_Name: RightHandMiddleDistal_jnt
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &6924825323665014433
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2134361757442361441}
+  m_LocalRotation: {x: -0.0022474818, y: 0.009686223, z: 0.09261643, w: 0.99565226}
+  m_LocalPosition: {x: 0.027549583, y: 5.684342e-16, z: 2.6645352e-17}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 207547438684117380}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &1208331674
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2134361757442361441}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 04a0706dc00d22946b853246bd3f1e88, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  jointType: 12
+--- !u!1 &2742069216750423848
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 5740912727245468853}
+  - component: {fileID: 1208331668}
+  m_Layer: 0
+  m_Name: RightHandRingProximal_jnt
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &5740912727245468853
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2742069216750423848}
+  m_LocalRotation: {x: -0.00013683904, y: -0.008997643, z: -0.015205947, w: 0.99984396}
+  m_LocalPosition: {x: 0.05478463, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 7429382140072283541}
+  m_Father: {fileID: 8621987109978654453}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &1208331668
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2742069216750423848}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 04a0706dc00d22946b853246bd3f1e88, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  jointType: 14
+--- !u!1 &3142562423635065069
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 781891339469389529}
+  - component: {fileID: 1208331679}
+  m_Layer: 0
+  m_Name: RightHandIndexIntermediate_jnt
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &781891339469389529
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3142562423635065069}
+  m_LocalRotation: {x: 0.000025190593, y: 0.007028435, z: -0.0034760437, w: 0.9999693}
+  m_LocalPosition: {x: 0.0379273, y: -5.684342e-16, z: 2.6645352e-17}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 8445328051013840570}
+  m_Father: {fileID: 8601950598458638778}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &1208331679
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3142562423635065069}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 04a0706dc00d22946b853246bd3f1e88, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  jointType: 7
+--- !u!1 &3190173116027319581
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 4898667081225270067}
+  - component: {fileID: 1208331671}
+  m_Layer: 0
+  m_Name: RightHandPinkyIntermediate_jnt
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &4898667081225270067
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3190173116027319581}
+  m_LocalRotation: {x: -0.0006031977, y: 0.043400448, z: 0.011686176, w: 0.9989892}
+  m_LocalPosition: {x: 0.030720409, y: -5.684342e-16, z: 1.0658141e-16}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 7292386992484522565}
+  m_Father: {fileID: 7610962866647427585}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &1208331671
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3190173116027319581}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 04a0706dc00d22946b853246bd3f1e88, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  jointType: 19
+--- !u!1 &4001134534974460426
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 8445328051013840570}
+  - component: {fileID: 1208331678}
+  m_Layer: 0
+  m_Name: RightHandIndexDistal_jnt
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &8445328051013840570
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4001134534974460426}
+  m_LocalRotation: {x: -0.004131363, y: 0.03168613, z: 0.07016855, w: 0.9970232}
+  m_LocalPosition: {x: 0.024303643, y: -1.7053025e-15, z: -4.440892e-18}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 781891339469389529}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &1208331678
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4001134534974460426}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 04a0706dc00d22946b853246bd3f1e88, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  jointType: 8
+--- !u!1 &4042704226405070581
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 8621987109978654453}
+  - component: {fileID: 1208331669}
+  m_Layer: 0
+  m_Name: RightHandRingMeta_jnt
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &8621987109978654453
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4042704226405070581}
+  m_LocalRotation: {x: -0.056348428, y: 0.043691937, z: -0.030203488, w: 0.99699736}
+  m_LocalPosition: {x: 0.034218278, y: -0.0029601145, z: -0.012878803}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 5740912727245468853}
+  m_Father: {fileID: 3384136876995542145}
+  m_RootOrder: 3
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &1208331669
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4042704226405070581}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 04a0706dc00d22946b853246bd3f1e88, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  jointType: 13
+--- !u!1 &4215261633207672257
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1497206458384270008}
+  - component: {fileID: 1208331665}
+  m_Layer: 0
+  m_Name: RightHandThumbTrapezium_jnt
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1497206458384270008
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4215261633207672257}
+  m_LocalRotation: {x: 0.54354405, y: -0.4130089, z: 0.09877131, w: 0.72403574}
+  m_LocalPosition: {x: 0.020069301, y: -0.011554099, z: 0.0104965195}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 3639740901892058516}
+  m_Father: {fileID: 3384136876995542145}
+  m_RootOrder: 4
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &1208331665
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4215261633207672257}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 04a0706dc00d22946b853246bd3f1e88, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  jointType: 1
+--- !u!1 &4270640376262535242
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1563848537045437428}
+  - component: {fileID: 1208331663}
+  m_Layer: 0
+  m_Name: RightHandThumbProximal_jnt
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1563848537045437428
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4270640376262535242}
+  m_LocalRotation: {x: 0.0020685187, y: 0.07442744, z: 0.08631405, w: 0.9934819}
+  m_LocalPosition: {x: 0.03251291, y: -5.32907e-16, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 2096063923127826587}
+  m_Father: {fileID: 3639740901892058516}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &1208331663
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4270640376262535242}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 04a0706dc00d22946b853246bd3f1e88, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  jointType: 3
+--- !u!1 &4400604008780454027
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7292386992484522565}
+  - component: {fileID: 1208331670}
+  m_Layer: 0
+  m_Name: RightHandPinkyDistal_jnt
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &7292386992484522565
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4400604008780454027}
+  m_LocalRotation: {x: -0.0012856473, y: -0.047315646, z: 0.027499026, w: 0.9985006}
+  m_LocalPosition: {x: 0.020311384, y: 1.7053025e-15, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 4898667081225270067}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0.002, y: -5.426, z: 3.155}
+--- !u!114 &1208331670
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4400604008780454027}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 04a0706dc00d22946b853246bd3f1e88, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  jointType: 20
+--- !u!1 &4759496185016578189
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 2096063923127826587}
+  - component: {fileID: 1208331662}
+  m_Layer: 0
+  m_Name: RightHandThumbDistal_jnt
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &2096063923127826587
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4759496185016578189}
+  m_LocalRotation: {x: -0.00000011661869, y: -0.000000118219795, z: 0.0000016075634,
+    w: 1}
+  m_LocalPosition: {x: 0.03379309, y: 2.664535e-16, z: 2.842171e-16}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1563848537045437428}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &1208331662
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4759496185016578189}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 04a0706dc00d22946b853246bd3f1e88, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  jointType: 4
+--- !u!1 &5041933527735791058
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 8601950598458638778}
+  - component: {fileID: 1208331680}
+  m_Layer: 0
+  m_Name: RightHandIndexProximal_jnt
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &8601950598458638778
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 5041933527735791058}
+  m_LocalRotation: {x: 0.00010576444, y: 0.104167886, z: -0.0010098027, w: 0.9945592}
+  m_LocalPosition: {x: 0.061460007, y: 0, z: 3.5527136e-17}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 781891339469389529}
+  m_Father: {fileID: 2218094727301053923}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &1208331680
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 5041933527735791058}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 04a0706dc00d22946b853246bd3f1e88, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  jointType: 6
+--- !u!1 &5975233525597213568
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 8856224555033642233}
+  - component: {fileID: 1208331677}
+  m_Layer: 0
+  m_Name: RightHandMiddleMeta_jnt
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &8856224555033642233
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 5975233525597213568}
+  m_LocalRotation: {x: -0.013067971, y: -0.016595608, z: -0.022907652, w: 0.9995144}
+  m_LocalPosition: {x: 0.03503387, y: 0.00021060446, z: -0.00032449898}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 957472278499989956}
+  m_Father: {fileID: 3384136876995542145}
+  m_RootOrder: 1
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &1208331677
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 5975233525597213568}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 04a0706dc00d22946b853246bd3f1e88, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  jointType: 9
+--- !u!1 &6344127943091957058
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 8295203010190031949}
+  - component: {fileID: 1208331666}
+  m_Layer: 0
+  m_Name: RightHandRingDistal_jnt
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &8295203010190031949
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6344127943091957058}
+  m_LocalRotation: {x: 0.0007043812, y: -0.027317839, z: 0.027049277, w: 0.99926054}
+  m_LocalPosition: {x: 0.026573397, y: -8.5265126e-16, z: 1.0658141e-16}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 7429382140072283541}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &1208331666
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6344127943091957058}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 04a0706dc00d22946b853246bd3f1e88, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  jointType: 16
+--- !u!1 &6414279475640352873
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7328490110973527630}
+  m_Layer: 0
+  m_Name: RightHandTracker_tgt
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &7328490110973527630
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6414279475640352873}
+  m_LocalRotation: {x: 0.43440753, y: 0.50643265, z: 0.48004407, w: 0.56953824}
+  m_LocalPosition: {x: 0.10407422, y: -0.037334505, z: 0.040733628}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 3384136876995542145}
+  m_RootOrder: 5
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &7203348047626937569
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 957472278499989956}
+  - component: {fileID: 1208331676}
+  m_Layer: 0
+  m_Name: RightHandMiddleProximal_jnt
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &957472278499989956
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7203348047626937569}
+  m_LocalRotation: {x: -0.00023874146, y: 0.05173696, z: -0.0480062, w: 0.99750626}
+  m_LocalPosition: {x: 0.060709894, y: 2.842171e-16, z: 1.5543122e-17}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 207547438684117380}
+  m_Father: {fileID: 8856224555033642233}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &1208331676
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7203348047626937569}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 04a0706dc00d22946b853246bd3f1e88, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  jointType: 10
+--- !u!1 &8219749451402264360
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 2218094727301053923}
+  - component: {fileID: 1208331681}
+  m_Layer: 0
+  m_Name: RightHandIndexMeta_jnt
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &2218094727301053923
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8219749451402264360}
+  m_LocalRotation: {x: 0.026563136, y: -0.07177673, z: -0.044874344, w: 0.9960566}
+  m_LocalPosition: {x: 0.035417024, y: -0.0015878897, z: 0.014909195}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 8601950598458638778}
+  m_Father: {fileID: 3384136876995542145}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &1208331681
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8219749451402264360}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 04a0706dc00d22946b853246bd3f1e88, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  jointType: 5
+--- !u!1 &8442595940716046746
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1386622505660052727}
+  - component: {fileID: 1208331673}
+  m_Layer: 0
+  m_Name: RightHandPinkyMeta_jnt
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1386622505660052727
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8442595940716046746}
+  m_LocalRotation: {x: -0.117761955, y: 0.13805799, z: -0.031055925, w: 0.9829078}
+  m_LocalPosition: {x: 0.034073558, y: -0.009419835, z: -0.022998573}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 7610962866647427585}
+  m_Father: {fileID: 3384136876995542145}
+  m_RootOrder: 2
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &1208331673
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8442595940716046746}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 04a0706dc00d22946b853246bd3f1e88, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  jointType: 17
+--- !u!1 &8452341999599833248
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7610962866647427585}
+  - component: {fileID: 1208331672}
+  m_Layer: 0
+  m_Name: RightHandPinkyProximal_jnt
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &7610962866647427585
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8452341999599833248}
+  m_LocalRotation: {x: -0.0038703554, y: -0.14000393, z: -0.027361685, w: 0.9897653}
+  m_LocalPosition: {x: 0.04565054, y: 5.684342e-16, z: 7.105427e-17}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 4898667081225270067}
+  m_Father: {fileID: 1386622505660052727}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &1208331672
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8452341999599833248}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 04a0706dc00d22946b853246bd3f1e88, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  jointType: 18
+--- !u!1 &9195030365049692335
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 9195030365049692334}
+  m_Layer: 0
+  m_Name: CustomRightHandPrefab
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &9195030365049692334
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 9195030365049692335}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 3384136876995542145}
+  m_Father: {fileID: 0}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CustomHandPoseExample/CustomRightHandPrefab.prefab.meta b/Assets/Oculus/Avatar2/Example/Scenes/CustomHandPoseExample/CustomRightHandPrefab.prefab.meta
new file mode 100644
index 0000000000000000000000000000000000000000..1cb0f291407220d4cf52b2b75aa4237ccc176bbf
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CustomHandPoseExample/CustomRightHandPrefab.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 92dc2374677ddee43acd479ecf441901
+PrefabImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CustomHandPoseExample/SkeletonRenderer.cs b/Assets/Oculus/Avatar2/Example/Scenes/CustomHandPoseExample/SkeletonRenderer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..9b7f99f6e1cd9a3c6667ae49daeff389ef44a4e7
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CustomHandPoseExample/SkeletonRenderer.cs
@@ -0,0 +1,69 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+#if UNITY_EDITOR
+using UnityEditor;
+#endif
+
+[ExecuteAlways]
+public class SkeletonRenderer : MonoBehaviour
+{
+    public Color color;
+
+    public bool drawAxes;
+    public float axisSize;
+
+#if UNITY_EDITOR
+    private void OnEnable()
+    {
+#if UNITY_2019_3_OR_NEWER
+        SceneView.duringSceneGui += Draw;
+#else
+        SceneView.onSceneGUIDelegate += Draw;
+#endif
+    }
+
+    private void OnDisable()
+    {
+#if UNITY_2019_3_OR_NEWER
+        SceneView.duringSceneGui -= Draw;
+#else
+        SceneView.onSceneGUIDelegate -= Draw;
+#endif
+    }
+#endif
+
+#if UNITY_EDITOR
+    private void Draw(SceneView sceneView)
+    {
+        Handles.matrix = Matrix4x4.identity;
+
+        foreach (var xform in GetComponentsInChildren<Transform>())
+        {
+            var parent = xform.parent;
+            var position = xform.position;
+            if (parent)
+            {
+                Handles.color = color;
+                Handles.DrawLine(position, parent.position);
+            }
+
+            if (drawAxes)
+            {
+                var xAxis = position + xform.rotation * Vector3.right * axisSize ;
+                var yAxis = position + xform.rotation * Vector3.up * axisSize ;
+                var zAxis = position + xform.rotation * Vector3.forward * axisSize;
+
+                Handles.color = Color.blue;
+                Handles.DrawLine(position, zAxis);
+
+                Handles.color = Color.green;
+                Handles.DrawLine(position, yAxis);
+
+                Handles.color = Color.red;
+                Handles.DrawLine(position, xAxis);
+            }
+        }
+    }
+#endif
+}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CustomHandPoseExample/SkeletonRenderer.cs.meta b/Assets/Oculus/Avatar2/Example/Scenes/CustomHandPoseExample/SkeletonRenderer.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..41851d4e00ef8c7601db3d887f585f396362444c
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CustomHandPoseExample/SkeletonRenderer.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: e23dd79c8b6109246bfc3b35e6ac44cd
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CustomHandPoseExample/TrackingTransforms.prefab b/Assets/Oculus/Avatar2/Example/Scenes/CustomHandPoseExample/TrackingTransforms.prefab
new file mode 100644
index 0000000000000000000000000000000000000000..02884c547b2e83496ae7b30ed626424f0b20e7e9
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CustomHandPoseExample/TrackingTransforms.prefab
@@ -0,0 +1,144 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &2657541398914224604
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7458883811061015689}
+  m_Layer: 0
+  m_Name: Hmd
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &7458883811061015689
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2657541398914224604}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 1.6267126, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 5338449701420077780}
+  m_RootOrder: 1
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &4684227533121428177
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 5338449701420077780}
+  - component: {fileID: 3834270478772424751}
+  m_Layer: 0
+  m_Name: TrackingTransforms
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &5338449701420077780
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4684227533121428177}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 5581449784809560603}
+  - {fileID: 7458883811061015689}
+  - {fileID: 4424921461273402042}
+  m_Father: {fileID: 0}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &3834270478772424751
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4684227533121428177}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 66a61802bb8f1e947aa50f674d42db85, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  useRuntimeTrackingService: 0
+  hmd: {fileID: 7458883811061015689}
+  leftController: {fileID: 5581449784809560603}
+  rightController: {fileID: 4424921461273402042}
+  controllerType: -1
+  controllersVisbile: 0
+--- !u!1 &6331394476399000285
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 4424921461273402042}
+  m_Layer: 0
+  m_Name: RightHand
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &4424921461273402042
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6331394476399000285}
+  m_LocalRotation: {x: -0.019876199, y: 0.2154651, z: -0.089681685, w: 0.97218156}
+  m_LocalPosition: {x: 0.2418296, y: 1.1047595, z: 0.12397504}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 5338449701420077780}
+  m_RootOrder: 2
+  m_LocalEulerAnglesHint: {x: 0, y: 24.993, z: 10.541}
+--- !u!1 &8369674385085310522
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 5581449784809560603}
+  m_Layer: 0
+  m_Name: LeftHand
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &5581449784809560603
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8369674385085310522}
+  m_LocalRotation: {x: -0.019876199, y: -0.2154651, z: 0.089681685, w: 0.97218156}
+  m_LocalPosition: {x: -0.24182963, y: 1.1047595, z: 0.12397504}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 5338449701420077780}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: -24.993002, z: 10.541}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/CustomHandPoseExample/TrackingTransforms.prefab.meta b/Assets/Oculus/Avatar2/Example/Scenes/CustomHandPoseExample/TrackingTransforms.prefab.meta
new file mode 100644
index 0000000000000000000000000000000000000000..034aea5ceecdd128644650d6972e785affb969c3
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/CustomHandPoseExample/TrackingTransforms.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: c3dc5412b9c44234a8ae43008b7a6979
+PrefabImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample.meta b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample.meta
new file mode 100644
index 0000000000000000000000000000000000000000..41f2c7ce26eaf5e11dc3dcda6789b494b079ca81
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 8bd875e0abadb92478f511ccf979364e
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/FBConnect LightRig.prefab b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/FBConnect LightRig.prefab
new file mode 100644
index 0000000000000000000000000000000000000000..e539b01ba3aa9a0805e84adc65efacd499aa8397
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/FBConnect LightRig.prefab	
@@ -0,0 +1,485 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &4152892597192438353
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1366144440593225027}
+  - component: {fileID: 4911876571282689241}
+  - component: {fileID: 2142597478597051127}
+  - component: {fileID: 2250886707108245042}
+  m_Layer: 0
+  m_Name: EnvironmentCube
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1366144440593225027
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4152892597192438353}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 100, y: 100, z: 100}
+  m_Children: []
+  m_Father: {fileID: 5650585432433268674}
+  m_RootOrder: 4
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &4911876571282689241
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4152892597192438353}
+  m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &2142597478597051127
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4152892597192438353}
+  m_Enabled: 1
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RayTracingMode: 2
+  m_RayTraceProcedural: 0
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: 8c8192965c0a7364887ecb0fec418bfa, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_ReceiveGI: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+  m_AdditionalVertexStreams: {fileID: 0}
+--- !u!65 &2250886707108245042
+BoxCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4152892597192438353}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Size: {x: 1, y: 1, z: 1}
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &6176357634634757532
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 6176357634634757534}
+  - component: {fileID: 6176357634634757535}
+  m_Layer: 0
+  m_Name: Rim Directional Light
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &6176357634634757534
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6176357634634757532}
+  m_LocalRotation: {x: 0.4283543, y: -0.07040803, z: -0.12478523, w: 0.89217937}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 5650585432433268674}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 48.311, y: -20.465, z: -25.181}
+--- !u!108 &6176357634634757535
+Light:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6176357634634757532}
+  m_Enabled: 1
+  serializedVersion: 10
+  m_Type: 1
+  m_Shape: 0
+  m_Color: {r: 1, g: 1, b: 1, a: 1}
+  m_Intensity: 2
+  m_Range: 10
+  m_SpotAngle: 30
+  m_InnerSpotAngle: 21.80208
+  m_CookieSize: 10
+  m_Shadows:
+    m_Type: 0
+    m_Resolution: -1
+    m_CustomResolution: -1
+    m_Strength: 1
+    m_Bias: 0.05
+    m_NormalBias: 0.4
+    m_NearPlane: 0.2
+    m_CullingMatrixOverride:
+      e00: 1
+      e01: 0
+      e02: 0
+      e03: 0
+      e10: 0
+      e11: 1
+      e12: 0
+      e13: 0
+      e20: 0
+      e21: 0
+      e22: 1
+      e23: 0
+      e30: 0
+      e31: 0
+      e32: 0
+      e33: 1
+    m_UseCullingMatrixOverride: 0
+  m_Cookie: {fileID: 0}
+  m_DrawHalo: 0
+  m_Flare: {fileID: 0}
+  m_RenderMode: 0
+  m_CullingMask:
+    serializedVersion: 2
+    m_Bits: 4294967295
+  m_RenderingLayerMask: 1
+  m_Lightmapping: 4
+  m_LightShadowCasterMode: 0
+  m_AreaSize: {x: 1, y: 1}
+  m_BounceIntensity: 1
+  m_ColorTemperature: 6570
+  m_UseColorTemperature: 0
+  m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
+  m_UseBoundingSphereOverride: 0
+  m_UseViewFrustumForShadowCasterCull: 1
+  m_ShadowRadius: 0
+  m_ShadowAngle: 0
+--- !u!1 &6176357635736794077
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 6176357635736794076}
+  - component: {fileID: 6176357635736794079}
+  m_Layer: 0
+  m_Name: Light Probe Group
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &6176357635736794076
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6176357635736794077}
+  m_LocalRotation: {x: 0.008333467, y: 0.9844971, z: -0.048752766, w: 0.16828324}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 5650585432433268674}
+  m_RootOrder: 3
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!220 &6176357635736794079
+LightProbeGroup:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6176357635736794077}
+  m_Enabled: 1
+  m_SourcePositions:
+  - {x: 0, y: 0, z: 0}
+  m_Dering: 0
+--- !u!1 &6449103579296214519
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 2045496478646130412}
+  - component: {fileID: 1213625666104222948}
+  m_Layer: 0
+  m_Name: Fill Directional Light
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &2045496478646130412
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6449103579296214519}
+  m_LocalRotation: {x: 0.54001343, y: 0.5843682, z: 0.41512582, w: 0.4411008}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 5650585432433268674}
+  m_RootOrder: 2
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!108 &1213625666104222948
+Light:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6449103579296214519}
+  m_Enabled: 1
+  serializedVersion: 10
+  m_Type: 1
+  m_Shape: 0
+  m_Color: {r: 0.72111154, g: 0.834465, b: 1, a: 1}
+  m_Intensity: 0.2
+  m_Range: 10
+  m_SpotAngle: 30
+  m_InnerSpotAngle: 21.80208
+  m_CookieSize: 10
+  m_Shadows:
+    m_Type: 0
+    m_Resolution: -1
+    m_CustomResolution: -1
+    m_Strength: 1
+    m_Bias: 0.05
+    m_NormalBias: 0.4
+    m_NearPlane: 0.2
+    m_CullingMatrixOverride:
+      e00: 1
+      e01: 0
+      e02: 0
+      e03: 0
+      e10: 0
+      e11: 1
+      e12: 0
+      e13: 0
+      e20: 0
+      e21: 0
+      e22: 1
+      e23: 0
+      e30: 0
+      e31: 0
+      e32: 0
+      e33: 1
+    m_UseCullingMatrixOverride: 0
+  m_Cookie: {fileID: 0}
+  m_DrawHalo: 0
+  m_Flare: {fileID: 0}
+  m_RenderMode: 0
+  m_CullingMask:
+    serializedVersion: 2
+    m_Bits: 4294967295
+  m_RenderingLayerMask: 1
+  m_Lightmapping: 4
+  m_LightShadowCasterMode: 0
+  m_AreaSize: {x: 1, y: 1}
+  m_BounceIntensity: 1
+  m_ColorTemperature: 6570
+  m_UseColorTemperature: 0
+  m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
+  m_UseBoundingSphereOverride: 0
+  m_UseViewFrustumForShadowCasterCull: 1
+  m_ShadowRadius: 0
+  m_ShadowAngle: 0
+--- !u!1 &7868569535731771042
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 2986542401637056387}
+  - component: {fileID: 6326507335235603723}
+  m_Layer: 0
+  m_Name: Key Directional Light
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &2986542401637056387
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7868569535731771042}
+  m_LocalRotation: {x: 0.60625184, y: 0.55221105, z: -0.37553748, w: -0.4318487}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 5650585432433268674}
+  m_RootOrder: 1
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!108 &6326507335235603723
+Light:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7868569535731771042}
+  m_Enabled: 1
+  serializedVersion: 10
+  m_Type: 1
+  m_Shape: 0
+  m_Color: {r: 0.64355105, g: 0.7586203, b: 1, a: 1}
+  m_Intensity: 0.5
+  m_Range: 10
+  m_SpotAngle: 30
+  m_InnerSpotAngle: 21.80208
+  m_CookieSize: 10
+  m_Shadows:
+    m_Type: 0
+    m_Resolution: -1
+    m_CustomResolution: -1
+    m_Strength: 1
+    m_Bias: 0.05
+    m_NormalBias: 0.4
+    m_NearPlane: 0.2
+    m_CullingMatrixOverride:
+      e00: 1
+      e01: 0
+      e02: 0
+      e03: 0
+      e10: 0
+      e11: 1
+      e12: 0
+      e13: 0
+      e20: 0
+      e21: 0
+      e22: 1
+      e23: 0
+      e30: 0
+      e31: 0
+      e32: 0
+      e33: 1
+    m_UseCullingMatrixOverride: 0
+  m_Cookie: {fileID: 0}
+  m_DrawHalo: 0
+  m_Flare: {fileID: 0}
+  m_RenderMode: 0
+  m_CullingMask:
+    serializedVersion: 2
+    m_Bits: 4294967295
+  m_RenderingLayerMask: 1
+  m_Lightmapping: 4
+  m_LightShadowCasterMode: 0
+  m_AreaSize: {x: 1, y: 1}
+  m_BounceIntensity: 1
+  m_ColorTemperature: 6570
+  m_UseColorTemperature: 0
+  m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
+  m_UseBoundingSphereOverride: 0
+  m_UseViewFrustumForShadowCasterCull: 1
+  m_ShadowRadius: 0
+  m_ShadowAngle: 0
+--- !u!1 &8423574076275988413
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 5650585432433268674}
+  - component: {fileID: 4994091506875508677}
+  m_Layer: 0
+  m_Name: FBConnect LightRig
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &5650585432433268674
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8423574076275988413}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 6176357634634757534}
+  - {fileID: 2986542401637056387}
+  - {fileID: 2045496478646130412}
+  - {fileID: 6176357635736794076}
+  - {fileID: 1366144440593225027}
+  m_Father: {fileID: 0}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &4994091506875508677
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8423574076275988413}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: cc8b2bc8875ed714f874f31c2183c84b, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  CurrentExposure: 1
+  ExposureShaderParameterName: u_Exposure
+  DiffuseEnvironmentCubeMap: {fileID: 8900000, guid: 1c9a7b5b31b38dd4f97e888ad38264f0,
+    type: 3}
+  DiffuseEnvironmentShaderParameterNames:
+  - u_DiffuseEnvSampler
+  - u_LambertianEnvSampler
+  SpecularEnvironmentCubeMap: {fileID: 8900000, guid: 8ebfa27451fc8dc4e834834a16ded0a0,
+    type: 3}
+  SpecularEnvironmentShaderParameterNames:
+  - u_SpecularEnvSampler
+  - u_GGXEnvSampler
+  SpecularMapMipCountShaderPameterName: u_MipCount
+  BrdfLutMap: {fileID: 2800000, guid: 918eb5f61eea9f6479953048b19170c0, type: 3}
+  BrdfLutShaderParameterNames:
+  - u_brdfLUT
+  - u_GGXLUT
+  CubeMapMaterial: {fileID: 2100000, guid: 8c8192965c0a7364887ecb0fec418bfa, type: 2}
+  CubeMapShaderParameterName: _Tex
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/FBConnect LightRig.prefab.meta b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/FBConnect LightRig.prefab.meta
new file mode 100644
index 0000000000000000000000000000000000000000..5393f8d63495c1177dce101ff81409ebbb360ea2
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/FBConnect LightRig.prefab.meta	
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 74f510f9ed0cf6346a8c7b6bb6e728a3
+PrefabImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/FBConnect Skybox.mat b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/FBConnect Skybox.mat
new file mode 100644
index 0000000000000000000000000000000000000000..e4719e770928db9e52eb0e58ee2c7fcdb90fa631
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/FBConnect Skybox.mat	
@@ -0,0 +1,85 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: FBConnect Skybox
+  m_Shader: {fileID: 4800000, guid: 4dda107104e02f9418ce35671143716c, type: 3}
+  m_ShaderKeywords: 
+  m_LightmapFlags: 4
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _Tex:
+        m_Texture: {fileID: 8900000, guid: 8ebfa27451fc8dc4e834834a16ded0a0, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _Exposure: 1
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _Rotation: 0
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 1, g: 1, b: 1, a: 1}
+    - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
+    - _Tint: {r: 0.5, g: 0.5, b: 0.5, a: 0.5}
+  m_BuildTextureStacks: []
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/FBConnect Skybox.mat.meta b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/FBConnect Skybox.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..15fd7f7e0fd4e92218dce9b16dc77049f88bb0a7
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/FBConnect Skybox.mat.meta	
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 8c8192965c0a7364887ecb0fec418bfa
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/FBConnectLightingExample.meta b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/FBConnectLightingExample.meta
new file mode 100644
index 0000000000000000000000000000000000000000..262573dc2bdd35d92acfb056a3164bbbfd4d36ce
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/FBConnectLightingExample.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: d1eab0e2145d6a6459983066a7a9463d
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/FBConnectLightingExample.unity b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/FBConnectLightingExample.unity
new file mode 100644
index 0000000000000000000000000000000000000000..923e7d54b074687e020c8a1a6e0d6051c78b6250
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/FBConnectLightingExample.unity
@@ -0,0 +1,640 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!29 &1
+OcclusionCullingSettings:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_OcclusionBakeSettings:
+    smallestOccluder: 5
+    smallestHole: 0.25
+    backfaceThreshold: 100
+  m_SceneGUID: 00000000000000000000000000000000
+  m_OcclusionCullingData: {fileID: 0}
+--- !u!104 &2
+RenderSettings:
+  m_ObjectHideFlags: 0
+  serializedVersion: 9
+  m_Fog: 0
+  m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
+  m_FogMode: 3
+  m_FogDensity: 0.01
+  m_LinearFogStart: 0
+  m_LinearFogEnd: 300
+  m_AmbientSkyColor: {r: 0, g: 0, b: 0, a: 1}
+  m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
+  m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
+  m_AmbientIntensity: 1
+  m_AmbientMode: 0
+  m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
+  m_SkyboxMaterial: {fileID: 0}
+  m_HaloStrength: 0.5
+  m_FlareStrength: 1
+  m_FlareFadeSpeed: 3
+  m_HaloTexture: {fileID: 0}
+  m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
+  m_DefaultReflectionMode: 0
+  m_DefaultReflectionResolution: 128
+  m_ReflectionBounces: 1
+  m_ReflectionIntensity: 1
+  m_CustomReflection: {fileID: 0}
+  m_Sun: {fileID: 0}
+  m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1}
+  m_UseRadianceAmbientProbe: 0
+--- !u!157 &3
+LightmapSettings:
+  m_ObjectHideFlags: 0
+  serializedVersion: 11
+  m_GIWorkflowMode: 1
+  m_GISettings:
+    serializedVersion: 2
+    m_BounceScale: 1
+    m_IndirectOutputScale: 1
+    m_AlbedoBoost: 1
+    m_EnvironmentLightingMode: 0
+    m_EnableBakedLightmaps: 1
+    m_EnableRealtimeLightmaps: 0
+  m_LightmapEditorSettings:
+    serializedVersion: 12
+    m_Resolution: 2
+    m_BakeResolution: 40
+    m_AtlasSize: 32
+    m_AO: 0
+    m_AOMaxDistance: 1
+    m_CompAOExponent: 1
+    m_CompAOExponentDirect: 0
+    m_ExtractAmbientOcclusion: 0
+    m_Padding: 2
+    m_LightmapParameters: {fileID: 15203, guid: 0000000000000000f000000000000000,
+      type: 0}
+    m_LightmapsBakeMode: 1
+    m_TextureCompression: 1
+    m_FinalGather: 0
+    m_FinalGatherFiltering: 1
+    m_FinalGatherRayCount: 256
+    m_ReflectionCompression: 2
+    m_MixedBakeMode: 0
+    m_BakeBackend: 1
+    m_PVRSampling: 1
+    m_PVRDirectSampleCount: 32
+    m_PVRSampleCount: 512
+    m_PVRBounces: 2
+    m_PVREnvironmentSampleCount: 512
+    m_PVREnvironmentReferencePointCount: 2048
+    m_PVRFilteringMode: 2
+    m_PVRDenoiserTypeDirect: 0
+    m_PVRDenoiserTypeIndirect: 0
+    m_PVRDenoiserTypeAO: 0
+    m_PVRFilterTypeDirect: 0
+    m_PVRFilterTypeIndirect: 0
+    m_PVRFilterTypeAO: 0
+    m_PVREnvironmentMIS: 0
+    m_PVRCulling: 1
+    m_PVRFilteringGaussRadiusDirect: 1
+    m_PVRFilteringGaussRadiusIndirect: 5
+    m_PVRFilteringGaussRadiusAO: 2
+    m_PVRFilteringAtrousPositionSigmaDirect: 0.5
+    m_PVRFilteringAtrousPositionSigmaIndirect: 2
+    m_PVRFilteringAtrousPositionSigmaAO: 1
+    m_ExportTrainingData: 0
+    m_TrainingDataDestination: TrainingData
+    m_LightProbeSampleCountMultiplier: 4
+  m_LightingDataAsset: {fileID: 112000000, guid: 9bcea40e9ee504c468cd289fadf03539,
+    type: 2}
+  m_UseShadowmask: 0
+--- !u!196 &4
+NavMeshSettings:
+  serializedVersion: 2
+  m_ObjectHideFlags: 0
+  m_BuildSettings:
+    serializedVersion: 2
+    agentTypeID: 0
+    agentRadius: 0.5
+    agentHeight: 2
+    agentSlope: 45
+    agentClimb: 0.4
+    ledgeDropHeight: 0
+    maxJumpAcrossDistance: 0
+    minRegionArea: 2
+    manualCellSize: 0
+    cellSize: 0.16666667
+    manualTileSize: 0
+    tileSize: 256
+    accuratePlacement: 0
+    debug:
+      m_Flags: 0
+  m_NavMeshData: {fileID: 0}
+--- !u!1001 &283062809
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 0}
+    m_Modifications:
+    - target: {fileID: 5650585432433268674, guid: 74f510f9ed0cf6346a8c7b6bb6e728a3,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 74f510f9ed0cf6346a8c7b6bb6e728a3,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 74f510f9ed0cf6346a8c7b6bb6e728a3,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 74f510f9ed0cf6346a8c7b6bb6e728a3,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 74f510f9ed0cf6346a8c7b6bb6e728a3,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 74f510f9ed0cf6346a8c7b6bb6e728a3,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 74f510f9ed0cf6346a8c7b6bb6e728a3,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 74f510f9ed0cf6346a8c7b6bb6e728a3,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 74f510f9ed0cf6346a8c7b6bb6e728a3,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 74f510f9ed0cf6346a8c7b6bb6e728a3,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 74f510f9ed0cf6346a8c7b6bb6e728a3,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 8423574076275988413, guid: 74f510f9ed0cf6346a8c7b6bb6e728a3,
+        type: 3}
+      propertyPath: m_Name
+      value: FBConnect LightRig
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: 74f510f9ed0cf6346a8c7b6bb6e728a3, type: 3}
+--- !u!1 &743882950
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 743882951}
+  m_Layer: 0
+  m_Name: Environment - (Offset to 0,0,0)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &743882951
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 743882950}
+  m_LocalRotation: {x: 0, y: 1, z: 0, w: 0}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 2208084581874630033}
+  m_Father: {fileID: 0}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!1001 &870842040
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 0}
+    m_Modifications:
+    - target: {fileID: 3157928069488761441, guid: 6df47a28fe7c7c444a658aebcc35ad94,
+        type: 3}
+      propertyPath: m_Name
+      value: AvatarSdkManagerKhronosBabylonMatch
+      objectReference: {fileID: 0}
+    - target: {fileID: 3157928069488761443, guid: 6df47a28fe7c7c444a658aebcc35ad94,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 4
+      objectReference: {fileID: 0}
+    - target: {fileID: 3157928069488761443, guid: 6df47a28fe7c7c444a658aebcc35ad94,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3157928069488761443, guid: 6df47a28fe7c7c444a658aebcc35ad94,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3157928069488761443, guid: 6df47a28fe7c7c444a658aebcc35ad94,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3157928069488761443, guid: 6df47a28fe7c7c444a658aebcc35ad94,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 3157928069488761443, guid: 6df47a28fe7c7c444a658aebcc35ad94,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3157928069488761443, guid: 6df47a28fe7c7c444a658aebcc35ad94,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3157928069488761443, guid: 6df47a28fe7c7c444a658aebcc35ad94,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3157928069488761443, guid: 6df47a28fe7c7c444a658aebcc35ad94,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3157928069488761443, guid: 6df47a28fe7c7c444a658aebcc35ad94,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3157928069488761443, guid: 6df47a28fe7c7c444a658aebcc35ad94,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: 6df47a28fe7c7c444a658aebcc35ad94, type: 3}
+--- !u!1001 &873585259
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 0}
+    m_Modifications:
+    - target: {fileID: 1396242425075152212, guid: 4747de51a127cf948a8c472571f1dd42,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 3
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: 4747de51a127cf948a8c472571f1dd42,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: 4747de51a127cf948a8c472571f1dd42,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0.2
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: 4747de51a127cf948a8c472571f1dd42,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: 4747de51a127cf948a8c472571f1dd42,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: 4747de51a127cf948a8c472571f1dd42,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: 4747de51a127cf948a8c472571f1dd42,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: 4747de51a127cf948a8c472571f1dd42,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: 4747de51a127cf948a8c472571f1dd42,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: 4747de51a127cf948a8c472571f1dd42,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 180
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: 4747de51a127cf948a8c472571f1dd42,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152213, guid: 4747de51a127cf948a8c472571f1dd42,
+        type: 3}
+      propertyPath: m_Name
+      value: ShowcaseAvatarsGroup1
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: 4747de51a127cf948a8c472571f1dd42, type: 3}
+--- !u!1 &1003308635
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1003308637}
+  - component: {fileID: 1003308636}
+  m_Layer: 0
+  m_Name: SceneSwitcher
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!114 &1003308636
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1003308635}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: ea6e0c6cd67922c48bbb5939267828e9, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _nextSceneInput:
+    controllerMask: 2
+    buttonMask: 1
+  _prevSceneInput:
+    controllerMask: 1
+    buttonMask: 1
+--- !u!4 &1003308637
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1003308635}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 0}
+  m_RootOrder: 5
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &1460366300
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1460366301}
+  - component: {fileID: 1460366302}
+  m_Layer: 0
+  m_Name: CameraOffset
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1460366301
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1460366300}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0.5, y: 1.8, z: -5}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 1716217489}
+  m_Father: {fileID: 0}
+  m_RootOrder: 2
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &1460366302
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1460366300}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 5b0eaccd645fc5641ba1cf708f6fd678, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  mirrorMovement: 0
+  movementSpeed: 2.5
+--- !u!1 &1716217486
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1716217489}
+  - component: {fileID: 1716217488}
+  - component: {fileID: 1716217487}
+  - component: {fileID: 1716217490}
+  m_Layer: 0
+  m_Name: Main Camera
+  m_TagString: MainCamera
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!81 &1716217487
+AudioListener:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1716217486}
+  m_Enabled: 1
+--- !u!20 &1716217488
+Camera:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1716217486}
+  m_Enabled: 1
+  serializedVersion: 2
+  m_ClearFlags: 1
+  m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
+  m_projectionMatrixMode: 1
+  m_GateFitMode: 2
+  m_FOVAxisMode: 0
+  m_SensorSize: {x: 36, y: 24}
+  m_LensShift: {x: 0, y: 0}
+  m_FocalLength: 50
+  m_NormalizedViewPortRect:
+    serializedVersion: 2
+    x: 0
+    y: 0
+    width: 1
+    height: 1
+  near clip plane: 0.3
+  far clip plane: 1000
+  field of view: 60
+  orthographic: 0
+  orthographic size: 5
+  m_Depth: -1
+  m_CullingMask:
+    serializedVersion: 2
+    m_Bits: 4294967295
+  m_RenderingPath: -1
+  m_TargetTexture: {fileID: 0}
+  m_TargetDisplay: 0
+  m_TargetEye: 3
+  m_HDR: 1
+  m_AllowMSAA: 1
+  m_AllowDynamicResolution: 0
+  m_ForceIntoRT: 0
+  m_OcclusionCulling: 1
+  m_StereoConvergence: 10
+  m_StereoSeparation: 0.022
+--- !u!4 &1716217489
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1716217486}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1460366301}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &1716217490
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1716217486}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 5a2a9c34df4095f47b9ca8f975175f5b, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_Device: 0
+  m_PoseSource: 2
+  m_PoseProviderComponent: {fileID: 0}
+  m_TrackingType: 0
+  m_UpdateType: 0
+  m_UseRelativeTransform: 0
+--- !u!1001 &806741081905082620
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 743882951}
+    m_Modifications:
+    - target: {fileID: 1555623716397632877, guid: 18dd326916f881d48a3f13a6a1cd0329,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1555623716397632877, guid: 18dd326916f881d48a3f13a6a1cd0329,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1555623716397632877, guid: 18dd326916f881d48a3f13a6a1cd0329,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1555623716397632877, guid: 18dd326916f881d48a3f13a6a1cd0329,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1555623716397632877, guid: 18dd326916f881d48a3f13a6a1cd0329,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 1555623716397632877, guid: 18dd326916f881d48a3f13a6a1cd0329,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1555623716397632877, guid: 18dd326916f881d48a3f13a6a1cd0329,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1555623716397632877, guid: 18dd326916f881d48a3f13a6a1cd0329,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1555623716397632877, guid: 18dd326916f881d48a3f13a6a1cd0329,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1555623716397632877, guid: 18dd326916f881d48a3f13a6a1cd0329,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1555623716397632877, guid: 18dd326916f881d48a3f13a6a1cd0329,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3602176088740148085, guid: 18dd326916f881d48a3f13a6a1cd0329,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: ea5877ea2c5fdce41b360ae15e6889b0, type: 2}
+    - target: {fileID: 6257641961810558837, guid: 18dd326916f881d48a3f13a6a1cd0329,
+        type: 3}
+      propertyPath: m_Name
+      value: FBConnectStreamRoom_Environment
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: 18dd326916f881d48a3f13a6a1cd0329, type: 3}
+--- !u!4 &2208084581874630033 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: 1555623716397632877, guid: 18dd326916f881d48a3f13a6a1cd0329,
+    type: 3}
+  m_PrefabInstance: {fileID: 806741081905082620}
+  m_PrefabAsset: {fileID: 0}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/FBConnectLightingExample.unity.meta b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/FBConnectLightingExample.unity.meta
new file mode 100644
index 0000000000000000000000000000000000000000..08b358064fffcb11a32f32947338d524f11320c4
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/FBConnectLightingExample.unity.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: eff2058b5a617274c9cc8cbc0cd0cc28
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/FBConnectLightingExample/LightingData.asset b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/FBConnectLightingExample/LightingData.asset
new file mode 100644
index 0000000000000000000000000000000000000000..a0fa0ead23cfbaad74fd53c5d7298e67e26fb08c
Binary files /dev/null and b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/FBConnectLightingExample/LightingData.asset differ
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/FBConnectLightingExample/LightingData.asset.meta b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/FBConnectLightingExample/LightingData.asset.meta
new file mode 100644
index 0000000000000000000000000000000000000000..1d2a113f425dca12edbdf8798c92155c44c22f9d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/FBConnectLightingExample/LightingData.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 9bcea40e9ee504c468cd289fadf03539
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials.meta b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials.meta
new file mode 100644
index 0000000000000000000000000000000000000000..fae9719bbbb6c97464b899f53609fcc7cca97c7f
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 4fbc60833f72d304582dcdaabb4b6364
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/Lighting.meta b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/Lighting.meta
new file mode 100644
index 0000000000000000000000000000000000000000..a1f15b063d91cd9543b74c5a1dfb2f1f7fcb5d94
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/Lighting.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: d7cf2d8b4410a794ea0209119f0fe084
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/Lighting/m_FCStreamRoomIndirect_back.mat b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/Lighting/m_FCStreamRoomIndirect_back.mat
new file mode 100644
index 0000000000000000000000000000000000000000..4f17a10584840e4a277528011afac4e7c293e931
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/Lighting/m_FCStreamRoomIndirect_back.mat
@@ -0,0 +1,77 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: m_FCStreamRoomIndirect_back
+  m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
+  m_ShaderKeywords: _EMISSION
+  m_LightmapFlags: 2
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 0.7960785, g: 0.7607844, b: 0.8352942, a: 1}
+    - _EmissionColor: {r: 0.8490127, g: 0.60752535, b: 1.0003219, a: 1}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/Lighting/m_FCStreamRoomIndirect_back.mat.meta b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/Lighting/m_FCStreamRoomIndirect_back.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..a117ff37b3e69e2320963e6f74d848af26fe9668
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/Lighting/m_FCStreamRoomIndirect_back.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 8d333a7b55f3403498641dcdd53c7576
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/Lighting/m_FCStreamRoomIndirect_environment.mat b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/Lighting/m_FCStreamRoomIndirect_environment.mat
new file mode 100644
index 0000000000000000000000000000000000000000..9ed003aba212d30ef5335040b4390acab45e1b45
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/Lighting/m_FCStreamRoomIndirect_environment.mat
@@ -0,0 +1,77 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: m_FCStreamRoomIndirect_environment
+  m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
+  m_ShaderKeywords: _EMISSION
+  m_LightmapFlags: 2
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 2800000, guid: 498c1c0913de29d4093d9b8975121aed, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 0, g: 0, b: 0, a: 1}
+    - _EmissionColor: {r: 0.7252566, g: 0.7252566, b: 0.7252566, a: 1}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/Lighting/m_FCStreamRoomIndirect_environment.mat.meta b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/Lighting/m_FCStreamRoomIndirect_environment.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..104daccd024d21858a68cd980487c51cb29bb343
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/Lighting/m_FCStreamRoomIndirect_environment.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: cf961a2693c76ac429da0e192a39b6af
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/Lighting/m_FCStreamRoomIndirect_front.mat b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/Lighting/m_FCStreamRoomIndirect_front.mat
new file mode 100644
index 0000000000000000000000000000000000000000..3d3044476a185a8aef5a389b0535e546a82576cd
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/Lighting/m_FCStreamRoomIndirect_front.mat
@@ -0,0 +1,77 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: m_FCStreamRoomIndirect_front
+  m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
+  m_ShaderKeywords: _EMISSION
+  m_LightmapFlags: 2
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 1, g: 1, b: 1, a: 1}
+    - _EmissionColor: {r: 0.8603976, g: 0.8288647, b: 0.7477801, a: 1}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/Lighting/m_FCStreamRoomIndirect_front.mat.meta b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/Lighting/m_FCStreamRoomIndirect_front.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..60b510759b871a6b22fc14244d294244cfe45744
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/Lighting/m_FCStreamRoomIndirect_front.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 22e36d7bc1acc6342a9a0fa03a34a63a
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/Lighting/m_FCStreamRoomIndirect_sides.mat b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/Lighting/m_FCStreamRoomIndirect_sides.mat
new file mode 100644
index 0000000000000000000000000000000000000000..dd62bbf8b00eb06a241d53ac6619607fa7ab908b
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/Lighting/m_FCStreamRoomIndirect_sides.mat
@@ -0,0 +1,77 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: m_FCStreamRoomIndirect_sides
+  m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
+  m_ShaderKeywords: _EMISSION
+  m_LightmapFlags: 2
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 1, g: 1, b: 1, a: 1}
+    - _EmissionColor: {r: 0.85882354, g: 0.7927602, b: 0.64411765, a: 1}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/Lighting/m_FCStreamRoomIndirect_sides.mat.meta b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/Lighting/m_FCStreamRoomIndirect_sides.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..897ff66f78cab31ddf44eede5f235ce542fa1232
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/Lighting/m_FCStreamRoomIndirect_sides.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: f4bfcbcb444a8644282e3af1e8a0dbb0
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/Lighting/m_FCStreamRoomIndirect_top.mat b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/Lighting/m_FCStreamRoomIndirect_top.mat
new file mode 100644
index 0000000000000000000000000000000000000000..e65d35e5b17d3acf262d6bf2fe971cbc0de4ec81
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/Lighting/m_FCStreamRoomIndirect_top.mat
@@ -0,0 +1,77 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: m_FCStreamRoomIndirect_top
+  m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
+  m_ShaderKeywords: _EMISSION
+  m_LightmapFlags: 2
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 1, g: 1, b: 1, a: 1}
+    - _EmissionColor: {r: 0.36146083, g: 0.8616218, b: 1.021337, a: 1}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/Lighting/m_FCStreamRoomIndirect_top.mat.meta b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/Lighting/m_FCStreamRoomIndirect_top.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..2c4c1bb0fccb610b9b841371d80ba06222dda829
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/Lighting/m_FCStreamRoomIndirect_top.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: a2cecc19a394f3142aaac5d860a9df5c
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/m_FCLogoLogoSheet.mat b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/m_FCLogoLogoSheet.mat
new file mode 100644
index 0000000000000000000000000000000000000000..062db4646aa100e55f282b8b5fe9180906b1a911
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/m_FCLogoLogoSheet.mat
@@ -0,0 +1,81 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: m_FCLogoLogoSheet
+  m_Shader: {fileID: 4800000, guid: fd9a9d30c0b934743a023e7bf1e60260, type: 3}
+  m_ShaderKeywords: _LOOPING_ON _USE_GAME_TIME_ON
+  m_LightmapFlags: 4
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BgTex:
+        m_Texture: {fileID: 2800000, guid: 498c1c0913de29d4093d9b8975121aed, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 2800000, guid: 09450a82555926f4dad06184e60d43a3, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _nTex:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _posTex:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _Dither: 0
+    - _ExponentB: 1
+    - _ExponentT: 1
+    - _FrameLock: 8
+    - _Gamma: 2.2
+    - _Glossiness: 0.5
+    - _HueShift: 0
+    - _HueShiftAmount: 0
+    - _HueShiftSpeed: 0
+    - _Intensity: 1
+    - _Looping: 1
+    - _Metallic: 0
+    - _NumCols: 4
+    - _NumRows: 4
+    - _SheetFrameRatio: 0.25
+    - _Sun: 0
+    - _SunAltitude: 30
+    - _SunAzimuth: 60
+    - _SunIntensity: 2
+    - _SunSize: 0.3
+    - _Tick: 0
+    - _TimeLock: 150
+    - _boundingMax: 1
+    - _boundingMin: 1
+    - _endFrame: 240
+    - _numOfFrames: 240
+    - _pack_normal: 0
+    - _speed: 0.33
+    - _startFrame: 1
+    - _timeOffset: 0
+    - _use_game_time: 1
+    m_Colors:
+    - _BgColor: {r: 1, g: 1, b: 1, a: 1}
+    - _Color: {r: 1, g: 1, b: 1, a: 1}
+    - _ColorB: {r: 1, g: 1, b: 1, a: 1}
+    - _ColorM: {r: 1, g: 1, b: 1, a: 1}
+    - _ColorT: {r: 1, g: 1, b: 1, a: 1}
+    - _FgColor: {r: 0, g: 0, b: 0, a: 1}
+    - _SunColor: {r: 1, g: 1, b: 1, a: 1}
+    - _SunVector: {r: 1, g: 0, b: 0, a: 0}
+    - _clothColor: {r: 0, g: 1, b: 1, a: 1}
+    - _hairColor: {r: 1, g: 0, b: 1, a: 1}
+    - _skinColor: {r: 1, g: 1, b: 0, a: 1}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/m_FCLogoLogoSheet.mat.meta b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/m_FCLogoLogoSheet.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..333f34558c8f888b3dafe62773dab4a23fc045a6
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/m_FCLogoLogoSheet.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: e0321c058f6ba90428c033bc0f6e9d56
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/m_FCStreamRoom.mat b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/m_FCStreamRoom.mat
new file mode 100644
index 0000000000000000000000000000000000000000..07bc3ff705c96bb4166e72101af35262143006b2
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/m_FCStreamRoom.mat
@@ -0,0 +1,96 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: m_FCStreamRoom
+  m_Shader: {fileID: 4800000, guid: 42b38d516695b1e458b7cd8e467bb46f, type: 3}
+  m_ShaderKeywords: ENABLE_REFLECTIONS
+  m_LightmapFlags: 4
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionTex:
+        m_Texture: {fileID: 2800000, guid: 4ed8d69324dc04646a3fcc7bc8134125, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 2800000, guid: 4ed8d69324dc04646a3fcc7bc8134125, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ReflectionCubemap:
+        m_Texture: {fileID: 8900000, guid: d6abb82e8d94aa44d83d730def697655, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _AmbientIntensity: 1
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _EmissionIntensity: 0
+    - _EnableAmbient: 0
+    - _EnableEmission: 0
+    - _EnableFog: 0
+    - _EnableReflections: 1
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _ReflectionIntensity: 1.2
+    - _RimWeight: 0.124
+    - _RimWidth: 0.098
+    - _Roughness: 0.65
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 0.9, g: 0.9, b: 0.9, a: 1}
+    - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
+    - _RimColor: {r: 0.6698113, g: 0.33707428, b: 0.13585795, a: 1}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/m_FCStreamRoom.mat.meta b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/m_FCStreamRoom.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..9fde14d55d06ed435ecbc806a883539d66ed6b8d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/m_FCStreamRoom.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 95e9de1a85c67b048add0a4f8d7cf32a
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 2100000
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/t_FCLogoLogoSheet.png b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/t_FCLogoLogoSheet.png
new file mode 100644
index 0000000000000000000000000000000000000000..f1ebe43525aa872a40fa9abac1c2d9fd5564e4ff
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/t_FCLogoLogoSheet.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:45fa8a8f20bc97aaf13247ed0aea8adcebbef92537f7e36cebd7c682cea52a7f
+size 44151
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/t_FCLogoLogoSheet.png.meta b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/t_FCLogoLogoSheet.png.meta
new file mode 100644
index 0000000000000000000000000000000000000000..8670f6c15514ef7d1549fb7b653177be12e5b836
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/t_FCLogoLogoSheet.png.meta
@@ -0,0 +1,119 @@
+fileFormatVersion: 2
+guid: 09450a82555926f4dad06184e60d43a3
+TextureImporter:
+  internalIDToNameTable: []
+  externalObjects: {}
+  serializedVersion: 11
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 1
+    sRGBTexture: 1
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 0
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: -1
+    aniso: -1
+    mipBias: -100
+    wrapU: -1
+    wrapV: -1
+    wrapW: -1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 1
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 0
+  textureShape: 1
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  applyGammaDecoding: 0
+  platformSettings:
+  - serializedVersion: 3
+    buildTarget: DefaultTexturePlatform
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: 1
+    textureCompression: 1
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  - serializedVersion: 3
+    buildTarget: Standalone
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: 1
+    textureCompression: 1
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  - serializedVersion: 3
+    buildTarget: Android
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: 1
+    textureCompression: 1
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    internalID: 0
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+    secondaryTextures: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/t_FCStreamRoom.png b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/t_FCStreamRoom.png
new file mode 100644
index 0000000000000000000000000000000000000000..2d6059cd8707aad58fc6de1a131519bcba447ef8
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/t_FCStreamRoom.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a3227fdf04e086c5d2e21372d84c33c91c3b7da21f9c30f48f4e8b056aed8379
+size 2080996
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/t_FCStreamRoom.png.meta b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/t_FCStreamRoom.png.meta
new file mode 100644
index 0000000000000000000000000000000000000000..5e54794591c5add792f6237e06f43511a5d83a13
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Materials/t_FCStreamRoom.png.meta
@@ -0,0 +1,132 @@
+fileFormatVersion: 2
+guid: 4ed8d69324dc04646a3fcc7bc8134125
+TextureImporter:
+  internalIDToNameTable: []
+  externalObjects: {}
+  serializedVersion: 11
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 1
+    sRGBTexture: 1
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 0
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: -1
+    aniso: -1
+    mipBias: -100
+    wrapU: -1
+    wrapV: -1
+    wrapW: -1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 1
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 0
+  textureShape: 1
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  applyGammaDecoding: 0
+  platformSettings:
+  - serializedVersion: 3
+    buildTarget: DefaultTexturePlatform
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 3
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  - serializedVersion: 3
+    buildTarget: Standalone
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 3
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  - serializedVersion: 3
+    buildTarget: iPhone
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 3
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  - serializedVersion: 3
+    buildTarget: Android
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: 69
+    textureCompression: 3
+    compressionQuality: 0
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 1
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    internalID: 0
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+    secondaryTextures: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Meshes.meta b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Meshes.meta
new file mode 100644
index 0000000000000000000000000000000000000000..7bdf17d96aaee9b6ebe4a6386ed3630d9926f155
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Meshes.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 5d209d98d6b3480468aac9f62153c7af
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Meshes/sm_FCStreamRoom.fbx b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Meshes/sm_FCStreamRoom.fbx
new file mode 100644
index 0000000000000000000000000000000000000000..ce723f701e592964ce401eeb85668827c51f3c14
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Meshes/sm_FCStreamRoom.fbx
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:9d92cbf1c5c0bdb245228d6ef323060903d0e23ae71052a08d9ae17a43c7c86d
+size 355196
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Meshes/sm_FCStreamRoom.fbx.meta b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Meshes/sm_FCStreamRoom.fbx.meta
new file mode 100644
index 0000000000000000000000000000000000000000..6ec8075b2d700b8a4561cb7a5a5c7fb69385a843
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Meshes/sm_FCStreamRoom.fbx.meta
@@ -0,0 +1,117 @@
+fileFormatVersion: 2
+guid: d8918c79070d3de4180dcb0b79c50e50
+ModelImporter:
+  serializedVersion: 23
+  fileIDToRecycleName:
+    100000: banners.003
+    100002: couch.002
+    100004: seats.002
+    100006: //RootNode
+    400000: banners.003
+    400002: couch.002
+    400004: seats.002
+    400006: //RootNode
+    2100000: MergedBake_BakeFinal
+    2300000: banners.003
+    2300002: couch.002
+    2300004: seats.002
+    2300006: //RootNode
+    3300000: banners.003
+    3300002: couch.002
+    3300004: seats.002
+    3300006: //RootNode
+    4300000: greenRoom_a_sm.001
+    4300002: banners.003
+    4300004: couch.002
+    4300006: seats.002
+    6400000: banners.003
+    6400002: couch.002
+    6400004: seats.002
+    6400006: //RootNode
+    9500000: //RootNode
+  externalObjects: {}
+  materials:
+    importMaterials: 1
+    materialName: 0
+    materialSearch: 1
+    materialLocation: 1
+  animations:
+    legacyGenerateAnimations: 4
+    bakeSimulation: 0
+    resampleCurves: 1
+    optimizeGameObjects: 0
+    motionNodeName: 
+    rigImportErrors: 
+    rigImportWarnings: 
+    animationImportErrors: 
+    animationImportWarnings: 
+    animationRetargetingWarnings: 
+    animationDoRetargetingWarnings: 0
+    importAnimatedCustomProperties: 0
+    importConstraints: 0
+    animationCompression: 1
+    animationRotationError: 0.5
+    animationPositionError: 0.5
+    animationScaleError: 0.5
+    animationWrapMode: 0
+    extraExposedTransformPaths: []
+    extraUserProperties: []
+    clipAnimations: []
+    isReadable: 0
+  meshes:
+    lODScreenPercentages: []
+    globalScale: 1
+    meshCompression: 0
+    addColliders: 1
+    useSRGBMaterialColor: 1
+    importVisibility: 1
+    importBlendShapes: 1
+    importCameras: 1
+    importLights: 1
+    swapUVChannels: 0
+    generateSecondaryUV: 0
+    useFileUnits: 1
+    optimizeMeshForGPU: 1
+    keepQuads: 0
+    weldVertices: 1
+    preserveHierarchy: 0
+    indexFormat: 0
+    secondaryUVAngleDistortion: 8
+    secondaryUVAreaDistortion: 15.000001
+    secondaryUVHardAngle: 88
+    secondaryUVPackMargin: 4
+    useFileScale: 1
+    previousCalculatedGlobalScale: 0.01
+    hasPreviousCalculatedGlobalScale: 1
+  tangentSpace:
+    normalSmoothAngle: 60
+    normalImportMode: 0
+    tangentImportMode: 3
+    normalCalculationMode: 4
+    legacyComputeAllNormalsFromSmoothingGroupsWhenMeshHasBlendShapes: 0
+    blendShapeNormalImportMode: 1
+    normalSmoothingSource: 0
+  importAnimation: 1
+  copyAvatar: 0
+  humanDescription:
+    serializedVersion: 2
+    human: []
+    skeleton: []
+    armTwist: 0.5
+    foreArmTwist: 0.5
+    upperLegTwist: 0.5
+    legTwist: 0.5
+    armStretch: 0.05
+    legStretch: 0.05
+    feetSpacing: 0
+    rootMotionBoneName: 
+    hasTranslationDoF: 0
+    hasExtraRoot: 0
+    skeletonHasParents: 1
+  lastHumanDescriptionAvatarSource: {instanceID: 0}
+  animationType: 2
+  humanoidOversampling: 1
+  additionalBone: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Prefabs.meta b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Prefabs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..ae641cbb2e3f10c35b8e9b25a4f6b6a9d9d9f878
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Prefabs.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 377b452f342c80d49b7ef47bb5cbd4f4
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Prefabs/p_FCStreamRoom_Environment.prefab b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Prefabs/p_FCStreamRoom_Environment.prefab
new file mode 100644
index 0000000000000000000000000000000000000000..eaa6de9b45ed24d979dcccc35c1746997eedcec3
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Prefabs/p_FCStreamRoom_Environment.prefab
@@ -0,0 +1,261 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &6257641961810558837
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1555623716397632877}
+  m_Layer: 0
+  m_Name: p_FCStreamRoom_Environment
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1555623716397632877
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6257641961810558837}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 3602176089265034201}
+  m_Father: {fileID: 0}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1001 &3602176089264900447
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 1555623716397632877}
+    m_Modifications:
+    - target: {fileID: -6860895033569716450, guid: d8918c79070d3de4180dcb0b79c50e50,
+        type: 3}
+      propertyPath: m_Enabled
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -5567052953455599184, guid: d8918c79070d3de4180dcb0b79c50e50,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: -4913025072782820189, guid: d8918c79070d3de4180dcb0b79c50e50,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 3
+      objectReference: {fileID: 0}
+    - target: {fileID: -4913025072782820189, guid: d8918c79070d3de4180dcb0b79c50e50,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -4913025072782820189, guid: d8918c79070d3de4180dcb0b79c50e50,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: -0.01
+      objectReference: {fileID: 0}
+    - target: {fileID: -4913025072782820189, guid: d8918c79070d3de4180dcb0b79c50e50,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: -4216859302048453862, guid: d8918c79070d3de4180dcb0b79c50e50,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 1.5
+      objectReference: {fileID: 0}
+    - target: {fileID: -4216859302048453862, guid: d8918c79070d3de4180dcb0b79c50e50,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: -0.76
+      objectReference: {fileID: 0}
+    - target: {fileID: -4216859302048453862, guid: d8918c79070d3de4180dcb0b79c50e50,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 2.172
+      objectReference: {fileID: 0}
+    - target: {fileID: -4216859302048453862, guid: d8918c79070d3de4180dcb0b79c50e50,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0.7071068
+      objectReference: {fileID: 0}
+    - target: {fileID: -4216859302048453862, guid: d8918c79070d3de4180dcb0b79c50e50,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -4216859302048453862, guid: d8918c79070d3de4180dcb0b79c50e50,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -4216859302048453862, guid: d8918c79070d3de4180dcb0b79c50e50,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0.7071068
+      objectReference: {fileID: 0}
+    - target: {fileID: -4216859302048453862, guid: d8918c79070d3de4180dcb0b79c50e50,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -4216859302048453862, guid: d8918c79070d3de4180dcb0b79c50e50,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: -90
+      objectReference: {fileID: 0}
+    - target: {fileID: -4216859302048453862, guid: d8918c79070d3de4180dcb0b79c50e50,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -4216859302048453862, guid: d8918c79070d3de4180dcb0b79c50e50,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -2098446110943673173, guid: d8918c79070d3de4180dcb0b79c50e50,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: 95e9de1a85c67b048add0a4f8d7cf32a, type: 2}
+    - target: {fileID: -2098446110943673173, guid: d8918c79070d3de4180dcb0b79c50e50,
+        type: 3}
+      propertyPath: m_CastShadows
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -2098446110943673173, guid: d8918c79070d3de4180dcb0b79c50e50,
+        type: 3}
+      propertyPath: m_ReceiveShadows
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -2098446110943673173, guid: d8918c79070d3de4180dcb0b79c50e50,
+        type: 3}
+      propertyPath: m_LightProbeUsage
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -2098446110943673173, guid: d8918c79070d3de4180dcb0b79c50e50,
+        type: 3}
+      propertyPath: m_ReflectionProbeUsage
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -1504981713932161579, guid: d8918c79070d3de4180dcb0b79c50e50,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: 95e9de1a85c67b048add0a4f8d7cf32a, type: 2}
+    - target: {fileID: -1504981713932161579, guid: d8918c79070d3de4180dcb0b79c50e50,
+        type: 3}
+      propertyPath: m_ReceiveShadows
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -1504981713932161579, guid: d8918c79070d3de4180dcb0b79c50e50,
+        type: 3}
+      propertyPath: m_CastShadows
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -1504981713932161579, guid: d8918c79070d3de4180dcb0b79c50e50,
+        type: 3}
+      propertyPath: m_LightProbeUsage
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -1504981713932161579, guid: d8918c79070d3de4180dcb0b79c50e50,
+        type: 3}
+      propertyPath: m_ReflectionProbeUsage
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -927199367670048503, guid: d8918c79070d3de4180dcb0b79c50e50,
+        type: 3}
+      propertyPath: m_Name
+      value: FBConnectStreamRoom_Mesh
+      objectReference: {fileID: 0}
+    - target: {fileID: 2300000, guid: d8918c79070d3de4180dcb0b79c50e50, type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: e0321c058f6ba90428c033bc0f6e9d56, type: 2}
+    - target: {fileID: 2300002, guid: d8918c79070d3de4180dcb0b79c50e50, type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: 95e9de1a85c67b048add0a4f8d7cf32a, type: 2}
+    - target: {fileID: 2300004, guid: d8918c79070d3de4180dcb0b79c50e50, type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: 95e9de1a85c67b048add0a4f8d7cf32a, type: 2}
+    - target: {fileID: 2300006, guid: d8918c79070d3de4180dcb0b79c50e50, type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: 95e9de1a85c67b048add0a4f8d7cf32a, type: 2}
+    - target: {fileID: 1121504365717960126, guid: d8918c79070d3de4180dcb0b79c50e50,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 2
+      objectReference: {fileID: 0}
+    - target: {fileID: 3369617708225560533, guid: d8918c79070d3de4180dcb0b79c50e50,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: 95e9de1a85c67b048add0a4f8d7cf32a, type: 2}
+    - target: {fileID: 3369617708225560533, guid: d8918c79070d3de4180dcb0b79c50e50,
+        type: 3}
+      propertyPath: m_CastShadows
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3369617708225560533, guid: d8918c79070d3de4180dcb0b79c50e50,
+        type: 3}
+      propertyPath: m_ReceiveShadows
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3369617708225560533, guid: d8918c79070d3de4180dcb0b79c50e50,
+        type: 3}
+      propertyPath: m_LightProbeUsage
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3369617708225560533, guid: d8918c79070d3de4180dcb0b79c50e50,
+        type: 3}
+      propertyPath: m_ReflectionProbeUsage
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4830659974302529568, guid: d8918c79070d3de4180dcb0b79c50e50,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: e0321c058f6ba90428c033bc0f6e9d56, type: 2}
+    - target: {fileID: 4830659974302529568, guid: d8918c79070d3de4180dcb0b79c50e50,
+        type: 3}
+      propertyPath: m_CastShadows
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4830659974302529568, guid: d8918c79070d3de4180dcb0b79c50e50,
+        type: 3}
+      propertyPath: m_ReceiveShadows
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4830659974302529568, guid: d8918c79070d3de4180dcb0b79c50e50,
+        type: 3}
+      propertyPath: m_LightProbeUsage
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 4830659974302529568, guid: d8918c79070d3de4180dcb0b79c50e50,
+        type: 3}
+      propertyPath: m_ReflectionProbeUsage
+      value: 0
+      objectReference: {fileID: 0}
+    m_RemovedComponents:
+    - {fileID: -1149473463039925693, guid: d8918c79070d3de4180dcb0b79c50e50, type: 3}
+    - {fileID: 8393006010760786415, guid: d8918c79070d3de4180dcb0b79c50e50, type: 3}
+  m_SourcePrefab: {fileID: 100100000, guid: d8918c79070d3de4180dcb0b79c50e50, type: 3}
+--- !u!4 &3602176089265034201 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: 400006, guid: d8918c79070d3de4180dcb0b79c50e50,
+    type: 3}
+  m_PrefabInstance: {fileID: 3602176089264900447}
+  m_PrefabAsset: {fileID: 0}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Prefabs/p_FCStreamRoom_Environment.prefab.meta b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Prefabs/p_FCStreamRoom_Environment.prefab.meta
new file mode 100644
index 0000000000000000000000000000000000000000..0da5b41790110814e89c25c17db718cb270ef3d0
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Prefabs/p_FCStreamRoom_Environment.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 18dd326916f881d48a3f13a6a1cd0329
+PrefabImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Prefabs/p_FCStreamRoom_IndirectLighting.prefab b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Prefabs/p_FCStreamRoom_IndirectLighting.prefab
new file mode 100644
index 0000000000000000000000000000000000000000..33ffcf2225bcdba88599f47c94e8327ebc97fd17
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Prefabs/p_FCStreamRoom_IndirectLighting.prefab
@@ -0,0 +1,1605 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &921554780214788487
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 8124300929108859239}
+  - component: {fileID: 4393705278310603053}
+  - component: {fileID: 605679980095348196}
+  - component: {fileID: 6338801420366895646}
+  m_Layer: 0
+  m_Name: banners.003
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &8124300929108859239
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 921554780214788487}
+  m_LocalRotation: {x: 0.7071068, y: 0, z: -0, w: 0.7071067}
+  m_LocalPosition: {x: 0, y: -0.01, z: -0}
+  m_LocalScale: {x: 0.01, y: 0.010000001, z: 0.010000001}
+  m_Children: []
+  m_Father: {fileID: 1014587351273328350}
+  m_RootOrder: 2
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &4393705278310603053
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 921554780214788487}
+  m_Mesh: {fileID: 4300002, guid: 56973063af1e86649a1a50b475807c25, type: 3}
+--- !u!23 &605679980095348196
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 921554780214788487}
+  m_Enabled: 1
+  m_CastShadows: 0
+  m_ReceiveShadows: 0
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: cf961a2693c76ac429da0e192a39b6af, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!64 &6338801420366895646
+MeshCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 921554780214788487}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 3
+  m_Convex: 0
+  m_CookingOptions: 30
+  m_Mesh: {fileID: -5709944060125395820, guid: d8918c79070d3de4180dcb0b79c50e50, type: 3}
+--- !u!1 &924992450015950475
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 4971515886859112570}
+  - component: {fileID: 6987021480605434869}
+  - component: {fileID: 7326640190309505553}
+  m_Layer: 0
+  m_Name: couch.002
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &4971515886859112570
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 924992450015950475}
+  m_LocalRotation: {x: 0, y: 0, z: -0.025867969, w: 0.9996654}
+  m_LocalPosition: {x: 0.022244344, y: 0.008, z: 0.007260959}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1014587351273328350}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &6987021480605434869
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 924992450015950475}
+  m_Mesh: {fileID: 4300004, guid: d8918c79070d3de4180dcb0b79c50e50, type: 3}
+--- !u!23 &7326640190309505553
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 924992450015950475}
+  m_Enabled: 1
+  m_CastShadows: 0
+  m_ReceiveShadows: 0
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: cf961a2693c76ac429da0e192a39b6af, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!1 &1700750665841645849
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 8778614949630215284}
+  - component: {fileID: 657032473520664438}
+  - component: {fileID: 2994594126076240239}
+  m_Layer: 0
+  m_Name: seats.002
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &8778614949630215284
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1700750665841645849}
+  m_LocalRotation: {x: 0, y: 0, z: -0.025867969, w: 0.9996654}
+  m_LocalPosition: {x: 0.022244344, y: 0.008, z: 0.007260959}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1014587351273328350}
+  m_RootOrder: 1
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &657032473520664438
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1700750665841645849}
+  m_Mesh: {fileID: 4300006, guid: 56973063af1e86649a1a50b475807c25, type: 3}
+--- !u!23 &2994594126076240239
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1700750665841645849}
+  m_Enabled: 1
+  m_CastShadows: 0
+  m_ReceiveShadows: 0
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: cf961a2693c76ac429da0e192a39b6af, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!1 &4057690534508412109
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1014587351273328350}
+  - component: {fileID: 8309017536315225320}
+  - component: {fileID: 2338219255721658385}
+  - component: {fileID: 7757179112724902618}
+  m_Layer: 0
+  m_Name: FBConnectStreamRoom_Mesh
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1014587351273328350
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4057690534508412109}
+  m_LocalRotation: {x: -0.7071068, y: -0, z: -0, w: 0.7071068}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 100, y: 100, z: 100}
+  m_Children:
+  - {fileID: 4971515886859112570}
+  - {fileID: 8778614949630215284}
+  - {fileID: 8124300929108859239}
+  m_Father: {fileID: 5435348994611382965}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: -90, y: 0, z: 0}
+--- !u!33 &8309017536315225320
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4057690534508412109}
+  m_Mesh: {fileID: 4300000, guid: d8918c79070d3de4180dcb0b79c50e50, type: 3}
+--- !u!23 &2338219255721658385
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4057690534508412109}
+  m_Enabled: 1
+  m_CastShadows: 0
+  m_ReceiveShadows: 0
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: cf961a2693c76ac429da0e192a39b6af, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!64 &7757179112724902618
+MeshCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4057690534508412109}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 0
+  serializedVersion: 3
+  m_Convex: 0
+  m_CookingOptions: 30
+  m_Mesh: {fileID: 7287630845561901212, guid: d8918c79070d3de4180dcb0b79c50e50, type: 3}
+--- !u!1 &4096432732090130544
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 4096432732090130548}
+  - component: {fileID: 4096432732090130547}
+  - component: {fileID: 4096432732090130546}
+  - component: {fileID: 4096432732090130545}
+  m_Layer: 0
+  m_Name: Right
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &4096432732090130548
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4096432732090130544}
+  m_LocalRotation: {x: 0.5454218, y: 0.4527581, z: 0.5503953, w: -0.44112366}
+  m_LocalPosition: {x: -2.4670486, y: 2.1330001, z: -1.4081002}
+  m_LocalScale: {x: 0.4410425, y: 1.0318542, z: 0.38345551}
+  m_Children: []
+  m_Father: {fileID: 5435348993894043648}
+  m_RootOrder: 1
+  m_LocalEulerAnglesHint: {x: 78.40401, y: 88.591, z: -2.3660002}
+--- !u!33 &4096432732090130547
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4096432732090130544}
+  m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &4096432732090130546
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4096432732090130544}
+  m_Enabled: 1
+  m_CastShadows: 0
+  m_ReceiveShadows: 0
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 0
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: f4bfcbcb444a8644282e3af1e8a0dbb0, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 1
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!64 &4096432732090130545
+MeshCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4096432732090130544}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 0
+  serializedVersion: 3
+  m_Convex: 0
+  m_CookingOptions: 30
+  m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!1 &4096432732421040140
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 4096432732421040144}
+  - component: {fileID: 4096432732421040143}
+  - component: {fileID: 4096432732421040142}
+  - component: {fileID: 4096432732421040141}
+  m_Layer: 0
+  m_Name: Back
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &4096432732421040144
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4096432732421040140}
+  m_LocalRotation: {x: 0.8016781, y: -0, z: -0, w: 0.597756}
+  m_LocalPosition: {x: 1.0421305, y: 2.4629958, z: -2.513992}
+  m_LocalScale: {x: 0.7534375, y: 1, z: 0.38003993}
+  m_Children: []
+  m_Father: {fileID: 5435348993894043648}
+  m_RootOrder: 4
+  m_LocalEulerAnglesHint: {x: 106.58099, y: -0.000015258789, z: -0.000015258789}
+--- !u!33 &4096432732421040143
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4096432732421040140}
+  m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &4096432732421040142
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4096432732421040140}
+  m_Enabled: 1
+  m_CastShadows: 0
+  m_ReceiveShadows: 0
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 0
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: 8d333a7b55f3403498641dcdd53c7576, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 1
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!64 &4096432732421040141
+MeshCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4096432732421040140}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 0
+  serializedVersion: 3
+  m_Convex: 0
+  m_CookingOptions: 30
+  m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!1 &4096432732480453958
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 4096432732480453962}
+  - component: {fileID: 4096432732480453961}
+  - component: {fileID: 4096432732480453960}
+  - component: {fileID: 4096432732480453959}
+  m_Layer: 0
+  m_Name: Top
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &4096432732480453962
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4096432732480453958}
+  m_LocalRotation: {x: 0.99931735, y: -0, z: -0, w: 0.036944527}
+  m_LocalPosition: {x: 1.11335, y: 3.408001, z: -1.624704}
+  m_LocalScale: {x: 0.64745, y: 0.9966203, z: 0.38341948}
+  m_Children: []
+  m_Father: {fileID: 5435348993894043648}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 355.766, y: 0, z: 0}
+--- !u!33 &4096432732480453961
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4096432732480453958}
+  m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &4096432732480453960
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4096432732480453958}
+  m_Enabled: 1
+  m_CastShadows: 0
+  m_ReceiveShadows: 0
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 0
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: a2cecc19a394f3142aaac5d860a9df5c, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 1
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!64 &4096432732480453959
+MeshCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4096432732480453958}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 0
+  serializedVersion: 3
+  m_Convex: 0
+  m_CookingOptions: 30
+  m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!1 &4096432732500508458
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 4096432732500508462}
+  - component: {fileID: 4096432732500508461}
+  - component: {fileID: 4096432732500508460}
+  - component: {fileID: 4096432732500508459}
+  m_Layer: 0
+  m_Name: Front
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &4096432732500508462
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4096432732500508458}
+  m_LocalRotation: {x: -0.98137826, y: 0.0026145438, z: 0.054694068, w: 0.18411566}
+  m_LocalPosition: {x: -0.6541884, y: 3.1930091, z: -0.29456246}
+  m_LocalScale: {x: 0.6156967, y: 1.9218615, z: 0.24043326}
+  m_Children: []
+  m_Father: {fileID: 5435348993894043648}
+  m_RootOrder: 3
+  m_LocalEulerAnglesHint: {x: 381.203, y: -6.5520005, z: -0.92200005}
+--- !u!33 &4096432732500508461
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4096432732500508458}
+  m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &4096432732500508460
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4096432732500508458}
+  m_Enabled: 1
+  m_CastShadows: 0
+  m_ReceiveShadows: 0
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 0
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: 22e36d7bc1acc6342a9a0fa03a34a63a, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 1
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!64 &4096432732500508459
+MeshCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4096432732500508458}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 0
+  serializedVersion: 3
+  m_Convex: 0
+  m_CookingOptions: 30
+  m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!1 &4096432732533153501
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 4096432732533153313}
+  - component: {fileID: 4096432732533153312}
+  - component: {fileID: 4096432732533153503}
+  - component: {fileID: 4096432732533153502}
+  m_Layer: 0
+  m_Name: Left
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &4096432732533153313
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4096432732533153501}
+  m_LocalRotation: {x: -0.52566284, y: 0.43929264, z: 0.58138007, w: 0.4389737}
+  m_LocalPosition: {x: 3.975079, y: 2.1330001, z: -1.4081002}
+  m_LocalScale: {x: 0.46660975, y: 1.0088773, z: 0.38041368}
+  m_Children: []
+  m_Father: {fileID: 5435348993894043648}
+  m_RootOrder: 2
+  m_LocalEulerAnglesHint: {x: 76.482, y: -105.23, z: -11.997001}
+--- !u!33 &4096432732533153312
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4096432732533153501}
+  m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &4096432732533153503
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4096432732533153501}
+  m_Enabled: 1
+  m_CastShadows: 0
+  m_ReceiveShadows: 0
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 0
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: f4bfcbcb444a8644282e3af1e8a0dbb0, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 1
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!64 &4096432732533153502
+MeshCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4096432732533153501}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 0
+  serializedVersion: 3
+  m_Convex: 0
+  m_CookingOptions: 30
+  m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!1 &5435348993513986690
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 5435348993513986689}
+  m_Layer: 0
+  m_Name: Light Probe Group - Leave Active
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 4294967295
+  m_IsActive: 1
+--- !u!4 &5435348993513986689
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 5435348993513986690}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 5435348993684803882}
+  m_Father: {fileID: 5435348993549855810}
+  m_RootOrder: 1
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &5435348993549855811
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 5435348993549855810}
+  m_Layer: 0
+  m_Name: p_FCStreamRoom_IndirectLighting
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &5435348993549855810
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 5435348993549855811}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 5435348994611382965}
+  - {fileID: 5435348993513986689}
+  m_Father: {fileID: 0}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &5435348993684803883
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 5435348993684803882}
+  - component: {fileID: 5435348993684803881}
+  m_Layer: 0
+  m_Name: -- MLP Combined Volume --
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &5435348993684803882
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 5435348993684803883}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: -0.62, y: 1.348, z: -7.306}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 5435348993513986689}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!220 &5435348993684803881
+LightProbeGroup:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 5435348993684803883}
+  m_Enabled: 1
+  m_SourcePositions:
+  - {x: -5.2073655, y: 0.08660984, z: 8.4}
+  - {x: -5.2073655, y: 0.08660984, z: 8.799998}
+  - {x: -5.197573, y: 0.48661327, z: 4.7999997}
+  - {x: -5.2073655, y: 0.48661327, z: 5.1999993}
+  - {x: -5.2073655, y: 0.48661232, z: 5.599999}
+  - {x: -5.2073655, y: 0.48661327, z: 6.399999}
+  - {x: -5.2073655, y: 0.48661327, z: 6.7999988}
+  - {x: -5.2073655, y: 0.48661232, z: 7.1999993}
+  - {x: -5.2073655, y: 0.48661232, z: 7.5999994}
+  - {x: -5.2073655, y: 0.48661232, z: 7.999999}
+  - {x: -5.2073655, y: 0.48661232, z: 8.400001}
+  - {x: -5.197573, y: 0.886611, z: 4.799999}
+  - {x: -5.2073655, y: 0.886611, z: 5.1999993}
+  - {x: -5.2073655, y: 0.886611, z: 5.599999}
+  - {x: -5.2073655, y: 0.886611, z: 5.999999}
+  - {x: -5.2073655, y: 0.886611, z: 6.399999}
+  - {x: -5.2073655, y: 0.886611, z: 6.7999988}
+  - {x: -5.2073655, y: 0.886611, z: 7.1999993}
+  - {x: -5.2073655, y: 0.886611, z: 7.5999994}
+  - {x: -5.2073655, y: 0.886611, z: 7.999999}
+  - {x: -5.2073655, y: 0.886611, z: 8.400001}
+  - {x: -5.2073655, y: 0.886611, z: 8.799999}
+  - {x: -5.197573, y: 1.2866106, z: 4.799999}
+  - {x: -5.2073655, y: 1.2866106, z: 5.1999993}
+  - {x: -5.2073655, y: 1.2866106, z: 5.599999}
+  - {x: -5.2073655, y: 1.2866106, z: 5.9999986}
+  - {x: -5.2073655, y: 1.2866106, z: 6.399999}
+  - {x: -5.2073655, y: 1.2866106, z: 6.7999988}
+  - {x: -5.2073655, y: 1.2866087, z: 7.1999993}
+  - {x: -5.2073655, y: 1.2866087, z: 7.599999}
+  - {x: -5.2073655, y: 1.2866106, z: 7.999999}
+  - {x: -5.2073655, y: 1.2866106, z: 8.400001}
+  - {x: -5.2073655, y: 1.2866106, z: 8.799999}
+  - {x: -5.197573, y: 1.686614, z: 4.799999}
+  - {x: -5.2073655, y: 1.6866102, z: 5.1999993}
+  - {x: -5.2073655, y: 1.686614, z: 5.599999}
+  - {x: -5.2073655, y: 1.6866121, z: 5.9999986}
+  - {x: -5.2073655, y: 1.686614, z: 6.3999987}
+  - {x: -5.2073655, y: 1.686614, z: 6.7999988}
+  - {x: -5.2073655, y: 1.6866102, z: 7.1999993}
+  - {x: -5.2073655, y: 1.6866102, z: 7.599999}
+  - {x: -5.2073655, y: 1.6866102, z: 7.999999}
+  - {x: -5.2073655, y: 1.6866102, z: 8.4}
+  - {x: -5.2073655, y: 1.6866102, z: 8.799998}
+  - {x: -4.0499997, y: -0.9828558, z: 7.599998}
+  - {x: -4.0499988, y: -0.98285484, z: 7.9999986}
+  - {x: -4.0499988, y: -0.98285484, z: 8.399999}
+  - {x: -4.0499988, y: -0.98285484, z: 8.799998}
+  - {x: -4.0500007, y: -0.57950115, z: 7.3999977}
+  - {x: -3.6500025, y: -0.9828558, z: 7.5999966}
+  - {x: -3.6500006, y: -0.98285484, z: 7.999998}
+  - {x: -3.6499987, y: -0.98285484, z: 8.4}
+  - {x: -3.6499987, y: -0.98285484, z: 8.799998}
+  - {x: -3.65, y: -0.5795002, z: 7.399998}
+  - {x: -3.2500024, y: -0.9828558, z: 7.5999966}
+  - {x: -3.25, y: -0.98285484, z: 7.999998}
+  - {x: -3.2499995, y: -0.98285484, z: 8.399999}
+  - {x: -3.249999, y: -0.98285484, z: 8.799998}
+  - {x: -3.25, y: -0.5795002, z: 7.399998}
+  - {x: -2.8500013, y: -0.9828558, z: 7.5999966}
+  - {x: -2.849999, y: -0.98285484, z: 7.9999986}
+  - {x: -2.85, y: -0.98285484, z: 8.399998}
+  - {x: -2.849999, y: -0.98285484, z: 8.799998}
+  - {x: -2.8500004, y: -0.5795002, z: 7.3999977}
+  - {x: -2.4499974, y: -0.9828558, z: 7.6}
+  - {x: -2.449996, y: -0.98285484, z: 8.000001}
+  - {x: -2.4499974, y: -0.98285484, z: 8.400001}
+  - {x: -2.4499989, y: -0.98285484, z: 8.799998}
+  - {x: -2.4500003, y: -0.5795002, z: 7.3999977}
+  - {x: -2.0499973, y: -0.9828558, z: 7.5999994}
+  - {x: -2.0499954, y: -0.98285484, z: 8}
+  - {x: -2.0499969, y: -0.98285484, z: 8.400001}
+  - {x: -2.0499973, y: -0.98285484, z: 8.799999}
+  - {x: -2.0500002, y: -0.5795002, z: 7.3999977}
+  - {x: -1.6499978, y: -0.9828558, z: 7.6}
+  - {x: -1.6499978, y: -0.98285484, z: 8}
+  - {x: -1.6499988, y: -0.98285484, z: 8.399999}
+  - {x: -1.65, y: -0.98285484, z: 8.799998}
+  - {x: -1.6499983, y: -0.57949924, z: 7.3999977}
+  - {x: -1.249999, y: -0.9828558, z: 7.5999994}
+  - {x: -1.2499993, y: -0.98285484, z: 7.999999}
+  - {x: -1.2499985, y: -0.98285484, z: 8.4}
+  - {x: -1.25, y: -0.98285484, z: 8.799998}
+  - {x: -1.2499983, y: -0.57949924, z: 7.3999977}
+  - {x: -0.84999883, y: -0.9828558, z: 7.5999994}
+  - {x: -0.84999824, y: -0.98285484, z: 7.9999995}
+  - {x: -0.84999967, y: -0.98285484, z: 8.399999}
+  - {x: -0.8500005, y: -0.98285484, z: 8.799998}
+  - {x: -0.84999835, y: -0.57949924, z: 7.3999977}
+  - {x: -0.84999895, y: 0.48661327, z: 0.65386474}
+  - {x: -0.84999895, y: 0.886611, z: 0.6538633}
+  - {x: -0.84999907, y: 1.2866106, z: 0.6538633}
+  - {x: -0.84999895, y: 1.6866121, z: 0.6538633}
+  - {x: -0.44999897, y: -0.9828558, z: 7.599999}
+  - {x: -0.4499992, y: -0.98285484, z: 7.9999986}
+  - {x: -0.44999897, y: -0.98285484, z: 8.399999}
+  - {x: -0.44999993, y: -0.98285484, z: 8.799998}
+  - {x: -0.44999874, y: -0.57949924, z: 7.3999977}
+  - {x: -0.049999, y: -0.9828558, z: 7.599999}
+  - {x: -0.049999118, y: -0.98285484, z: 7.9999986}
+  - {x: -0.04999888, y: -0.98285484, z: 8.4}
+  - {x: -0.049999118, y: -0.98285484, z: 8.799998}
+  - {x: -0.04999864, y: -0.5794983, z: 7.3999977}
+  - {x: 0.3500012, y: -0.9828558, z: 7.5999994}
+  - {x: 0.35000095, y: -0.98285484, z: 7.9999995}
+  - {x: 0.35000125, y: -0.98285484, z: 8.4}
+  - {x: 0.3500011, y: -0.98285484, z: 8.799999}
+  - {x: 0.3500012, y: -0.5794983, z: 7.399998}
+  - {x: 0.750001, y: -0.9828558, z: 7.5999994}
+  - {x: 0.7500006, y: -0.98285484, z: 7.999999}
+  - {x: 0.750001, y: -0.98285484, z: 8.4}
+  - {x: 0.7500006, y: -0.98285484, z: 8.799998}
+  - {x: 0.7500012, y: -0.5794983, z: 7.399998}
+  - {x: 1.1500006, y: -0.9828558, z: 7.5999994}
+  - {x: 1.1500009, y: -0.98285484, z: 7.9999995}
+  - {x: 1.1500013, y: -0.98285484, z: 8.4}
+  - {x: 1.1500012, y: -0.98285484, z: 8.799999}
+  - {x: 1.1500015, y: -0.5794983, z: 7.399998}
+  - {x: 1.5500009, y: -0.9828558, z: 7.5999994}
+  - {x: 1.5500007, y: -0.98285484, z: 7.9999995}
+  - {x: 1.5500011, y: -0.98285484, z: 8.4}
+  - {x: 1.5500011, y: -0.98285484, z: 8.799999}
+  - {x: 1.5500016, y: -0.5794983, z: 7.399998}
+  - {x: 1.950001, y: -0.9828558, z: 7.5999994}
+  - {x: 1.9500004, y: -0.98285484, z: 7.9999986}
+  - {x: 1.950001, y: -0.98285484, z: 8.4}
+  - {x: 1.9500012, y: -0.98285484, z: 8.799999}
+  - {x: 1.9500015, y: -0.57949734, z: 7.399998}
+  - {x: 2.35, y: -0.9828558, z: 7.5999985}
+  - {x: 2.3500004, y: -0.98285484, z: 7.9999995}
+  - {x: 2.3500004, y: -0.98285484, z: 8.4}
+  - {x: 2.3500006, y: -0.98285484, z: 8.799999}
+  - {x: 2.3500018, y: -0.57949734, z: 7.399998}
+  - {x: 2.750001, y: -0.9828558, z: 7.6}
+  - {x: 2.7500005, y: -0.98285484, z: 7.9999995}
+  - {x: 2.7500005, y: -0.98285484, z: 8.399999}
+  - {x: 2.7500005, y: -0.98285484, z: 8.799999}
+  - {x: 2.750002, y: -0.57949734, z: 7.399998}
+  - {x: 3.150001, y: -0.9828558, z: 7.6}
+  - {x: 3.150001, y: -0.98285484, z: 7.9999986}
+  - {x: 3.150001, y: -0.98285484, z: 8.399999}
+  - {x: 3.150001, y: -0.98285484, z: 8.799998}
+  - {x: 3.1500025, y: -0.57949734, z: 7.399998}
+  - {x: 3.5500007, y: -0.9828558, z: 7.5999994}
+  - {x: 3.5500011, y: -0.98285484, z: 7.999999}
+  - {x: 3.5500007, y: -0.98285484, z: 8.4}
+  - {x: 3.5500007, y: -0.98285484, z: 8.799999}
+  - {x: 3.550002, y: -0.5794964, z: 7.399998}
+  - {x: 3.9500008, y: -0.9828558, z: 7.599999}
+  - {x: 3.9500003, y: -0.98285484, z: 7.9999986}
+  - {x: 3.9500003, y: -0.98285484, z: 8.399999}
+  - {x: 3.9500008, y: -0.98285484, z: 8.799999}
+  - {x: 3.9500022, y: -0.5794964, z: 7.399998}
+  - {x: 4.350003, y: -0.5794964, z: 7.399998}
+  - {x: 5.1024942, y: 0.08660984, z: 7.199998}
+  - {x: 5.102495, y: 0.08660984, z: 7.5999985}
+  - {x: 5.102495, y: 0.08660984, z: 7.999998}
+  - {x: 5.1024942, y: 0.086610794, z: 8.399999}
+  - {x: 5.1024942, y: 0.08660984, z: 8.799997}
+  - {x: 5.102495, y: 0.48661327, z: 5.999999}
+  - {x: 5.102495, y: 0.48661327, z: 6.399999}
+  - {x: 5.102495, y: 0.48661327, z: 6.7999988}
+  - {x: 5.102495, y: 0.4866104, z: 7.1999993}
+  - {x: 5.102495, y: 0.4866104, z: 7.6}
+  - {x: 5.102495, y: 0.48661232, z: 7.999999}
+  - {x: 5.1024942, y: 0.48661232, z: 8.4}
+  - {x: 5.102495, y: 0.48661232, z: 8.799998}
+  - {x: 5.102495, y: 0.886611, z: 5.999999}
+  - {x: 5.1024942, y: 0.886611, z: 6.3999987}
+  - {x: 5.102495, y: 0.886611, z: 6.7999988}
+  - {x: 5.102495, y: 0.8866091, z: 7.1999993}
+  - {x: 5.102495, y: 0.8866091, z: 7.6}
+  - {x: 5.102495, y: 0.886611, z: 8}
+  - {x: 5.102495, y: 0.886611, z: 8.400001}
+  - {x: 5.102495, y: 0.886611, z: 8.799999}
+  - {x: 5.102495, y: 1.2866106, z: 5.999999}
+  - {x: 5.1024957, y: 1.2866106, z: 6.3999987}
+  - {x: 5.102495, y: 1.2866106, z: 6.7999988}
+  - {x: 5.102495, y: 1.2866087, z: 7.1999993}
+  - {x: 5.102495, y: 1.2866087, z: 7.5999994}
+  - {x: 5.102495, y: 1.2866087, z: 7.9999995}
+  - {x: 5.102495, y: 1.2866087, z: 8.4}
+  - {x: 5.102495, y: 1.2866087, z: 8.799999}
+  - {x: 5.102495, y: 1.686614, z: 5.9999986}
+  - {x: 5.102495, y: 1.6866121, z: 6.3999987}
+  - {x: 5.102495, y: 1.686614, z: 6.799998}
+  - {x: 5.102495, y: 1.6866102, z: 7.199999}
+  - {x: 5.102495, y: 1.6866102, z: 7.5999994}
+  - {x: 5.102495, y: 1.6866102, z: 7.999999}
+  - {x: 5.102495, y: 1.6866102, z: 8.399999}
+  - {x: 5.1024957, y: 1.6866102, z: 8.799998}
+  - {x: -5.1211457, y: -0.9324274, z: 7.5999985}
+  - {x: -5.123639, y: -0.9344425, z: 8.399999}
+  - {x: -4.826761, y: -0.5878506, z: 3.3129106}
+  - {x: -5.2201996, y: -0.6164923, z: 4.7999973}
+  - {x: -5.229316, y: -0.6172142, z: 5.599997}
+  - {x: -5.2293124, y: -0.61716175, z: 5.9999943}
+  - {x: -5.2293067, y: -0.61711216, z: 6.399997}
+  - {x: -5.229297, y: -0.6170111, z: 7.1999955}
+  - {x: -4.8095365, y: 0.08660221, z: 3.3965914}
+  - {x: -4.8095365, y: 0.88660336, z: 3.3965902}
+  - {x: -4.8095365, y: 1.6866026, z: 3.39659}
+  - {x: -4.7316403, y: -0.6078615, z: 2.799999}
+  - {x: -4.7731776, y: -0.6120806, z: 3.9999952}
+  - {x: -4.1097217, y: -0.58517075, z: 1.9346197}
+  - {x: -4.104045, y: 0.08660698, z: 1.9408348}
+  - {x: -4.1040454, y: 0.88660717, z: 1.9408338}
+  - {x: -4.1040454, y: 1.6866064, z: 1.9408338}
+  - {x: -3.7862334, y: -0.50794506, z: 1.5540295}
+  - {x: -3.7477975, y: 0.08660984, z: 1.5669994}
+  - {x: -3.653864, y: 0.48661327, z: 1.9961119}
+  - {x: -3.747796, y: 0.886611, z: 1.5669985}
+  - {x: -3.653864, y: 1.2866106, z: 1.9961116}
+  - {x: -3.7477984, y: 1.6866102, z: 1.5670004}
+  - {x: -3.1770473, y: -0.5913973, z: 1.0774661}
+  - {x: -2.8445573, y: -0.6195259, z: 5.199997}
+  - {x: -3.1525564, y: 0.086606026, z: 1.0363376}
+  - {x: -3.152556, y: 0.88660717, z: 1.0363368}
+  - {x: -3.1525607, y: 1.6866083, z: 1.0363415}
+  - {x: -2.5659194, y: -0.59058, z: 0.7298622}
+  - {x: -2.069291, y: -0.62077904, z: 4.978128}
+  - {x: -2.4500012, y: -0.5990095, z: 5.7909703}
+  - {x: -2.5569625, y: 0.08660507, z: 0.7352853}
+  - {x: -2.323835, y: 0.08661461, z: 4.859035}
+  - {x: -2.5491657, y: 0.48661232, z: 1.1434624}
+  - {x: -2.5569625, y: 0.88660717, z: 0.7352851}
+  - {x: -2.5491657, y: 1.2866087, z: 1.1434623}
+  - {x: -2.556963, y: 1.6866064, z: 0.73528546}
+  - {x: -2.0499992, y: -0.6123209, z: 0.87844205}
+  - {x: -2.1367817, y: 0.032190323, z: 5.155997}
+  - {x: -2.1157613, y: -0.031492233, z: 5.600004}
+  - {x: -1.7648917, y: -0.42781258, z: 0.36661792}
+  - {x: -1.6499995, y: -0.5993118, z: 4.6103673}
+  - {x: -1.6500005, y: -0.61336327, z: 5.661877}
+  - {x: -1.7382635, y: 0.08661175, z: 0.37435436}
+  - {x: -1.6500024, y: -0.0644474, z: 4.9670987}
+  - {x: -1.7382642, y: 0.8866129, z: 0.37435427}
+  - {x: -1.7382652, y: 1.6866121, z: 0.37435418}
+  - {x: -1.2499985, y: -0.5929785, z: 0.24521023}
+  - {x: -0.84999907, y: -0.5973902, z: 0.6095356}
+  - {x: -0.84999967, y: -0.6202097, z: 4.4125814}
+  - {x: -0.8499994, y: -0.59621525, z: 5.4139547}
+  - {x: -0.85000145, y: -0.030052185, z: 4.739644}
+  - {x: -0.049999, y: -0.6073055, z: 0.5260369}
+  - {x: -0.049999237, y: -0.616745, z: 4.372524}
+  - {x: -0.05000025, y: -0.6014633, z: 5.3711815}
+  - {x: -0.050001144, y: -0.038059235, z: 4.709118}
+  - {x: 0.07751316, y: 0.08660698, z: 0.15059334}
+  - {x: 0.45129037, y: -0.027296066, z: 4.725339}
+  - {x: 0.4346038, y: -0.036455154, z: 5.200005}
+  - {x: 0.07751435, y: 0.8866091, z: 0.1505925}
+  - {x: 0.07751441, y: 1.6866083, z: 0.15059257}
+  - {x: 0.75000095, y: -0.59793377, z: 0.6051458}
+  - {x: 0.75000024, y: -0.5836878, z: 4.4767876}
+  - {x: 0.7500011, y: -0.59482574, z: 5.425073}
+  - {x: 1.513902, y: -0.61872005, z: 0.66158473}
+  - {x: 1.1500006, y: 0.08659649, z: 0.29966444}
+  - {x: 1.2779254, y: 0.48661232, z: 0.7513423}
+  - {x: 1.1500006, y: 0.88660145, z: 0.29966444}
+  - {x: 1.2779257, y: 1.2866087, z: 0.75134224}
+  - {x: 1.1500006, y: 1.6866026, z: 0.29966444}
+  - {x: 1.5500002, y: -0.60306644, z: 4.6416726}
+  - {x: 1.5500011, y: -0.6093893, z: 5.7008057}
+  - {x: 1.4471759, y: -0.008312225, z: 5.0709863}
+  - {x: 1.7921368, y: -0.06563282, z: 5.6000047}
+  - {x: 2.291964, y: -0.6142893, z: 1.1999955}
+  - {x: 1.7952216, y: 0.08660698, z: 0.44454288}
+  - {x: 1.9499981, y: 0.31282806, z: 5.193194}
+  - {x: 1.7952217, y: 0.88660717, z: 0.44454288}
+  - {x: 1.7952216, y: 1.6866083, z: 0.44454288}
+  - {x: 2.3500018, y: -0.60314274, z: 5.0427637}
+  - {x: 2.9003825, y: 0.08660412, z: 0.93609375}
+  - {x: 2.9003754, y: 0.88660526, z: 0.9360903}
+  - {x: 2.900383, y: 1.6866045, z: 0.9360938}
+  - {x: 3.199613, y: -0.41252708, z: 1.1609153}
+  - {x: 3.150004, y: -0.5902014, z: 1.8663976}
+  - {x: 3.1632667, y: 0.48661327, z: 1.1895493}
+  - {x: 3.1632671, y: 1.2866087, z: 1.1895491}
+  - {x: 3.6328254, y: -0.43222237, z: 1.5256536}
+  - {x: 3.6092787, y: 0.08661175, z: 1.5467899}
+  - {x: 3.6092787, y: 0.886611, z: 1.5467899}
+  - {x: 3.6092787, y: 1.6866121, z: 1.5467898}
+  - {x: 4.027622, y: -0.43437672, z: 1.9148729}
+  - {x: 4.0067067, y: 0.086610794, z: 1.9378107}
+  - {x: 3.9802227, y: 0.08661461, z: 3.1567104}
+  - {x: 4.0067067, y: 0.886611, z: 1.9378104}
+  - {x: 3.9802232, y: 0.8866148, z: 3.1567101}
+  - {x: 4.0067067, y: 1.6866121, z: 1.9378097}
+  - {x: 3.9802232, y: 1.686614, z: 3.15671}
+  - {x: 4.62148, y: -0.60672855, z: 2.799996}
+  - {x: 4.350104, y: -0.04551506, z: 3.5993843}
+  - {x: 4.3294406, y: 0.08661461, z: 3.2373207}
+  - {x: 4.3960066, y: 0.08661556, z: 3.8855867}
+  - {x: 4.3294406, y: 0.8866148, z: 3.2373197}
+  - {x: 4.3960066, y: 0.8866148, z: 3.8855863}
+  - {x: 4.3294406, y: 1.686614, z: 3.2373192}
+  - {x: 4.396007, y: 1.686614, z: 3.8855858}
+  - {x: 5.0203395, y: -0.93412685, z: 7.5999985}
+  - {x: 5.0222917, y: -0.93572044, z: 8.399999}
+  - {x: 4.790567, y: -0.4334154, z: 3.1055603}
+  - {x: 4.963663, y: -0.59984875, z: 3.5999954}
+  - {x: 5.094974, y: -0.6141777, z: 4.399997}
+  - {x: 5.1247773, y: -0.61685276, z: 5.1999974}
+  - {x: 5.124768, y: -0.6167488, z: 5.999997}
+  - {x: 5.1247582, y: -0.6166487, z: 6.799997}
+  - {x: 4.7767706, y: 0.08661175, z: 3.1376765}
+  - {x: 5.0072036, y: 0.08660984, z: 3.9999998}
+  - {x: 5.1344323, y: 0.086610794, z: 5.186195}
+  - {x: 4.5307016, y: 0.48660564, z: 3.5999973}
+  - {x: 4.7767706, y: 0.8866129, z: 3.137677}
+  - {x: 5.0072036, y: 0.88660717, z: 3.9999993}
+  - {x: 5.1344323, y: 0.8866091, z: 5.1861944}
+  - {x: 4.5307007, y: 1.2866182, z: 3.5999951}
+  - {x: 4.7767706, y: 1.6866121, z: 3.1376758}
+  - {x: 5.0072036, y: 1.6866083, z: 3.9999988}
+  - {x: 5.1344323, y: 1.6866121, z: 5.1861944}
+  - {x: -4.449999, y: -0.313385, z: 7.6}
+  - {x: -4.449999, y: 0.08661461, z: 5.2000003}
+  - {x: -4.449999, y: 0.8866148, z: 8.400001}
+  - {x: -4.449999, y: 1.2866144, z: 5.2000003}
+  - {x: -4.449999, y: 1.2866144, z: 5.6}
+  - {x: -4.449999, y: 1.2866144, z: 6}
+  - {x: -4.449999, y: 1.2866144, z: 8}
+  - {x: -4.449999, y: 1.686614, z: 6}
+  - {x: -4.049999, y: 0.08661461, z: 3.1999998}
+  - {x: -4.049999, y: 0.08661461, z: 3.9999998}
+  - {x: -4.049999, y: 0.08661461, z: 4.4}
+  - {x: -4.049999, y: 0.08661461, z: 4.8}
+  - {x: -4.049999, y: 0.08661461, z: 5.6}
+  - {x: -4.049999, y: 0.08661461, z: 6}
+  - {x: -4.049999, y: 0.08661461, z: 7.2000003}
+  - {x: -4.049999, y: 0.48661518, z: 8}
+  - {x: -4.049999, y: 0.48661518, z: 8.400001}
+  - {x: -4.049999, y: 0.8866148, z: 3.9999998}
+  - {x: -4.049999, y: 0.8866148, z: 5.2000003}
+  - {x: -4.049999, y: 1.2866144, z: 7.6}
+  - {x: -4.049999, y: 1.686614, z: 4.4}
+  - {x: -3.6499991, y: 0.48661518, z: 2.7999997}
+  - {x: -3.6499991, y: 0.48661518, z: 5.6}
+  - {x: -3.6499991, y: 0.8866148, z: 3.1999998}
+  - {x: -3.6499991, y: 0.8866148, z: 3.6}
+  - {x: -3.6499991, y: 0.8866148, z: 4.4}
+  - {x: -3.6499991, y: 0.8866148, z: 6.8}
+  - {x: -3.6499991, y: 1.2866144, z: 6.4}
+  - {x: -3.6499991, y: 1.686614, z: 5.6}
+  - {x: -3.249999, y: 0.08661461, z: 2.7999997}
+  - {x: -3.249999, y: 0.08661461, z: 3.9999998}
+  - {x: -3.249999, y: 0.8866148, z: 4.8}
+  - {x: -3.249999, y: 0.8866148, z: 8.8}
+  - {x: -3.249999, y: 1.2866144, z: 3.9999998}
+  - {x: -3.249999, y: 1.2866144, z: 6}
+  - {x: -2.849999, y: 0.08661461, z: 7.6}
+  - {x: -2.849999, y: 0.48661518, z: 7.2000003}
+  - {x: -2.849999, y: 0.8866148, z: 2.7999997}
+  - {x: -2.849999, y: 0.8866148, z: 3.9999998}
+  - {x: -2.849999, y: 0.8866148, z: 4.4}
+  - {x: -2.849999, y: 1.2866144, z: 2.7999997}
+  - {x: -2.849999, y: 1.2866144, z: 3.6}
+  - {x: -2.849999, y: 1.2866144, z: 3.9999998}
+  - {x: -2.849999, y: 1.686614, z: 2.7999997}
+  - {x: -2.4499993, y: 0.08661461, z: 1.5999998}
+  - {x: -2.4499993, y: 0.08661461, z: 3.6}
+  - {x: -2.4499993, y: 0.08661461, z: 6.4}
+  - {x: -2.4499993, y: 0.08661461, z: 7.6}
+  - {x: -2.4499993, y: 0.48661518, z: 6.8}
+  - {x: -2.4499993, y: 0.8866148, z: 4.8}
+  - {x: -2.4499993, y: 1.2866144, z: 2.7999997}
+  - {x: -2.4499993, y: 1.2866144, z: 5.6}
+  - {x: -2.4499993, y: 1.2866144, z: 7.2000003}
+  - {x: -2.4499993, y: 1.686614, z: 2.7999997}
+  - {x: -2.4499993, y: 1.686614, z: 4.8}
+  - {x: -2.0499992, y: -0.313385, z: 7.6}
+  - {x: -2.0499992, y: 0.08661461, z: 3.9999998}
+  - {x: -2.0499992, y: 0.48661518, z: 1.5999998}
+  - {x: -2.0499992, y: 0.48661518, z: 6.8}
+  - {x: -2.0499992, y: 0.8866148, z: 4.8}
+  - {x: -2.0499992, y: 0.8866148, z: 8}
+  - {x: -2.0499992, y: 0.8866148, z: 8.8}
+  - {x: -2.0499992, y: 1.2866144, z: 5.2000003}
+  - {x: -2.0499992, y: 1.2866144, z: 7.2000003}
+  - {x: -2.0499992, y: 1.2866144, z: 8}
+  - {x: -2.0499992, y: 1.2866144, z: 8.400001}
+  - {x: -2.0499992, y: 1.686614, z: 1.9999998}
+  - {x: -1.649999, y: 0.08661461, z: 8}
+  - {x: -1.649999, y: 0.08661461, z: 8.8}
+  - {x: -1.649999, y: 0.48661518, z: 6.8}
+  - {x: -1.649999, y: 0.48661518, z: 8.8}
+  - {x: -1.649999, y: 0.8866148, z: 4.4}
+  - {x: -1.649999, y: 0.8866148, z: 6.4}
+  - {x: -1.649999, y: 1.686614, z: 1.5999998}
+  - {x: -1.649999, y: 1.686614, z: 2.3999999}
+  - {x: -1.649999, y: 1.686614, z: 2.7999997}
+  - {x: -1.649999, y: 1.686614, z: 8.400001}
+  - {x: -1.2499992, y: -0.313385, z: 8}
+  - {x: -1.2499992, y: 0.08661461, z: 7.6}
+  - {x: -1.2499992, y: 0.8866148, z: 4.8}
+  - {x: -1.2499992, y: 0.8866148, z: 7.2000003}
+  - {x: -1.2499992, y: 1.2866144, z: 3.1999998}
+  - {x: -1.2499992, y: 1.2866144, z: 3.6}
+  - {x: -1.2499992, y: 1.2866144, z: 8.400001}
+  - {x: -1.2499992, y: 1.686614, z: 3.6}
+  - {x: -1.2499992, y: 1.686614, z: 5.6}
+  - {x: -0.84999907, y: -0.313385, z: 8.400001}
+  - {x: -0.84999907, y: 0.08661461, z: 5.6}
+  - {x: -0.84999907, y: 0.08661461, z: 6}
+  - {x: -0.84999907, y: 0.48661518, z: 1.5999998}
+  - {x: -0.84999907, y: 0.8866148, z: 1.9999998}
+  - {x: -0.84999907, y: 0.8866148, z: 8}
+  - {x: -0.84999907, y: 0.8866148, z: 8.400001}
+  - {x: -0.84999907, y: 1.2866144, z: 1.1999998}
+  - {x: -0.84999907, y: 1.2866144, z: 5.2000003}
+  - {x: -0.84999907, y: 1.2866144, z: 6}
+  - {x: -0.84999907, y: 1.2866144, z: 7.6}
+  - {x: -0.84999907, y: 1.2866144, z: 8}
+  - {x: -0.84999907, y: 1.686614, z: 1.9999998}
+  - {x: -0.84999907, y: 1.686614, z: 3.9999998}
+  - {x: -0.44999897, y: -0.313385, z: 7.6}
+  - {x: -0.44999897, y: 0.08661461, z: 2.3999999}
+  - {x: -0.44999897, y: 0.08661461, z: 3.6}
+  - {x: -0.44999897, y: 0.08661461, z: 6}
+  - {x: -0.44999897, y: 0.08661461, z: 8}
+  - {x: -0.44999897, y: 0.48661518, z: 6.4}
+  - {x: -0.44999897, y: 0.8866148, z: 7.2000003}
+  - {x: -0.44999897, y: 1.2866144, z: 5.6}
+  - {x: -0.44999897, y: 1.2866144, z: 7.2000003}
+  - {x: -0.44999897, y: 1.2866144, z: 7.6}
+  - {x: -0.44999897, y: 1.686614, z: 8.8}
+  - {x: -0.04999888, y: 0.08661461, z: 1.9999998}
+  - {x: -0.04999888, y: 0.08661461, z: 6}
+  - {x: -0.04999888, y: 0.08661461, z: 6.8}
+  - {x: -0.04999888, y: 0.48661518, z: 8.8}
+  - {x: -0.04999888, y: 1.2866144, z: 1.5999998}
+  - {x: -0.04999888, y: 1.686614, z: 1.5999998}
+  - {x: -0.04999888, y: 1.686614, z: 4.4}
+  - {x: -0.04999888, y: 1.686614, z: 8.400001}
+  - {x: 0.35000074, y: 0.08661461, z: 3.6}
+  - {x: 0.35000074, y: 0.48661518, z: 1.9999998}
+  - {x: 0.35000074, y: 0.48661518, z: 2.3999999}
+  - {x: 0.35000074, y: 0.48661518, z: 5.2000003}
+  - {x: 0.35000074, y: 0.48661518, z: 5.6}
+  - {x: 0.35000074, y: 0.8866148, z: 3.6}
+  - {x: 0.35000074, y: 0.8866148, z: 5.2000003}
+  - {x: 0.35000074, y: 1.686614, z: 2.7999997}
+  - {x: 0.35000074, y: 1.686614, z: 3.9999998}
+  - {x: 0.35000074, y: 1.686614, z: 6}
+  - {x: 0.35000074, y: 1.686614, z: 7.6}
+  - {x: 0.35000074, y: 1.686614, z: 8.8}
+  - {x: 0.75000083, y: -0.313385, z: 8.8}
+  - {x: 0.75000083, y: 0.08661461, z: 1.5999998}
+  - {x: 0.75000083, y: 0.08661461, z: 2.7999997}
+  - {x: 0.75000083, y: 0.08661461, z: 7.2000003}
+  - {x: 0.75000083, y: 0.8866148, z: 1.5999998}
+  - {x: 0.75000083, y: 1.2866144, z: 1.5999998}
+  - {x: 1.1500009, y: -0.313385, z: 7.6}
+  - {x: 1.1500009, y: 0.08661461, z: 6.4}
+  - {x: 1.1500009, y: 0.08661461, z: 7.6}
+  - {x: 1.1500009, y: 0.8866148, z: 4.8}
+  - {x: 1.1500009, y: 0.8866148, z: 7.2000003}
+  - {x: 1.1500009, y: 1.2866144, z: 4.8}
+  - {x: 1.1500009, y: 1.2866144, z: 5.2000003}
+  - {x: 1.1500009, y: 1.2866144, z: 8}
+  - {x: 1.1500009, y: 1.686614, z: 2.7999997}
+  - {x: 1.1500009, y: 1.686614, z: 4.4}
+  - {x: 1.550001, y: 0.08661461, z: 2.7999997}
+  - {x: 1.550001, y: 0.08661461, z: 3.6}
+  - {x: 1.550001, y: 0.08661461, z: 6.4}
+  - {x: 1.550001, y: 0.08661461, z: 7.2000003}
+  - {x: 1.550001, y: 0.08661461, z: 7.6}
+  - {x: 1.550001, y: 0.08661461, z: 8.400001}
+  - {x: 1.550001, y: 0.48661518, z: 5.6}
+  - {x: 1.550001, y: 0.48661518, z: 6.8}
+  - {x: 1.550001, y: 0.8866148, z: 1.9999998}
+  - {x: 1.550001, y: 1.2866144, z: 3.6}
+  - {x: 1.550001, y: 1.686614, z: 4.4}
+  - {x: 1.550001, y: 1.686614, z: 8.400001}
+  - {x: 1.9500011, y: 0.08661461, z: 3.9999998}
+  - {x: 1.9500011, y: 0.08661461, z: 6.8}
+  - {x: 1.9500011, y: 0.08661461, z: 7.2000003}
+  - {x: 1.9500011, y: 0.08661461, z: 7.6}
+  - {x: 1.9500011, y: 0.48661518, z: 7.6}
+  - {x: 1.9500011, y: 0.8866148, z: 3.1999998}
+  - {x: 1.9500011, y: 0.8866148, z: 5.2000003}
+  - {x: 1.9500011, y: 0.8866148, z: 5.6}
+  - {x: 1.9500011, y: 0.8866148, z: 8}
+  - {x: 1.9500011, y: 1.2866144, z: 2.3999999}
+  - {x: 1.9500011, y: 1.2866144, z: 2.7999997}
+  - {x: 1.9500011, y: 1.2866144, z: 4.4}
+  - {x: 1.9500011, y: 1.2866144, z: 8.8}
+  - {x: 2.3500009, y: 0.08661461, z: 8.400001}
+  - {x: 2.3500009, y: 0.48661518, z: 2.3999999}
+  - {x: 2.3500009, y: 0.8866148, z: 4.4}
+  - {x: 2.3500009, y: 1.2866144, z: 3.9999998}
+  - {x: 2.3500009, y: 1.2866144, z: 4.4}
+  - {x: 2.3500009, y: 1.686614, z: 1.9999998}
+  - {x: 2.750001, y: -0.313385, z: 7.6}
+  - {x: 2.750001, y: 0.08661461, z: 7.2000003}
+  - {x: 2.750001, y: 0.48661518, z: 4.4}
+  - {x: 2.750001, y: 0.48661518, z: 6}
+  - {x: 2.750001, y: 0.48661518, z: 7.6}
+  - {x: 2.750001, y: 0.48661518, z: 8.400001}
+  - {x: 2.750001, y: 0.8866148, z: 5.6}
+  - {x: 2.750001, y: 1.2866144, z: 1.9999998}
+  - {x: 2.750001, y: 1.2866144, z: 2.3999999}
+  - {x: 2.750001, y: 1.2866144, z: 2.7999997}
+  - {x: 2.750001, y: 1.2866144, z: 3.9999998}
+  - {x: 2.750001, y: 1.2866144, z: 6.8}
+  - {x: 2.750001, y: 1.686614, z: 1.9999998}
+  - {x: 2.750001, y: 1.686614, z: 6.8}
+  - {x: 2.750001, y: 1.686614, z: 8}
+  - {x: 3.1500015, y: 0.08661461, z: 4.4}
+  - {x: 3.1500015, y: 0.08661461, z: 8}
+  - {x: 3.1500015, y: 0.48661518, z: 4.4}
+  - {x: 3.1500015, y: 0.48661518, z: 6.8}
+  - {x: 3.1500015, y: 0.48661518, z: 8}
+  - {x: 3.1500015, y: 0.48661518, z: 8.400001}
+  - {x: 3.1500015, y: 0.8866148, z: 3.1999998}
+  - {x: 3.1500015, y: 0.8866148, z: 8.400001}
+  - {x: 3.1500015, y: 1.2866144, z: 7.6}
+  - {x: 3.1500015, y: 1.686614, z: 3.6}
+  - {x: 3.5500011, y: -0.313385, z: 8}
+  - {x: 3.5500011, y: 0.08661461, z: 7.2000003}
+  - {x: 3.5500011, y: 0.08661461, z: 8}
+  - {x: 3.5500011, y: 0.48661518, z: 6.4}
+  - {x: 3.5500011, y: 0.48661518, z: 7.2000003}
+  - {x: 3.5500011, y: 0.8866148, z: 1.9999998}
+  - {x: 3.5500011, y: 0.8866148, z: 3.1999998}
+  - {x: 3.5500011, y: 1.2866144, z: 1.9999998}
+  - {x: 3.5500011, y: 1.686614, z: 5.2000003}
+  - {x: 3.5500011, y: 1.686614, z: 8}
+  - {x: 3.9500008, y: 0.8866148, z: 6}
+  - {x: 3.9500008, y: 1.2866144, z: 3.9999998}
+  - {x: 3.9500008, y: 1.2866144, z: 5.2000003}
+  - {x: 3.9500008, y: 1.2866144, z: 8.8}
+  - {x: 4.3500013, y: -0.313385, z: 8}
+  - {x: 4.3500013, y: 0.08661461, z: 6.8}
+  - {x: 4.3500013, y: 0.48661518, z: 6}
+  - {x: 4.3500013, y: 1.2866144, z: 5.2000003}
+  - {x: 4.3500013, y: 1.2866144, z: 8.8}
+  m_Dering: 1
+--- !u!1 &5435348993894043649
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 5435348993894043648}
+  m_Layer: 0
+  m_Name: Fake Bounce Cards
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 4294967295
+  m_IsActive: 1
+--- !u!4 &5435348993894043648
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 5435348993894043649}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: -1.53, y: 0.53, z: -0.69}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 4096432732480453962}
+  - {fileID: 4096432732090130548}
+  - {fileID: 4096432732533153313}
+  - {fileID: 4096432732500508462}
+  - {fileID: 4096432732421040144}
+  - {fileID: 5435348994571669416}
+  m_Father: {fileID: 5435348994611382965}
+  m_RootOrder: 1
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &5435348994571669417
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 5435348994571669416}
+  - component: {fileID: 5435348994571669421}
+  - component: {fileID: 5435348994571669418}
+  - component: {fileID: 5435348994571669419}
+  m_Layer: 0
+  m_Name: Front (1)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &5435348994571669416
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 5435348994571669417}
+  m_LocalRotation: {x: 0.8824733, y: 0.0952239, z: 0.17684846, w: -0.425321}
+  m_LocalPosition: {x: 2.1298466, y: 2.6730127, z: 0.3705101}
+  m_LocalScale: {x: 0.21090287, y: 1.350054, z: 0.21227196}
+  m_Children: []
+  m_Father: {fileID: 5435348993894043648}
+  m_RootOrder: 5
+  m_LocalEulerAnglesHint: {x: 411.661, y: 21.876001, z: -1.628}
+--- !u!33 &5435348994571669421
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 5435348994571669417}
+  m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &5435348994571669418
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 5435348994571669417}
+  m_Enabled: 1
+  m_CastShadows: 0
+  m_ReceiveShadows: 0
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 0
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: 22e36d7bc1acc6342a9a0fa03a34a63a, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 1
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!64 &5435348994571669419
+MeshCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 5435348994571669417}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 0
+  serializedVersion: 3
+  m_Convex: 0
+  m_CookingOptions: 30
+  m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!1 &5435348994611382966
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 5435348994611382965}
+  m_Layer: 0
+  m_Name: Indirect Lighting Assets - Hide after bake
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 4294967295
+  m_IsActive: 1
+--- !u!4 &5435348994611382965
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 5435348994611382966}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 1014587351273328350}
+  - {fileID: 5435348993894043648}
+  m_Father: {fileID: 5435348993549855810}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Prefabs/p_FCStreamRoom_IndirectLighting.prefab.meta b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Prefabs/p_FCStreamRoom_IndirectLighting.prefab.meta
new file mode 100644
index 0000000000000000000000000000000000000000..2564e4c999508e8d417cf21ddc7f909bd5f91a03
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Prefabs/p_FCStreamRoom_IndirectLighting.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: c9d92c260530bf141ac14850cbe82822
+PrefabImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Shaders.meta b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Shaders.meta
new file mode 100644
index 0000000000000000000000000000000000000000..18a378d95e3d5738a818941ad3ad53f2493d4e3d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Shaders.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 3d29068707e0aae47b053db31f4d3217
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Shaders/s_CheapoPBR.shader b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Shaders/s_CheapoPBR.shader
new file mode 100644
index 0000000000000000000000000000000000000000..645313e38b362a6048bc5aadcc0e1282f9541ed3
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Shaders/s_CheapoPBR.shader
@@ -0,0 +1,163 @@
+Shader "Venues/Environment/CheapoPBR"
+{
+	Properties
+	{
+		_Color ("Color Tint", Color) = (1, 1, 1, 1)
+		[NoScaleOffset] _MainTex ("Base Texture || (A) for Metallic", 2D) = "white" {}
+
+		_Metallic ("Global Metallic", Range(0, 1)) = 0.0
+		_Roughness ("Global Roughness", Range(0, 1)) = 0.5
+
+		[Header(Emission)]
+		[Toggle(ENABLE_EMISSION)] _EnableEmission("Enable Emission", Float) = 1
+		[HideIf(ENABLE_EMISSION, false)][NoScaleOffset] _EmissionTex("Emission Texture || (A) for Roughness", 2D) = "white" {}
+		[HideIf(ENABLE_EMISSION, false)] _EmissionIntensity("Emission Intensity", Range(0, 2)) = 1
+
+		[Header(Environment)]
+		[Toggle(ENABLE_AMBIENT)] _EnableAmbient("Enable Video Illumination", Float) = 1
+		[HideIf(ENABLE_AMBIENT, false)] _AmbientIntensity("Video Illumination Intensity", Range(0, 2)) = 1
+		[Toggle(ENABLE_REFLECTIONS)] _EnableReflections("Enable Reflections", Float) = 1
+		[HideIf(ENABLE_REFLECTIONS, false)][NoScaleOffset] _ReflectionCubemap("Reflection Cubemap", Cube) = "white" {}
+		[HideIf(ENABLE_REFLECTIONS, false)]_ReflectionIntensity("Reflection Intensity", Range(0, 2)) = 1
+		[Toggle(ENABLE_FOG)] _EnableFog("Enable Fog", Float) = 1
+
+		[Header(Rim Light)]
+		[HDR] _RimColor ("Rim Tint", Color) = (0.5, 0.5, 0.5, 1)
+		_RimWidth ("Rim Width", Range(0, 1)) = 0.4
+		_RimWeight ("Rim Weight", Range(0.0001, 2)) = 0.55
+
+	}
+	SubShader
+	{
+		// Use this with Universal Rendering Pipeline (URP)
+		// Tags{ "RenderType"="Opaque" "LightMode" = "LightweightForward"}
+		// Use this without Universal Rendering Pipeline (URP)
+		Tags { "RenderType"="Opaque" "LightMode" = "ForwardBase"}
+
+		Pass
+		{
+			CGPROGRAM
+			#pragma vertex vert
+			#pragma fragment frag
+			#pragma multi_compile_fog
+
+			#include "UnityCG.cginc"
+
+			#pragma shader_feature ENABLE_EMISSION
+			#pragma shader_feature ENABLE_AMBIENT
+			#pragma shader_feature ENABLE_REFLECTIONS
+			#pragma shader_feature ENABLE_FOG
+
+			struct appdata
+			{
+				half4 vertex : POSITION;
+				half2 uv : TEXCOORD0;
+				half3 normal : NORMAL;
+				half4 vertColor : COLOR;
+				UNITY_VERTEX_INPUT_INSTANCE_ID
+			};
+
+			struct v2f
+			{
+				half2 uv : TEXCOORD0;
+				half4 vertex : SV_POSITION;
+				half4 vertColor : COLOR;
+				half3 reflection : TEXCOORD1;
+				half3 rim : TEXCOORD2;
+				UNITY_FOG_COORDS(3)
+				UNITY_VERTEX_OUTPUT_STEREO
+			};
+
+			half4 _Color;
+			sampler2D _MainTex;
+
+			#if ENABLE_EMISSION
+				sampler2D _EmissionTex;
+			#endif
+
+			#if ENABLE_REFLECTIONS
+				UNITY_DECLARE_TEXCUBE(_ReflectionCubemap);
+			#endif
+
+			half _AmbientIntensity, _EmissionIntensity, _ReflectionIntensity;
+			half _Metallic, _Roughness;
+
+			half4 _RimColor;
+			half _RimWidth, _RimWeight;
+
+
+			v2f vert (appdata v)
+			{
+				v2f o;
+				UNITY_SETUP_INSTANCE_ID(v);
+				UNITY_INITIALIZE_OUTPUT(v2f, o);
+				UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
+				o.vertex = UnityObjectToClipPos(v.vertex);
+				o.uv = v.uv;
+
+				// Vertex color & tint
+				o.vertColor = v.vertColor * _Color;
+
+				// Reflections
+				half3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
+				half3 worldNormal = UnityObjectToWorldNormal(v.normal);
+				half3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos)); //Direction of ray from the camera towards the object surface
+				o.reflection = reflect(-worldViewDir, worldNormal); // Direction of ray after hitting the surface of object
+				_Roughness *= (1.7 - (0.7 * _Roughness)); // some standard shader pbr thing
+
+				#if ENABLE_AMBIENT
+					// Probes for video illumination
+					o.vertColor.rgb *= ShadeSH9(float4(worldNormal, 1)) * _AmbientIntensity;
+				#endif
+
+				// Rim
+				o.rim = pow(smoothstep(_RimWidth, 0, dot(worldNormal, worldViewDir)), 1 / _RimWeight);
+				o.rim *= _RimColor;
+
+				#ifdef ENABLE_FOG
+					UNITY_TRANSFER_FOG(o, o.vertex);
+				#endif
+
+				return o;
+			}
+
+			fixed4 frag (v2f i) : SV_Target
+			{
+				// Main Texture
+				fixed4 mainTex = tex2D(_MainTex, i.uv);
+				fixed3 col = mainTex.rgb;
+				_Metallic = _Metallic * mainTex.a; // metallic value stored in main texture alpha
+
+				#if ENABLE_EMISSION
+					fixed4 emission = tex2D(_EmissionTex, i.uv);
+					_Roughness = _Roughness * emission.a; // roughness value stored in emissive texture alpha
+				#endif
+
+				// Vertex color, tint, & ambient probes, metallic overrides all
+				col *= lerp(i.vertColor.rgb, 1, _Metallic);
+
+				// Reflections & Rim Light
+				half3 rimMultiplier = half3(1., 1., 1.);
+				#if ENABLE_REFLECTIONS
+					half4 refl = UNITY_SAMPLE_TEXCUBE_LOD(_ReflectionCubemap, i.reflection, _Roughness * 6);
+					col = lerp(col, col * (refl.rgb * _ReflectionIntensity), (_Metallic * 0.75) + 0.25);
+					rimMultiplier = refl.rgb;
+				#endif
+				col += i.rim * (1.2 - _Roughness) * rimMultiplier;
+
+				// Emission
+				#if ENABLE_EMISSION
+					col += emission.rgb * _EmissionIntensity;
+				#endif
+
+				// Fog
+				#ifdef ENABLE_FOG
+					UNITY_APPLY_FOG(i.fogCoord, col);
+				#endif
+
+				return fixed4(col, 1);
+			}
+			ENDCG
+		}
+	}
+}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Shaders/s_CheapoPBR.shader.meta b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Shaders/s_CheapoPBR.shader.meta
new file mode 100644
index 0000000000000000000000000000000000000000..67f8543c23177562dec2ccd1b3e858a6f1072bbd
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Shaders/s_CheapoPBR.shader.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 42b38d516695b1e458b7cd8e467bb46f
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Shaders/s_FCStreamRoomLogoSheet.shader b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Shaders/s_FCStreamRoomLogoSheet.shader
new file mode 100644
index 0000000000000000000000000000000000000000..ca7c4a5b16382f0db8fa36f0690c70eaf75d9bf6
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Shaders/s_FCStreamRoomLogoSheet.shader
@@ -0,0 +1,83 @@
+Shader "Venues/Environment/FC_Logo"
+{
+    Properties
+    {
+        _MainTex ("Texture", 2D) = "white" {}
+        _BgTex ("BG Texture", 2D) = "white" {}
+        _Color ("Color", color) = (1, 1, 1)
+        _Tick ("Animation Tick", Range(0, 1)) = 0
+        _NumRows ("# Rows", float) = 4
+        _NumCols ("# Columns", float) = 4
+        _SheetFrameRatio ("Size Ratio frame / sheet", Range(0, 1)) = 0.25
+        _FgColor ("Text Color", color) = (0, 0, 0, 1)
+        _TimeLock("Time spent on locked frame (5 ~= 10sec)", float) = 5
+        _FrameLock("Index of locked frame", float) = 8
+    }
+    SubShader
+    {
+        Tags { "RenderType"="Opaque" }
+
+        Pass
+        {
+            CGPROGRAM
+            #pragma vertex vert
+            #pragma fragment frag
+
+            #include "UnityCG.cginc"
+
+            struct appdata
+            {
+                float4 vertex : POSITION;
+                float2 uv1 : TEXCOORD0;
+                float2 uv2 : TEXCOORD1;
+                UNITY_VERTEX_INPUT_INSTANCE_ID
+            };
+
+            struct v2f
+            {
+                float4 vertex : SV_POSITION;
+                float2 uv1 : TEXCOORD0;
+                float2 uv2 : TEXCOORD1;
+                UNITY_VERTEX_OUTPUT_STEREO
+            };
+
+            sampler2D _MainTex, _BgTex;
+            half _NumRows, _NumCols, _SheetFrameRatio, _Tick, _TimeLock, _FrameLock;
+            fixed3 _FgColor;
+
+            v2f vert (appdata v)
+            {
+                v2f o;
+                UNITY_SETUP_INSTANCE_ID(v);
+                UNITY_INITIALIZE_OUTPUT(v2f, o);
+                UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
+                o.vertex = UnityObjectToClipPos(v.vertex);
+
+                // UV Scaling
+                float num_frames = 16.;
+                float frame_rate = 8.;
+                float t = _Time.y * frame_rate;
+                t = lerp(fmod(t, num_frames), num_frames - 1., step(num_frames, fmod(t, num_frames * _TimeLock)));
+                t += _FrameLock;
+
+                half x_offset = fmod(floor(t), _NumRows);
+                half y_offset = _NumCols - floor(t / _NumCols) - 1;
+                o.uv2.x = v.uv2.x;
+                o.uv2 = float2(o.uv2.x + x_offset, v.uv2.y + y_offset) * _SheetFrameRatio;
+                o.uv2.y = o.uv2.y;
+                o.uv1 = v.uv1;
+
+                return o;
+            }
+
+            fixed4 frag (v2f i) : SV_Target
+            {
+                half tex = tex2D(_MainTex, i.uv2).a;
+                half3 bg = tex2D(_BgTex, i.uv1).rgb;
+                fixed4 col = fixed4(lerp(bg, fixed3(0., 0., 0.), tex), 1);
+                return col;
+            }
+            ENDCG
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Shaders/s_FCStreamRoomLogoSheet.shader.meta b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Shaders/s_FCStreamRoomLogoSheet.shader.meta
new file mode 100644
index 0000000000000000000000000000000000000000..7824b52c611ec5c34e89e484905b43dc5bf872a2
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/Shaders/s_FCStreamRoomLogoSheet.shader.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: fd9a9d30c0b934743a023e7bf1e60260
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/diffuse-argb16.DDS b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/diffuse-argb16.DDS
new file mode 100644
index 0000000000000000000000000000000000000000..04268780c626a735aad1250fa2f50d2434d15473
Binary files /dev/null and b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/diffuse-argb16.DDS differ
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/diffuse-argb16.DDS.meta b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/diffuse-argb16.DDS.meta
new file mode 100644
index 0000000000000000000000000000000000000000..26c181e4f88c53ce2c6c37e7f6c334fdc3196622
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/diffuse-argb16.DDS.meta
@@ -0,0 +1,19 @@
+fileFormatVersion: 2
+guid: 1c9a7b5b31b38dd4f97e888ad38264f0
+IHVImageFormatImporter:
+  externalObjects: {}
+  textureSettings:
+    serializedVersion: 2
+    filterMode: 1
+    aniso: 1
+    mipBias: 0
+    wrapU: 0
+    wrapV: 0
+    wrapW: 0
+  isReadable: 0
+  sRGBTexture: 1
+  streamingMipmaps: 0
+  streamingMipmapsPriority: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/specular-argb16.DDS b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/specular-argb16.DDS
new file mode 100644
index 0000000000000000000000000000000000000000..3b0f4939e2bd968d14b977dbe8856013ac52cafd
Binary files /dev/null and b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/specular-argb16.DDS differ
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/specular-argb16.DDS.meta b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/specular-argb16.DDS.meta
new file mode 100644
index 0000000000000000000000000000000000000000..8af49084cb075fcfc7faa8ab3313c7176bffefe4
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/FBConnectLightingExample/specular-argb16.DDS.meta
@@ -0,0 +1,19 @@
+fileFormatVersion: 2
+guid: 8ebfa27451fc8dc4e834834a16ded0a0
+IHVImageFormatImporter:
+  externalObjects: {}
+  textureSettings:
+    serializedVersion: 2
+    filterMode: 1
+    aniso: 1
+    mipBias: 0
+    wrapU: 0
+    wrapV: 0
+    wrapW: 0
+  isReadable: 0
+  sRGBTexture: 1
+  streamingMipmaps: 0
+  streamingMipmapsPriority: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/GazeTrackingExample.meta b/Assets/Oculus/Avatar2/Example/Scenes/GazeTrackingExample.meta
new file mode 100644
index 0000000000000000000000000000000000000000..bc7c17decf391527019f0225970972e67e3462bc
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/GazeTrackingExample.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 7b37715a92ae84f4dbe0240707081048
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/GazeTrackingExample/Floor.mat b/Assets/Oculus/Avatar2/Example/Scenes/GazeTrackingExample/Floor.mat
new file mode 100644
index 0000000000000000000000000000000000000000..863906473299702630815752a7b29cdcbb1a493b
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/GazeTrackingExample/Floor.mat
@@ -0,0 +1,77 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: Floor
+  m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
+  m_ShaderKeywords: 
+  m_LightmapFlags: 4
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 4, y: 5}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 4, y: 5}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 0.31572625, g: 0.4056604, b: 0.31572625, a: 1}
+    - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/GazeTrackingExample/Floor.mat.meta b/Assets/Oculus/Avatar2/Example/Scenes/GazeTrackingExample/Floor.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..cf0b628ead04a24b920b3fbf8d7c15193b28fd33
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/GazeTrackingExample/Floor.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 37fd9885d95756843ae12c5c858828b2
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/GazeTrackingExample/GazeTrackingExample.meta b/Assets/Oculus/Avatar2/Example/Scenes/GazeTrackingExample/GazeTrackingExample.meta
new file mode 100644
index 0000000000000000000000000000000000000000..a98ee4a5027139a69d2cf628d2dd91da553d8b27
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/GazeTrackingExample/GazeTrackingExample.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: e9c4b45b329d0f84d814d8e8ae87c8b1
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/GazeTrackingExample/GazeTrackingExample.unity b/Assets/Oculus/Avatar2/Example/Scenes/GazeTrackingExample/GazeTrackingExample.unity
new file mode 100644
index 0000000000000000000000000000000000000000..193f8fc1e267496a12bac4dd7c1997f3548953f6
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/GazeTrackingExample/GazeTrackingExample.unity
@@ -0,0 +1,1322 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!29 &1
+OcclusionCullingSettings:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_OcclusionBakeSettings:
+    smallestOccluder: 5
+    smallestHole: 0.25
+    backfaceThreshold: 100
+  m_SceneGUID: 00000000000000000000000000000000
+  m_OcclusionCullingData: {fileID: 0}
+--- !u!104 &2
+RenderSettings:
+  m_ObjectHideFlags: 0
+  serializedVersion: 9
+  m_Fog: 0
+  m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
+  m_FogMode: 3
+  m_FogDensity: 0.01
+  m_LinearFogStart: 0
+  m_LinearFogEnd: 300
+  m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
+  m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
+  m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
+  m_AmbientIntensity: 1
+  m_AmbientMode: 0
+  m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
+  m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0}
+  m_HaloStrength: 0.5
+  m_FlareStrength: 1
+  m_FlareFadeSpeed: 3
+  m_HaloTexture: {fileID: 0}
+  m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
+  m_DefaultReflectionMode: 0
+  m_DefaultReflectionResolution: 128
+  m_ReflectionBounces: 1
+  m_ReflectionIntensity: 1
+  m_CustomReflection: {fileID: 0}
+  m_Sun: {fileID: 705507994}
+  m_IndirectSpecularColor: {r: 0.44993275, g: 0.4091351, b: 0.3642775, a: 1}
+  m_UseRadianceAmbientProbe: 0
+--- !u!157 &3
+LightmapSettings:
+  m_ObjectHideFlags: 0
+  serializedVersion: 11
+  m_GIWorkflowMode: 1
+  m_GISettings:
+    serializedVersion: 2
+    m_BounceScale: 1
+    m_IndirectOutputScale: 1
+    m_AlbedoBoost: 1
+    m_EnvironmentLightingMode: 0
+    m_EnableBakedLightmaps: 1
+    m_EnableRealtimeLightmaps: 0
+  m_LightmapEditorSettings:
+    serializedVersion: 12
+    m_Resolution: 2
+    m_BakeResolution: 40
+    m_AtlasSize: 1024
+    m_AO: 0
+    m_AOMaxDistance: 1
+    m_CompAOExponent: 1
+    m_CompAOExponentDirect: 0
+    m_ExtractAmbientOcclusion: 0
+    m_Padding: 2
+    m_LightmapParameters: {fileID: 0}
+    m_LightmapsBakeMode: 1
+    m_TextureCompression: 1
+    m_FinalGather: 0
+    m_FinalGatherFiltering: 1
+    m_FinalGatherRayCount: 256
+    m_ReflectionCompression: 2
+    m_MixedBakeMode: 2
+    m_BakeBackend: 1
+    m_PVRSampling: 1
+    m_PVRDirectSampleCount: 32
+    m_PVRSampleCount: 500
+    m_PVRBounces: 2
+    m_PVREnvironmentSampleCount: 500
+    m_PVREnvironmentReferencePointCount: 2048
+    m_PVRFilteringMode: 2
+    m_PVRDenoiserTypeDirect: 0
+    m_PVRDenoiserTypeIndirect: 0
+    m_PVRDenoiserTypeAO: 0
+    m_PVRFilterTypeDirect: 0
+    m_PVRFilterTypeIndirect: 0
+    m_PVRFilterTypeAO: 0
+    m_PVREnvironmentMIS: 0
+    m_PVRCulling: 1
+    m_PVRFilteringGaussRadiusDirect: 1
+    m_PVRFilteringGaussRadiusIndirect: 5
+    m_PVRFilteringGaussRadiusAO: 2
+    m_PVRFilteringAtrousPositionSigmaDirect: 0.5
+    m_PVRFilteringAtrousPositionSigmaIndirect: 2
+    m_PVRFilteringAtrousPositionSigmaAO: 1
+    m_ExportTrainingData: 0
+    m_TrainingDataDestination: TrainingData
+    m_LightProbeSampleCountMultiplier: 4
+  m_LightingDataAsset: {fileID: 112000000, guid: f0c68c730be06da4e8d2e671bffde0dc,
+    type: 2}
+  m_UseShadowmask: 1
+--- !u!196 &4
+NavMeshSettings:
+  serializedVersion: 2
+  m_ObjectHideFlags: 0
+  m_BuildSettings:
+    serializedVersion: 2
+    agentTypeID: 0
+    agentRadius: 0.5
+    agentHeight: 2
+    agentSlope: 45
+    agentClimb: 0.4
+    ledgeDropHeight: 0
+    maxJumpAcrossDistance: 0
+    minRegionArea: 2
+    manualCellSize: 0
+    cellSize: 0.16666667
+    manualTileSize: 0
+    tileSize: 256
+    accuratePlacement: 0
+    debug:
+      m_Flags: 0
+  m_NavMeshData: {fileID: 0}
+--- !u!1001 &107401265
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 1954331789}
+    m_Modifications:
+    - target: {fileID: 1792793605742586424, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: _speedX
+      value: 0.5
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586424, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: _speedY
+      value: 0.125
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586424, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: _magnitudeX
+      value: 0.375
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586424, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: _magnitudeY
+      value: 0.375
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalScale.x
+      value: 0.25
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalScale.y
+      value: 0.25
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalScale.z
+      value: 0.25
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 1.6
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: -1
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586430, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_Name
+      value: MovingGazeTarget
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: 56451d33c6996504ab03a9b157b9f3c4, type: 3}
+--- !u!4 &107401266 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+    type: 3}
+  m_PrefabInstance: {fileID: 107401265}
+  m_PrefabAsset: {fileID: 0}
+--- !u!1 &482114163
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 482114167}
+  - component: {fileID: 482114166}
+  - component: {fileID: 482114165}
+  - component: {fileID: 482114164}
+  m_Layer: 0
+  m_Name: Floor
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!64 &482114164
+MeshCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 482114163}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 4
+  m_Convex: 0
+  m_CookingOptions: 30
+  m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &482114165
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 482114163}
+  m_Enabled: 1
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RayTracingMode: 2
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: 37fd9885d95756843ae12c5c858828b2, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_ReceiveGI: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!33 &482114166
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 482114163}
+  m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!4 &482114167
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 482114163}
+  m_LocalRotation: {x: 0.7071068, y: 0, z: 0, w: 0.7071068}
+  m_LocalPosition: {x: -1.386, y: -0, z: 0}
+  m_LocalScale: {x: 8, y: 8, z: 2}
+  m_Children: []
+  m_Father: {fileID: 0}
+  m_RootOrder: 5
+  m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0}
+--- !u!1001 &594887852
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 0}
+    m_Modifications:
+    - target: {fileID: 5848076533691757917, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_Name
+      value: AvatarSdkManagerHorizon
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: d0377a811c2a95841ba015ae0b2c8d45, type: 3}
+--- !u!1 &625919962
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 625919963}
+  m_Layer: 0
+  m_Name: Entities
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &625919963
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 625919962}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: -1.386, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 1717923040}
+  - {fileID: 1554471818}
+  - {fileID: 1404394615}
+  m_Father: {fileID: 0}
+  m_RootOrder: 3
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1001 &674228526
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 1954331789}
+    m_Modifications:
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 3
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalScale.x
+      value: 0.25
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalScale.y
+      value: 0.25
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalScale.z
+      value: 0.25
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 2
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 1.5
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: -0.8
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586430, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_Name
+      value: StationaryGazeTarget2
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: 56451d33c6996504ab03a9b157b9f3c4, type: 3}
+--- !u!4 &674228527 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+    type: 3}
+  m_PrefabInstance: {fileID: 674228526}
+  m_PrefabAsset: {fileID: 0}
+--- !u!1 &705507993
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 705507995}
+  - component: {fileID: 705507994}
+  m_Layer: 0
+  m_Name: Directional Light
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!108 &705507994
+Light:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 705507993}
+  m_Enabled: 1
+  serializedVersion: 10
+  m_Type: 1
+  m_Shape: 0
+  m_Color: {r: 1, g: 1, b: 1, a: 1}
+  m_Intensity: 1
+  m_Range: 10
+  m_SpotAngle: 30
+  m_InnerSpotAngle: 21.80208
+  m_CookieSize: 10
+  m_Shadows:
+    m_Type: 0
+    m_Resolution: -1
+    m_CustomResolution: -1
+    m_Strength: 1
+    m_Bias: 0.05
+    m_NormalBias: 0.4
+    m_NearPlane: 0.2
+    m_CullingMatrixOverride:
+      e00: 1
+      e01: 0
+      e02: 0
+      e03: 0
+      e10: 0
+      e11: 1
+      e12: 0
+      e13: 0
+      e20: 0
+      e21: 0
+      e22: 1
+      e23: 0
+      e30: 0
+      e31: 0
+      e32: 0
+      e33: 1
+    m_UseCullingMatrixOverride: 0
+  m_Cookie: {fileID: 0}
+  m_DrawHalo: 0
+  m_Flare: {fileID: 0}
+  m_RenderMode: 0
+  m_CullingMask:
+    serializedVersion: 2
+    m_Bits: 4294967295
+  m_RenderingLayerMask: 1
+  m_Lightmapping: 1
+  m_LightShadowCasterMode: 0
+  m_AreaSize: {x: 1, y: 1}
+  m_BounceIntensity: 1
+  m_ColorTemperature: 6570
+  m_UseColorTemperature: 0
+  m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
+  m_UseBoundingSphereOverride: 0
+  m_ShadowRadius: 0
+  m_ShadowAngle: 0
+--- !u!4 &705507995
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 705507993}
+  m_LocalRotation: {x: 0.2742849, y: 0.23248513, z: -0.82116294, w: 0.44318154}
+  m_LocalPosition: {x: -1.386, y: 3, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 0}
+  m_RootOrder: 1
+  m_LocalEulerAnglesHint: {x: 38.677002, y: -18.244001, z: -129.73901}
+--- !u!1001 &1114567421
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 1954331789}
+    m_Modifications:
+    - target: {fileID: 1792793605742586424, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: _speedY
+      value: 0.5
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586424, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: _magnitudeY
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalScale.x
+      value: 0.25
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalScale.y
+      value: 0.25
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalScale.z
+      value: 0.25
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 1.5
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: -0.6
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586430, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_Name
+      value: MovingGazeTarget2
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: 56451d33c6996504ab03a9b157b9f3c4, type: 3}
+--- !u!4 &1114567422 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+    type: 3}
+  m_PrefabInstance: {fileID: 1114567421}
+  m_PrefabAsset: {fileID: 0}
+--- !u!1 &1288068492
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1288068493}
+  - component: {fileID: 1288068494}
+  - component: {fileID: 1288068495}
+  m_Layer: 0
+  m_Name: CameraOffset
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1288068493
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1288068492}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: -1.386, y: 0, z: -2}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 1828775177}
+  m_Father: {fileID: 0}
+  m_RootOrder: 2
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &1288068494
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1288068492}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 5b0eaccd645fc5641ba1cf708f6fd678, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+  movementSpeed: 2.5
+--- !u!114 &1288068495
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1288068492}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 4e93961d3dfb44505a84d80b1f7fffef, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+  _mode: 1
+  _coordinateSpace: 0
+  _position: {x: 0, y: 1.5, z: 0}
+  _rotation: {x: 0, y: 0, z: 0}
+--- !u!1 &1400735768
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1400735770}
+  - component: {fileID: 1400735769}
+  m_Layer: 0
+  m_Name: SceneSwitcher
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!114 &1400735769
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1400735768}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: ea6e0c6cd67922c48bbb5939267828e9, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+  _nextSceneInput:
+    controllerMask: 2
+    buttonMask: 1
+  _prevSceneInput:
+    controllerMask: 1
+    buttonMask: 1
+--- !u!4 &1400735770
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1400735768}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 0}
+  m_RootOrder: 6
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &1404394614
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1404394615}
+  - component: {fileID: 1404394616}
+  m_Layer: 0
+  m_Name: Preset 2
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1404394615
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1404394614}
+  m_LocalRotation: {x: 0, y: 0.2588191, z: 0, w: 0.9659258}
+  m_LocalPosition: {x: -0.8, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 625919963}
+  m_RootOrder: 2
+  m_LocalEulerAnglesHint: {x: 0, y: 30, z: 0}
+--- !u!114 &1404394616
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1404394614}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+  _creationInfo:
+    features: 342
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+  _activeView: 2
+  _activeManifestation: 2
+  _bodyTracking: {fileID: 0}
+  _lipSync: {fileID: 0}
+  _criticalJointTypes:
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  _useAdvancedLodSystem: 1
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _loadUserFromCdn: 0
+  _assetSource: 0
+  _assetPaths:
+  - 2_
+  _assets:
+  - source: 0
+    path: 2
+  _assetPostfixDefault: _rift.glb
+  _assetPostfixAndroid: _quest.glb
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 1
+  _debugDrawGazePosColor: {r: 1, g: 0.7176471, b: 0, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!1 &1554471817
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1554471818}
+  - component: {fileID: 1554471819}
+  m_Layer: 0
+  m_Name: Preset 1
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1554471818
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1554471817}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 625919963}
+  m_RootOrder: 1
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &1554471819
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1554471817}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+  _creationInfo:
+    features: 342
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+  _activeView: 2
+  _activeManifestation: 2
+  _bodyTracking: {fileID: 0}
+  _lipSync: {fileID: 0}
+  _criticalJointTypes:
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  _useAdvancedLodSystem: 1
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _loadUserFromCdn: 0
+  _assetSource: 0
+  _assetPaths:
+  - 1_
+  _assets:
+  - source: 0
+    path: 1
+  _assetPostfixDefault: _rift.glb
+  _assetPostfixAndroid: _quest.glb
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 1
+  _debugDrawGazePosColor: {r: 0, g: 0.40936995, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!1 &1717923039
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1717923040}
+  - component: {fileID: 1717923041}
+  m_Layer: 0
+  m_Name: Preset 0
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1717923040
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1717923039}
+  m_LocalRotation: {x: 0, y: -0.258819, z: 0, w: 0.9659259}
+  m_LocalPosition: {x: 0.8, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 625919963}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: -30, z: 0}
+--- !u!114 &1717923041
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1717923039}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+  _creationInfo:
+    features: 342
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+  _activeView: 2
+  _activeManifestation: 2
+  _bodyTracking: {fileID: 0}
+  _lipSync: {fileID: 0}
+  _criticalJointTypes:
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  _useAdvancedLodSystem: 1
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _loadUserFromCdn: 0
+  _assetSource: 0
+  _assetPaths:
+  - 0_
+  _assets:
+  - source: 0
+    path: 0
+  _assetPostfixDefault: _rift.glb
+  _assetPostfixAndroid: _quest.glb
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 1
+  _debugDrawGazePosColor: {r: 0.5882353, g: 0.56078434, b: 0.7372549, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!1 &1828775176
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1828775177}
+  - component: {fileID: 1828775179}
+  - component: {fileID: 1828775178}
+  - component: {fileID: 1828775180}
+  m_Layer: 0
+  m_Name: Main Camera
+  m_TagString: MainCamera
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1828775177
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1828775176}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1288068493}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!81 &1828775178
+AudioListener:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1828775176}
+  m_Enabled: 1
+--- !u!20 &1828775179
+Camera:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1828775176}
+  m_Enabled: 1
+  serializedVersion: 2
+  m_ClearFlags: 1
+  m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
+  m_projectionMatrixMode: 1
+  m_GateFitMode: 2
+  m_FOVAxisMode: 0
+  m_SensorSize: {x: 36, y: 24}
+  m_LensShift: {x: 0, y: 0}
+  m_FocalLength: 50
+  m_NormalizedViewPortRect:
+    serializedVersion: 2
+    x: 0
+    y: 0
+    width: 1
+    height: 1
+  near clip plane: 0.3
+  far clip plane: 1000
+  field of view: 60
+  orthographic: 0
+  orthographic size: 5
+  m_Depth: -1
+  m_CullingMask:
+    serializedVersion: 2
+    m_Bits: 4294967295
+  m_RenderingPath: -1
+  m_TargetTexture: {fileID: 0}
+  m_TargetDisplay: 0
+  m_TargetEye: 3
+  m_HDR: 1
+  m_AllowMSAA: 1
+  m_AllowDynamicResolution: 0
+  m_ForceIntoRT: 0
+  m_OcclusionCulling: 1
+  m_StereoConvergence: 10
+  m_StereoSeparation: 0.022
+--- !u!114 &1828775180
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1828775176}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 5a2a9c34df4095f47b9ca8f975175f5b, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+  m_Device: 0
+  m_PoseSource: 2
+  m_PoseProviderComponent: {fileID: 0}
+  m_TrackingType: 0
+  m_UpdateType: 0
+  m_UseRelativeTransform: 0
+--- !u!1 &1954331788
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1954331789}
+  m_Layer: 0
+  m_Name: GazeTargets
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1954331789
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1954331788}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: -1.386, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 107401266}
+  - {fileID: 1114567422}
+  - {fileID: 268502550685930315}
+  - {fileID: 674228527}
+  m_Father: {fileID: 0}
+  m_RootOrder: 4
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!4 &268502550685930315 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+    type: 3}
+  m_PrefabInstance: {fileID: 1970502874560149873}
+  m_PrefabAsset: {fileID: 0}
+--- !u!1001 &1970502874560149873
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 1954331789}
+    m_Modifications:
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 2
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalScale.x
+      value: 0.25
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalScale.y
+      value: 0.25
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalScale.z
+      value: 0.25
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: -1.5
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 1.8
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: -0.5
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586426, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1792793605742586430, guid: 56451d33c6996504ab03a9b157b9f3c4,
+        type: 3}
+      propertyPath: m_Name
+      value: StationaryGazeTarget
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: 56451d33c6996504ab03a9b157b9f3c4, type: 3}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/GazeTrackingExample/GazeTrackingExample.unity.meta b/Assets/Oculus/Avatar2/Example/Scenes/GazeTrackingExample/GazeTrackingExample.unity.meta
new file mode 100644
index 0000000000000000000000000000000000000000..c898be76e9ad3db77fc259f39c98dc55e806e4e6
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/GazeTrackingExample/GazeTrackingExample.unity.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 71c0a112e957503448f41007b965a1fd
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/GazeTrackingExample/GazeTrackingExample/LightingData.asset b/Assets/Oculus/Avatar2/Example/Scenes/GazeTrackingExample/GazeTrackingExample/LightingData.asset
new file mode 100644
index 0000000000000000000000000000000000000000..8176701ad0f1368cd4a11e4decc1f839c887e511
Binary files /dev/null and b/Assets/Oculus/Avatar2/Example/Scenes/GazeTrackingExample/GazeTrackingExample/LightingData.asset differ
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/GazeTrackingExample/GazeTrackingExample/LightingData.asset.meta b/Assets/Oculus/Avatar2/Example/Scenes/GazeTrackingExample/GazeTrackingExample/LightingData.asset.meta
new file mode 100644
index 0000000000000000000000000000000000000000..21b02ba3e28beb50d0eec1221d69010d9f60ad97
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/GazeTrackingExample/GazeTrackingExample/LightingData.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 009b46234e28e424ca444b8336a05e75
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 112000000
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/GazeTrackingExample/SampleGazeTarget.prefab b/Assets/Oculus/Avatar2/Example/Scenes/GazeTrackingExample/SampleGazeTarget.prefab
new file mode 100644
index 0000000000000000000000000000000000000000..f902906deb3acefe7a74a77301518e5ec6caf374
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/GazeTrackingExample/SampleGazeTarget.prefab
@@ -0,0 +1,126 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &1792793605742586430
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1792793605742586426}
+  - component: {fileID: 1792793605742586429}
+  - component: {fileID: 1792793605742586428}
+  - component: {fileID: 1792793605742586431}
+  - component: {fileID: 1792793605742586427}
+  - component: {fileID: 1792793605742586424}
+  m_Layer: 0
+  m_Name: GazeTarget
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1792793605742586426
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1792793605742586430}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 0}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &1792793605742586429
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1792793605742586430}
+  m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &1792793605742586428
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1792793605742586430}
+  m_Enabled: 1
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 10303, guid: 0000000000000000f000000000000000, type: 0}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 0
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!65 &1792793605742586431
+BoxCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1792793605742586430}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Size: {x: 1, y: 1, z: 1}
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!114 &1792793605742586427
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1792793605742586430}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 2ca1c3c8e8ea8844689c9e255d729f0e, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _targetType: 2
+--- !u!114 &1792793605742586424
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1792793605742586430}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: bf0c0670f07b18b43b36868a9ec6c43f, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _magnitudeX: 0
+  _magnitudeY: 0
+  _magnitudeZ: 0
+  _speedX: 1
+  _speedY: 1
+  _speedZ: 1
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/GazeTrackingExample/SampleGazeTarget.prefab.meta b/Assets/Oculus/Avatar2/Example/Scenes/GazeTrackingExample/SampleGazeTarget.prefab.meta
new file mode 100644
index 0000000000000000000000000000000000000000..6295561b71d4e36080883c8c09ccda50cb8357e4
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/GazeTrackingExample/SampleGazeTarget.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 56451d33c6996504ab03a9b157b9f3c4
+PrefabImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/GazeTrackingExample/SampleGazeTargetMotion.cs b/Assets/Oculus/Avatar2/Example/Scenes/GazeTrackingExample/SampleGazeTargetMotion.cs
new file mode 100644
index 0000000000000000000000000000000000000000..684f6fecd766361c5a671a9d940cff1a9ca113be
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/GazeTrackingExample/SampleGazeTargetMotion.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+public class SampleGazeTargetMotion : MonoBehaviour
+{
+    [SerializeField]
+    private float _magnitudeX = 0f;
+    [SerializeField]
+    private float _magnitudeY = 0f;
+    [SerializeField]
+    private float _magnitudeZ = 0f;
+
+    [SerializeField]
+    private float _speedX = 1f;
+    [SerializeField]
+    private float _speedY = 1f;
+    [SerializeField]
+    private float _speedZ = 1f;
+
+    private Vector3 _startPos;
+
+    void Awake()
+    {
+        _startPos = transform.localPosition;
+    }
+
+    void Update()
+    {
+        var t = transform;
+        float radians = Time.time * Mathf.PI;
+
+        // Only update axis that are actually moving - so that we can drag in the editor when its stationary
+        Vector3 newPos = t.localPosition;
+        newPos.x = _magnitudeX > 0f ? _startPos.x + Mathf.Sin(radians * _speedX) * _magnitudeX : newPos.x;
+        newPos.y = _magnitudeY > 0f ? _startPos.y + Mathf.Sin(radians * _speedY) * _magnitudeY : newPos.y;
+        newPos.z = _magnitudeZ > 0f ? _startPos.z + Mathf.Sin(radians * _speedZ) * _magnitudeZ : newPos.z;
+
+        t.localPosition = newPos;
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/GazeTrackingExample/SampleGazeTargetMotion.cs.meta b/Assets/Oculus/Avatar2/Example/Scenes/GazeTrackingExample/SampleGazeTargetMotion.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..669588a0774660931633e7f4c78f2dad821fcef0
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/GazeTrackingExample/SampleGazeTargetMotion.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: bf0c0670f07b18b43b36868a9ec6c43f
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/MirrorScene.meta b/Assets/Oculus/Avatar2/Example/Scenes/MirrorScene.meta
new file mode 100644
index 0000000000000000000000000000000000000000..a20f07ff7559d7868335eec41f02b008902964ce
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/MirrorScene.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 8cf874caa290a1847a36edcbf433ba2d
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/MirrorScene/Floor.mat b/Assets/Oculus/Avatar2/Example/Scenes/MirrorScene/Floor.mat
new file mode 100644
index 0000000000000000000000000000000000000000..066080614ffb800039b6a1ceebbac224b972b3c0
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/MirrorScene/Floor.mat
@@ -0,0 +1,77 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: Floor
+  m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
+  m_ShaderKeywords: 
+  m_LightmapFlags: 4
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 4, y: 5}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 4, y: 5}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 0.5019608, g: 0.5019608, b: 0.5019608, a: 1}
+    - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/MirrorScene/Floor.mat.meta b/Assets/Oculus/Avatar2/Example/Scenes/MirrorScene/Floor.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..73f68b27933613ea26e374a260af4034f44ac7f3
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/MirrorScene/Floor.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 6961428059f782e42b6a7c24fb04c9ec
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/MirrorScene/MirrorScene.meta b/Assets/Oculus/Avatar2/Example/Scenes/MirrorScene/MirrorScene.meta
new file mode 100644
index 0000000000000000000000000000000000000000..974ff7bfd0ebbb8cdd365675774e83076a513fff
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/MirrorScene/MirrorScene.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 4e56beecb469bd442a967cc550780924
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/MirrorScene/MirrorScene.unity b/Assets/Oculus/Avatar2/Example/Scenes/MirrorScene/MirrorScene.unity
new file mode 100644
index 0000000000000000000000000000000000000000..17e40c5bf6a675b5e7a07da3e45542d85b8d8328
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/MirrorScene/MirrorScene.unity
@@ -0,0 +1,1245 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!29 &1
+OcclusionCullingSettings:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_OcclusionBakeSettings:
+    smallestOccluder: 5
+    smallestHole: 0.25
+    backfaceThreshold: 100
+  m_SceneGUID: 00000000000000000000000000000000
+  m_OcclusionCullingData: {fileID: 0}
+--- !u!104 &2
+RenderSettings:
+  m_ObjectHideFlags: 0
+  serializedVersion: 9
+  m_Fog: 0
+  m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
+  m_FogMode: 3
+  m_FogDensity: 0.01
+  m_LinearFogStart: 0
+  m_LinearFogEnd: 300
+  m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
+  m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
+  m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
+  m_AmbientIntensity: 1
+  m_AmbientMode: 0
+  m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
+  m_SkyboxMaterial: {fileID: 2100000, guid: 35f80c2b3fdf34d4aa85db1bf46f0983, type: 2}
+  m_HaloStrength: 0.5
+  m_FlareStrength: 1
+  m_FlareFadeSpeed: 3
+  m_HaloTexture: {fileID: 0}
+  m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
+  m_DefaultReflectionMode: 0
+  m_DefaultReflectionResolution: 128
+  m_ReflectionBounces: 1
+  m_ReflectionIntensity: 1
+  m_CustomReflection: {fileID: 0}
+  m_Sun: {fileID: 0}
+  m_IndirectSpecularColor: {r: 0.44993275, g: 0.4091351, b: 0.3642775, a: 1}
+  m_UseRadianceAmbientProbe: 0
+--- !u!157 &3
+LightmapSettings:
+  m_ObjectHideFlags: 0
+  serializedVersion: 12
+  m_GIWorkflowMode: 1
+  m_GISettings:
+    serializedVersion: 2
+    m_BounceScale: 1
+    m_IndirectOutputScale: 1
+    m_AlbedoBoost: 1
+    m_EnvironmentLightingMode: 0
+    m_EnableBakedLightmaps: 0
+    m_EnableRealtimeLightmaps: 0
+  m_LightmapEditorSettings:
+    serializedVersion: 12
+    m_Resolution: 2
+    m_BakeResolution: 40
+    m_AtlasSize: 1024
+    m_AO: 0
+    m_AOMaxDistance: 1
+    m_CompAOExponent: 1
+    m_CompAOExponentDirect: 0
+    m_ExtractAmbientOcclusion: 0
+    m_Padding: 2
+    m_LightmapParameters: {fileID: 0}
+    m_LightmapsBakeMode: 1
+    m_TextureCompression: 1
+    m_FinalGather: 0
+    m_FinalGatherFiltering: 1
+    m_FinalGatherRayCount: 256
+    m_ReflectionCompression: 2
+    m_MixedBakeMode: 2
+    m_BakeBackend: 1
+    m_PVRSampling: 1
+    m_PVRDirectSampleCount: 32
+    m_PVRSampleCount: 500
+    m_PVRBounces: 2
+    m_PVREnvironmentSampleCount: 500
+    m_PVREnvironmentReferencePointCount: 2048
+    m_PVRFilteringMode: 2
+    m_PVRDenoiserTypeDirect: 0
+    m_PVRDenoiserTypeIndirect: 0
+    m_PVRDenoiserTypeAO: 0
+    m_PVRFilterTypeDirect: 0
+    m_PVRFilterTypeIndirect: 0
+    m_PVRFilterTypeAO: 0
+    m_PVREnvironmentMIS: 0
+    m_PVRCulling: 1
+    m_PVRFilteringGaussRadiusDirect: 1
+    m_PVRFilteringGaussRadiusIndirect: 5
+    m_PVRFilteringGaussRadiusAO: 2
+    m_PVRFilteringAtrousPositionSigmaDirect: 0.5
+    m_PVRFilteringAtrousPositionSigmaIndirect: 2
+    m_PVRFilteringAtrousPositionSigmaAO: 1
+    m_ExportTrainingData: 0
+    m_TrainingDataDestination: TrainingData
+    m_LightProbeSampleCountMultiplier: 4
+  m_LightingDataAsset: {fileID: 112000000, guid: f0c68c730be06da4e8d2e671bffde0dc,
+    type: 2}
+  m_LightingSettings: {fileID: 1120315176}
+--- !u!196 &4
+NavMeshSettings:
+  serializedVersion: 2
+  m_ObjectHideFlags: 0
+  m_BuildSettings:
+    serializedVersion: 2
+    agentTypeID: 0
+    agentRadius: 0.5
+    agentHeight: 2
+    agentSlope: 45
+    agentClimb: 0.4
+    ledgeDropHeight: 0
+    maxJumpAcrossDistance: 0
+    minRegionArea: 2
+    manualCellSize: 0
+    cellSize: 0.16666667
+    manualTileSize: 0
+    tileSize: 256
+    accuratePlacement: 0
+    maxJobWorkers: 0
+    preserveTilesOutsideBounds: 0
+    debug:
+      m_Flags: 0
+  m_NavMeshData: {fileID: 0}
+--- !u!1 &168388877
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 168388879}
+  - component: {fileID: 168388878}
+  m_Layer: 0
+  m_Name: Second Test Point Light
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 0
+--- !u!108 &168388878
+Light:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 168388877}
+  m_Enabled: 1
+  serializedVersion: 10
+  m_Type: 2
+  m_Shape: 0
+  m_Color: {r: 1, g: 0, b: 0, a: 1}
+  m_Intensity: 4
+  m_Range: 10
+  m_SpotAngle: 46.8
+  m_InnerSpotAngle: 34.55462
+  m_CookieSize: 10
+  m_Shadows:
+    m_Type: 0
+    m_Resolution: -1
+    m_CustomResolution: -1
+    m_Strength: 1
+    m_Bias: 0.05
+    m_NormalBias: 0.4
+    m_NearPlane: 0.2
+    m_CullingMatrixOverride:
+      e00: 1
+      e01: 0
+      e02: 0
+      e03: 0
+      e10: 0
+      e11: 1
+      e12: 0
+      e13: 0
+      e20: 0
+      e21: 0
+      e22: 1
+      e23: 0
+      e30: 0
+      e31: 0
+      e32: 0
+      e33: 1
+    m_UseCullingMatrixOverride: 0
+  m_Cookie: {fileID: 0}
+  m_DrawHalo: 0
+  m_Flare: {fileID: 0}
+  m_RenderMode: 0
+  m_CullingMask:
+    serializedVersion: 2
+    m_Bits: 4294967295
+  m_RenderingLayerMask: 1
+  m_Lightmapping: 4
+  m_LightShadowCasterMode: 0
+  m_AreaSize: {x: 1, y: 1}
+  m_BounceIntensity: 1
+  m_ColorTemperature: 6570
+  m_UseColorTemperature: 0
+  m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
+  m_UseBoundingSphereOverride: 0
+  m_UseViewFrustumForShadowCasterCull: 1
+  m_ShadowRadius: 0
+  m_ShadowAngle: 0
+--- !u!4 &168388879
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 168388877}
+  m_LocalRotation: {x: -0.21729134, y: 0, z: 0, w: -0.9761068}
+  m_LocalPosition: {x: 0, y: 2, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 0}
+  m_RootOrder: 1
+  m_LocalEulerAnglesHint: {x: 385.1, y: 0, z: 0}
+--- !u!1001 &171261753
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 0}
+    m_Modifications:
+    - target: {fileID: 5650585432433268674, guid: 9a975a19ea46baf448b38e3a8b495351,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 9a975a19ea46baf448b38e3a8b495351,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 9a975a19ea46baf448b38e3a8b495351,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 9a975a19ea46baf448b38e3a8b495351,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 9a975a19ea46baf448b38e3a8b495351,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 9a975a19ea46baf448b38e3a8b495351,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 9a975a19ea46baf448b38e3a8b495351,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 9a975a19ea46baf448b38e3a8b495351,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 9a975a19ea46baf448b38e3a8b495351,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 9a975a19ea46baf448b38e3a8b495351,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 9a975a19ea46baf448b38e3a8b495351,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 8423574076275988413, guid: 9a975a19ea46baf448b38e3a8b495351,
+        type: 3}
+      propertyPath: m_Name
+      value: FootprintCourt LightRig
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: 9a975a19ea46baf448b38e3a8b495351, type: 3}
+--- !u!1 &305611635
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 305611636}
+  - component: {fileID: 305611637}
+  m_Layer: 0
+  m_Name: Mirror Avatar
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &305611636
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 305611635}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 3}
+  m_LocalScale: {x: 1, y: 1, z: -1}
+  m_Children: []
+  m_Father: {fileID: 625919963}
+  m_RootOrder: 1
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &305611637
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 305611635}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+  _creationInfo:
+    features: 382
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+      subMeshInclusionFlags: 2047
+  _activeView: 2
+  _activeManifestation: 2
+  _activeSubMeshes: 2047
+  _activeSubMeshesIncludeUntyped: 1
+  _bodyTracking: {fileID: 2077391344}
+  _facePoseBehavior: {fileID: 1640224949}
+  _eyePoseBehavior: {fileID: 1640224948}
+  _lipSync: {fileID: 307099418}
+  _criticalJointTypes:
+  _useExperimentalFeatures: 0
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  OnCreatedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnSkeletonLoadedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnDefaultAvatarLoadedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnFastLoadAvatarLoadedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnUserAvatarLoadedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  PreTeardownEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnLoadFailedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 3
+  MotionSmoothingSettings: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _highQuality: 0
+  _loadUserFromCdn: 1
+  _deferLoading: 0
+  _assets:
+  - source: 0
+    path: 0
+  _underscorePostfix: 1
+  _overridePostfix:
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!114 &307099418 stripped
+MonoBehaviour:
+  m_CorrespondingSourceObject: {fileID: 2430523750807860367, guid: c019572605bc29e42b3ed5a058599618,
+    type: 3}
+  m_PrefabInstance: {fileID: 2430523750576325525}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 0}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: a509dbf839e0e4044a7affab5b5a3e55, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+--- !u!1 &467760007
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 467760009}
+  - component: {fileID: 467760008}
+  m_Layer: 0
+  m_Name: SceneSwitcher
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!114 &467760008
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 467760007}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: ea6e0c6cd67922c48bbb5939267828e9, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+  _nextSceneInput:
+    controllerMask: 2
+    buttonMask: 1
+  _prevSceneInput:
+    controllerMask: 1
+    buttonMask: 1
+--- !u!4 &467760009
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 467760007}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 0}
+  m_RootOrder: 8
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &482114163
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 482114167}
+  - component: {fileID: 482114166}
+  - component: {fileID: 482114165}
+  - component: {fileID: 482114164}
+  m_Layer: 0
+  m_Name: Floor
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!64 &482114164
+MeshCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 482114163}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 4
+  m_Convex: 0
+  m_CookingOptions: 30
+  m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &482114165
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 482114163}
+  m_Enabled: 1
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RayTracingMode: 2
+  m_RayTraceProcedural: 0
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: 6961428059f782e42b6a7c24fb04c9ec, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_ReceiveGI: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+  m_AdditionalVertexStreams: {fileID: 0}
+--- !u!33 &482114166
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 482114163}
+  m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!4 &482114167
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 482114163}
+  m_LocalRotation: {x: 0.7071068, y: 0, z: 0, w: 0.7071068}
+  m_LocalPosition: {x: 0, y: -0, z: 2}
+  m_LocalScale: {x: 3, y: 8, z: 2}
+  m_Children: []
+  m_Father: {fileID: 0}
+  m_RootOrder: 6
+  m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0}
+--- !u!1 &625919962
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 625919963}
+  m_Layer: 0
+  m_Name: Entities
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &625919963
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 625919962}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 2043905275}
+  - {fileID: 305611636}
+  m_Father: {fileID: 0}
+  m_RootOrder: 3
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &778375652
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 778375654}
+  - component: {fileID: 778375653}
+  m_Layer: 0
+  m_Name: Third Test Spot Light
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 0
+--- !u!108 &778375653
+Light:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 778375652}
+  m_Enabled: 1
+  serializedVersion: 10
+  m_Type: 0
+  m_Shape: 0
+  m_Color: {r: 0, g: 0.10499716, b: 1, a: 1}
+  m_Intensity: 4
+  m_Range: 10
+  m_SpotAngle: 45
+  m_InnerSpotAngle: 33.15822
+  m_CookieSize: 10
+  m_Shadows:
+    m_Type: 0
+    m_Resolution: -1
+    m_CustomResolution: -1
+    m_Strength: 1
+    m_Bias: 0.05
+    m_NormalBias: 0.4
+    m_NearPlane: 0.2
+    m_CullingMatrixOverride:
+      e00: 1
+      e01: 0
+      e02: 0
+      e03: 0
+      e10: 0
+      e11: 1
+      e12: 0
+      e13: 0
+      e20: 0
+      e21: 0
+      e22: 1
+      e23: 0
+      e30: 0
+      e31: 0
+      e32: 0
+      e33: 1
+    m_UseCullingMatrixOverride: 0
+  m_Cookie: {fileID: 0}
+  m_DrawHalo: 0
+  m_Flare: {fileID: 0}
+  m_RenderMode: 0
+  m_CullingMask:
+    serializedVersion: 2
+    m_Bits: 4294967295
+  m_RenderingLayerMask: 1
+  m_Lightmapping: 4
+  m_LightShadowCasterMode: 0
+  m_AreaSize: {x: 1, y: 1}
+  m_BounceIntensity: 1
+  m_ColorTemperature: 6570
+  m_UseColorTemperature: 0
+  m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
+  m_UseBoundingSphereOverride: 0
+  m_UseViewFrustumForShadowCasterCull: 1
+  m_ShadowRadius: 0
+  m_ShadowAngle: 0
+--- !u!4 &778375654
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 778375652}
+  m_LocalRotation: {x: 0, y: 0.97886735, z: -0.2044961, w: 0}
+  m_LocalPosition: {x: 0, y: 2, z: 4}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 0}
+  m_RootOrder: 2
+  m_LocalEulerAnglesHint: {x: 23.6, y: 180, z: 0}
+--- !u!1 &963194225
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 963194228}
+  - component: {fileID: 963194227}
+  - component: {fileID: 963194226}
+  - component: {fileID: 963194229}
+  - component: {fileID: 963194230}
+  m_Layer: 0
+  m_Name: Main Camera
+  m_TagString: MainCamera
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!81 &963194226
+AudioListener:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 963194225}
+  m_Enabled: 1
+--- !u!20 &963194227
+Camera:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 963194225}
+  m_Enabled: 1
+  serializedVersion: 2
+  m_ClearFlags: 1
+  m_BackGroundColor: {r: 0, g: 0, b: 0, a: 0}
+  m_projectionMatrixMode: 1
+  m_GateFitMode: 2
+  m_FOVAxisMode: 0
+  m_SensorSize: {x: 36, y: 24}
+  m_LensShift: {x: 0, y: 0}
+  m_FocalLength: 50
+  m_NormalizedViewPortRect:
+    serializedVersion: 2
+    x: 0
+    y: 0
+    width: 1
+    height: 1
+  near clip plane: 0.1
+  far clip plane: 1000
+  field of view: 60
+  orthographic: 0
+  orthographic size: 5
+  m_Depth: -1
+  m_CullingMask:
+    serializedVersion: 2
+    m_Bits: 4294967295
+  m_RenderingPath: -1
+  m_TargetTexture: {fileID: 0}
+  m_TargetDisplay: 0
+  m_TargetEye: 3
+  m_HDR: 1
+  m_AllowMSAA: 1
+  m_AllowDynamicResolution: 0
+  m_ForceIntoRT: 0
+  m_OcclusionCulling: 1
+  m_StereoConvergence: 10
+  m_StereoSeparation: 0.022
+--- !u!4 &963194228
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 963194225}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 2043905275}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &963194229
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 963194225}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 5a2a9c34df4095f47b9ca8f975175f5b, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+  m_Device: 0
+  m_PoseSource: 2
+  m_PoseProviderComponent: {fileID: 0}
+  m_TrackingType: 0
+  m_UpdateType: 0
+  m_UseRelativeTransform: 0
+--- !u!114 &963194230
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 963194225}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 4e93961d3dfb44505a84d80b1f7fffef, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+  _mode: 0
+  _coordinateSpace: 0
+  _position: {x: 0, y: 1.5, z: 6}
+  _rotation: {x: 0, y: 180, z: 0}
+--- !u!850595691 &1120315176
+LightingSettings:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: Settings.lighting
+  serializedVersion: 3
+  m_GIWorkflowMode: 1
+  m_EnableBakedLightmaps: 0
+  m_EnableRealtimeLightmaps: 0
+  m_RealtimeEnvironmentLighting: 1
+  m_BounceScale: 1
+  m_AlbedoBoost: 1
+  m_IndirectOutputScale: 1
+  m_UsingShadowmask: 1
+  m_BakeBackend: 1
+  m_LightmapMaxSize: 1024
+  m_BakeResolution: 40
+  m_Padding: 2
+  m_TextureCompression: 1
+  m_AO: 0
+  m_AOMaxDistance: 1
+  m_CompAOExponent: 1
+  m_CompAOExponentDirect: 0
+  m_ExtractAO: 0
+  m_MixedBakeMode: 2
+  m_LightmapsBakeMode: 1
+  m_FilterMode: 1
+  m_LightmapParameters: {fileID: 15204, guid: 0000000000000000f000000000000000, type: 0}
+  m_ExportTrainingData: 0
+  m_TrainingDataDestination: TrainingData
+  m_RealtimeResolution: 2
+  m_ForceWhiteAlbedo: 0
+  m_ForceUpdates: 0
+  m_FinalGather: 0
+  m_FinalGatherRayCount: 256
+  m_FinalGatherFiltering: 1
+  m_PVRCulling: 1
+  m_PVRSampling: 1
+  m_PVRDirectSampleCount: 32
+  m_PVRSampleCount: 500
+  m_PVREnvironmentSampleCount: 500
+  m_PVREnvironmentReferencePointCount: 2048
+  m_LightProbeSampleCountMultiplier: 4
+  m_PVRBounces: 2
+  m_PVRMinBounces: 2
+  m_PVREnvironmentMIS: 0
+  m_PVRFilteringMode: 2
+  m_PVRDenoiserTypeDirect: 0
+  m_PVRDenoiserTypeIndirect: 0
+  m_PVRDenoiserTypeAO: 0
+  m_PVRFilterTypeDirect: 0
+  m_PVRFilterTypeIndirect: 0
+  m_PVRFilterTypeAO: 0
+  m_PVRFilteringGaussRadiusDirect: 1
+  m_PVRFilteringGaussRadiusIndirect: 5
+  m_PVRFilteringGaussRadiusAO: 2
+  m_PVRFilteringAtrousPositionSigmaDirect: 0.5
+  m_PVRFilteringAtrousPositionSigmaIndirect: 2
+  m_PVRFilteringAtrousPositionSigmaAO: 1
+--- !u!1 &1640224947 stripped
+GameObject:
+  m_CorrespondingSourceObject: {fileID: 5848076533691757917, guid: d0377a811c2a95841ba015ae0b2c8d45,
+    type: 3}
+  m_PrefabInstance: {fileID: 2104555916}
+  m_PrefabAsset: {fileID: 0}
+--- !u!114 &1640224948
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1640224947}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 27d9ca4252a965544a4613f39f18d361, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+--- !u!114 &1640224949
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1640224947}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 852db897867e8a0418e5c20c10cb0cb2, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+--- !u!1 &1735107478
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1735107480}
+  - component: {fileID: 1735107479}
+  m_Layer: 0
+  m_Name: OpenAvatarEditor
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!114 &1735107479
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1735107478}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: df522fdce5f894b4f986a3489491b7de, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+--- !u!4 &1735107480
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1735107478}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 4.270977, y: 1.2448722, z: -4.486235}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 0}
+  m_RootOrder: 5
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &2043905274
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 2043905275}
+  - component: {fileID: 2043905277}
+  - component: {fileID: 2043905276}
+  - component: {fileID: 2043905278}
+  m_Layer: 0
+  m_Name: Avatar - Zip Combined Model
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &2043905275
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2043905274}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 1}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 963194228}
+  m_Father: {fileID: 625919963}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &2043905276
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2043905274}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 202d697196ae07b499aba5134d698aff, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+--- !u!114 &2043905277
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2043905274}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+  _creationInfo:
+    features: 382
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+      subMeshInclusionFlags: 2047
+  _activeView: 1
+  _activeManifestation: 2
+  _activeSubMeshes: 2047
+  _activeSubMeshesIncludeUntyped: 1
+  _bodyTracking: {fileID: 2077391344}
+  _facePoseBehavior: {fileID: 1640224949}
+  _eyePoseBehavior: {fileID: 1640224948}
+  _lipSync: {fileID: 307099418}
+  _criticalJointTypes: 0f0000001d00000031000000
+  _useExperimentalFeatures: 0
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  OnCreatedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnSkeletonLoadedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnDefaultAvatarLoadedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnFastLoadAvatarLoadedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnUserAvatarLoadedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  PreTeardownEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnLoadFailedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 3
+  MotionSmoothingSettings: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _highQuality: 0
+  _loadUserFromCdn: 1
+  _deferLoading: 0
+  _assets:
+  - source: 0
+    path: 0
+  _underscorePostfix: 1
+  _overridePostfix:
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!114 &2043905278
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2043905274}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 5b0eaccd645fc5641ba1cf708f6fd678, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+  movementSpeed: 1
+--- !u!114 &2077391344 stripped
+MonoBehaviour:
+  m_CorrespondingSourceObject: {fileID: 3865823349605503150, guid: d0377a811c2a95841ba015ae0b2c8d45,
+    type: 3}
+  m_PrefabInstance: {fileID: 2104555916}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1640224947}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 6937544d08e2ba34da6ff4ab90cbfd84, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+--- !u!1001 &2104555916
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 0}
+    m_Modifications:
+    - target: {fileID: 5848076533691757917, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_Name
+      value: AvatarSdkManagerHorizon
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757918, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: UseFastLoadAvatar
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 7
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 8687292189353299701, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: SourceMorphFormat
+      value: 3
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: d0377a811c2a95841ba015ae0b2c8d45, type: 3}
+--- !u!1001 &2430523750576325525
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 0}
+    m_Modifications:
+    - target: {fileID: 2430523750807860354, guid: c019572605bc29e42b3ed5a058599618,
+        type: 3}
+      propertyPath: m_Name
+      value: LipSyncInput
+      objectReference: {fileID: 0}
+    - target: {fileID: 2430523750807860366, guid: c019572605bc29e42b3ed5a058599618,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 4
+      objectReference: {fileID: 0}
+    - target: {fileID: 2430523750807860366, guid: c019572605bc29e42b3ed5a058599618,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2430523750807860366, guid: c019572605bc29e42b3ed5a058599618,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2430523750807860366, guid: c019572605bc29e42b3ed5a058599618,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2430523750807860366, guid: c019572605bc29e42b3ed5a058599618,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 2430523750807860366, guid: c019572605bc29e42b3ed5a058599618,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2430523750807860366, guid: c019572605bc29e42b3ed5a058599618,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2430523750807860366, guid: c019572605bc29e42b3ed5a058599618,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2430523750807860366, guid: c019572605bc29e42b3ed5a058599618,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2430523750807860366, guid: c019572605bc29e42b3ed5a058599618,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2430523750807860366, guid: c019572605bc29e42b3ed5a058599618,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: c019572605bc29e42b3ed5a058599618, type: 3}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/MirrorScene/MirrorScene.unity.meta b/Assets/Oculus/Avatar2/Example/Scenes/MirrorScene/MirrorScene.unity.meta
new file mode 100644
index 0000000000000000000000000000000000000000..57d1f908554aae3dd2b344a85ddd17395b9b3432
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/MirrorScene/MirrorScene.unity.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 9f2e1693a4f24bc498477b3dfeb166aa
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/MirrorScene/MirrorScene/LightingData.asset b/Assets/Oculus/Avatar2/Example/Scenes/MirrorScene/MirrorScene/LightingData.asset
new file mode 100644
index 0000000000000000000000000000000000000000..824f11a2f860a025fe3558b64b83b1a5d8922543
Binary files /dev/null and b/Assets/Oculus/Avatar2/Example/Scenes/MirrorScene/MirrorScene/LightingData.asset differ
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/MirrorScene/MirrorScene/LightingData.asset.meta b/Assets/Oculus/Avatar2/Example/Scenes/MirrorScene/MirrorScene/LightingData.asset.meta
new file mode 100644
index 0000000000000000000000000000000000000000..df97cd4a747843033c1bc3c5075dc51734da1ce8
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/MirrorScene/MirrorScene/LightingData.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: f0c68c730be06da4e8d2e671bffde0dc
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 112000000
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/MirrorScene/MirrorScene/ReflectionProbe-0.exr b/Assets/Oculus/Avatar2/Example/Scenes/MirrorScene/MirrorScene/ReflectionProbe-0.exr
new file mode 100644
index 0000000000000000000000000000000000000000..757be420fa2166719fbc077cfc87101169407937
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/MirrorScene/MirrorScene/ReflectionProbe-0.exr
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:cc1312c910a387bc988e0cba4bd009396110d1f56fa5cbd8b03e147a3c401c1e
+size 115295
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/MirrorScene/MirrorScene/ReflectionProbe-0.exr.meta b/Assets/Oculus/Avatar2/Example/Scenes/MirrorScene/MirrorScene/ReflectionProbe-0.exr.meta
new file mode 100644
index 0000000000000000000000000000000000000000..557f9f0cb9bb4927e6776301d630be6b3200ea0b
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/MirrorScene/MirrorScene/ReflectionProbe-0.exr.meta
@@ -0,0 +1,89 @@
+fileFormatVersion: 2
+guid: 238c7c9ffebf55c4fa01a8cba9de8707
+TextureImporter:
+  fileIDToRecycleName:
+    8900000: generatedCubemap
+  externalObjects: {}
+  serializedVersion: 9
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 1
+    sRGBTexture: 1
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 0
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 1
+  seamlessCubemap: 1
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: 2
+    aniso: 0
+    mipBias: 0
+    wrapU: 1
+    wrapV: 1
+    wrapW: 1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 1
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 0
+  textureShape: 2
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  platformSettings:
+  - serializedVersion: 2
+    buildTarget: DefaultTexturePlatform
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 1
+    compressionQuality: 100
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/NetworkLoopbackExample.meta b/Assets/Oculus/Avatar2/Example/Scenes/NetworkLoopbackExample.meta
new file mode 100644
index 0000000000000000000000000000000000000000..a82c52c69ee87bc4fcd2ed5dded36068330409fc
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/NetworkLoopbackExample.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 5e631161f2008774c9b19c9c0463a35a
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/NetworkLoopbackExample/NetworkLoopbackExample.unity b/Assets/Oculus/Avatar2/Example/Scenes/NetworkLoopbackExample/NetworkLoopbackExample.unity
new file mode 100644
index 0000000000000000000000000000000000000000..7d12425b380708f07b568f19fd0be87591e35960
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/NetworkLoopbackExample/NetworkLoopbackExample.unity
@@ -0,0 +1,950 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!29 &1
+OcclusionCullingSettings:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_OcclusionBakeSettings:
+    smallestOccluder: 5
+    smallestHole: 0.25
+    backfaceThreshold: 100
+  m_SceneGUID: 00000000000000000000000000000000
+  m_OcclusionCullingData: {fileID: 0}
+--- !u!104 &2
+RenderSettings:
+  m_ObjectHideFlags: 0
+  serializedVersion: 9
+  m_Fog: 0
+  m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
+  m_FogMode: 3
+  m_FogDensity: 0.01
+  m_LinearFogStart: 0
+  m_LinearFogEnd: 300
+  m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
+  m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
+  m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
+  m_AmbientIntensity: 1
+  m_AmbientMode: 0
+  m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
+  m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0}
+  m_HaloStrength: 0.5
+  m_FlareStrength: 1
+  m_FlareFadeSpeed: 3
+  m_HaloTexture: {fileID: 0}
+  m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
+  m_DefaultReflectionMode: 0
+  m_DefaultReflectionResolution: 128
+  m_ReflectionBounces: 1
+  m_ReflectionIntensity: 1
+  m_CustomReflection: {fileID: 0}
+  m_Sun: {fileID: 705507994}
+  m_IndirectSpecularColor: {r: 0.17232697, g: 0.17143948, b: 0.16943966, a: 1}
+  m_UseRadianceAmbientProbe: 0
+--- !u!157 &3
+LightmapSettings:
+  m_ObjectHideFlags: 0
+  serializedVersion: 11
+  m_GIWorkflowMode: 0
+  m_GISettings:
+    serializedVersion: 2
+    m_BounceScale: 1
+    m_IndirectOutputScale: 1
+    m_AlbedoBoost: 1
+    m_EnvironmentLightingMode: 0
+    m_EnableBakedLightmaps: 1
+    m_EnableRealtimeLightmaps: 0
+  m_LightmapEditorSettings:
+    serializedVersion: 12
+    m_Resolution: 2
+    m_BakeResolution: 40
+    m_AtlasSize: 1024
+    m_AO: 0
+    m_AOMaxDistance: 1
+    m_CompAOExponent: 1
+    m_CompAOExponentDirect: 0
+    m_ExtractAmbientOcclusion: 0
+    m_Padding: 2
+    m_LightmapParameters: {fileID: 0}
+    m_LightmapsBakeMode: 1
+    m_TextureCompression: 1
+    m_FinalGather: 0
+    m_FinalGatherFiltering: 1
+    m_FinalGatherRayCount: 256
+    m_ReflectionCompression: 2
+    m_MixedBakeMode: 2
+    m_BakeBackend: 1
+    m_PVRSampling: 1
+    m_PVRDirectSampleCount: 32
+    m_PVRSampleCount: 500
+    m_PVRBounces: 2
+    m_PVREnvironmentSampleCount: 500
+    m_PVREnvironmentReferencePointCount: 2048
+    m_PVRFilteringMode: 2
+    m_PVRDenoiserTypeDirect: 0
+    m_PVRDenoiserTypeIndirect: 0
+    m_PVRDenoiserTypeAO: 0
+    m_PVRFilterTypeDirect: 0
+    m_PVRFilterTypeIndirect: 0
+    m_PVRFilterTypeAO: 0
+    m_PVREnvironmentMIS: 0
+    m_PVRCulling: 1
+    m_PVRFilteringGaussRadiusDirect: 1
+    m_PVRFilteringGaussRadiusIndirect: 5
+    m_PVRFilteringGaussRadiusAO: 2
+    m_PVRFilteringAtrousPositionSigmaDirect: 0.5
+    m_PVRFilteringAtrousPositionSigmaIndirect: 2
+    m_PVRFilteringAtrousPositionSigmaAO: 1
+    m_ExportTrainingData: 0
+    m_TrainingDataDestination: TrainingData
+    m_LightProbeSampleCountMultiplier: 4
+  m_LightingDataAsset: {fileID: 0}
+  m_UseShadowmask: 1
+--- !u!196 &4
+NavMeshSettings:
+  serializedVersion: 2
+  m_ObjectHideFlags: 0
+  m_BuildSettings:
+    serializedVersion: 2
+    agentTypeID: 0
+    agentRadius: 0.5
+    agentHeight: 2
+    agentSlope: 45
+    agentClimb: 0.4
+    ledgeDropHeight: 0
+    maxJumpAcrossDistance: 0
+    minRegionArea: 2
+    manualCellSize: 0
+    cellSize: 0.16666667
+    manualTileSize: 0
+    tileSize: 256
+    accuratePlacement: 0
+    debug:
+      m_Flags: 0
+  m_NavMeshData: {fileID: 0}
+--- !u!1001 &81415626
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 0}
+    m_Modifications:
+    - target: {fileID: 2430523750807860354, guid: c019572605bc29e42b3ed5a058599618,
+        type: 3}
+      propertyPath: m_Name
+      value: LipSyncInput
+      objectReference: {fileID: 0}
+    - target: {fileID: 2430523750807860366, guid: c019572605bc29e42b3ed5a058599618,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 5
+      objectReference: {fileID: 0}
+    - target: {fileID: 2430523750807860366, guid: c019572605bc29e42b3ed5a058599618,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2430523750807860366, guid: c019572605bc29e42b3ed5a058599618,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2430523750807860366, guid: c019572605bc29e42b3ed5a058599618,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2430523750807860366, guid: c019572605bc29e42b3ed5a058599618,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 2430523750807860366, guid: c019572605bc29e42b3ed5a058599618,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2430523750807860366, guid: c019572605bc29e42b3ed5a058599618,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2430523750807860366, guid: c019572605bc29e42b3ed5a058599618,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2430523750807860366, guid: c019572605bc29e42b3ed5a058599618,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2430523750807860366, guid: c019572605bc29e42b3ed5a058599618,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2430523750807860366, guid: c019572605bc29e42b3ed5a058599618,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: c019572605bc29e42b3ed5a058599618, type: 3}
+--- !u!114 &81415627 stripped
+MonoBehaviour:
+  m_CorrespondingSourceObject: {fileID: 2430523750807860367, guid: c019572605bc29e42b3ed5a058599618,
+    type: 3}
+  m_PrefabInstance: {fileID: 81415626}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 0}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: a509dbf839e0e4044a7affab5b5a3e55, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+--- !u!1 &332462890
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 332462894}
+  - component: {fileID: 332462893}
+  - component: {fileID: 332462892}
+  - component: {fileID: 332462891}
+  m_Layer: 0
+  m_Name: Base
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!136 &332462891
+CapsuleCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 332462890}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  m_Radius: 0.5000001
+  m_Height: 2
+  m_Direction: 1
+  m_Center: {x: 0.000000059604645, y: 0, z: -0.00000008940697}
+--- !u!23 &332462892
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 332462890}
+  m_Enabled: 0
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RayTracingMode: 2
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: 6500e5e25ff64254aa97291b203a9902, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_ReceiveGI: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!33 &332462893
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 332462890}
+  m_Mesh: {fileID: 10206, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!4 &332462894
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 332462890}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 10, y: 0.1, z: 10}
+  m_Children: []
+  m_Father: {fileID: 838494930}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &374415785
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 374415787}
+  - component: {fileID: 374415786}
+  m_Layer: 0
+  m_Name: NetworkLoopbackManager
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!114 &374415786
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 374415785}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 8762631024b24aa48978db41c7ea26fb, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+  _localAvatar: {fileID: 2043905277}
+  _loopbackAvatars:
+  - {fileID: 1569438323}
+  _simulatedLatencySettings:
+    fakeLatencyMax: 0.1
+    fakeLatencyMin: 0.02
+    latencyWeight: 0.25
+    maxSamples: 4
+--- !u!4 &374415787
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 374415785}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 40.13266, y: -13.256848, z: 9.051752}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 0}
+  m_RootOrder: 4
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &625919962
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 625919963}
+  m_Layer: 0
+  m_Name: Entities
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &625919963
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 625919962}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 1}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 2043905275}
+  - {fileID: 1569438322}
+  m_Father: {fileID: 0}
+  m_RootOrder: 3
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &664668818
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 664668820}
+  - component: {fileID: 664668819}
+  m_Layer: 0
+  m_Name: SceneSwitcher
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!114 &664668819
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 664668818}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: ea6e0c6cd67922c48bbb5939267828e9, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+  _nextSceneInput:
+    controllerMask: 2
+    buttonMask: 1
+  _prevSceneInput:
+    controllerMask: 1
+    buttonMask: 1
+--- !u!4 &664668820
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 664668818}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 0}
+  m_RootOrder: 6
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &705507993
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 705507995}
+  - component: {fileID: 705507994}
+  m_Layer: 0
+  m_Name: Directional Light
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!108 &705507994
+Light:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 705507993}
+  m_Enabled: 1
+  serializedVersion: 10
+  m_Type: 1
+  m_Shape: 0
+  m_Color: {r: 1, g: 1, b: 1, a: 1}
+  m_Intensity: 1
+  m_Range: 10
+  m_SpotAngle: 30
+  m_InnerSpotAngle: 21.80208
+  m_CookieSize: 10
+  m_Shadows:
+    m_Type: 2
+    m_Resolution: -1
+    m_CustomResolution: -1
+    m_Strength: 1
+    m_Bias: 0.05
+    m_NormalBias: 0.4
+    m_NearPlane: 0.2
+    m_CullingMatrixOverride:
+      e00: 1
+      e01: 0
+      e02: 0
+      e03: 0
+      e10: 0
+      e11: 1
+      e12: 0
+      e13: 0
+      e20: 0
+      e21: 0
+      e22: 1
+      e23: 0
+      e30: 0
+      e31: 0
+      e32: 0
+      e33: 1
+    m_UseCullingMatrixOverride: 0
+  m_Cookie: {fileID: 0}
+  m_DrawHalo: 0
+  m_Flare: {fileID: 0}
+  m_RenderMode: 0
+  m_CullingMask:
+    serializedVersion: 2
+    m_Bits: 4294967295
+  m_RenderingLayerMask: 1
+  m_Lightmapping: 1
+  m_LightShadowCasterMode: 0
+  m_AreaSize: {x: 1, y: 1}
+  m_BounceIntensity: 1
+  m_ColorTemperature: 6570
+  m_UseColorTemperature: 0
+  m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
+  m_UseBoundingSphereOverride: 0
+  m_UseViewFrustumForShadowCasterCull: 1
+  m_ShadowRadius: 0
+  m_ShadowAngle: 0
+--- !u!4 &705507995
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 705507993}
+  m_LocalRotation: {x: 0.77918, y: -0.15470326, z: -0.37738466, w: 0.47594765}
+  m_LocalPosition: {x: 0, y: 3, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 0}
+  m_RootOrder: 1
+  m_LocalEulerAnglesHint: {x: 38.677002, y: -109.618004, z: -129.73901}
+--- !u!1 &838494929
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 838494930}
+  m_Layer: 0
+  m_Name: Environment
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &838494930
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 838494929}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 332462894}
+  m_Father: {fileID: 0}
+  m_RootOrder: 2
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &963194225
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 963194228}
+  - component: {fileID: 963194227}
+  - component: {fileID: 963194226}
+  - component: {fileID: 963194229}
+  - component: {fileID: 963194230}
+  m_Layer: 0
+  m_Name: Main Camera
+  m_TagString: MainCamera
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!81 &963194226
+AudioListener:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 963194225}
+  m_Enabled: 1
+--- !u!20 &963194227
+Camera:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 963194225}
+  m_Enabled: 1
+  serializedVersion: 2
+  m_ClearFlags: 2
+  m_BackGroundColor: {r: 0, g: 0, b: 0, a: 0}
+  m_projectionMatrixMode: 1
+  m_GateFitMode: 2
+  m_FOVAxisMode: 0
+  m_SensorSize: {x: 36, y: 24}
+  m_LensShift: {x: 0, y: 0}
+  m_FocalLength: 50
+  m_NormalizedViewPortRect:
+    serializedVersion: 2
+    x: 0
+    y: 0
+    width: 1
+    height: 1
+  near clip plane: 0.1
+  far clip plane: 1000
+  field of view: 60
+  orthographic: 0
+  orthographic size: 5
+  m_Depth: -1
+  m_CullingMask:
+    serializedVersion: 2
+    m_Bits: 4294967295
+  m_RenderingPath: -1
+  m_TargetTexture: {fileID: 0}
+  m_TargetDisplay: 0
+  m_TargetEye: 3
+  m_HDR: 1
+  m_AllowMSAA: 1
+  m_AllowDynamicResolution: 0
+  m_ForceIntoRT: 0
+  m_OcclusionCulling: 1
+  m_StereoConvergence: 10
+  m_StereoSeparation: 0.022
+--- !u!4 &963194228
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 963194225}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 2043905275}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &963194229
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 963194225}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 5a2a9c34df4095f47b9ca8f975175f5b, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+  m_Device: 0
+  m_PoseSource: 2
+  m_PoseProviderComponent: {fileID: 0}
+  m_TrackingType: 0
+  m_UpdateType: 0
+  m_UseRelativeTransform: 0
+--- !u!114 &963194230
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 963194225}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 4e93961d3dfb44505a84d80b1f7fffef, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+  _mode: 0
+  _coordinateSpace: 0
+  _position: {x: 0, y: 1.5, z: 3}
+  _rotation: {x: 0, y: 180, z: 0}
+--- !u!1 &1569438321
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1569438322}
+  - component: {fileID: 1569438323}
+  - component: {fileID: 1569438324}
+  m_Layer: 0
+  m_Name: RemoteLoopbackAvatar
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1569438322
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1569438321}
+  m_LocalRotation: {x: 0, y: 1, z: 0, w: 0}
+  m_LocalPosition: {x: 0, y: 0, z: 1}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 625919963}
+  m_RootOrder: 1
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!114 &1569438323
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1569438321}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+  _creationInfo:
+    features: 38
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+  _activeView: 2
+  _activeManifestation: 2
+  _bodyTracking: {fileID: 0}
+  _lipSync: {fileID: 0}
+  _criticalJointTypes: 0f000000310000001d000000
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  _useAdvancedLodSystem: 1
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 0
+  _loadUserFromCdn: 0
+  _assetSource: 0
+  _assetPaths:
+  - 19_
+  _assets:
+  - source: 0
+    path: 19
+  _assetPostfixDefault: _rift.glb
+  _assetPostfixAndroid: _quest.glb
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!114 &1569438324
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1569438321}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 202d697196ae07b499aba5134d698aff, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+--- !u!1001 &1899488020
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 0}
+    m_Modifications:
+    - target: {fileID: 5848076533691757917, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_Name
+      value: AvatarSdkManagerHorizon
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: d0377a811c2a95841ba015ae0b2c8d45, type: 3}
+--- !u!114 &1899488021 stripped
+MonoBehaviour:
+  m_CorrespondingSourceObject: {fileID: 3865823349605503150, guid: d0377a811c2a95841ba015ae0b2c8d45,
+    type: 3}
+  m_PrefabInstance: {fileID: 1899488020}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 0}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 6937544d08e2ba34da6ff4ab90cbfd84, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+--- !u!1 &2043905274
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 2043905275}
+  - component: {fileID: 2043905277}
+  m_Layer: 0
+  m_Name: LocalAvatar
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &2043905275
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2043905274}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 963194228}
+  m_Father: {fileID: 625919963}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &2043905277
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2043905274}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+  _creationInfo:
+    features: 374
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+  _activeView: 1
+  _activeManifestation: 2
+  _bodyTracking: {fileID: 1899488021}
+  _lipSync: {fileID: 81415627}
+  _criticalJointTypes:
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  _useAdvancedLodSystem: 1
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _loadUserFromCdn: 0
+  _assetSource: 0
+  _assetPaths:
+  - 19_
+  _assets:
+  - source: 0
+    path: 19
+  _assetPostfixDefault: _rift.glb
+  _assetPostfixAndroid: _quest.glb
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/NetworkLoopbackExample/NetworkLoopbackExample.unity.meta b/Assets/Oculus/Avatar2/Example/Scenes/NetworkLoopbackExample/NetworkLoopbackExample.unity.meta
new file mode 100644
index 0000000000000000000000000000000000000000..3c7d4767ac73d5ede65e993860449bff3f5d9e3a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/NetworkLoopbackExample/NetworkLoopbackExample.unity.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 0b80808d555493f42937ca45e5970ce7
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/NetworkLoopbackExample/SampleRemoteLoopbackManager.cs b/Assets/Oculus/Avatar2/Example/Scenes/NetworkLoopbackExample/SampleRemoteLoopbackManager.cs
new file mode 100644
index 0000000000000000000000000000000000000000..803523fe72828c5b6ec13d46a51efc8fa6369745
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/NetworkLoopbackExample/SampleRemoteLoopbackManager.cs
@@ -0,0 +1,397 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+
+using Oculus.Avatar2;
+
+using Unity.Collections;
+
+using UnityEngine;
+
+using StreamLOD = Oculus.Avatar2.OvrAvatarEntity.StreamLOD;
+
+
+/* This class is an example of how to use the Streaming functions of the avatar to send and receive data over the network
+ * For this example, data isn't sent over a real network, but simply added to a queue and then "received" by a second, "remote" avatar.
+ * For a real network, much of the logic of preparing snapshots and receiving based on the desired fidelity is the same
+ */
+public class SampleRemoteLoopbackManager : MonoBehaviour
+{
+    private const string logScope = "SampleRemoteLoopbackManager";
+
+    // Const & Static Variables
+    private const float PLAYBACK_SMOOTH_FACTOR = 0.25f;
+    private const int MAX_PACKETS_PER_FRAME = 3;
+
+    private static readonly float[] StreamLodSnapshotIntervalSeconds = new float[OvrAvatarEntity.StreamLODCount] { 1f / 72, 2f / 72, 3f / 72, 4f / 72 };
+
+    // Public functions
+
+    // Configure the local and loopback avatars programmatically instead of from serialized fields. Must be called
+    // immediately after adding the component
+    public void Configure(OvrAvatarEntity localAvatar, List<OvrAvatarEntity> loopbackAvatars, SimulatedLatencySettings latencySettings = null)
+    {
+        _localAvatar = localAvatar;
+        _loopbackAvatars = loopbackAvatars;
+        if (latencySettings != null)
+        {
+            _simulatedLatencySettings = latencySettings;
+        }
+    }
+
+    #region Internal Classes
+
+    class PacketData : IDisposable
+    {
+        public NativeArray<byte> data;
+        public StreamLOD lod;
+        public float fakeLatency;
+        public UInt32 dataByteCount;
+
+        private uint refCount = 0;
+
+        public PacketData() { }
+
+        ~PacketData()
+        {
+            Dispose(false);
+        }
+
+        public void Dispose()
+        {
+            Dispose(true);
+            GC.SuppressFinalize(this);
+        }
+
+        private void Dispose(bool disposing)
+        {
+            if (data.IsCreated)
+            {
+                data.Dispose();
+            }
+            data = default;
+        }
+
+        public bool Unretained => refCount == 0;
+        public PacketData Retain() { ++refCount; return this; }
+        public bool Release()
+        {
+            return --refCount == 0;
+        }
+    };
+
+    class LoopbackState
+    {
+        public List<PacketData> packetQueue = new List<PacketData>(64);
+        public StreamLOD requestedLod = StreamLOD.Low;
+        public float smoothedPlaybackDelay = 0f;
+    };
+
+    [System.Serializable]
+    public class SimulatedLatencySettings
+    {
+        [Range(0.0f, 0.5f)]
+        public float fakeLatencyMax = 0.25f; //250 ms max latency
+
+        [Range(0.0f, 0.5f)]
+        public float fakeLatencyMin = 0.02f; //20ms min latency
+
+        [Range(0.0f, 1.0f)]
+        public float latencyWeight = 0.25f; // How much the latest sample impacts the current latency
+
+        [Range(0, 10)]
+        public int maxSamples = 4; //How many samples in our window
+
+        internal float averageWindow = 0f;
+        internal float latencySum = 0f;
+        internal List<float> latencyValues = new List<float>();
+
+        public float NextValue()
+        {
+            averageWindow = latencySum / (float)latencyValues.Count;
+            float randomLatency = UnityEngine.Random.Range(fakeLatencyMin, fakeLatencyMax);
+            float fakeLatency = averageWindow * (1f - latencyWeight) + latencyWeight * randomLatency;
+
+            if (latencyValues.Count >= maxSamples)
+            {
+                latencySum -= latencyValues.First().Value;
+                latencyValues.RemoveFirst();
+            }
+
+            latencySum += fakeLatency;
+            latencyValues.AddLast(fakeLatency);
+
+            return fakeLatency;
+        }
+    };
+
+    #endregion
+
+    // Serialized Variables
+    [SerializeField]
+    private OvrAvatarEntity _localAvatar = null;
+    [SerializeField]
+    private List<OvrAvatarEntity> _loopbackAvatars = null;
+    [SerializeField]
+    private SimulatedLatencySettings _simulatedLatencySettings = new SimulatedLatencySettings();
+
+    // Private Variables
+    private Dictionary<OvrAvatarEntity, LoopbackState> _loopbackStates =
+        new Dictionary<OvrAvatarEntity, LoopbackState>();
+
+    private readonly List<PacketData> _packetPool = new List<PacketData>(32);
+    private readonly List<PacketData> _deadList = new List<PacketData>(16);
+
+    private PacketData GetPacketForEntityAtLOD(OvrAvatarEntity entity, StreamLOD lod)
+    {
+        PacketData packet;
+        int poolCount = _packetPool.Count;
+        if (poolCount > 0)
+        {
+            var lastIdx = poolCount - 1;
+            packet = _packetPool[lastIdx];
+            _packetPool.RemoveAt(lastIdx);
+        }
+        else
+        {
+            packet = new PacketData();
+        }
+
+        packet.lod = lod;
+        return packet.Retain();
+    }
+    private void ReturnPacket(PacketData packet)
+    {
+        Debug.Assert(packet.Unretained);
+        _packetPool.Add(packet);
+    }
+
+    private readonly float[] _streamLodSnapshotElapsedTime = new float[OvrAvatarEntity.StreamLODCount];
+
+    byte[] _packetBuffer = new byte[16 * 1024];
+    GCHandle _pinnedBuffer;
+
+    public List<OvrAvatarEntity> LoopbackAvatars
+    {
+        get
+        {
+            return _loopbackAvatars;
+        }
+
+        set
+        {
+            _loopbackAvatars = value;
+            CreateStates();
+        }
+    }
+
+    #region Core Unity Functions
+
+    protected void Start()
+    {
+        // Check for other LoopbackManagers in the current scene
+        var loopbackManagers = FindObjectsOfType<SampleRemoteLoopbackManager>();
+        if (loopbackManagers.Length > 1)
+        {
+            foreach (var loopbackManager in loopbackManagers)
+            {
+                if (loopbackManager == this || !loopbackManager.isActiveAndEnabled) { continue; }
+
+                OvrAvatarLog.LogError($"Multiple active LoopbackManagers detected! Please update the scene."
+                    , logScope, this);
+                break;
+            }
+        }
+
+        // assume _useAdvancedLodSystem is enabled
+        AvatarLODManager.Instance.firstPersonAvatarLod = _localAvatar.AvatarLOD;
+        AvatarLODManager.Instance.enableDynamicStreaming = true;
+
+        float firstValue = UnityEngine.Random.Range(_simulatedLatencySettings.fakeLatencyMin, _simulatedLatencySettings.fakeLatencyMax);
+        _simulatedLatencySettings.latencyValues.Insert(0, firstValue);
+        _simulatedLatencySettings.latencySum += firstValue;
+
+        _pinnedBuffer = GCHandle.Alloc(_packetBuffer, GCHandleType.Pinned);
+
+        CreateStates();
+    }
+
+    private void CreateStates()
+    {
+        foreach (var item in _loopbackStates)
+        {
+            foreach (var packet in item.Value.packetQueue)
+            {
+                if (packet.Release())
+                {
+                    ReturnPacket(packet);
+                }
+            }
+        }
+        _loopbackStates.Clear();
+
+        foreach (var loopbackAvatar in _loopbackAvatars)
+        {
+            _loopbackStates.Add(loopbackAvatar, new LoopbackState());
+        }
+    }
+
+    private void OnDestroy()
+    {
+        if (_pinnedBuffer.IsAllocated)
+        {
+            _pinnedBuffer.Free();
+        }
+
+        foreach (var item in _loopbackStates)
+        {
+            foreach (var packet in item.Value.packetQueue)
+            {
+                if (packet.Release())
+                {
+                    ReturnPacket(packet);
+                }
+            }
+        }
+
+        foreach (var packet in _packetPool)
+        {
+            packet.Dispose();
+        }
+        _packetPool.Clear();
+    }
+
+    private void Update()
+    {
+        for (int i = 0; i < OvrAvatarEntity.StreamLODCount; ++i)
+        {
+            // Assume remote Avatar StreamLOD sizes are the same
+            float streamBytesPerSecond = _localAvatar.GetLastByteSizeForLodIndex(i) / StreamLodSnapshotIntervalSeconds[i];
+            AvatarLODManager.Instance.dynamicStreamLodBitsPerSecond[i] = (long)(streamBytesPerSecond * 8);
+        }
+
+        foreach (var item in _loopbackStates)
+        {
+            var loopbackAvatar = item.Key;
+            var loopbackState = item.Value;
+
+            if (!loopbackAvatar.IsCreated)
+            {
+                continue;
+            }
+
+            UpdatePlaybackTimeDelay(loopbackAvatar, loopbackState);
+
+            // "Remote" avatar receives incoming data and applies if it is the correct lod
+            if (loopbackState.packetQueue.Count > 0)
+            {
+                foreach (var packet in loopbackState.packetQueue)
+                {
+                    packet.fakeLatency -= Time.deltaTime;
+
+                    if (packet.fakeLatency <= 0f)
+                    {
+                        var dataSlice = packet.data.Slice(0, (int)packet.dataByteCount);
+                        ReceivePacketData(loopbackAvatar, in dataSlice, packet.lod);
+                        _deadList.Add(packet);
+                    }
+                }
+
+                foreach (var packet in _deadList)
+                {
+                    loopbackState.packetQueue.Remove(packet);
+                    if (packet.Release())
+                    {
+                        ReturnPacket(packet);
+                    }
+                }
+                _deadList.Clear();
+            }
+
+            // "Send" the lod that "remote" avatar wants to use back over the network
+            // TODO delay this reception for an accurate test
+            loopbackState.requestedLod = loopbackAvatar.activeStreamLod;
+        }
+    }
+
+    private void LateUpdate()
+    {
+        // Local avatar has fully updated this frame and can send data to the network
+        SendSnapshot();
+    }
+
+    #endregion
+
+    #region Local Avatar
+
+    private void SendSnapshot()
+    {
+        if (!_localAvatar.HasJoints) { return; }
+
+        for (int streamLod = (int)StreamLOD.High; streamLod <= (int)StreamLOD.Low; ++streamLod)
+        {
+            int packetsSentThisFrame = 0;
+            _streamLodSnapshotElapsedTime[streamLod] += Time.unscaledDeltaTime;
+            while (_streamLodSnapshotElapsedTime[streamLod] > StreamLodSnapshotIntervalSeconds[streamLod])
+            {
+                SendPacket((StreamLOD)streamLod);
+                _streamLodSnapshotElapsedTime[streamLod] -= StreamLodSnapshotIntervalSeconds[streamLod];
+                if (++packetsSentThisFrame >= MAX_PACKETS_PER_FRAME)
+                {
+                    _streamLodSnapshotElapsedTime[streamLod] = 0;
+                    break;
+                }
+            }
+        }
+    }
+
+    private void SendPacket(StreamLOD lod)
+    {
+        var packet = GetPacketForEntityAtLOD(_localAvatar, lod);
+
+        packet.dataByteCount = _localAvatar.RecordStreamData_AutoBuffer(lod, ref packet.data);
+        Debug.Assert(packet.dataByteCount > 0);
+
+        foreach (var loopbackState in _loopbackStates.Values)
+        {
+            if (loopbackState.requestedLod == lod)
+            {
+                packet.fakeLatency = _simulatedLatencySettings.NextValue();
+                loopbackState.packetQueue.Add(packet.Retain());
+            }
+        }
+
+        if (packet.Release())
+        {
+            ReturnPacket(packet);
+        }
+    }
+
+    #endregion
+
+    #region "Remote" Loopback Avatar
+
+    private void UpdatePlaybackTimeDelay(OvrAvatarEntity loopbackAvatar, LoopbackState loopbackState)
+    {
+        // In a real network, maximum packet variation should be computed from the network jitter
+        float latencyVariationS = (_simulatedLatencySettings.fakeLatencyMax - _simulatedLatencySettings.fakeLatencyMin);
+
+        // Push back the playback time by the snapshot interval
+        float snapshotIntervalS = StreamLodSnapshotIntervalSeconds[(int)loopbackAvatar.activeStreamLod];
+
+        // Sum the latency variation and snapshot rate to determine the playback position
+        float playbackDelayS = latencyVariationS + snapshotIntervalS;
+
+        // blend to the target using PLAYBACK_SMOOTH_FACTOR
+        loopbackState.smoothedPlaybackDelay = Mathf.Lerp(loopbackState.smoothedPlaybackDelay, playbackDelayS, PLAYBACK_SMOOTH_FACTOR);
+
+        loopbackAvatar.SetPlaybackTimeDelay(loopbackState.smoothedPlaybackDelay);
+    }
+
+    private void ReceivePacketData(OvrAvatarEntity loopbackAvatar, in NativeSlice<byte> data, StreamLOD lod)
+    {
+        loopbackAvatar.ApplyStreamData(in data);
+    }
+
+    #endregion
+}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/NetworkLoopbackExample/SampleRemoteLoopbackManager.cs.meta b/Assets/Oculus/Avatar2/Example/Scenes/NetworkLoopbackExample/SampleRemoteLoopbackManager.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..2344fe536d40f30f352d5e03a88a62ff4be6cb4c
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/NetworkLoopbackExample/SampleRemoteLoopbackManager.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 8762631024b24aa48978db41c7ea26fb
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/SimpleExample.unity b/Assets/Oculus/Avatar2/Example/Scenes/SimpleExample.unity
new file mode 100644
index 0000000000000000000000000000000000000000..b1a685a2208f843e2e9c009c64d654bc09f3840d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/SimpleExample.unity
@@ -0,0 +1,447 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!29 &1
+OcclusionCullingSettings:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_OcclusionBakeSettings:
+    smallestOccluder: 5
+    smallestHole: 0.25
+    backfaceThreshold: 100
+  m_SceneGUID: 00000000000000000000000000000000
+  m_OcclusionCullingData: {fileID: 0}
+--- !u!104 &2
+RenderSettings:
+  m_ObjectHideFlags: 0
+  serializedVersion: 9
+  m_Fog: 0
+  m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
+  m_FogMode: 3
+  m_FogDensity: 0.01
+  m_LinearFogStart: 0
+  m_LinearFogEnd: 300
+  m_AmbientSkyColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
+  m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
+  m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
+  m_AmbientIntensity: 0
+  m_AmbientMode: 3
+  m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
+  m_SkyboxMaterial: {fileID: 10304, guid: 0000000000000000f000000000000000, type: 0}
+  m_HaloStrength: 0.5
+  m_FlareStrength: 1
+  m_FlareFadeSpeed: 3
+  m_HaloTexture: {fileID: 0}
+  m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
+  m_DefaultReflectionMode: 0
+  m_DefaultReflectionResolution: 128
+  m_ReflectionBounces: 1
+  m_ReflectionIntensity: 1
+  m_CustomReflection: {fileID: 0}
+  m_Sun: {fileID: 705507994}
+  m_IndirectSpecularColor: {r: 0.16223332, g: 0.20294926, b: 0.2825347, a: 1}
+  m_UseRadianceAmbientProbe: 0
+--- !u!157 &3
+LightmapSettings:
+  m_ObjectHideFlags: 0
+  serializedVersion: 11
+  m_GIWorkflowMode: 1
+  m_GISettings:
+    serializedVersion: 2
+    m_BounceScale: 1
+    m_IndirectOutputScale: 1
+    m_AlbedoBoost: 1
+    m_EnvironmentLightingMode: 0
+    m_EnableBakedLightmaps: 1
+    m_EnableRealtimeLightmaps: 0
+  m_LightmapEditorSettings:
+    serializedVersion: 12
+    m_Resolution: 2
+    m_BakeResolution: 40
+    m_AtlasSize: 32
+    m_AO: 0
+    m_AOMaxDistance: 1
+    m_CompAOExponent: 1
+    m_CompAOExponentDirect: 0
+    m_ExtractAmbientOcclusion: 0
+    m_Padding: 2
+    m_LightmapParameters: {fileID: 0}
+    m_LightmapsBakeMode: 1
+    m_TextureCompression: 1
+    m_FinalGather: 0
+    m_FinalGatherFiltering: 1
+    m_FinalGatherRayCount: 256
+    m_ReflectionCompression: 2
+    m_MixedBakeMode: 2
+    m_BakeBackend: 0
+    m_PVRSampling: 1
+    m_PVRDirectSampleCount: 32
+    m_PVRSampleCount: 500
+    m_PVRBounces: 2
+    m_PVREnvironmentSampleCount: 500
+    m_PVREnvironmentReferencePointCount: 2048
+    m_PVRFilteringMode: 2
+    m_PVRDenoiserTypeDirect: 0
+    m_PVRDenoiserTypeIndirect: 0
+    m_PVRDenoiserTypeAO: 0
+    m_PVRFilterTypeDirect: 0
+    m_PVRFilterTypeIndirect: 0
+    m_PVRFilterTypeAO: 0
+    m_PVREnvironmentMIS: 0
+    m_PVRCulling: 1
+    m_PVRFilteringGaussRadiusDirect: 1
+    m_PVRFilteringGaussRadiusIndirect: 5
+    m_PVRFilteringGaussRadiusAO: 2
+    m_PVRFilteringAtrousPositionSigmaDirect: 0.5
+    m_PVRFilteringAtrousPositionSigmaIndirect: 2
+    m_PVRFilteringAtrousPositionSigmaAO: 1
+    m_ExportTrainingData: 0
+    m_TrainingDataDestination: TrainingData
+    m_LightProbeSampleCountMultiplier: 4
+  m_LightingDataAsset: {fileID: 112000000, guid: 632c5b76c10844847a0cf85a0cc2cb61,
+    type: 2}
+  m_UseShadowmask: 1
+--- !u!196 &4
+NavMeshSettings:
+  serializedVersion: 2
+  m_ObjectHideFlags: 0
+  m_BuildSettings:
+    serializedVersion: 2
+    agentTypeID: 0
+    agentRadius: 0.5
+    agentHeight: 2
+    agentSlope: 45
+    agentClimb: 0.4
+    ledgeDropHeight: 0
+    maxJumpAcrossDistance: 0
+    minRegionArea: 2
+    manualCellSize: 0
+    cellSize: 0.16666667
+    manualTileSize: 0
+    tileSize: 256
+    accuratePlacement: 0
+    debug:
+      m_Flags: 0
+  m_NavMeshData: {fileID: 0}
+--- !u!1001 &423485114
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 0}
+    m_Modifications:
+    - target: {fileID: 5848076533691757917, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_Name
+      value: AvatarSdkManagerHorizon
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 3
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: d0377a811c2a95841ba015ae0b2c8d45, type: 3}
+--- !u!1 &705507993
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 705507995}
+  - component: {fileID: 705507994}
+  m_Layer: 0
+  m_Name: Directional Light
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!108 &705507994
+Light:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 705507993}
+  m_Enabled: 1
+  serializedVersion: 10
+  m_Type: 1
+  m_Shape: 0
+  m_Color: {r: 1, g: 1, b: 1, a: 1}
+  m_Intensity: 1
+  m_Range: 10
+  m_SpotAngle: 30
+  m_InnerSpotAngle: 21.80208
+  m_CookieSize: 10
+  m_Shadows:
+    m_Type: 2
+    m_Resolution: -1
+    m_CustomResolution: -1
+    m_Strength: 1
+    m_Bias: 0.05
+    m_NormalBias: 0.4
+    m_NearPlane: 0.2
+    m_CullingMatrixOverride:
+      e00: 1
+      e01: 0
+      e02: 0
+      e03: 0
+      e10: 0
+      e11: 1
+      e12: 0
+      e13: 0
+      e20: 0
+      e21: 0
+      e22: 1
+      e23: 0
+      e30: 0
+      e31: 0
+      e32: 0
+      e33: 1
+    m_UseCullingMatrixOverride: 0
+  m_Cookie: {fileID: 0}
+  m_DrawHalo: 0
+  m_Flare: {fileID: 0}
+  m_RenderMode: 0
+  m_CullingMask:
+    serializedVersion: 2
+    m_Bits: 4294967295
+  m_RenderingLayerMask: 1
+  m_Lightmapping: 1
+  m_LightShadowCasterMode: 0
+  m_AreaSize: {x: 1, y: 1}
+  m_BounceIntensity: 1
+  m_ColorTemperature: 6570
+  m_UseColorTemperature: 0
+  m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
+  m_UseBoundingSphereOverride: 0
+  m_ShadowRadius: 0
+  m_ShadowAngle: 0
+--- !u!4 &705507995
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 705507993}
+  m_LocalRotation: {x: 0.7242991, y: -0.37069848, z: 0.28188178, w: 0.5084449}
+  m_LocalPosition: {x: 0, y: 3, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 0}
+  m_RootOrder: 2
+  m_LocalEulerAnglesHint: {x: 71, y: -185.53, z: -129.73901}
+--- !u!1 &963194225
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 963194228}
+  - component: {fileID: 963194227}
+  m_Layer: 0
+  m_Name: Main Camera
+  m_TagString: MainCamera
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!20 &963194227
+Camera:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 963194225}
+  m_Enabled: 1
+  serializedVersion: 2
+  m_ClearFlags: 1
+  m_BackGroundColor: {r: 0, g: 0, b: 0, a: 0}
+  m_projectionMatrixMode: 1
+  m_GateFitMode: 2
+  m_FOVAxisMode: 0
+  m_SensorSize: {x: 36, y: 24}
+  m_LensShift: {x: 0, y: 0}
+  m_FocalLength: 50
+  m_NormalizedViewPortRect:
+    serializedVersion: 2
+    x: 0
+    y: 0
+    width: 1
+    height: 1
+  near clip plane: 0.3
+  far clip plane: 1000
+  field of view: 60
+  orthographic: 0
+  orthographic size: 5
+  m_Depth: -1
+  m_CullingMask:
+    serializedVersion: 2
+    m_Bits: 4294967295
+  m_RenderingPath: -1
+  m_TargetTexture: {fileID: 0}
+  m_TargetDisplay: 0
+  m_TargetEye: 3
+  m_HDR: 1
+  m_AllowMSAA: 1
+  m_AllowDynamicResolution: 0
+  m_ForceIntoRT: 0
+  m_OcclusionCulling: 1
+  m_StereoConvergence: 10
+  m_StereoSeparation: 0.022
+--- !u!4 &963194228
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 963194225}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 0}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &648041123805463616
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 803721280982377708}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _creationInfo:
+    features: 342
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+  _activeView: 2
+  _activeManifestation: 2
+  _bodyTracking: {fileID: 0}
+  _lipSync: {fileID: 0}
+  _criticalJointTypes: 
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  _useAdvancedLodSystem: 1
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _loadUserFromCdn: 0
+  _assets:
+  - source: 0
+    path: 23
+  _assetPostfixDefault: _rift.glb
+  _assetPostfixAndroid: _quest.glb
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!1 &803721280982377708
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 5270651527844057159}
+  - component: {fileID: 648041123805463616}
+  m_Layer: 0
+  m_Name: Preset 23
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &5270651527844057159
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 803721280982377708}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: -1.5, z: 1}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 0}
+  m_RootOrder: 1
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/SimpleExample.unity.meta b/Assets/Oculus/Avatar2/Example/Scenes/SimpleExample.unity.meta
new file mode 100644
index 0000000000000000000000000000000000000000..2a95ee8f9904d17d310e84284e2b16e2ef7bb3bb
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/SimpleExample.unity.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 9d651860700ec4f64899b27f1cd59359
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/SkinningTypesExample.meta b/Assets/Oculus/Avatar2/Example/Scenes/SkinningTypesExample.meta
new file mode 100644
index 0000000000000000000000000000000000000000..f4f068a938e522a6b941acdb755e57f36c76a505
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/SkinningTypesExample.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 8b1aa3e1e21ec8143a0314b35ed3012f
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/SkinningTypesExample/Floor.mat b/Assets/Oculus/Avatar2/Example/Scenes/SkinningTypesExample/Floor.mat
new file mode 100644
index 0000000000000000000000000000000000000000..066080614ffb800039b6a1ceebbac224b972b3c0
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/SkinningTypesExample/Floor.mat
@@ -0,0 +1,77 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: Floor
+  m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
+  m_ShaderKeywords: 
+  m_LightmapFlags: 4
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 4, y: 5}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 4, y: 5}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 0.5019608, g: 0.5019608, b: 0.5019608, a: 1}
+    - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/SkinningTypesExample/Floor.mat.meta b/Assets/Oculus/Avatar2/Example/Scenes/SkinningTypesExample/Floor.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..f6ddaae8b05adc31f79b5c6ce05a0be0c4467039
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/SkinningTypesExample/Floor.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: ce996e069fb94eaf8f7cc69a608f9f19
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/SkinningTypesExample/SampleAvatarAttachments.cs b/Assets/Oculus/Avatar2/Example/Scenes/SkinningTypesExample/SampleAvatarAttachments.cs
new file mode 100644
index 0000000000000000000000000000000000000000..f3f8ea1b3d5f23823ee8e6191bba23ea38a9270a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/SkinningTypesExample/SampleAvatarAttachments.cs
@@ -0,0 +1,42 @@
+using Oculus.Avatar2;
+using System.Collections;
+using UnityEngine;
+
+/* This class is an example of how to attach GameObjects to an avatar's critical joints. It retrieves all of a SampleAvatarEntity's
+ * critical joints, and attache a cube primitive to each of them. As the avatar tracks body movement, the attached objects move with it.
+ */
+[RequireComponent(typeof(SampleAvatarEntity))]
+public class SampleAvatarAttachments : MonoBehaviour
+{
+    private SampleAvatarEntity _avatarEnt;
+
+    [SerializeField]
+    private Vector3 AttachmentScale = new Vector3(0.1f, 0.1f, 0.1f);
+
+    [SerializeField]
+    private Color AttachmentColor = new Color(1.0f, 0.0f, 0.0f);
+
+    protected IEnumerator Start()
+    {
+        _avatarEnt = GetComponent<SampleAvatarEntity>();
+        yield return new WaitUntil(() => _avatarEnt.HasJoints);
+
+        var criticalJoints = _avatarEnt.GetCriticalJoints();
+
+        foreach (var jointType in criticalJoints)
+        {
+            Transform jointTransform = _avatarEnt.GetSkeletonTransform(jointType);
+
+            if (!jointTransform)
+            {
+                OvrAvatarLog.LogError($"SampleAvatarAttachments: No joint transform found for {jointType} on {_avatarEnt.name} ");
+                continue;
+            }
+
+            var attachmentObj = GameObject.CreatePrimitive(PrimitiveType.Cube);
+            attachmentObj.transform.localScale = AttachmentScale;
+            attachmentObj.GetComponent<Renderer>().material.color = AttachmentColor;
+            attachmentObj.transform.SetParent(jointTransform, false);
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/SkinningTypesExample/SampleAvatarAttachments.cs.meta b/Assets/Oculus/Avatar2/Example/Scenes/SkinningTypesExample/SampleAvatarAttachments.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..c23b2f83feafa4ec55bda82d8d1ef4f034176d92
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/SkinningTypesExample/SampleAvatarAttachments.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 64fa3c181fcb5744dbbbbb80bd32206e
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/SkinningTypesExample/SkinningTypesExample.unity b/Assets/Oculus/Avatar2/Example/Scenes/SkinningTypesExample/SkinningTypesExample.unity
new file mode 100644
index 0000000000000000000000000000000000000000..1e8e8d02fdf0fd6aa621d2ef27680fe131dab8a6
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/SkinningTypesExample/SkinningTypesExample.unity
@@ -0,0 +1,1398 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!29 &1
+OcclusionCullingSettings:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_OcclusionBakeSettings:
+    smallestOccluder: 5
+    smallestHole: 0.25
+    backfaceThreshold: 100
+  m_SceneGUID: 00000000000000000000000000000000
+  m_OcclusionCullingData: {fileID: 0}
+--- !u!104 &2
+RenderSettings:
+  m_ObjectHideFlags: 0
+  serializedVersion: 9
+  m_Fog: 0
+  m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
+  m_FogMode: 3
+  m_FogDensity: 0.01
+  m_LinearFogStart: 0
+  m_LinearFogEnd: 300
+  m_AmbientSkyColor: {r: 0.212, g: 0.227, b: 0.259, a: 1}
+  m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
+  m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
+  m_AmbientIntensity: 1
+  m_AmbientMode: 0
+  m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
+  m_SkyboxMaterial: {fileID: 2100000, guid: 35f80c2b3fdf34d4aa85db1bf46f0983, type: 2}
+  m_HaloStrength: 0.5
+  m_FlareStrength: 1
+  m_FlareFadeSpeed: 3
+  m_HaloTexture: {fileID: 0}
+  m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
+  m_DefaultReflectionMode: 0
+  m_DefaultReflectionResolution: 128
+  m_ReflectionBounces: 1
+  m_ReflectionIntensity: 1
+  m_CustomReflection: {fileID: 0}
+  m_Sun: {fileID: 0}
+  m_IndirectSpecularColor: {r: 0.44993275, g: 0.4091351, b: 0.3642775, a: 1}
+  m_UseRadianceAmbientProbe: 0
+--- !u!157 &3
+LightmapSettings:
+  m_ObjectHideFlags: 0
+  serializedVersion: 11
+  m_GIWorkflowMode: 1
+  m_GISettings:
+    serializedVersion: 2
+    m_BounceScale: 1
+    m_IndirectOutputScale: 1
+    m_AlbedoBoost: 1
+    m_EnvironmentLightingMode: 0
+    m_EnableBakedLightmaps: 0
+    m_EnableRealtimeLightmaps: 0
+  m_LightmapEditorSettings:
+    serializedVersion: 12
+    m_Resolution: 2
+    m_BakeResolution: 40
+    m_AtlasSize: 1024
+    m_AO: 0
+    m_AOMaxDistance: 1
+    m_CompAOExponent: 1
+    m_CompAOExponentDirect: 0
+    m_ExtractAmbientOcclusion: 0
+    m_Padding: 2
+    m_LightmapParameters: {fileID: 0}
+    m_LightmapsBakeMode: 1
+    m_TextureCompression: 1
+    m_FinalGather: 0
+    m_FinalGatherFiltering: 1
+    m_FinalGatherRayCount: 256
+    m_ReflectionCompression: 2
+    m_MixedBakeMode: 2
+    m_BakeBackend: 1
+    m_PVRSampling: 1
+    m_PVRDirectSampleCount: 32
+    m_PVRSampleCount: 500
+    m_PVRBounces: 2
+    m_PVREnvironmentSampleCount: 500
+    m_PVREnvironmentReferencePointCount: 2048
+    m_PVRFilteringMode: 2
+    m_PVRDenoiserTypeDirect: 0
+    m_PVRDenoiserTypeIndirect: 0
+    m_PVRDenoiserTypeAO: 0
+    m_PVRFilterTypeDirect: 0
+    m_PVRFilterTypeIndirect: 0
+    m_PVRFilterTypeAO: 0
+    m_PVREnvironmentMIS: 0
+    m_PVRCulling: 1
+    m_PVRFilteringGaussRadiusDirect: 1
+    m_PVRFilteringGaussRadiusIndirect: 5
+    m_PVRFilteringGaussRadiusAO: 2
+    m_PVRFilteringAtrousPositionSigmaDirect: 0.5
+    m_PVRFilteringAtrousPositionSigmaIndirect: 2
+    m_PVRFilteringAtrousPositionSigmaAO: 1
+    m_ExportTrainingData: 0
+    m_TrainingDataDestination: TrainingData
+    m_LightProbeSampleCountMultiplier: 4
+  m_LightingDataAsset: {fileID: 112000000, guid: f0c68c730be06da4e8d2e671bffde0dc,
+    type: 2}
+  m_UseShadowmask: 1
+--- !u!196 &4
+NavMeshSettings:
+  serializedVersion: 2
+  m_ObjectHideFlags: 0
+  m_BuildSettings:
+    serializedVersion: 2
+    agentTypeID: 0
+    agentRadius: 0.5
+    agentHeight: 2
+    agentSlope: 45
+    agentClimb: 0.4
+    ledgeDropHeight: 0
+    maxJumpAcrossDistance: 0
+    minRegionArea: 2
+    manualCellSize: 0
+    cellSize: 0.16666667
+    manualTileSize: 0
+    tileSize: 256
+    accuratePlacement: 0
+    debug:
+      m_Flags: 0
+  m_NavMeshData: {fileID: 0}
+--- !u!1 &119374383
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 119374384}
+  - component: {fileID: 119374385}
+  - component: {fileID: 119374386}
+  m_Layer: 0
+  m_Name: Mirror Avatar, GPU Skinning w/ Motion Smoothing
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &119374384
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 119374383}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0.75, y: 0, z: 3}
+  m_LocalScale: {x: 1, y: 1, z: -1}
+  m_Children: []
+  m_Father: {fileID: 625919963}
+  m_RootOrder: 3
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &119374385
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 119374383}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+  _creationInfo:
+    features: 382
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+      subMeshInclusionFlags: 2047
+  _activeView: 2
+  _activeManifestation: 2
+  _activeSubMeshes: 2047
+  _activeSubMeshesIncludeUntyped: 1
+  _bodyTracking: {fileID: 2077391344}
+  _lipSync: {fileID: 307099418}
+  _criticalJointTypes: 18000000190000001a0000001b0000001c0000001d0000001e0000001f000000200000002100000022000000230000002400000025000000260000002700000028000000290000002a0000002b0000002c0000002d0000002e0000002f000000300000003100000032000000330000003400000035000000360000003700000038000000390000003a0000003b0000003c0000003d0000003e0000003f000000
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  OnCreatedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnSkeletonLoadedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnDefaultAvatarLoadedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnUserAvatarLoadedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  PreTeardownEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnLoadFailedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 3
+  MotionSmoothingSettings: 1
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _loadUserFromCdn: 1
+  _deferLoading: 0
+  _assets:
+  - source: 0
+    path: 2
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!114 &119374386
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 119374383}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 64fa3c181fcb5744dbbbbb80bd32206e, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+  AttachmentScale: {x: 0.03, y: 0.03, z: 0.03}
+  AttachmentColor: {r: 0, g: 0, b: 1, a: 1}
+--- !u!1 &168388877
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 168388879}
+  - component: {fileID: 168388878}
+  m_Layer: 0
+  m_Name: Second Test Point Light
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 0
+--- !u!108 &168388878
+Light:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 168388877}
+  m_Enabled: 1
+  serializedVersion: 10
+  m_Type: 2
+  m_Shape: 0
+  m_Color: {r: 1, g: 0, b: 0, a: 1}
+  m_Intensity: 4
+  m_Range: 10
+  m_SpotAngle: 46.8
+  m_InnerSpotAngle: 34.55462
+  m_CookieSize: 10
+  m_Shadows:
+    m_Type: 0
+    m_Resolution: -1
+    m_CustomResolution: -1
+    m_Strength: 1
+    m_Bias: 0.05
+    m_NormalBias: 0.4
+    m_NearPlane: 0.2
+    m_CullingMatrixOverride:
+      e00: 1
+      e01: 0
+      e02: 0
+      e03: 0
+      e10: 0
+      e11: 1
+      e12: 0
+      e13: 0
+      e20: 0
+      e21: 0
+      e22: 1
+      e23: 0
+      e30: 0
+      e31: 0
+      e32: 0
+      e33: 1
+    m_UseCullingMatrixOverride: 0
+  m_Cookie: {fileID: 0}
+  m_DrawHalo: 0
+  m_Flare: {fileID: 0}
+  m_RenderMode: 0
+  m_CullingMask:
+    serializedVersion: 2
+    m_Bits: 4294967295
+  m_RenderingLayerMask: 1
+  m_Lightmapping: 4
+  m_LightShadowCasterMode: 0
+  m_AreaSize: {x: 1, y: 1}
+  m_BounceIntensity: 1
+  m_ColorTemperature: 6570
+  m_UseColorTemperature: 0
+  m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
+  m_UseBoundingSphereOverride: 0
+  m_ShadowRadius: 0
+  m_ShadowAngle: 0
+--- !u!4 &168388879
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 168388877}
+  m_LocalRotation: {x: -0.21729134, y: 0, z: 0, w: -0.9761068}
+  m_LocalPosition: {x: 0, y: 2, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 0}
+  m_RootOrder: 1
+  m_LocalEulerAnglesHint: {x: 385.1, y: 0, z: 0}
+--- !u!1001 &171261753
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 0}
+    m_Modifications:
+    - target: {fileID: 5650585432433268674, guid: 9a975a19ea46baf448b38e3a8b495351,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 9a975a19ea46baf448b38e3a8b495351,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 9a975a19ea46baf448b38e3a8b495351,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 9a975a19ea46baf448b38e3a8b495351,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 9a975a19ea46baf448b38e3a8b495351,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 9a975a19ea46baf448b38e3a8b495351,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 9a975a19ea46baf448b38e3a8b495351,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 9a975a19ea46baf448b38e3a8b495351,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 9a975a19ea46baf448b38e3a8b495351,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 9a975a19ea46baf448b38e3a8b495351,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 9a975a19ea46baf448b38e3a8b495351,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 8423574076275988413, guid: 9a975a19ea46baf448b38e3a8b495351,
+        type: 3}
+      propertyPath: m_Name
+      value: FootprintCourt LightRig
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: 9a975a19ea46baf448b38e3a8b495351, type: 3}
+--- !u!1 &258885914
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 258885915}
+  - component: {fileID: 258885916}
+  - component: {fileID: 258885917}
+  m_Layer: 0
+  m_Name: Mirror Avatar, GPU Skinning (no interpolation)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &258885915
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 258885914}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 3}
+  m_LocalScale: {x: 1, y: 1, z: -1}
+  m_Children: []
+  m_Father: {fileID: 625919963}
+  m_RootOrder: 2
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &258885916
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 258885914}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+  _creationInfo:
+    features: 382
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+      subMeshInclusionFlags: 2047
+  _activeView: 2
+  _activeManifestation: 2
+  _activeSubMeshes: 2047
+  _activeSubMeshesIncludeUntyped: 1
+  _bodyTracking: {fileID: 2077391344}
+  _lipSync: {fileID: 307099418}
+  _criticalJointTypes: 18000000190000001a0000001b0000001c0000001d0000001e0000001f000000200000002100000022000000230000002400000025000000260000002700000028000000290000002a0000002b0000002c0000002d0000002e0000002f000000300000003100000032000000330000003400000035000000360000003700000038000000390000003a0000003b0000003c0000003d0000003e0000003f000000
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  OnCreatedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnSkeletonLoadedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnDefaultAvatarLoadedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnUserAvatarLoadedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  PreTeardownEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnLoadFailedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 3
+  MotionSmoothingSettings: 2
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _loadUserFromCdn: 1
+  _deferLoading: 0
+  _assets:
+  - source: 0
+    path: 2
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!114 &258885917
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 258885914}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 64fa3c181fcb5744dbbbbb80bd32206e, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+  AttachmentScale: {x: 0.03, y: 0.03, z: 0.03}
+  AttachmentColor: {r: 0, g: 1, b: 0, a: 1}
+--- !u!1 &305611635
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 305611636}
+  - component: {fileID: 305611637}
+  - component: {fileID: 305611638}
+  m_Layer: 0
+  m_Name: Mirror Avatar, Unity Skinning
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &305611636
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 305611635}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: -0.75, y: 0, z: 3}
+  m_LocalScale: {x: 1, y: 1, z: -1}
+  m_Children: []
+  m_Father: {fileID: 625919963}
+  m_RootOrder: 1
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &305611637
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 305611635}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+  _creationInfo:
+    features: 382
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+      subMeshInclusionFlags: 2047
+  _activeView: 2
+  _activeManifestation: 2
+  _activeSubMeshes: 2047
+  _activeSubMeshesIncludeUntyped: 1
+  _bodyTracking: {fileID: 2077391344}
+  _lipSync: {fileID: 307099418}
+  _criticalJointTypes: 18000000190000001a0000001b0000001c0000001d0000001e0000001f000000200000002100000022000000230000002400000025000000260000002700000028000000290000002a0000002b0000002c0000002d0000002e0000002f000000300000003100000032000000330000003400000035000000360000003700000038000000390000003a0000003b0000003c0000003d0000003e0000003f000000
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  OnCreatedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnSkeletonLoadedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnDefaultAvatarLoadedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnUserAvatarLoadedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  PreTeardownEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnLoadFailedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 2
+  MotionSmoothingSettings: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _loadUserFromCdn: 1
+  _deferLoading: 0
+  _assets:
+  - source: 0
+    path: 2
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!114 &305611638
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 305611635}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 64fa3c181fcb5744dbbbbb80bd32206e, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+  AttachmentScale: {x: 0.03, y: 0.03, z: 0.03}
+  AttachmentColor: {r: 1, g: 0, b: 0, a: 1}
+--- !u!114 &307099418 stripped
+MonoBehaviour:
+  m_CorrespondingSourceObject: {fileID: 2430523750807860367, guid: c019572605bc29e42b3ed5a058599618,
+    type: 3}
+  m_PrefabInstance: {fileID: 2430523750576325525}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 0}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: a509dbf839e0e4044a7affab5b5a3e55, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+--- !u!1 &467760007
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 467760009}
+  - component: {fileID: 467760008}
+  m_Layer: 0
+  m_Name: SceneSwitcher
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!114 &467760008
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 467760007}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: ea6e0c6cd67922c48bbb5939267828e9, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+  _nextSceneInput:
+    controllerMask: 2
+    buttonMask: 1
+  _prevSceneInput:
+    controllerMask: 1
+    buttonMask: 1
+--- !u!4 &467760009
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 467760007}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 0}
+  m_RootOrder: 8
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &482114163
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 482114167}
+  - component: {fileID: 482114166}
+  - component: {fileID: 482114165}
+  - component: {fileID: 482114164}
+  m_Layer: 0
+  m_Name: Floor
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!64 &482114164
+MeshCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 482114163}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 4
+  m_Convex: 0
+  m_CookingOptions: 30
+  m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &482114165
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 482114163}
+  m_Enabled: 1
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RayTracingMode: 2
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: 6961428059f782e42b6a7c24fb04c9ec, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_ReceiveGI: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!33 &482114166
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 482114163}
+  m_Mesh: {fileID: 10210, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!4 &482114167
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 482114163}
+  m_LocalRotation: {x: 0.7071068, y: 0, z: 0, w: 0.7071068}
+  m_LocalPosition: {x: 0, y: -0, z: 2}
+  m_LocalScale: {x: 3, y: 8, z: 2}
+  m_Children: []
+  m_Father: {fileID: 0}
+  m_RootOrder: 6
+  m_LocalEulerAnglesHint: {x: 90, y: 0, z: 0}
+--- !u!1 &625919962
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 625919963}
+  m_Layer: 0
+  m_Name: Entities
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &625919963
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 625919962}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 2043905275}
+  - {fileID: 305611636}
+  - {fileID: 258885915}
+  - {fileID: 119374384}
+  m_Father: {fileID: 0}
+  m_RootOrder: 3
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &778375652
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 778375654}
+  - component: {fileID: 778375653}
+  m_Layer: 0
+  m_Name: Third Test Spot Light
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 0
+--- !u!108 &778375653
+Light:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 778375652}
+  m_Enabled: 1
+  serializedVersion: 10
+  m_Type: 0
+  m_Shape: 0
+  m_Color: {r: 0, g: 0.10499716, b: 1, a: 1}
+  m_Intensity: 4
+  m_Range: 10
+  m_SpotAngle: 45
+  m_InnerSpotAngle: 33.15822
+  m_CookieSize: 10
+  m_Shadows:
+    m_Type: 0
+    m_Resolution: -1
+    m_CustomResolution: -1
+    m_Strength: 1
+    m_Bias: 0.05
+    m_NormalBias: 0.4
+    m_NearPlane: 0.2
+    m_CullingMatrixOverride:
+      e00: 1
+      e01: 0
+      e02: 0
+      e03: 0
+      e10: 0
+      e11: 1
+      e12: 0
+      e13: 0
+      e20: 0
+      e21: 0
+      e22: 1
+      e23: 0
+      e30: 0
+      e31: 0
+      e32: 0
+      e33: 1
+    m_UseCullingMatrixOverride: 0
+  m_Cookie: {fileID: 0}
+  m_DrawHalo: 0
+  m_Flare: {fileID: 0}
+  m_RenderMode: 0
+  m_CullingMask:
+    serializedVersion: 2
+    m_Bits: 4294967295
+  m_RenderingLayerMask: 1
+  m_Lightmapping: 4
+  m_LightShadowCasterMode: 0
+  m_AreaSize: {x: 1, y: 1}
+  m_BounceIntensity: 1
+  m_ColorTemperature: 6570
+  m_UseColorTemperature: 0
+  m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
+  m_UseBoundingSphereOverride: 0
+  m_ShadowRadius: 0
+  m_ShadowAngle: 0
+--- !u!4 &778375654
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 778375652}
+  m_LocalRotation: {x: 0, y: 0.97886735, z: -0.2044961, w: 0}
+  m_LocalPosition: {x: 0, y: 2, z: 4}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 0}
+  m_RootOrder: 2
+  m_LocalEulerAnglesHint: {x: 23.6, y: 180, z: 0}
+--- !u!1 &963194225
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 963194228}
+  - component: {fileID: 963194227}
+  - component: {fileID: 963194226}
+  - component: {fileID: 963194229}
+  - component: {fileID: 963194230}
+  m_Layer: 0
+  m_Name: Main Camera
+  m_TagString: MainCamera
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!81 &963194226
+AudioListener:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 963194225}
+  m_Enabled: 1
+--- !u!20 &963194227
+Camera:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 963194225}
+  m_Enabled: 1
+  serializedVersion: 2
+  m_ClearFlags: 1
+  m_BackGroundColor: {r: 0, g: 0, b: 0, a: 0}
+  m_projectionMatrixMode: 1
+  m_GateFitMode: 2
+  m_FOVAxisMode: 0
+  m_SensorSize: {x: 36, y: 24}
+  m_LensShift: {x: 0, y: 0}
+  m_FocalLength: 50
+  m_NormalizedViewPortRect:
+    serializedVersion: 2
+    x: 0
+    y: 0
+    width: 1
+    height: 1
+  near clip plane: 0.1
+  far clip plane: 1000
+  field of view: 60
+  orthographic: 0
+  orthographic size: 5
+  m_Depth: -1
+  m_CullingMask:
+    serializedVersion: 2
+    m_Bits: 4294967295
+  m_RenderingPath: -1
+  m_TargetTexture: {fileID: 0}
+  m_TargetDisplay: 0
+  m_TargetEye: 3
+  m_HDR: 1
+  m_AllowMSAA: 1
+  m_AllowDynamicResolution: 0
+  m_ForceIntoRT: 0
+  m_OcclusionCulling: 1
+  m_StereoConvergence: 10
+  m_StereoSeparation: 0.022
+--- !u!4 &963194228
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 963194225}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 2043905275}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &963194229
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 963194225}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 5a2a9c34df4095f47b9ca8f975175f5b, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+  m_Device: 0
+  m_PoseSource: 2
+  m_PoseProviderComponent: {fileID: 0}
+  m_TrackingType: 0
+  m_UpdateType: 0
+  m_UseRelativeTransform: 0
+--- !u!114 &963194230
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 963194225}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 4e93961d3dfb44505a84d80b1f7fffef, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+  _mode: 0
+  _coordinateSpace: 0
+  _position: {x: 0, y: 1.5, z: 4}
+  _rotation: {x: 0, y: 180, z: 0}
+--- !u!1 &1735107478
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1735107480}
+  - component: {fileID: 1735107479}
+  m_Layer: 0
+  m_Name: OpenAvatarEditor
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!114 &1735107479
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1735107478}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: df522fdce5f894b4f986a3489491b7de, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+--- !u!4 &1735107480
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1735107478}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 4.270977, y: 1.2448722, z: -4.486235}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 0}
+  m_RootOrder: 5
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &2043905274
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 2043905275}
+  - component: {fileID: 2043905277}
+  - component: {fileID: 2043905276}
+  - component: {fileID: 2043905278}
+  m_Layer: 0
+  m_Name: Avatar - Zip Combined Model
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &2043905275
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2043905274}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 1}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 963194228}
+  m_Father: {fileID: 625919963}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &2043905276
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2043905274}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 202d697196ae07b499aba5134d698aff, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+--- !u!114 &2043905277
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2043905274}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: f775fcfeef42d0543abbd1e03dfe74fb, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+  _creationInfo:
+    features: 382
+    renderFilters:
+      lodFlags: 31
+      manifestationFlags: 2
+      viewFlags: 3
+      subMeshInclusionFlags: 2047
+  _activeView: 1
+  _activeManifestation: 2
+  _activeSubMeshes: 2047
+  _activeSubMeshesIncludeUntyped: 1
+  _bodyTracking: {fileID: 2077391344}
+  _lipSync: {fileID: 307099418}
+  _criticalJointTypes: 0f0000001d00000031000000
+  _debugDrawing:
+    drawTrackingPose: 0
+    drawBoneNames: 0
+    drawSkelHierarchy: 0
+    drawSkelHierarchyInGame: 0
+    drawSkinTransformsInGame: 0
+    drawCriticalJoints: 0
+    skeletonColor: {r: 1, g: 0, b: 0, a: 1}
+  LoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  EntityLoadingStateChanged:
+    m_PersistentCalls:
+      m_Calls: []
+  OnCreatedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnSkeletonLoadedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnDefaultAvatarLoadedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnUserAvatarLoadedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  PreTeardownEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  OnLoadFailedEvent:
+    m_PersistentCalls:
+      m_Calls: []
+  SkinningType: 3
+  MotionSmoothingSettings: 0
+  _hidden: 0
+  useRenderLods: 1
+  _isLocal: 1
+  _loadUserFromCdn: 1
+  _deferLoading: 0
+  _assets:
+  - source: 0
+    path: 2
+  _autoCdnRetry: 1
+  _autoCheckChanges: 0
+  _changeCheckInterval: 8
+  _debugDrawGazePos: 0
+  _debugDrawGazePosColor: {r: 1, g: 0, b: 1, a: 1}
+  _overrideStreamLod: 0
+  shaderGrayToSolidColorBlend_: 0.7
+  shaderDesatBlend_: 0
+  shaderSolidColor_: {r: 0.12941177, g: 0.19607843, b: 0.3882353, a: 0}
+--- !u!114 &2043905278
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2043905274}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 5b0eaccd645fc5641ba1cf708f6fd678, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+  movementSpeed: 1
+--- !u!114 &2077391344 stripped
+MonoBehaviour:
+  m_CorrespondingSourceObject: {fileID: 3865823349605503150, guid: d0377a811c2a95841ba015ae0b2c8d45,
+    type: 3}
+  m_PrefabInstance: {fileID: 2104555916}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 0}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 6937544d08e2ba34da6ff4ab90cbfd84, type: 3}
+  m_Name:
+  m_EditorClassIdentifier:
+--- !u!1001 &2104555916
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 0}
+    m_Modifications:
+    - target: {fileID: 5848076533691757917, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_Name
+      value: AvatarSdkManagerHorizon
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757918, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: _skinnersSupported
+      value: -1
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 7
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5848076533691757919, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 8687292189353299701, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: MotionSmoothing
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 8687292189353299701, guid: d0377a811c2a95841ba015ae0b2c8d45,
+        type: 3}
+      propertyPath: SourceMorphFormat
+      value: 3
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: d0377a811c2a95841ba015ae0b2c8d45, type: 3}
+--- !u!1001 &2430523750576325525
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 0}
+    m_Modifications:
+    - target: {fileID: 2430523750807860354, guid: c019572605bc29e42b3ed5a058599618,
+        type: 3}
+      propertyPath: m_Name
+      value: LipSyncInput
+      objectReference: {fileID: 0}
+    - target: {fileID: 2430523750807860366, guid: c019572605bc29e42b3ed5a058599618,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 4
+      objectReference: {fileID: 0}
+    - target: {fileID: 2430523750807860366, guid: c019572605bc29e42b3ed5a058599618,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2430523750807860366, guid: c019572605bc29e42b3ed5a058599618,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2430523750807860366, guid: c019572605bc29e42b3ed5a058599618,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2430523750807860366, guid: c019572605bc29e42b3ed5a058599618,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 2430523750807860366, guid: c019572605bc29e42b3ed5a058599618,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2430523750807860366, guid: c019572605bc29e42b3ed5a058599618,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2430523750807860366, guid: c019572605bc29e42b3ed5a058599618,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2430523750807860366, guid: c019572605bc29e42b3ed5a058599618,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2430523750807860366, guid: c019572605bc29e42b3ed5a058599618,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2430523750807860366, guid: c019572605bc29e42b3ed5a058599618,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: c019572605bc29e42b3ed5a058599618, type: 3}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/SkinningTypesExample/SkinningTypesExample.unity.meta b/Assets/Oculus/Avatar2/Example/Scenes/SkinningTypesExample/SkinningTypesExample.unity.meta
new file mode 100644
index 0000000000000000000000000000000000000000..d17dbc7219a1f643772c3b8abdd91be8ac0bd4c1
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/SkinningTypesExample/SkinningTypesExample.unity.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: e8a4b1a65c6d41469d33220eb06f66a1
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample.meta
new file mode 100644
index 0000000000000000000000000000000000000000..9451e010a3523791eb4da1a71bc7519132ddd75a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: e8c69c3214fd3c24ab9f81b16ae22a0f
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials.meta
new file mode 100644
index 0000000000000000000000000000000000000000..1bfba1be796e7da3ae3e9e6b8f2c7dbc6b221240
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 1f292c4b813d4804a90f1be86e3634ae
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting.meta
new file mode 100644
index 0000000000000000000000000000000000000000..d542c8fa8abefcfe3eaf7de86173a922e8733d87
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: dd121739e31dff247a490c582c631ffb
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_Centerpiece.mat b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_Centerpiece.mat
new file mode 100644
index 0000000000000000000000000000000000000000..f02ce1e63d7a39925dd44d2d8540d513dd564b85
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_Centerpiece.mat
@@ -0,0 +1,87 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: m_lobbyHalloweenIndirect_Centerpiece
+  m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
+  m_ShaderKeywords: _EMISSION
+  m_LightmapFlags: 2
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 2800000, guid: e260e767787267643b3ef20eb5a9f0c3, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 2800000, guid: e260e767787267643b3ef20eb5a9f0c3, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _hallwayCenterpieceHalloween_bc:
+        m_Texture: {fileID: 2800000, guid: e260e767787267643b3ef20eb5a9f0c3, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _texcoord:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _FlameFlickerSpeed: 0.28
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 1, g: 1, b: 1, a: 1}
+    - _EmissionColor: {r: 1.2596959, g: 1.2596959, b: 1.2596959, a: 1}
+    - _Scale: {r: 8, g: 50, b: 50, a: 0}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_Centerpiece.mat.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_Centerpiece.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..0c06aa3ce7b4609730dbb473e89a712964cd921e
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_Centerpiece.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: f0476de09c36ca046aa06c597a9af78f
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 2100000
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_Entrance.mat b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_Entrance.mat
new file mode 100644
index 0000000000000000000000000000000000000000..7a373e068f0098005cd999de932e320464bef7cc
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_Entrance.mat
@@ -0,0 +1,77 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: m_lobbyHalloweenIndirect_Entrance
+  m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
+  m_ShaderKeywords: _EMISSION
+  m_LightmapFlags: 2
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 2800000, guid: 73e6bc914d39caf46830cfb0b0c96ea1, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 2800000, guid: 73e6bc914d39caf46830cfb0b0c96ea1, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 1, g: 1, b: 1, a: 1}
+    - _EmissionColor: {r: 1.2596959, g: 1.2596959, b: 1.2596959, a: 1}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_Entrance.mat.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_Entrance.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..241ebefe2e948778c5bfe8823db6e880483e877e
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_Entrance.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 582bdea2c17bbb94f9c722adeb3228d7
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 2100000
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_EntranceSteps.mat b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_EntranceSteps.mat
new file mode 100644
index 0000000000000000000000000000000000000000..6eb1bfc37839a09205abc402636ea25ae2e1be19
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_EntranceSteps.mat
@@ -0,0 +1,77 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: m_lobbyHalloweenIndirect_EntranceSteps
+  m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
+  m_ShaderKeywords: _EMISSION
+  m_LightmapFlags: 2
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 2800000, guid: 161eb64efb42cab4a83d7f9be76577f3, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 2800000, guid: 161eb64efb42cab4a83d7f9be76577f3, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 1, g: 1, b: 1, a: 1}
+    - _EmissionColor: {r: 1.2596959, g: 1.2596959, b: 1.2596959, a: 1}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_EntranceSteps.mat.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_EntranceSteps.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..e0fa7d06d976174e792d5f9066fffd60c2f51c1d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_EntranceSteps.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: bf291f345848121478757ff18e1ed172
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 2100000
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_FakeBounceCoreLight.mat b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_FakeBounceCoreLight.mat
new file mode 100644
index 0000000000000000000000000000000000000000..57f493d195207a3dc860f4f0a743761ae6e8ce83
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_FakeBounceCoreLight.mat
@@ -0,0 +1,77 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: m_lobbyHalloweenIndirect_FakeBounceCoreLight
+  m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
+  m_ShaderKeywords: _EMISSION
+  m_LightmapFlags: 2
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 1
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 1, g: 1, b: 1, a: 1}
+    - _EmissionColor: {r: 0.6367924, g: 0.7418753, b: 1, a: 1}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_FakeBounceCoreLight.mat.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_FakeBounceCoreLight.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..0d04902be4c287dbc5d809832bd7f0c04386353b
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_FakeBounceCoreLight.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: b721037d70058144c93b805f29a17bb4
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_Floor.mat b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_Floor.mat
new file mode 100644
index 0000000000000000000000000000000000000000..fd2bcfe848a97fd26d1331564b5d05ec0086317f
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_Floor.mat
@@ -0,0 +1,77 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: m_lobbyHalloweenIndirect_Floor
+  m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
+  m_ShaderKeywords: _EMISSION
+  m_LightmapFlags: 2
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 2800000, guid: e772000c3da2fcb42a6e019aed44180d, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 2800000, guid: e772000c3da2fcb42a6e019aed44180d, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 1, g: 1, b: 1, a: 1}
+    - _EmissionColor: {r: 1.2596959, g: 1.2596959, b: 1.2596959, a: 1}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_Floor.mat.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_Floor.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..08228aa63fc97224998054d927a78990f37bc507
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_Floor.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: ad20951c7d4fc954da8ecf4c6a94f472
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 2100000
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_Left.mat b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_Left.mat
new file mode 100644
index 0000000000000000000000000000000000000000..23a0faa6537768d8182c8b98396a63dc592047aa
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_Left.mat
@@ -0,0 +1,77 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: m_lobbyHalloweenIndirect_Left
+  m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
+  m_ShaderKeywords: _EMISSION
+  m_LightmapFlags: 2
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 2800000, guid: c8ec9d37614512e42a769c5284e319d7, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 2800000, guid: c8ec9d37614512e42a769c5284e319d7, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 1, g: 1, b: 1, a: 1}
+    - _EmissionColor: {r: 1.2596959, g: 1.2596959, b: 1.2596959, a: 1}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_Left.mat.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_Left.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..4cc08ffdb5740231646a975c9a53c274b971cc73
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_Left.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 9597afba2529f864cb3816b7a1320223
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 2100000
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_Logo.mat b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_Logo.mat
new file mode 100644
index 0000000000000000000000000000000000000000..068a94a70e3213aaf74d616897e534bd48046021
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_Logo.mat
@@ -0,0 +1,77 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: m_lobbyHalloweenIndirect_Logo
+  m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
+  m_ShaderKeywords: _EMISSION
+  m_LightmapFlags: 2
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 2800000, guid: a9dd91119de1a46458413b3998cb30d5, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 2800000, guid: a9dd91119de1a46458413b3998cb30d5, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 1, g: 1, b: 1, a: 1}
+    - _EmissionColor: {r: 1.2596959, g: 1.2596959, b: 1.2596959, a: 1}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_Logo.mat.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_Logo.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..3cc8afecf474308593ca61c32e97aee28bd38a18
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_Logo.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 4859830042bdbac4f8183f3d601226f1
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 2100000
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_Right.mat b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_Right.mat
new file mode 100644
index 0000000000000000000000000000000000000000..53995a6dfc52386e586b99a86488a2cc787036fc
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_Right.mat
@@ -0,0 +1,77 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: m_lobbyHalloweenIndirect_Right
+  m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
+  m_ShaderKeywords: _EMISSION
+  m_LightmapFlags: 2
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 2800000, guid: d0bf1bf0b0028de4a9393f017275e915, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 2800000, guid: d0bf1bf0b0028de4a9393f017275e915, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 1, g: 1, b: 1, a: 1}
+    - _EmissionColor: {r: 1.2596959, g: 1.2596959, b: 1.2596959, a: 1}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_Right.mat.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_Right.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..561f4a2a908b6347c930f6ab846a5aa79f89bbd1
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_Right.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: e776c09d3cbad96479b111d0f7428914
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 2100000
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_Signage.mat b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_Signage.mat
new file mode 100644
index 0000000000000000000000000000000000000000..d6d92469a24c76e0333c76a385db07fddb213359
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_Signage.mat
@@ -0,0 +1,77 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: m_lobbyHalloweenIndirect_Signage
+  m_Shader: {fileID: 46, guid: 0000000000000000f000000000000000, type: 0}
+  m_ShaderKeywords: _EMISSION
+  m_LightmapFlags: 2
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 2800000, guid: 99880f989042f01419dc854e8fd4d439, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 2800000, guid: 99880f989042f01419dc854e8fd4d439, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 1, g: 1, b: 1, a: 1}
+    - _EmissionColor: {r: 1, g: 1, b: 1, a: 1}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_Signage.mat.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_Signage.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..70bce048bb51bd2b2c47e751a89290eae287c160
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/Lighting/m_lobbyHalloweenIndirect_Signage.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: cba8e8dd69eb11d4eaa4a72680faf20d
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 2100000
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_backroundBuildings.mat b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_backroundBuildings.mat
new file mode 100644
index 0000000000000000000000000000000000000000..4876833db7e6cd5f4e578e0c0b7617d34364bd4c
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_backroundBuildings.mat
@@ -0,0 +1,52 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: m_backroundBuildings
+  m_Shader: {fileID: 4800000, guid: 860976c0d634347459e23b85a6e84611, type: 3}
+  m_ShaderKeywords: 
+  m_LightmapFlags: 4
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BuildingTex:
+        m_Texture: {fileID: 2800000, guid: 725b34d0ac8a1f841a7c6f1bf240f10d, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 2800000, guid: 725b34d0ac8a1f841a7c6f1bf240f10d, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _WindowTex:
+        m_Texture: {fileID: 2800000, guid: 80ebe7cd3ef898a4282f20afae0bbfd7, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _Debug: 0
+    - _EnableWindowTex: 0
+    - _LightIntensity: 1
+    - _LitThreshold: 0.461
+    - _MainTexSize: 1024
+    - _RandomSeed: 4.47
+    - _Speed: 0.0052
+    - _TexSizeAndPadding: 1024
+    - _Threshold: 0.81
+    - _WindowFromTex: 0
+    - _WindowTexSize: 1024
+    - _WindowThreshold: 0.672
+    m_Colors:
+    - _TexSizeAndPadding: {r: 1024, g: 1024, b: 1, a: 1}
+    - _WallColor: {r: 0.15143575, g: 0.138844, b: 0.206, a: 1}
+    - _WindowOffColor: {r: 0.2290466, g: 0.20570599, b: 0.32599998, a: 1}
+    - _WindowOnColor: {r: 1, g: 0.91082966, b: 0.6745283, a: 1}
+    - _WindowSizeAndPadding: {r: 9, g: 5, b: 30, a: 30}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_backroundBuildings.mat.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_backroundBuildings.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..44646d313e5db0c07490864b3a4bfa9be1cdc6a3
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_backroundBuildings.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: f20167634cdc0a040869b709fcf11dd7
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 2100000
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenCenterpiece.mat b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenCenterpiece.mat
new file mode 100644
index 0000000000000000000000000000000000000000..75a9a74e72a85f9bc60a11633b652907538fa4bb
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenCenterpiece.mat
@@ -0,0 +1,87 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: m_lobbyHalloweenCenterpiece
+  m_Shader: {fileID: 4800000, guid: f652a252ca50345e68c98404003292fc, type: 3}
+  m_ShaderKeywords: 
+  m_LightmapFlags: 4
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _hallwayCenterpieceHalloween_bc:
+        m_Texture: {fileID: 2800000, guid: 01362d47c603a634ba062db3fc0c8f19, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _texcoord:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _FlameFlickerSpeed: 0.28
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 1, g: 1, b: 1, a: 1}
+    - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
+    - _Scale: {r: 8, g: 50, b: 50, a: 0}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenCenterpiece.mat.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenCenterpiece.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..8e54afdbb4e4f6e80efaf400c2f5497c055be99e
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenCenterpiece.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 68df9835a31742e45a73423e851988b2
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 2100000
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenEntrance.mat b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenEntrance.mat
new file mode 100644
index 0000000000000000000000000000000000000000..186674af9dada705b489b8f9b4fee18997f051b6
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenEntrance.mat
@@ -0,0 +1,77 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: m_lobbyHalloweenEntrance
+  m_Shader: {fileID: 10752, guid: 0000000000000000f000000000000000, type: 0}
+  m_ShaderKeywords: 
+  m_LightmapFlags: 4
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 2800000, guid: 73e6bc914d39caf46830cfb0b0c96ea1, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 1, g: 1, b: 1, a: 1}
+    - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenEntrance.mat.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenEntrance.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..3a45f96feb43c23f8d3d25e6ca4448703239aecf
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenEntrance.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: c599b49289edec24b80e7203dd71e923
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 2100000
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenEntranceSteps.mat b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenEntranceSteps.mat
new file mode 100644
index 0000000000000000000000000000000000000000..0f79fbcbdec6f8feac3495b316337c5a63b4ac08
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenEntranceSteps.mat
@@ -0,0 +1,77 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: m_lobbyHalloweenEntranceSteps
+  m_Shader: {fileID: 10752, guid: 0000000000000000f000000000000000, type: 0}
+  m_ShaderKeywords: 
+  m_LightmapFlags: 4
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 2800000, guid: 161eb64efb42cab4a83d7f9be76577f3, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 1, g: 1, b: 1, a: 1}
+    - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenEntranceSteps.mat.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenEntranceSteps.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..00b1ecf936435cc2a1f30924bd27f29332e10126
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenEntranceSteps.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 9ecb43986f768494196721abee350438
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 2100000
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenFloor.mat b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenFloor.mat
new file mode 100644
index 0000000000000000000000000000000000000000..bd5e88122af17e7567570f5eae8c1c88fcfa3da3
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenFloor.mat
@@ -0,0 +1,77 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: m_lobbyHalloweenFloor
+  m_Shader: {fileID: 10752, guid: 0000000000000000f000000000000000, type: 0}
+  m_ShaderKeywords: 
+  m_LightmapFlags: 4
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 2800000, guid: e772000c3da2fcb42a6e019aed44180d, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 0.5754717, g: 0.5754717, b: 0.5754717, a: 1}
+    - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenFloor.mat.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenFloor.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..5c10b9f7610513ed12b292ae23f883ca3e3ef061
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenFloor.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 171613c76cf8ab34bb0e7730aea4518a
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 2100000
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenLeft.mat b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenLeft.mat
new file mode 100644
index 0000000000000000000000000000000000000000..3a68618d73328032acc7feb99b1766c87d77d6a0
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenLeft.mat
@@ -0,0 +1,77 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: m_lobbyHalloweenLeft
+  m_Shader: {fileID: 10752, guid: 0000000000000000f000000000000000, type: 0}
+  m_ShaderKeywords: 
+  m_LightmapFlags: 4
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 2800000, guid: c8ec9d37614512e42a769c5284e319d7, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 1, g: 1, b: 1, a: 1}
+    - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenLeft.mat.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenLeft.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..32f0c4c42f837a945cb78c5d5a6eeb75bbbd0875
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenLeft.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 706592767a8bc4a44a17742be6b9f861
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 2100000
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenLogo.mat b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenLogo.mat
new file mode 100644
index 0000000000000000000000000000000000000000..eb6dbebae2ffe28a65df51a3f36b4607d7bd1fc5
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenLogo.mat
@@ -0,0 +1,77 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: m_lobbyHalloweenLogo
+  m_Shader: {fileID: 10752, guid: 0000000000000000f000000000000000, type: 0}
+  m_ShaderKeywords: 
+  m_LightmapFlags: 4
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 2800000, guid: a9dd91119de1a46458413b3998cb30d5, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 1, g: 1, b: 1, a: 1}
+    - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenLogo.mat.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenLogo.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..10fe10a22bb4f82a93d1fd588eef794d9d636ff2
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenLogo.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: dc91c15231f7cdf45b765740f508fc77
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 2100000
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenRight.mat b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenRight.mat
new file mode 100644
index 0000000000000000000000000000000000000000..1691e36b84351d11876f3653d7ef0e750768d2d6
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenRight.mat
@@ -0,0 +1,77 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: m_lobbyHalloweenRight
+  m_Shader: {fileID: 10752, guid: 0000000000000000f000000000000000, type: 0}
+  m_ShaderKeywords: 
+  m_LightmapFlags: 4
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 2800000, guid: d0bf1bf0b0028de4a9393f017275e915, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 1, g: 1, b: 1, a: 1}
+    - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenRight.mat.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenRight.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..c779c73a609d5a7575c5b65f838bbfb440c01559
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenRight.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 82dd423c6668e594cb2a458ded33c717
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 2100000
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenSignage.mat b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenSignage.mat
new file mode 100644
index 0000000000000000000000000000000000000000..e3547bf6566a3c31da3c0c7d9a8d2138dabb7c06
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenSignage.mat
@@ -0,0 +1,77 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: m_lobbyHalloweenSignage
+  m_Shader: {fileID: 10752, guid: 0000000000000000f000000000000000, type: 0}
+  m_ShaderKeywords: 
+  m_LightmapFlags: 4
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 2800000, guid: 99880f989042f01419dc854e8fd4d439, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 1, g: 1, b: 1, a: 1}
+    - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenSignage.mat.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenSignage.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..9234d99cb95edc2459a4d3d8c94d2288728e8c90
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/m_lobbyHalloweenSignage.mat.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 0649fbada42027d498fb1e6c027a1bce
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 2100000
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_backroundBuildings.jpg b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_backroundBuildings.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..14f7d7cbf11734bbbb1bca02852b5bfb72ae5f9e
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_backroundBuildings.jpg
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:ab88c6003b4d4469fdcf3798ea5efc4d89370d582a9a7f8c0ca00b80c648948c
+size 129513
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_backroundBuildings.jpg.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_backroundBuildings.jpg.meta
new file mode 100644
index 0000000000000000000000000000000000000000..9c3ee44c200f0a3d69ae8e167d424c656d096211
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_backroundBuildings.jpg.meta
@@ -0,0 +1,132 @@
+fileFormatVersion: 2
+guid: 725b34d0ac8a1f841a7c6f1bf240f10d
+TextureImporter:
+  internalIDToNameTable: []
+  externalObjects: {}
+  serializedVersion: 11
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 1
+    sRGBTexture: 1
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 0
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: -1
+    aniso: -1
+    mipBias: -100
+    wrapU: -1
+    wrapV: -1
+    wrapW: -1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 1
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 0
+  textureShape: 1
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  applyGammaDecoding: 1
+  platformSettings:
+  - serializedVersion: 3
+    buildTarget: DefaultTexturePlatform
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 3
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  - serializedVersion: 3
+    buildTarget: Standalone
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 3
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  - serializedVersion: 3
+    buildTarget: iPhone
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 3
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  - serializedVersion: 3
+    buildTarget: Android
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 3
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    internalID: 0
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+    secondaryTextures: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenCenterpiece_bc.png b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenCenterpiece_bc.png
new file mode 100644
index 0000000000000000000000000000000000000000..950afa6d01f6b766bbff07e77beac7b45367e406
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenCenterpiece_bc.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a639876c6f99eba2e554397bc6b7dce458254ebc463519bf1fa931b16929e319
+size 965263
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenCenterpiece_bc.png.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenCenterpiece_bc.png.meta
new file mode 100644
index 0000000000000000000000000000000000000000..392bd34e2e6fc906340c1ba2aeb50493f428b096
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenCenterpiece_bc.png.meta
@@ -0,0 +1,132 @@
+fileFormatVersion: 2
+guid: 01362d47c603a634ba062db3fc0c8f19
+TextureImporter:
+  internalIDToNameTable: []
+  externalObjects: {}
+  serializedVersion: 11
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 1
+    sRGBTexture: 1
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 0
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: -1
+    aniso: -1
+    mipBias: -100
+    wrapU: -1
+    wrapV: -1
+    wrapW: -1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 1
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 0
+  textureShape: 1
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  applyGammaDecoding: 0
+  platformSettings:
+  - serializedVersion: 3
+    buildTarget: DefaultTexturePlatform
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 3
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  - serializedVersion: 3
+    buildTarget: Standalone
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 3
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  - serializedVersion: 3
+    buildTarget: iPhone
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 3
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  - serializedVersion: 3
+    buildTarget: Android
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: 51
+    textureCompression: 3
+    compressionQuality: 0
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 1
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    internalID: 0
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+    secondaryTextures: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenEntrance_Steps_bc.png b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenEntrance_Steps_bc.png
new file mode 100644
index 0000000000000000000000000000000000000000..d52dcc8bca5ea7643174e826e3e34dda88919fb3
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenEntrance_Steps_bc.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:841be165c9734e156f4836e4bb1b59a9742255a7e2d40b3aa42e9edc252c11fb
+size 736394
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenEntrance_Steps_bc.png.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenEntrance_Steps_bc.png.meta
new file mode 100644
index 0000000000000000000000000000000000000000..11a103d6b8c2e3fffa06ffa012c50fc194b34eb4
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenEntrance_Steps_bc.png.meta
@@ -0,0 +1,132 @@
+fileFormatVersion: 2
+guid: 161eb64efb42cab4a83d7f9be76577f3
+TextureImporter:
+  internalIDToNameTable: []
+  externalObjects: {}
+  serializedVersion: 11
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 1
+    sRGBTexture: 1
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 0
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: -1
+    aniso: -1
+    mipBias: -100
+    wrapU: -1
+    wrapV: -1
+    wrapW: -1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 1
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 0
+  textureShape: 1
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  applyGammaDecoding: 0
+  platformSettings:
+  - serializedVersion: 3
+    buildTarget: DefaultTexturePlatform
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 3
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  - serializedVersion: 3
+    buildTarget: Standalone
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 3
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  - serializedVersion: 3
+    buildTarget: Android
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: 51
+    textureCompression: 1
+    compressionQuality: 0
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 1
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  - serializedVersion: 3
+    buildTarget: iPhone
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 3
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    internalID: 0
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+    secondaryTextures: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenEntrance_bc.png b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenEntrance_bc.png
new file mode 100644
index 0000000000000000000000000000000000000000..42b640e6700fab55cd4b49211fc2e3a14e6011f1
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenEntrance_bc.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:db8f2923385f301f403d631faccf4b7fbb71149883bb4b2c3bb6810b0996a2e3
+size 649223
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenEntrance_bc.png.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenEntrance_bc.png.meta
new file mode 100644
index 0000000000000000000000000000000000000000..f2d472ce8e16af69a69f41bd0e69be5a8dfa2db3
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenEntrance_bc.png.meta
@@ -0,0 +1,132 @@
+fileFormatVersion: 2
+guid: 73e6bc914d39caf46830cfb0b0c96ea1
+TextureImporter:
+  internalIDToNameTable: []
+  externalObjects: {}
+  serializedVersion: 11
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 1
+    sRGBTexture: 1
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 0
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: -1
+    aniso: -1
+    mipBias: -100
+    wrapU: -1
+    wrapV: -1
+    wrapW: -1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 1
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 0
+  textureShape: 1
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  applyGammaDecoding: 0
+  platformSettings:
+  - serializedVersion: 3
+    buildTarget: DefaultTexturePlatform
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 3
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  - serializedVersion: 3
+    buildTarget: Standalone
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 3
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  - serializedVersion: 3
+    buildTarget: Android
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: 51
+    textureCompression: 1
+    compressionQuality: 0
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 1
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  - serializedVersion: 3
+    buildTarget: iPhone
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 3
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    internalID: 0
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+    secondaryTextures: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenFloor_bc.png b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenFloor_bc.png
new file mode 100644
index 0000000000000000000000000000000000000000..a51ee89fbbd46eca4b3b683802b452c07282a4f9
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenFloor_bc.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:daa0daf2aa74fb3317e0a8f5169435f6241581ce6e13cacbcbbf78ebd379c2b8
+size 496820
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenFloor_bc.png.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenFloor_bc.png.meta
new file mode 100644
index 0000000000000000000000000000000000000000..611b73a4d4cb21b32e020e9f07fb2c4dae46768a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenFloor_bc.png.meta
@@ -0,0 +1,132 @@
+fileFormatVersion: 2
+guid: e772000c3da2fcb42a6e019aed44180d
+TextureImporter:
+  internalIDToNameTable: []
+  externalObjects: {}
+  serializedVersion: 11
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 1
+    sRGBTexture: 1
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 0
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: -1
+    aniso: -1
+    mipBias: -100
+    wrapU: -1
+    wrapV: -1
+    wrapW: -1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 1
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 0
+  textureShape: 1
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  applyGammaDecoding: 0
+  platformSettings:
+  - serializedVersion: 3
+    buildTarget: DefaultTexturePlatform
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 3
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  - serializedVersion: 3
+    buildTarget: Standalone
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 3
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  - serializedVersion: 3
+    buildTarget: Android
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: 51
+    textureCompression: 1
+    compressionQuality: 0
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 1
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  - serializedVersion: 3
+    buildTarget: iPhone
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 3
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    internalID: 0
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+    secondaryTextures: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenLeft_bc.png b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenLeft_bc.png
new file mode 100644
index 0000000000000000000000000000000000000000..f376cb7ae2d2ca588d097979a2ebbb98a219cc2a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenLeft_bc.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:6b5c65c4d51e5594f81a60987579f9cd489e2645a507ce347a962f9a8c237f01
+size 720519
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenLeft_bc.png.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenLeft_bc.png.meta
new file mode 100644
index 0000000000000000000000000000000000000000..154fd71d163aa7711ce79db9a5b84b711590d1cf
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenLeft_bc.png.meta
@@ -0,0 +1,132 @@
+fileFormatVersion: 2
+guid: c8ec9d37614512e42a769c5284e319d7
+TextureImporter:
+  internalIDToNameTable: []
+  externalObjects: {}
+  serializedVersion: 11
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 1
+    sRGBTexture: 1
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 0
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: -1
+    aniso: -1
+    mipBias: -100
+    wrapU: -1
+    wrapV: -1
+    wrapW: -1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 1
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 0
+  textureShape: 1
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  applyGammaDecoding: 0
+  platformSettings:
+  - serializedVersion: 3
+    buildTarget: DefaultTexturePlatform
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 3
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  - serializedVersion: 3
+    buildTarget: Standalone
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 3
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  - serializedVersion: 3
+    buildTarget: Android
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: 51
+    textureCompression: 1
+    compressionQuality: 0
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 1
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  - serializedVersion: 3
+    buildTarget: iPhone
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 3
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    internalID: 0
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+    secondaryTextures: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenLogo_bc.png b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenLogo_bc.png
new file mode 100644
index 0000000000000000000000000000000000000000..0709a336af23dfe50aedf3803dc109ad89f2be01
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenLogo_bc.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:895e03609a16b917ebe6cee1242bdf1322587876b27f58bfe2a36da3abe1b090
+size 713959
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenLogo_bc.png.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenLogo_bc.png.meta
new file mode 100644
index 0000000000000000000000000000000000000000..d926d1732fef86a44757df0b9de9b99aa46e354e
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenLogo_bc.png.meta
@@ -0,0 +1,132 @@
+fileFormatVersion: 2
+guid: a9dd91119de1a46458413b3998cb30d5
+TextureImporter:
+  internalIDToNameTable: []
+  externalObjects: {}
+  serializedVersion: 11
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 1
+    sRGBTexture: 1
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 0
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: -1
+    aniso: -1
+    mipBias: -100
+    wrapU: -1
+    wrapV: -1
+    wrapW: -1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 1
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 0
+  textureShape: 1
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  applyGammaDecoding: 0
+  platformSettings:
+  - serializedVersion: 3
+    buildTarget: DefaultTexturePlatform
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 3
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  - serializedVersion: 3
+    buildTarget: Standalone
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 3
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  - serializedVersion: 3
+    buildTarget: Android
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: 51
+    textureCompression: 1
+    compressionQuality: 0
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 1
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  - serializedVersion: 3
+    buildTarget: iPhone
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 3
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    internalID: 0
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+    secondaryTextures: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenRight_bc.png b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenRight_bc.png
new file mode 100644
index 0000000000000000000000000000000000000000..315e0c5e6da0f43b9aad64d8de7420012da57203
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenRight_bc.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:3c909c03d2a8340e3094e780066b05d33e4bd4980aace7cf7702b6739da5affa
+size 789374
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenRight_bc.png.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenRight_bc.png.meta
new file mode 100644
index 0000000000000000000000000000000000000000..0cd775855fdfdba276d1e0660a30d90dce276af6
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenRight_bc.png.meta
@@ -0,0 +1,132 @@
+fileFormatVersion: 2
+guid: d0bf1bf0b0028de4a9393f017275e915
+TextureImporter:
+  internalIDToNameTable: []
+  externalObjects: {}
+  serializedVersion: 11
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 1
+    sRGBTexture: 1
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 0
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: -1
+    aniso: -1
+    mipBias: -100
+    wrapU: -1
+    wrapV: -1
+    wrapW: -1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 1
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 0
+  textureShape: 1
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  applyGammaDecoding: 0
+  platformSettings:
+  - serializedVersion: 3
+    buildTarget: DefaultTexturePlatform
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 3
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  - serializedVersion: 3
+    buildTarget: Standalone
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 3
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  - serializedVersion: 3
+    buildTarget: Android
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: 51
+    textureCompression: 1
+    compressionQuality: 0
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 1
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  - serializedVersion: 3
+    buildTarget: iPhone
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 3
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    internalID: 0
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+    secondaryTextures: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenSignage_bc.png b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenSignage_bc.png
new file mode 100644
index 0000000000000000000000000000000000000000..9d93da87cec383812658d7cbafed6c689d4229aa
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenSignage_bc.png
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:787f2d18bcff111181296c54264b1421b981b06bd924e65c1676c93e48d2d102
+size 737628
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenSignage_bc.png.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenSignage_bc.png.meta
new file mode 100644
index 0000000000000000000000000000000000000000..685a3b7cafce6a8b6c3e46013abfaee2b43433ad
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Materials/t_lobbyHalloweenSignage_bc.png.meta
@@ -0,0 +1,132 @@
+fileFormatVersion: 2
+guid: 99880f989042f01419dc854e8fd4d439
+TextureImporter:
+  internalIDToNameTable: []
+  externalObjects: {}
+  serializedVersion: 11
+  mipmaps:
+    mipMapMode: 0
+    enableMipMap: 1
+    sRGBTexture: 1
+    linearTexture: 0
+    fadeOut: 0
+    borderMipMap: 0
+    mipMapsPreserveCoverage: 0
+    alphaTestReferenceValue: 0.5
+    mipMapFadeDistanceStart: 1
+    mipMapFadeDistanceEnd: 3
+  bumpmap:
+    convertToNormalMap: 0
+    externalNormalMap: 0
+    heightScale: 0.25
+    normalMapFilter: 0
+  isReadable: 0
+  streamingMipmaps: 0
+  streamingMipmapsPriority: 0
+  grayScaleToAlpha: 0
+  generateCubemap: 6
+  cubemapConvolution: 0
+  seamlessCubemap: 0
+  textureFormat: 1
+  maxTextureSize: 2048
+  textureSettings:
+    serializedVersion: 2
+    filterMode: 2
+    aniso: 4
+    mipBias: -100
+    wrapU: -1
+    wrapV: -1
+    wrapW: -1
+  nPOTScale: 1
+  lightmap: 0
+  compressionQuality: 50
+  spriteMode: 0
+  spriteExtrude: 1
+  spriteMeshType: 1
+  alignment: 0
+  spritePivot: {x: 0.5, y: 0.5}
+  spritePixelsToUnits: 100
+  spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+  spriteGenerateFallbackPhysicsShape: 1
+  alphaUsage: 1
+  alphaIsTransparency: 0
+  spriteTessellationDetail: -1
+  textureType: 0
+  textureShape: 1
+  singleChannelComponent: 0
+  maxTextureSizeSet: 0
+  compressionQualitySet: 0
+  textureFormatSet: 0
+  applyGammaDecoding: 0
+  platformSettings:
+  - serializedVersion: 3
+    buildTarget: DefaultTexturePlatform
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 3
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  - serializedVersion: 3
+    buildTarget: Standalone
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 3
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  - serializedVersion: 3
+    buildTarget: Android
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: 50
+    textureCompression: 1
+    compressionQuality: 0
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 1
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  - serializedVersion: 3
+    buildTarget: iPhone
+    // @lint-ignore SOCIALVRTEXTUREIMPORTS
+    maxTextureSize: 2048
+    resizeAlgorithm: 0
+    textureFormat: -1
+    textureCompression: 3
+    compressionQuality: 50
+    crunchedCompression: 0
+    allowsAlphaSplitting: 0
+    overridden: 0
+    androidETC2FallbackOverride: 0
+    forceMaximumCompressionQuality_BC6H_BC7: 0
+  spriteSheet:
+    serializedVersion: 2
+    sprites: []
+    outline: []
+    physicsShape: []
+    bones: []
+    spriteID: 
+    internalID: 0
+    vertices: []
+    indices: 
+    edges: []
+    weights: []
+    secondaryTextures: []
+  spritePackingTag: 
+  pSDRemoveMatte: 0
+  pSDShowRemoveMatteOption: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Meshes.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Meshes.meta
new file mode 100644
index 0000000000000000000000000000000000000000..f21b0dfa847061a58b52ec49c5e657102471d4b4
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Meshes.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 0f04e6c2d35eebf48ac7c41690628649
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Meshes/sm_backroundBuildings.fbx b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Meshes/sm_backroundBuildings.fbx
new file mode 100644
index 0000000000000000000000000000000000000000..b13d4c19d596998fc3b9d4122e3b493c76a9d196
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Meshes/sm_backroundBuildings.fbx
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:d4b2ee6037d18b78dc30416db5f62903a656fcc5775cb51d37abf7b2ed077f22
+size 33104
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Meshes/sm_backroundBuildings.fbx.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Meshes/sm_backroundBuildings.fbx.meta
new file mode 100644
index 0000000000000000000000000000000000000000..787cd73acf14b223a93e3491036724ee50af4bb7
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Meshes/sm_backroundBuildings.fbx.meta
@@ -0,0 +1,97 @@
+fileFormatVersion: 2
+guid: 44c4aea1bdd6151468edcdce75c97976
+ModelImporter:
+  serializedVersion: 23
+  fileIDToRecycleName:
+    100000: //RootNode
+    400000: //RootNode
+    2100000: background buildings
+    2300000: //RootNode
+    3300000: //RootNode
+    4300000: mdl_building
+  externalObjects: {}
+  materials:
+    importMaterials: 1
+    materialName: 0
+    materialSearch: 1
+    materialLocation: 1
+  animations:
+    legacyGenerateAnimations: 4
+    bakeSimulation: 0
+    resampleCurves: 1
+    optimizeGameObjects: 0
+    motionNodeName: 
+    rigImportErrors: 
+    rigImportWarnings: 
+    animationImportErrors: 
+    animationImportWarnings: 
+    animationRetargetingWarnings: 
+    animationDoRetargetingWarnings: 0
+    importAnimatedCustomProperties: 0
+    importConstraints: 0
+    animationCompression: 1
+    animationRotationError: 0.5
+    animationPositionError: 0.5
+    animationScaleError: 0.5
+    animationWrapMode: 0
+    extraExposedTransformPaths: []
+    extraUserProperties: []
+    clipAnimations: []
+    isReadable: 0
+  meshes:
+    lODScreenPercentages: []
+    globalScale: 1
+    meshCompression: 0
+    addColliders: 0
+    useSRGBMaterialColor: 1
+    importVisibility: 1
+    importBlendShapes: 1
+    importCameras: 1
+    importLights: 1
+    swapUVChannels: 0
+    generateSecondaryUV: 0
+    useFileUnits: 1
+    optimizeMeshForGPU: 1
+    keepQuads: 0
+    weldVertices: 1
+    preserveHierarchy: 0
+    indexFormat: 0
+    secondaryUVAngleDistortion: 8
+    secondaryUVAreaDistortion: 15.000001
+    secondaryUVHardAngle: 88
+    secondaryUVPackMargin: 4
+    useFileScale: 1
+    previousCalculatedGlobalScale: 1
+    hasPreviousCalculatedGlobalScale: 0
+  tangentSpace:
+    normalSmoothAngle: 60
+    normalImportMode: 0
+    tangentImportMode: 3
+    normalCalculationMode: 4
+    legacyComputeAllNormalsFromSmoothingGroupsWhenMeshHasBlendShapes: 0
+    blendShapeNormalImportMode: 1
+    normalSmoothingSource: 0
+  importAnimation: 1
+  copyAvatar: 0
+  humanDescription:
+    serializedVersion: 2
+    human: []
+    skeleton: []
+    armTwist: 0.5
+    foreArmTwist: 0.5
+    upperLegTwist: 0.5
+    legTwist: 0.5
+    armStretch: 0.05
+    legStretch: 0.05
+    feetSpacing: 0
+    rootMotionBoneName: 
+    hasTranslationDoF: 0
+    hasExtraRoot: 0
+    skeletonHasParents: 1
+  lastHumanDescriptionAvatarSource: {instanceID: 0}
+  animationType: 0
+  humanoidOversampling: 1
+  additionalBone: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Meshes/sm_lobbyHalloween.fbx b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Meshes/sm_lobbyHalloween.fbx
new file mode 100644
index 0000000000000000000000000000000000000000..97887cefc3186fbe2c3e90855fa7b9ca04e77ca2
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Meshes/sm_lobbyHalloween.fbx
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:38d6093739de8c4b0528f5a0f0c3279a3055dc457d270c890658653d4d5af3e3
+size 1259164
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Meshes/sm_lobbyHalloween.fbx.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Meshes/sm_lobbyHalloween.fbx.meta
new file mode 100644
index 0000000000000000000000000000000000000000..761ca5787303661f4bae48fd69a89908bbabcfe9
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Meshes/sm_lobbyHalloween.fbx.meta
@@ -0,0 +1,135 @@
+fileFormatVersion: 2
+guid: 8b471d963a4288f40be104b5e2c8b2c5
+ModelImporter:
+  serializedVersion: 23
+  fileIDToRecycleName:
+    100000: lobby_halloween_centerpiece.001
+    100002: lobby_halloween_COLLISION
+    100004: lobby_halloween_entrance.001
+    100006: lobby_halloween_entrancesteps.001
+    100008: lobby_halloween_floor.001
+    100010: lobby_halloween_left.001
+    100012: lobby_halloween_logo.001
+    100014: lobby_halloween_right.001
+    100016: //RootNode
+    400000: lobby_halloween_centerpiece.001
+    400002: lobby_halloween_COLLISION
+    400004: lobby_halloween_entrance.001
+    400006: lobby_halloween_entrancesteps.001
+    400008: lobby_halloween_floor.001
+    400010: lobby_halloween_left.001
+    400012: lobby_halloween_logo.001
+    400014: lobby_halloween_right.001
+    400016: //RootNode
+    2100000: No Name
+    2300000: lobby_halloween_centerpiece.001
+    2300002: lobby_halloween_COLLISION
+    2300004: lobby_halloween_entrance.001
+    2300006: lobby_halloween_entrancesteps.001
+    2300008: lobby_halloween_floor.001
+    2300010: lobby_halloween_left.001
+    2300012: lobby_halloween_logo.001
+    2300014: lobby_halloween_right.001
+    3300000: lobby_halloween_centerpiece.001
+    3300002: lobby_halloween_COLLISION
+    3300004: lobby_halloween_entrance.001
+    3300006: lobby_halloween_entrancesteps.001
+    3300008: lobby_halloween_floor.001
+    3300010: lobby_halloween_left.001
+    3300012: lobby_halloween_logo.001
+    3300014: lobby_halloween_right.001
+    4300000: lobby_halloween_COLLISION
+    4300002: lobby_halloween_left.001
+    4300004: lobby_halloween_logo.001
+    4300006: lobby_halloween_right.001
+    4300008: lobby_halloween_entrance.001
+    4300010: lobby_halloween_entrancesteps.001
+    4300012: lobby_halloween_floor.001
+    4300014: lobby_halloween_centerpiece.001
+    9500000: //RootNode
+  externalObjects: {}
+  materials:
+    importMaterials: 1
+    materialName: 0
+    materialSearch: 1
+    materialLocation: 1
+  animations:
+    legacyGenerateAnimations: 4
+    bakeSimulation: 0
+    resampleCurves: 1
+    optimizeGameObjects: 0
+    motionNodeName: 
+    rigImportErrors: 
+    rigImportWarnings: 
+    animationImportErrors: 
+    animationImportWarnings: 
+    animationRetargetingWarnings: 
+    animationDoRetargetingWarnings: 0
+    importAnimatedCustomProperties: 0
+    importConstraints: 0
+    animationCompression: 1
+    animationRotationError: 0.5
+    animationPositionError: 0.5
+    animationScaleError: 0.5
+    animationWrapMode: 0
+    extraExposedTransformPaths: []
+    extraUserProperties: []
+    clipAnimations: []
+    isReadable: 0
+  meshes:
+    lODScreenPercentages: []
+    globalScale: 1
+    meshCompression: 0
+    addColliders: 0
+    useSRGBMaterialColor: 1
+    importVisibility: 1
+    importBlendShapes: 1
+    importCameras: 1
+    importLights: 1
+    swapUVChannels: 0
+    generateSecondaryUV: 0
+    useFileUnits: 1
+    optimizeMeshForGPU: 1
+    keepQuads: 0
+    weldVertices: 1
+    preserveHierarchy: 0
+    indexFormat: 0
+    secondaryUVAngleDistortion: 8
+    secondaryUVAreaDistortion: 15.000001
+    secondaryUVHardAngle: 88
+    secondaryUVPackMargin: 4
+    useFileScale: 1
+    previousCalculatedGlobalScale: 0.01
+    hasPreviousCalculatedGlobalScale: 1
+  tangentSpace:
+    normalSmoothAngle: 60
+    normalImportMode: 0
+    tangentImportMode: 3
+    normalCalculationMode: 4
+    legacyComputeAllNormalsFromSmoothingGroupsWhenMeshHasBlendShapes: 0
+    blendShapeNormalImportMode: 1
+    normalSmoothingSource: 0
+  importAnimation: 1
+  copyAvatar: 0
+  humanDescription:
+    serializedVersion: 2
+    human: []
+    skeleton: []
+    armTwist: 0.5
+    foreArmTwist: 0.5
+    upperLegTwist: 0.5
+    legTwist: 0.5
+    armStretch: 0.05
+    legStretch: 0.05
+    feetSpacing: 0
+    rootMotionBoneName: 
+    hasTranslationDoF: 0
+    hasExtraRoot: 0
+    skeletonHasParents: 1
+  lastHumanDescriptionAvatarSource: {instanceID: 0}
+  animationType: 2
+  humanoidOversampling: 1
+  additionalBone: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Meshes/sm_lobbyHalloweenSignage.fbx b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Meshes/sm_lobbyHalloweenSignage.fbx
new file mode 100644
index 0000000000000000000000000000000000000000..592aa80f4aef7fe535300b80fd42f36ea85d9845
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Meshes/sm_lobbyHalloweenSignage.fbx
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:54fa83a3e76ea33714ef8c10273db24f5746c505bd6edd71b47633a007b82235
+size 60059
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Meshes/sm_lobbyHalloweenSignage.fbx.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Meshes/sm_lobbyHalloweenSignage.fbx.meta
new file mode 100644
index 0000000000000000000000000000000000000000..66a41148da1ade575d97793b58c862af87020478
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Meshes/sm_lobbyHalloweenSignage.fbx.meta
@@ -0,0 +1,98 @@
+fileFormatVersion: 2
+guid: 94ed071c059f6e04c895dc605415789c
+ModelImporter:
+  serializedVersion: 23
+  fileIDToRecycleName:
+    100000: //RootNode
+    400000: //RootNode
+    2100000: hallwaySignage_mat
+    2300000: //RootNode
+    3300000: //RootNode
+    4300000: hallwaySignage_sm
+    9500000: //RootNode
+  externalObjects: {}
+  materials:
+    importMaterials: 1
+    materialName: 0
+    materialSearch: 1
+    materialLocation: 1
+  animations:
+    legacyGenerateAnimations: 4
+    bakeSimulation: 0
+    resampleCurves: 1
+    optimizeGameObjects: 0
+    motionNodeName: 
+    rigImportErrors: 
+    rigImportWarnings: 
+    animationImportErrors: 
+    animationImportWarnings: 
+    animationRetargetingWarnings: 
+    animationDoRetargetingWarnings: 0
+    importAnimatedCustomProperties: 0
+    importConstraints: 0
+    animationCompression: 1
+    animationRotationError: 0.5
+    animationPositionError: 0.5
+    animationScaleError: 0.5
+    animationWrapMode: 0
+    extraExposedTransformPaths: []
+    extraUserProperties: []
+    clipAnimations: []
+    isReadable: 0
+  meshes:
+    lODScreenPercentages: []
+    globalScale: 1
+    meshCompression: 0
+    addColliders: 0
+    useSRGBMaterialColor: 1
+    importVisibility: 1
+    importBlendShapes: 1
+    importCameras: 1
+    importLights: 1
+    swapUVChannels: 0
+    generateSecondaryUV: 0
+    useFileUnits: 1
+    optimizeMeshForGPU: 1
+    keepQuads: 0
+    weldVertices: 1
+    preserveHierarchy: 0
+    indexFormat: 0
+    secondaryUVAngleDistortion: 8
+    secondaryUVAreaDistortion: 15.000001
+    secondaryUVHardAngle: 88
+    secondaryUVPackMargin: 4
+    useFileScale: 1
+    previousCalculatedGlobalScale: 0.01
+    hasPreviousCalculatedGlobalScale: 1
+  tangentSpace:
+    normalSmoothAngle: 60
+    normalImportMode: 0
+    tangentImportMode: 3
+    normalCalculationMode: 4
+    legacyComputeAllNormalsFromSmoothingGroupsWhenMeshHasBlendShapes: 0
+    blendShapeNormalImportMode: 1
+    normalSmoothingSource: 0
+  importAnimation: 1
+  copyAvatar: 0
+  humanDescription:
+    serializedVersion: 2
+    human: []
+    skeleton: []
+    armTwist: 0.5
+    foreArmTwist: 0.5
+    upperLegTwist: 0.5
+    legTwist: 0.5
+    armStretch: 0.05
+    legStretch: 0.05
+    feetSpacing: 0
+    rootMotionBoneName: 
+    hasTranslationDoF: 0
+    hasExtraRoot: 0
+    skeletonHasParents: 1
+  lastHumanDescriptionAvatarSource: {instanceID: 0}
+  animationType: 2
+  humanoidOversampling: 1
+  additionalBone: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Prefabs.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Prefabs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..b251d77b051263d5cc90a8088110893bfd314713
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Prefabs.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 7716ebf2f71aa6f49a893933eeb5ee17
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Prefabs/p_lobbyHalloween_Environment.prefab b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Prefabs/p_lobbyHalloween_Environment.prefab
new file mode 100644
index 0000000000000000000000000000000000000000..eba116cd3446cf503105dc53c21b365cd0fea27b
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Prefabs/p_lobbyHalloween_Environment.prefab
@@ -0,0 +1,3717 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &55582728167133232
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 2064768525936009149}
+  - component: {fileID: 2445407753217654580}
+  - component: {fileID: 7265343296571672608}
+  - component: {fileID: 3270326559153655526}
+  m_Layer: 0
+  m_Name: collider1 (6)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &2064768525936009149
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 55582728167133232}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: -12.39, y: 9.42, z: -7.08}
+  m_LocalScale: {x: 10.304073, y: 12.76757, z: 3.3629544}
+  m_Children: []
+  m_Father: {fileID: 3353729650955080407}
+  m_RootOrder: 2
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &2445407753217654580
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 55582728167133232}
+  m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &7265343296571672608
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 55582728167133232}
+  m_Enabled: 0
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 0}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!65 &3270326559153655526
+BoxCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 55582728167133232}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Size: {x: 1, y: 1, z: 1}
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &102361312753525059
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 4196022544768814164}
+  - component: {fileID: 6310792436135542796}
+  - component: {fileID: 6268752020957039124}
+  - component: {fileID: 8023312618151559770}
+  m_Layer: 0
+  m_Name: collider1 (46)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &4196022544768814164
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 102361312753525059}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0.06, y: 11.0324, z: -37.48}
+  m_LocalScale: {x: 18.204464, y: 13.689464, z: 4.6154585}
+  m_Children: []
+  m_Father: {fileID: 3353729650955080407}
+  m_RootOrder: 15
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &6310792436135542796
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 102361312753525059}
+  m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &6268752020957039124
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 102361312753525059}
+  m_Enabled: 0
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 0}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!65 &8023312618151559770
+BoxCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 102361312753525059}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Size: {x: 1, y: 1, z: 1}
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &180987917631426796
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 3583911581462720150}
+  - component: {fileID: 3676964913895221675}
+  - component: {fileID: 2653469132624082816}
+  - component: {fileID: 8339052562223647102}
+  m_Layer: 0
+  m_Name: collider1 (34)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &3583911581462720150
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 180987917631426796}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 14.64, y: 9.19, z: -16.47}
+  m_LocalScale: {x: 2.8407543, y: 6.809734, z: 3.8615947}
+  m_Children: []
+  m_Father: {fileID: 3353729650955080407}
+  m_RootOrder: 32
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &3676964913895221675
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 180987917631426796}
+  m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &2653469132624082816
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 180987917631426796}
+  m_Enabled: 0
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 0}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!65 &8339052562223647102
+BoxCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 180987917631426796}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Size: {x: 1, y: 1, z: 1}
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &243886351912059980
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 9022022389515057316}
+  - component: {fileID: 8890316104923553196}
+  - component: {fileID: 5752761045824021755}
+  - component: {fileID: 106987677476199435}
+  m_Layer: 0
+  m_Name: collider1 (13)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &9022022389515057316
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 243886351912059980}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 12.56, y: 9.42, z: -17.04}
+  m_LocalScale: {x: 10.648528, y: 12.76757, z: 3.3273466}
+  m_Children: []
+  m_Father: {fileID: 3353729650955080407}
+  m_RootOrder: 28
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &8890316104923553196
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 243886351912059980}
+  m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &5752761045824021755
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 243886351912059980}
+  m_Enabled: 0
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 0}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!65 &106987677476199435
+BoxCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 243886351912059980}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Size: {x: 1, y: 1, z: 1}
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &971301258112731526
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 8040505461054105852}
+  - component: {fileID: 9083252759474641008}
+  - component: {fileID: 6898992166305903773}
+  - component: {fileID: 4817906150866191969}
+  m_Layer: 0
+  m_Name: collider1 (24)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &8040505461054105852
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 971301258112731526}
+  m_LocalRotation: {x: -0, y: 1, z: -0, w: 0}
+  m_LocalPosition: {x: 17.655827, y: 9.19, z: -8.0414505}
+  m_LocalScale: {x: 1.6025015, y: 6.809734, z: 1.1547327}
+  m_Children: []
+  m_Father: {fileID: 3353729650955080407}
+  m_RootOrder: 9
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &9083252759474641008
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 971301258112731526}
+  m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &6898992166305903773
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 971301258112731526}
+  m_Enabled: 0
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 0}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!65 &4817906150866191969
+BoxCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 971301258112731526}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Size: {x: 1, y: 1, z: 1}
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &1006482927311661150
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 4028062206930188956}
+  - component: {fileID: 584883587096837199}
+  - component: {fileID: 7008345515320884223}
+  - component: {fileID: 5326399505310547099}
+  m_Layer: 0
+  m_Name: collider1 (14)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &4028062206930188956
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1006482927311661150}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 14.64, y: 9.19, z: 2.09}
+  m_LocalScale: {x: 2.8407543, y: 6.809734, z: 3.8615947}
+  m_Children: []
+  m_Father: {fileID: 3353729650955080407}
+  m_RootOrder: 29
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &584883587096837199
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1006482927311661150}
+  m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &7008345515320884223
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1006482927311661150}
+  m_Enabled: 0
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 0}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!65 &5326399505310547099
+BoxCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1006482927311661150}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Size: {x: 1, y: 1, z: 1}
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &1085403854665725716
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 6173980292281361691}
+  - component: {fileID: 4374575554922505875}
+  - component: {fileID: 7299190905765016631}
+  - component: {fileID: 4953831447562123714}
+  m_Layer: 0
+  m_Name: collider1 (16)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &6173980292281361691
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1085403854665725716}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: -17.603, y: 9.19, z: -6.2320004}
+  m_LocalScale: {x: 1.6025015, y: 6.809734, z: 1.1547327}
+  m_Children: []
+  m_Father: {fileID: 3353729650955080407}
+  m_RootOrder: 6
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &4374575554922505875
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1085403854665725716}
+  m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &7299190905765016631
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1085403854665725716}
+  m_Enabled: 0
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 0}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!65 &4953831447562123714
+BoxCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1085403854665725716}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Size: {x: 1, y: 1, z: 1}
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &1213581082506065215
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 6994417639021962998}
+  - component: {fileID: 2685299916631613789}
+  - component: {fileID: 9156717729050136268}
+  - component: {fileID: 8074234416481335605}
+  m_Layer: 0
+  m_Name: collider1 (35)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &6994417639021962998
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1213581082506065215}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 14.64, y: 9.19, z: -26.41}
+  m_LocalScale: {x: 2.8407543, y: 6.809734, z: 3.8615947}
+  m_Children: []
+  m_Father: {fileID: 3353729650955080407}
+  m_RootOrder: 34
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &2685299916631613789
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1213581082506065215}
+  m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &9156717729050136268
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1213581082506065215}
+  m_Enabled: 0
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 0}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!65 &8074234416481335605
+BoxCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1213581082506065215}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Size: {x: 1, y: 1, z: 1}
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &1554666321640776991
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 344473347311092259}
+  - component: {fileID: 5465435055694698124}
+  - component: {fileID: 1030230606095263163}
+  - component: {fileID: 5387337815566509469}
+  m_Layer: 0
+  m_Name: collider1 (30)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &344473347311092259
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1554666321640776991}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: -14.64, y: 9.19, z: -16.47}
+  m_LocalScale: {x: 2.8407543, y: 6.809734, z: 3.8615947}
+  m_Children: []
+  m_Father: {fileID: 3353729650955080407}
+  m_RootOrder: 23
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &5465435055694698124
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1554666321640776991}
+  m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &1030230606095263163
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1554666321640776991}
+  m_Enabled: 0
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 0}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!65 &5387337815566509469
+BoxCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1554666321640776991}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Size: {x: 1, y: 1, z: 1}
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &2249397051891648242
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 3197306933664388246}
+  - component: {fileID: 2288190272639224125}
+  - component: {fileID: 5106795222614013207}
+  - component: {fileID: 5481161881869240290}
+  m_Layer: 0
+  m_Name: collider1 (32)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &3197306933664388246
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2249397051891648242}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 11.88, y: 9.42, z: 5.07}
+  m_LocalScale: {x: 9.443277, y: 12.76757, z: 7.666875}
+  m_Children: []
+  m_Father: {fileID: 3353729650955080407}
+  m_RootOrder: 26
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &2288190272639224125
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2249397051891648242}
+  m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &5106795222614013207
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2249397051891648242}
+  m_Enabled: 0
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 0}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!65 &5481161881869240290
+BoxCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2249397051891648242}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Size: {x: 1, y: 1, z: 1}
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &2314440408106418640
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 5323802657594753364}
+  - component: {fileID: 5311276818823461195}
+  - component: {fileID: 8546268317445898475}
+  - component: {fileID: 4277622296119232733}
+  m_Layer: 0
+  m_Name: collider1 (15)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &5323802657594753364
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2314440408106418640}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 14.64, y: 9.19, z: -7.91}
+  m_LocalScale: {x: 2.8407543, y: 6.809734, z: 3.8615947}
+  m_Children: []
+  m_Father: {fileID: 3353729650955080407}
+  m_RootOrder: 31
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &5311276818823461195
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2314440408106418640}
+  m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &8546268317445898475
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2314440408106418640}
+  m_Enabled: 0
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 0}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!65 &4277622296119232733
+BoxCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2314440408106418640}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Size: {x: 1, y: 1, z: 1}
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &2327416774218649801
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 4555507070702984820}
+  - component: {fileID: 6753889233756820115}
+  - component: {fileID: 7202350489774184804}
+  - component: {fileID: 4317451570284963964}
+  m_Layer: 0
+  m_Name: collider1 (11)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &4555507070702984820
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2327416774218649801}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 12.39, y: 9.42, z: -7.08}
+  m_LocalScale: {x: 10.304073, y: 12.76757, z: 3.3629544}
+  m_Children: []
+  m_Father: {fileID: 3353729650955080407}
+  m_RootOrder: 27
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &6753889233756820115
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2327416774218649801}
+  m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &7202350489774184804
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2327416774218649801}
+  m_Enabled: 0
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 0}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!65 &4317451570284963964
+BoxCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2327416774218649801}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Size: {x: 1, y: 1, z: 1}
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &2537780761971518445
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 9039823294720213719}
+  m_Layer: 0
+  m_Name: p_lobbyHalloween_Environment
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &9039823294720213719
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2537780761971518445}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 5273238663270862498}
+  - {fileID: 2468495850804887366}
+  - {fileID: 7959423107985583039}
+  - {fileID: 3353729650955080407}
+  m_Father: {fileID: 0}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &2999475219152601601
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 3936678173783502513}
+  - component: {fileID: 5569208066686976084}
+  - component: {fileID: 2237011750212248559}
+  - component: {fileID: 4897437581923645006}
+  m_Layer: 0
+  m_Name: collider1 (7)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &3936678173783502513
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2999475219152601601}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: -14.64, y: 9.19, z: -17.86}
+  m_LocalScale: {x: 2.8407543, y: 6.809734, z: 3.8615947}
+  m_Children: []
+  m_Father: {fileID: 3353729650955080407}
+  m_RootOrder: 3
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &5569208066686976084
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2999475219152601601}
+  m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &2237011750212248559
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2999475219152601601}
+  m_Enabled: 0
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 0}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!65 &4897437581923645006
+BoxCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2999475219152601601}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Size: {x: 1, y: 1, z: 1}
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &3025386790161722994
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 6100307934943179590}
+  - component: {fileID: 6361155783864993651}
+  - component: {fileID: 8266283065563172921}
+  - component: {fileID: 5540794547078620340}
+  m_Layer: 0
+  m_Name: collider1 (3)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &6100307934943179590
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3025386790161722994}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 12.52, y: 11.5, z: -27.13}
+  m_LocalScale: {x: 10.564761, y: 12.533997, z: 3.5918987}
+  m_Children: []
+  m_Father: {fileID: 3353729650955080407}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &6361155783864993651
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3025386790161722994}
+  m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &8266283065563172921
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3025386790161722994}
+  m_Enabled: 0
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 0}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!65 &5540794547078620340
+BoxCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3025386790161722994}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Size: {x: 1, y: 1, z: 1}
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &3026109493489212333
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1919892219126379021}
+  - component: {fileID: 1699695942681303956}
+  - component: {fileID: 911182518007812963}
+  - component: {fileID: 7891028562107312248}
+  m_Layer: 0
+  m_Name: collider1 (42)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1919892219126379021
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3026109493489212333}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0.06, y: 9.42, z: 15.24}
+  m_LocalScale: {x: 9.984349, y: 12.76757, z: 7.666875}
+  m_Children: []
+  m_Father: {fileID: 3353729650955080407}
+  m_RootOrder: 14
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &1699695942681303956
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3026109493489212333}
+  m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &911182518007812963
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3026109493489212333}
+  m_Enabled: 0
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 0}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!65 &7891028562107312248
+BoxCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3026109493489212333}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Size: {x: 1, y: 1, z: 1}
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &3365881227177075193
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 8632265636757073846}
+  - component: {fileID: 1338788996703799793}
+  - component: {fileID: 1185725937715576670}
+  - component: {fileID: 2242193535682124475}
+  m_Layer: 0
+  m_Name: collider1 (17)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &8632265636757073846
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3365881227177075193}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 14.64, y: 9.19, z: -17.85}
+  m_LocalScale: {x: 2.8407543, y: 6.809734, z: 3.8615947}
+  m_Children: []
+  m_Father: {fileID: 3353729650955080407}
+  m_RootOrder: 33
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &1338788996703799793
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3365881227177075193}
+  m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &1185725937715576670
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3365881227177075193}
+  m_Enabled: 0
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 0}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!65 &2242193535682124475
+BoxCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3365881227177075193}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Size: {x: 1, y: 1, z: 1}
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &3563285688155943883
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 453811408668914605}
+  - component: {fileID: 2475687060440071780}
+  - component: {fileID: 5273041491426364691}
+  - component: {fileID: 7363262878268923360}
+  m_Layer: 0
+  m_Name: collider1 (29)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &453811408668914605
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3563285688155943883}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: -14.64, y: 9.19, z: -26.42}
+  m_LocalScale: {x: 2.8407543, y: 6.809734, z: 3.8615947}
+  m_Children: []
+  m_Father: {fileID: 3353729650955080407}
+  m_RootOrder: 21
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &2475687060440071780
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3563285688155943883}
+  m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &5273041491426364691
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3563285688155943883}
+  m_Enabled: 0
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 0}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!65 &7363262878268923360
+BoxCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3563285688155943883}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Size: {x: 1, y: 1, z: 1}
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &3878536218892755006
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 3684774942655400293}
+  - component: {fileID: 6409720175602698742}
+  - component: {fileID: 7888816099441106069}
+  - component: {fileID: 3964035063269094144}
+  m_Layer: 0
+  m_Name: collider1 (40)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &3684774942655400293
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3878536218892755006}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: -9.07, y: 9.42, z: 10.24}
+  m_LocalScale: {x: 8.7836275, y: 12.76757, z: 7.666875}
+  m_Children: []
+  m_Father: {fileID: 3353729650955080407}
+  m_RootOrder: 12
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &6409720175602698742
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3878536218892755006}
+  m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &7888816099441106069
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3878536218892755006}
+  m_Enabled: 0
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 0}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!65 &3964035063269094144
+BoxCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3878536218892755006}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Size: {x: 1, y: 1, z: 1}
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &4279960460529072320
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 6854583485449430462}
+  - component: {fileID: 2041667388369479378}
+  - component: {fileID: 2114154569574501420}
+  - component: {fileID: 7574101390368780318}
+  m_Layer: 0
+  m_Name: Cylinder
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &6854583485449430462
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4279960460529072320}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 9.23, z: -16.66}
+  m_LocalScale: {x: 5.9347, y: 9.688398, z: 5.9347}
+  m_Children: []
+  m_Father: {fileID: 3353729650955080407}
+  m_RootOrder: 35
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &2041667388369479378
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4279960460529072320}
+  m_Mesh: {fileID: 10206, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &2114154569574501420
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4279960460529072320}
+  m_Enabled: 0
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 0}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!136 &7574101390368780318
+CapsuleCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4279960460529072320}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  m_Radius: 0.5000001
+  m_Height: 2
+  m_Direction: 1
+  m_Center: {x: 0.000000059604645, y: 0, z: -0.00000008940697}
+--- !u!1 &4544208781333215134
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 9143878714892837037}
+  - component: {fileID: 8854302168975853600}
+  - component: {fileID: 2362798242187133835}
+  - component: {fileID: 1734707405353698455}
+  m_Layer: 0
+  m_Name: collider1 (31)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &9143878714892837037
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4544208781333215134}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: -14.64, y: 9.19, z: -6.47}
+  m_LocalScale: {x: 2.8407543, y: 6.809734, z: 3.8615947}
+  m_Children: []
+  m_Father: {fileID: 3353729650955080407}
+  m_RootOrder: 25
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &8854302168975853600
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4544208781333215134}
+  m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &2362798242187133835
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4544208781333215134}
+  m_Enabled: 0
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 0}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!65 &1734707405353698455
+BoxCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4544208781333215134}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Size: {x: 1, y: 1, z: 1}
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &4610550024638864349
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 2826125526851245970}
+  - component: {fileID: 9159847278068188560}
+  - component: {fileID: 795559306866936833}
+  - component: {fileID: 2272834850807297961}
+  m_Layer: 0
+  m_Name: collider1 (33)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &2826125526851245970
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4610550024638864349}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 14.64, y: 9.19, z: -6.47}
+  m_LocalScale: {x: 2.8407543, y: 6.809734, z: 3.8615947}
+  m_Children: []
+  m_Father: {fileID: 3353729650955080407}
+  m_RootOrder: 30
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &9159847278068188560
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4610550024638864349}
+  m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &795559306866936833
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4610550024638864349}
+  m_Enabled: 0
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 0}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!65 &2272834850807297961
+BoxCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4610550024638864349}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Size: {x: 1, y: 1, z: 1}
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &4801269095606634109
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 2557549872821171606}
+  - component: {fileID: 4026863309804049211}
+  - component: {fileID: 7539847625724962300}
+  - component: {fileID: 8316800696469517690}
+  m_Layer: 0
+  m_Name: collider1 (24)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &2557549872821171606
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4801269095606634109}
+  m_LocalRotation: {x: -0, y: 1, z: -0, w: 0}
+  m_LocalPosition: {x: 17.655827, y: 9.19, z: 1.9085503}
+  m_LocalScale: {x: 1.6025015, y: 6.809734, z: 1.1547327}
+  m_Children: []
+  m_Father: {fileID: 3353729650955080407}
+  m_RootOrder: 8
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &4026863309804049211
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4801269095606634109}
+  m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &7539847625724962300
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4801269095606634109}
+  m_Enabled: 0
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 0}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!65 &8316800696469517690
+BoxCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4801269095606634109}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Size: {x: 1, y: 1, z: 1}
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &5413150174527835193
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 8658294350301949091}
+  - component: {fileID: 6828518111463856930}
+  - component: {fileID: 5339030895510184727}
+  - component: {fileID: 5368769653877363835}
+  m_Layer: 0
+  m_Name: collider1 (24)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &8658294350301949091
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 5413150174527835193}
+  m_LocalRotation: {x: -0, y: 1, z: -0, w: 0}
+  m_LocalPosition: {x: 17.655827, y: 9.19, z: -17.98145}
+  m_LocalScale: {x: 1.6025015, y: 6.809734, z: 1.1547327}
+  m_Children: []
+  m_Father: {fileID: 3353729650955080407}
+  m_RootOrder: 10
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &6828518111463856930
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 5413150174527835193}
+  m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &5339030895510184727
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 5413150174527835193}
+  m_Enabled: 0
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 0}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!65 &5368769653877363835
+BoxCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 5413150174527835193}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Size: {x: 1, y: 1, z: 1}
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &5629365971434602205
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 8683747692448146843}
+  - component: {fileID: 6122172263264869669}
+  - component: {fileID: 1922188851258417324}
+  - component: {fileID: 8746562859571797540}
+  m_Layer: 0
+  m_Name: collider1 (23)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &8683747692448146843
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 5629365971434602205}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: -11.88, y: 9.42, z: 5.07}
+  m_LocalScale: {x: 9.443277, y: 12.76757, z: 7.666875}
+  m_Children: []
+  m_Father: {fileID: 3353729650955080407}
+  m_RootOrder: 11
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &6122172263264869669
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 5629365971434602205}
+  m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &1922188851258417324
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 5629365971434602205}
+  m_Enabled: 0
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 0}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!65 &8746562859571797540
+BoxCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 5629365971434602205}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Size: {x: 1, y: 1, z: 1}
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &6070464745058264932
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 4576582585629494333}
+  - component: {fileID: 7845138345036857270}
+  - component: {fileID: 2400249572786858936}
+  - component: {fileID: 1107244846703681100}
+  m_Layer: 0
+  m_Name: collider1 (47)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &4576582585629494333
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6070464745058264932}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 4.0504, y: 13.25, z: -33.01}
+  m_LocalScale: {x: 1.8742486, y: 14.227589, z: 2.3068948}
+  m_Children: []
+  m_Father: {fileID: 3353729650955080407}
+  m_RootOrder: 16
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &7845138345036857270
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6070464745058264932}
+  m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &2400249572786858936
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6070464745058264932}
+  m_Enabled: 0
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 0}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!65 &1107244846703681100
+BoxCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6070464745058264932}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Size: {x: 1, y: 1, z: 1}
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &6438781031045554024
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 6804365461352390175}
+  - component: {fileID: 7798170884491104166}
+  - component: {fileID: 260900583523212020}
+  - component: {fileID: 742076614568730851}
+  m_Layer: 0
+  m_Name: collider1 (8)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &6804365461352390175
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6438781031045554024}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: -17.603, y: 9.19, z: -26.132}
+  m_LocalScale: {x: 1.6025015, y: 6.809734, z: 1.1547327}
+  m_Children: []
+  m_Father: {fileID: 3353729650955080407}
+  m_RootOrder: 4
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &7798170884491104166
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6438781031045554024}
+  m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &260900583523212020
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6438781031045554024}
+  m_Enabled: 0
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 0}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!65 &742076614568730851
+BoxCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6438781031045554024}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Size: {x: 1, y: 1, z: 1}
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &6838642489918717148
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 2839663547559184751}
+  - component: {fileID: 259784268022908437}
+  - component: {fileID: 6895946935062357399}
+  - component: {fileID: 4866959564099957225}
+  m_Layer: 0
+  m_Name: collider1 (4)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &2839663547559184751
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6838642489918717148}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: -12.52, y: 13.750999, z: -27.13}
+  m_LocalScale: {x: 10.564761, y: 23.49999, z: 3.5918987}
+  m_Children: []
+  m_Father: {fileID: 3353729650955080407}
+  m_RootOrder: 19
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &259784268022908437
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6838642489918717148}
+  m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &6895946935062357399
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6838642489918717148}
+  m_Enabled: 0
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 0}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!65 &4866959564099957225
+BoxCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6838642489918717148}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Size: {x: 1, y: 1, z: 1}
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &7054211172789087858
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 3791295855901438031}
+  - component: {fileID: 5615267846184380455}
+  - component: {fileID: 6644387240665879152}
+  - component: {fileID: 2403930657124786786}
+  m_Layer: 0
+  m_Name: collider1 (27)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &3791295855901438031
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7054211172789087858}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 9.79, y: 11.5, z: -31.88}
+  m_LocalScale: {x: 2.0781941, y: 12.533997, z: 6.9015126}
+  m_Children: []
+  m_Father: {fileID: 3353729650955080407}
+  m_RootOrder: 18
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &5615267846184380455
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7054211172789087858}
+  m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &6644387240665879152
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7054211172789087858}
+  m_Enabled: 0
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 0}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!65 &2403930657124786786
+BoxCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7054211172789087858}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Size: {x: 1, y: 1, z: 1}
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &7234307509013310473
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 6052332818851074224}
+  - component: {fileID: 8382486935647586079}
+  - component: {fileID: 7947532065422359403}
+  - component: {fileID: 8758363576253705817}
+  m_Layer: 0
+  m_Name: collider1 (10)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &6052332818851074224
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7234307509013310473}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: -14.64, y: 9.19, z: 2.09}
+  m_LocalScale: {x: 2.8407543, y: 6.809734, z: 3.8615947}
+  m_Children: []
+  m_Father: {fileID: 3353729650955080407}
+  m_RootOrder: 24
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &8382486935647586079
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7234307509013310473}
+  m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &7947532065422359403
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7234307509013310473}
+  m_Enabled: 0
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 0}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!65 &8758363576253705817
+BoxCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7234307509013310473}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Size: {x: 1, y: 1, z: 1}
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &7246194239116054060
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 6298084235723794777}
+  - component: {fileID: 1401584425091951995}
+  - component: {fileID: 5771997397935824442}
+  - component: {fileID: 3851849691581082830}
+  m_Layer: 0
+  m_Name: collider1 (48)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &6298084235723794777
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7246194239116054060}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: -4.01, y: 13.25, z: -33.01}
+  m_LocalScale: {x: 1.8742486, y: 14.227589, z: 2.3068948}
+  m_Children: []
+  m_Father: {fileID: 3353729650955080407}
+  m_RootOrder: 17
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &1401584425091951995
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7246194239116054060}
+  m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &5771997397935824442
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7246194239116054060}
+  m_Enabled: 0
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 0}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!65 &3851849691581082830
+BoxCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7246194239116054060}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Size: {x: 1, y: 1, z: 1}
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &7733812364203767997
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 3751235125362988562}
+  - component: {fileID: 2206113207928596020}
+  - component: {fileID: 698978750644206358}
+  - component: {fileID: 8345050945026926745}
+  m_Layer: 0
+  m_Name: collider1 (20)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &3751235125362988562
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7733812364203767997}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: -17.603, y: 9.19, z: -6.2320004}
+  m_LocalScale: {x: 1.6025015, y: 6.809734, z: 1.1547327}
+  m_Children: []
+  m_Father: {fileID: 3353729650955080407}
+  m_RootOrder: 7
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &2206113207928596020
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7733812364203767997}
+  m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &698978750644206358
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7733812364203767997}
+  m_Enabled: 0
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 0}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!65 &8345050945026926745
+BoxCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7733812364203767997}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Size: {x: 1, y: 1, z: 1}
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &7976408419964617346
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7302611201762983669}
+  - component: {fileID: 9058330331080536091}
+  - component: {fileID: 2805013115650338979}
+  - component: {fileID: 1579195642571310227}
+  m_Layer: 0
+  m_Name: collider1 (12)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &7302611201762983669
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7976408419964617346}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: -17.603, y: 9.19, z: -16.202}
+  m_LocalScale: {x: 1.6025015, y: 6.809734, z: 1.1547327}
+  m_Children: []
+  m_Father: {fileID: 3353729650955080407}
+  m_RootOrder: 5
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &9058330331080536091
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7976408419964617346}
+  m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &2805013115650338979
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7976408419964617346}
+  m_Enabled: 0
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 0}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!65 &1579195642571310227
+BoxCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7976408419964617346}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Size: {x: 1, y: 1, z: 1}
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &8082849104798335939
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 5877264199003203356}
+  - component: {fileID: 1835615322452216082}
+  - component: {fileID: 4481233501183239601}
+  - component: {fileID: 5364307987553006968}
+  m_Layer: 0
+  m_Name: collider1 (41)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &5877264199003203356
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8082849104798335939}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 9.03, y: 9.42, z: 10.24}
+  m_LocalScale: {x: 8.7836275, y: 12.76757, z: 7.666875}
+  m_Children: []
+  m_Father: {fileID: 3353729650955080407}
+  m_RootOrder: 13
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &1835615322452216082
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8082849104798335939}
+  m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &4481233501183239601
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8082849104798335939}
+  m_Enabled: 0
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 0}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!65 &5364307987553006968
+BoxCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8082849104798335939}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Size: {x: 1, y: 1, z: 1}
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &8421534541625317693
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 175027371803051544}
+  - component: {fileID: 4999429031242357595}
+  - component: {fileID: 4321752368935189132}
+  - component: {fileID: 7313311720453002744}
+  m_Layer: 0
+  m_Name: collider1 (5)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &175027371803051544
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8421534541625317693}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: -12.56, y: 9.42, z: -17.04}
+  m_LocalScale: {x: 10.648528, y: 12.76757, z: 3.3273466}
+  m_Children: []
+  m_Father: {fileID: 3353729650955080407}
+  m_RootOrder: 1
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &4999429031242357595
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8421534541625317693}
+  m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &4321752368935189132
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8421534541625317693}
+  m_Enabled: 0
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 0}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!65 &7313311720453002744
+BoxCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8421534541625317693}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Size: {x: 1, y: 1, z: 1}
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &8838550163109315074
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 6723680794232567970}
+  - component: {fileID: 6803551128872083749}
+  - component: {fileID: 4168840732123361907}
+  - component: {fileID: 2023947344284713280}
+  m_Layer: 0
+  m_Name: collider1 (28)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &6723680794232567970
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8838550163109315074}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: -9.79, y: 11.5, z: -31.88}
+  m_LocalScale: {x: 2.0781941, y: 12.533997, z: 6.9015126}
+  m_Children: []
+  m_Father: {fileID: 3353729650955080407}
+  m_RootOrder: 20
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &6803551128872083749
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8838550163109315074}
+  m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &4168840732123361907
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8838550163109315074}
+  m_Enabled: 0
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 0}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!65 &2023947344284713280
+BoxCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8838550163109315074}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Size: {x: 1, y: 1, z: 1}
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &8901592362406973855
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 3353729650955080407}
+  m_Layer: 0
+  m_Name: LobbyAdditiveCollision
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &3353729650955080407
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8901592362406973855}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 6100307934943179590}
+  - {fileID: 175027371803051544}
+  - {fileID: 2064768525936009149}
+  - {fileID: 3936678173783502513}
+  - {fileID: 6804365461352390175}
+  - {fileID: 7302611201762983669}
+  - {fileID: 6173980292281361691}
+  - {fileID: 3751235125362988562}
+  - {fileID: 2557549872821171606}
+  - {fileID: 8040505461054105852}
+  - {fileID: 8658294350301949091}
+  - {fileID: 8683747692448146843}
+  - {fileID: 3684774942655400293}
+  - {fileID: 5877264199003203356}
+  - {fileID: 1919892219126379021}
+  - {fileID: 4196022544768814164}
+  - {fileID: 4576582585629494333}
+  - {fileID: 6298084235723794777}
+  - {fileID: 3791295855901438031}
+  - {fileID: 2839663547559184751}
+  - {fileID: 6723680794232567970}
+  - {fileID: 453811408668914605}
+  - {fileID: 6069335078130300583}
+  - {fileID: 344473347311092259}
+  - {fileID: 6052332818851074224}
+  - {fileID: 9143878714892837037}
+  - {fileID: 3197306933664388246}
+  - {fileID: 4555507070702984820}
+  - {fileID: 9022022389515057316}
+  - {fileID: 4028062206930188956}
+  - {fileID: 2826125526851245970}
+  - {fileID: 5323802657594753364}
+  - {fileID: 3583911581462720150}
+  - {fileID: 8632265636757073846}
+  - {fileID: 6994417639021962998}
+  - {fileID: 6854583485449430462}
+  m_Father: {fileID: 9039823294720213719}
+  m_RootOrder: 3
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &8944211733768219931
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 6069335078130300583}
+  - component: {fileID: 6827516272097124890}
+  - component: {fileID: 6468391960920743402}
+  - component: {fileID: 2021738081339696759}
+  m_Layer: 0
+  m_Name: collider1 (9)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &6069335078130300583
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8944211733768219931}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: -14.64, y: 9.19, z: -7.91}
+  m_LocalScale: {x: 2.8407543, y: 6.809734, z: 3.8615947}
+  m_Children: []
+  m_Father: {fileID: 3353729650955080407}
+  m_RootOrder: 22
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &6827516272097124890
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8944211733768219931}
+  m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &6468391960920743402
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8944211733768219931}
+  m_Enabled: 0
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 0}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!65 &2021738081339696759
+BoxCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8944211733768219931}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Size: {x: 1, y: 1, z: 1}
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &9184272823341513905
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 5273238663270862498}
+  - component: {fileID: 3780006760410166420}
+  - component: {fileID: 7442692564342884461}
+  m_Layer: 0
+  m_Name: LobbyBuildings
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &5273238663270862498
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 9184272823341513905}
+  m_LocalRotation: {x: 0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 9039823294720213719}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &3780006760410166420
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 9184272823341513905}
+  m_Mesh: {fileID: 4300000, guid: 44c4aea1bdd6151468edcdce75c97976, type: 3}
+--- !u!23 &7442692564342884461
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 9184272823341513905}
+  m_Enabled: 1
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: f20167634cdc0a040869b709fcf11dd7, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!1001 &2468495850805274054
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 9039823294720213719}
+    m_Modifications:
+    - target: {fileID: -4216859302048453862, guid: 94ed071c059f6e04c895dc605415789c,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: -0.01
+      objectReference: {fileID: 0}
+    - target: {fileID: -4216859302048453862, guid: 94ed071c059f6e04c895dc605415789c,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 8.180899
+      objectReference: {fileID: 0}
+    - target: {fileID: -4216859302048453862, guid: 94ed071c059f6e04c895dc605415789c,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: -12.08
+      objectReference: {fileID: 0}
+    - target: {fileID: -4216859302048453862, guid: 94ed071c059f6e04c895dc605415789c,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: -4216859302048453862, guid: 94ed071c059f6e04c895dc605415789c,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: -4216859302048453862, guid: 94ed071c059f6e04c895dc605415789c,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: -4216859302048453862, guid: 94ed071c059f6e04c895dc605415789c,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: -4216859302048453862, guid: 94ed071c059f6e04c895dc605415789c,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: -4216859302048453862, guid: 94ed071c059f6e04c895dc605415789c,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -4216859302048453862, guid: 94ed071c059f6e04c895dc605415789c,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -4216859302048453862, guid: 94ed071c059f6e04c895dc605415789c,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -1504981713932161579, guid: 94ed071c059f6e04c895dc605415789c,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: 0649fbada42027d498fb1e6c027a1bce, type: 2}
+    - target: {fileID: -927199367670048503, guid: 94ed071c059f6e04c895dc605415789c,
+        type: 3}
+      propertyPath: m_Name
+      value: LobbyHalloweenSignage
+      objectReference: {fileID: 0}
+    - target: {fileID: 400000, guid: 94ed071c059f6e04c895dc605415789c, type: 3}
+      propertyPath: m_RootOrder
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 400000, guid: 94ed071c059f6e04c895dc605415789c, type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: 400000, guid: 94ed071c059f6e04c895dc605415789c, type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0.43
+      objectReference: {fileID: 0}
+    - target: {fileID: 400000, guid: 94ed071c059f6e04c895dc605415789c, type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 8.180899
+      objectReference: {fileID: 0}
+    - target: {fileID: 400000, guid: 94ed071c059f6e04c895dc605415789c, type: 3}
+      propertyPath: m_LocalPosition.z
+      value: -12.08
+      objectReference: {fileID: 0}
+    - target: {fileID: 2300000, guid: 94ed071c059f6e04c895dc605415789c, type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: 0649fbada42027d498fb1e6c027a1bce, type: 2}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: 94ed071c059f6e04c895dc605415789c, type: 3}
+--- !u!4 &2468495850804887366 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: 400000, guid: 94ed071c059f6e04c895dc605415789c,
+    type: 3}
+  m_PrefabInstance: {fileID: 2468495850805274054}
+  m_PrefabAsset: {fileID: 0}
+--- !u!1001 &7959423107985453359
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 9039823294720213719}
+    m_Modifications:
+    - target: {fileID: -5412350193682189554, guid: 8b471d963a4288f40be104b5e2c8b2c5,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: dc91c15231f7cdf45b765740f508fc77, type: 2}
+    - target: {fileID: -4992169411502168867, guid: 8b471d963a4288f40be104b5e2c8b2c5,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: 82dd423c6668e594cb2a458ded33c717, type: 2}
+    - target: {fileID: -4216859302048453862, guid: 8b471d963a4288f40be104b5e2c8b2c5,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -4216859302048453862, guid: 8b471d963a4288f40be104b5e2c8b2c5,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -4216859302048453862, guid: 8b471d963a4288f40be104b5e2c8b2c5,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: -0.5311989
+      objectReference: {fileID: 0}
+    - target: {fileID: -4216859302048453862, guid: 8b471d963a4288f40be104b5e2c8b2c5,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0.000000081460335
+      objectReference: {fileID: 0}
+    - target: {fileID: -4216859302048453862, guid: 8b471d963a4288f40be104b5e2c8b2c5,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: -4216859302048453862, guid: 8b471d963a4288f40be104b5e2c8b2c5,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: -4216859302048453862, guid: 8b471d963a4288f40be104b5e2c8b2c5,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: -4216859302048453862, guid: 8b471d963a4288f40be104b5e2c8b2c5,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -4216859302048453862, guid: 8b471d963a4288f40be104b5e2c8b2c5,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -4216859302048453862, guid: 8b471d963a4288f40be104b5e2c8b2c5,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -4216859302048453862, guid: 8b471d963a4288f40be104b5e2c8b2c5,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -2414680528241819822, guid: 8b471d963a4288f40be104b5e2c8b2c5,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: 171613c76cf8ab34bb0e7730aea4518a, type: 2}
+    - target: {fileID: -2068853157031909594, guid: 8b471d963a4288f40be104b5e2c8b2c5,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: 171613c76cf8ab34bb0e7730aea4518a, type: 2}
+    - target: {fileID: -2068853157031909594, guid: 8b471d963a4288f40be104b5e2c8b2c5,
+        type: 3}
+      propertyPath: m_Enabled
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -1390465355410837172, guid: 8b471d963a4288f40be104b5e2c8b2c5,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: 706592767a8bc4a44a17742be6b9f861, type: 2}
+    - target: {fileID: -1364722309770950135, guid: 8b471d963a4288f40be104b5e2c8b2c5,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: 68df9835a31742e45a73423e851988b2, type: 2}
+    - target: {fileID: -927199367670048503, guid: 8b471d963a4288f40be104b5e2c8b2c5,
+        type: 3}
+      propertyPath: m_Name
+      value: LobbyHalloween
+      objectReference: {fileID: 0}
+    - target: {fileID: 400016, guid: 8b471d963a4288f40be104b5e2c8b2c5, type: 3}
+      propertyPath: m_RootOrder
+      value: 2
+      objectReference: {fileID: 0}
+    - target: {fileID: 2300000, guid: 8b471d963a4288f40be104b5e2c8b2c5, type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: 68df9835a31742e45a73423e851988b2, type: 2}
+    - target: {fileID: 2300002, guid: 8b471d963a4288f40be104b5e2c8b2c5, type: 3}
+      propertyPath: m_Enabled
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 2300004, guid: 8b471d963a4288f40be104b5e2c8b2c5, type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: c599b49289edec24b80e7203dd71e923, type: 2}
+    - target: {fileID: 2300006, guid: 8b471d963a4288f40be104b5e2c8b2c5, type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: 9ecb43986f768494196721abee350438, type: 2}
+    - target: {fileID: 2300008, guid: 8b471d963a4288f40be104b5e2c8b2c5, type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: 171613c76cf8ab34bb0e7730aea4518a, type: 2}
+    - target: {fileID: 2300010, guid: 8b471d963a4288f40be104b5e2c8b2c5, type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: 706592767a8bc4a44a17742be6b9f861, type: 2}
+    - target: {fileID: 2300012, guid: 8b471d963a4288f40be104b5e2c8b2c5, type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: dc91c15231f7cdf45b765740f508fc77, type: 2}
+    - target: {fileID: 2300014, guid: 8b471d963a4288f40be104b5e2c8b2c5, type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: 82dd423c6668e594cb2a458ded33c717, type: 2}
+    - target: {fileID: 799515919294232840, guid: 8b471d963a4288f40be104b5e2c8b2c5,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: c599b49289edec24b80e7203dd71e923, type: 2}
+    - target: {fileID: 1643812156500419214, guid: 8b471d963a4288f40be104b5e2c8b2c5,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: 9ecb43986f768494196721abee350438, type: 2}
+    m_RemovedComponents:
+    - {fileID: -2068853157031909594, guid: 8b471d963a4288f40be104b5e2c8b2c5, type: 3}
+  m_SourcePrefab: {fileID: 100100000, guid: 8b471d963a4288f40be104b5e2c8b2c5, type: 3}
+--- !u!4 &7959423107985583039 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: 400016, guid: 8b471d963a4288f40be104b5e2c8b2c5,
+    type: 3}
+  m_PrefabInstance: {fileID: 7959423107985453359}
+  m_PrefabAsset: {fileID: 0}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Prefabs/p_lobbyHalloween_Environment.prefab.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Prefabs/p_lobbyHalloween_Environment.prefab.meta
new file mode 100644
index 0000000000000000000000000000000000000000..ce273874dafaca5185a01f4c1441366084f709e8
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Prefabs/p_lobbyHalloween_Environment.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 328b22dc79cb39c4790ef4d18e578c29
+PrefabImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Prefabs/p_lobbyHalloween_IndirectLighting.prefab b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Prefabs/p_lobbyHalloween_IndirectLighting.prefab
new file mode 100644
index 0000000000000000000000000000000000000000..65a0bcb35b3a9e9723383b168fc0aab8c88db3bf
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Prefabs/p_lobbyHalloween_IndirectLighting.prefab
@@ -0,0 +1,1870 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &911055054232583162
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 6484244832721863754}
+  - component: {fileID: 5470691928289273514}
+  - component: {fileID: 7642376952309883690}
+  m_Layer: 0
+  m_Name: lobby_halloween_right.001
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 4294967295
+  m_IsActive: 1
+--- !u!4 &6484244832721863754
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 911055054232583162}
+  m_LocalRotation: {x: 0.7071067, y: 0, z: -0, w: 0.7071068}
+  m_LocalPosition: {x: 0.389845, y: 0.023437014, z: 0.60138685}
+  m_LocalScale: {x: 0.01, y: 0.01, z: 0.01}
+  m_Children: []
+  m_Father: {fileID: 1571151301987675373}
+  m_RootOrder: 6
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &5470691928289273514
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 911055054232583162}
+  m_Mesh: {fileID: 4300006, guid: 8b471d963a4288f40be104b5e2c8b2c5, type: 3}
+--- !u!23 &7642376952309883690
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 911055054232583162}
+  m_Enabled: 1
+  m_CastShadows: 0
+  m_ReceiveShadows: 0
+  m_DynamicOccludee: 1
+  m_MotionVectors: 2
+  m_LightProbeUsage: 0
+  m_ReflectionProbeUsage: 0
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: e776c09d3cbad96479b111d0f7428914, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!1 &2564377844122181374
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1571151301987675373}
+  m_Layer: 0
+  m_Name: LobbyHalloween
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 4294967295
+  m_IsActive: 1
+--- !u!4 &1571151301987675373
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 2564377844122181374}
+  m_LocalRotation: {x: 0.000000081460335, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: -0.5311989}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 7613532128838385189}
+  - {fileID: 3817721655325656216}
+  - {fileID: 5983657285698611491}
+  - {fileID: 5330336715315649412}
+  - {fileID: 2224227096297820239}
+  - {fileID: 1542849551541405358}
+  - {fileID: 6484244832721863754}
+  m_Father: {fileID: 5816237317842038418}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &3286818532037487517
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 2224227096297820239}
+  - component: {fileID: 47230080921356833}
+  - component: {fileID: 4324438769835472571}
+  m_Layer: 0
+  m_Name: lobby_halloween_left.001
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 4294967295
+  m_IsActive: 1
+--- !u!4 &2224227096297820239
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3286818532037487517}
+  m_LocalRotation: {x: 0.7071067, y: 0, z: -0, w: 0.7071068}
+  m_LocalPosition: {x: 0.389845, y: 0.023437014, z: 0.60138685}
+  m_LocalScale: {x: 0.01, y: 0.01, z: 0.01}
+  m_Children: []
+  m_Father: {fileID: 1571151301987675373}
+  m_RootOrder: 4
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &47230080921356833
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3286818532037487517}
+  m_Mesh: {fileID: 4300002, guid: 8b471d963a4288f40be104b5e2c8b2c5, type: 3}
+--- !u!23 &4324438769835472571
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 3286818532037487517}
+  m_Enabled: 1
+  m_CastShadows: 0
+  m_ReceiveShadows: 0
+  m_DynamicOccludee: 1
+  m_MotionVectors: 2
+  m_LightProbeUsage: 0
+  m_ReflectionProbeUsage: 0
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: 9597afba2529f864cb3816b7a1320223, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!1 &4330793396793324955
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1542849551541405358}
+  - component: {fileID: 1768615923057564021}
+  - component: {fileID: 7229426575514137849}
+  m_Layer: 0
+  m_Name: lobby_halloween_logo.001
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 4294967295
+  m_IsActive: 1
+--- !u!4 &1542849551541405358
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4330793396793324955}
+  m_LocalRotation: {x: 0.7071067, y: 0, z: -0, w: 0.7071068}
+  m_LocalPosition: {x: 0.389845, y: 6.424206, z: 10.695343}
+  m_LocalScale: {x: 0.0094, y: 0.01, z: 0.01}
+  m_Children: []
+  m_Father: {fileID: 1571151301987675373}
+  m_RootOrder: 5
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &1768615923057564021
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4330793396793324955}
+  m_Mesh: {fileID: 4300004, guid: 8b471d963a4288f40be104b5e2c8b2c5, type: 3}
+--- !u!23 &7229426575514137849
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4330793396793324955}
+  m_Enabled: 1
+  m_CastShadows: 0
+  m_ReceiveShadows: 0
+  m_DynamicOccludee: 1
+  m_MotionVectors: 2
+  m_LightProbeUsage: 0
+  m_ReflectionProbeUsage: 0
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: 4859830042bdbac4f8183f3d601226f1, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!1 &4644310040266947286
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 4644310040266947290}
+  - component: {fileID: 4644310040266947285}
+  - component: {fileID: 4644310040266947284}
+  m_Layer: 0
+  m_Name: Core Sky Light - Fake Bounce
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 4294967295
+  m_IsActive: 1
+--- !u!4 &4644310040266947290
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4644310040266947286}
+  m_LocalRotation: {x: -0, y: -0, z: 0.99144363, w: -0.13053581}
+  m_LocalPosition: {x: 0.76, y: 11.8, z: -21.15}
+  m_LocalScale: {x: 1.0565728, y: 1, z: 3.5012696}
+  m_Children: []
+  m_Father: {fileID: 5816237316411896871}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 195.00099}
+--- !u!33 &4644310040266947285
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4644310040266947286}
+  m_Mesh: {fileID: 10209, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &4644310040266947284
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4644310040266947286}
+  m_Enabled: 1
+  m_CastShadows: 0
+  m_ReceiveShadows: 0
+  m_DynamicOccludee: 1
+  m_MotionVectors: 2
+  m_LightProbeUsage: 0
+  m_ReflectionProbeUsage: 0
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: b721037d70058144c93b805f29a17bb4, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!1 &4644310041114672054
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 4644310041114672055}
+  - component: {fileID: 4644310041114672053}
+  m_Layer: 0
+  m_Name: -- MLP Combined Volume --
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 4294967295
+  m_IsActive: 1
+--- !u!4 &4644310041114672055
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4644310041114672054}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 2.7115185, y: -17.198053, z: 25.658787}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 5816237316631983877}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!220 &4644310041114672053
+LightProbeGroup:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4644310041114672054}
+  m_Enabled: 1
+  m_SourcePositions:
+  - {x: 4.9585733, y: 14.363403, z: -19.201904}
+  - {x: 4.7585716, y: 16.132559, z: -17.404438}
+  - {x: 6.558572, y: 14.363403, z: -19.201904}
+  - {x: 6.5585737, y: 17.425129, z: -19.201902}
+  - {x: 6.5585737, y: 16.132559, z: -17.404438}
+  - {x: 8.158571, y: 14.363403, z: -19.201904}
+  - {x: 8.158573, y: 17.425125, z: -19.201902}
+  - {x: 8.158573, y: 16.132559, z: -17.404438}
+  - {x: 9.758571, y: 14.363403, z: -19.201904}
+  - {x: 10.808578, y: 17.425125, z: -19.201904}
+  - {x: 9.758574, y: 16.132559, z: -17.404438}
+  - {x: 4.611347, y: 14.355105, z: -17.201906}
+  - {x: 4.611347, y: 14.355106, z: -16.401905}
+  - {x: 4.6113453, y: 14.355106, z: -15.601906}
+  - {x: 4.611347, y: 14.355105, z: -14.801906}
+  - {x: 4.6113462, y: 14.355106, z: -14.0019045}
+  - {x: 4.611347, y: 14.355104, z: -13.201906}
+  - {x: 4.626171, y: 14.30526, z: -12.186348}
+  - {x: 4.608515, y: 14.932559, z: -12.242656}
+  - {x: 4.611108, y: 15.332559, z: -12.226068}
+  - {x: 4.611108, y: 16.132559, z: -12.226068}
+  - {x: 4.611108, y: 16.93256, z: -12.226068}
+  - {x: 5.358573, y: 14.299509, z: -17.35098}
+  - {x: 5.7585707, y: 14.299509, z: -17.350983}
+  - {x: 6.558568, y: 14.299509, z: -17.350983}
+  - {x: 6.558573, y: 17.506565, z: -17.365507}
+  - {x: 7.35857, y: 14.299509, z: -17.350983}
+  - {x: 7.358575, y: 17.506565, z: -17.365507}
+  - {x: 8.158572, y: 14.299509, z: -17.350983}
+  - {x: 8.158573, y: 17.506565, z: -17.365507}
+  - {x: 8.95857, y: 14.299509, z: -17.350983}
+  - {x: 8.958573, y: 17.506565, z: -17.365507}
+  - {x: 9.758574, y: 14.299509, z: -17.350983}
+  - {x: 9.758574, y: 17.506565, z: -17.365507}
+  - {x: 10.64554, y: 14.344871, z: -18.001905}
+  - {x: 10.669477, y: 14.293213, z: -17.344202}
+  - {x: 10.650563, y: 14.932559, z: -17.38815}
+  - {x: 10.650563, y: 15.732559, z: -17.38815}
+  - {x: 10.650563, y: 16.532558, z: -17.38815}
+  - {x: 10.719091, y: 17.486631, z: -19.201902}
+  - {x: 10.719093, y: 17.486633, z: -18.801903}
+  - {x: 10.628679, y: 17.452469, z: -18.001904}
+  - {x: 10.664129, y: 17.5131, z: -17.356628}
+  - {x: 10.958568, y: 14.283453, z: -18.469978}
+  - {x: 10.958566, y: 16.858074, z: -18.455717}
+  - {x: 11.358573, y: 14.283453, z: -18.469975}
+  - {x: 4.9585733, y: 16.132559, z: -19.201904}
+  - {x: -2.3524222, y: 14.363403, z: -19.201904}
+  - {x: -2.3524222, y: 14.363403, z: -17.601904}
+  - {x: -2.3524222, y: 14.363403, z: -16.001905}
+  - {x: -2.3524222, y: 14.363404, z: -14.401905}
+  - {x: -2.3524222, y: 14.363404, z: -12.801905}
+  - {x: -2.3524222, y: 14.363403, z: -11.201906}
+  - {x: -2.3524222, y: 16.132559, z: -7.234437}
+  - {x: -0.75242233, y: 14.363403, z: -19.201904}
+  - {x: -0.75242233, y: 14.363403, z: -17.601906}
+  - {x: -0.7524222, y: 14.363403, z: -16.001905}
+  - {x: -0.75242233, y: 14.363403, z: -14.401905}
+  - {x: -0.75242233, y: 14.363403, z: -12.801905}
+  - {x: -0.75242233, y: 14.363403, z: -11.201906}
+  - {x: -0.7524222, y: 16.132559, z: -7.234437}
+  - {x: 0.8475778, y: 14.363403, z: -19.201904}
+  - {x: 0.8475778, y: 14.363403, z: -17.601906}
+  - {x: 0.8475778, y: 14.363404, z: -14.401905}
+  - {x: 0.8475778, y: 14.363404, z: -12.801905}
+  - {x: 0.8475778, y: 14.363403, z: -11.201906}
+  - {x: 1.9481862, y: 16.132559, z: -11.951908}
+  - {x: 1.9481862, y: 16.132559, z: -9.601904}
+  - {x: 0.8475778, y: 16.132559, z: -7.234437}
+  - {x: 1.9481862, y: 16.132559, z: -8.001905}
+  - {x: 2.4475782, y: 14.363403, z: -19.201904}
+  - {x: 2.4475782, y: 14.363403, z: -17.601904}
+  - {x: 2.4475782, y: 14.363403, z: -16.001905}
+  - {x: 2.4475782, y: 14.363404, z: -14.401905}
+  - {x: 2.247577, y: 16.132559, z: -12.234438}
+  - {x: -2.7524223, y: 14.523572, z: -9.186944}
+  - {x: -2.752422, y: 14.939478, z: -8.413924}
+  - {x: -2.7524228, y: 15.307703, z: -7.176738}
+  - {x: -1.9524224, y: 14.523572, z: -9.186944}
+  - {x: -1.9524229, y: 14.939478, z: -8.413924}
+  - {x: -1.9524229, y: 15.307703, z: -7.176738}
+  - {x: -1.1524222, y: 14.523572, z: -9.186944}
+  - {x: -1.1524222, y: 14.939478, z: -8.413924}
+  - {x: -1.1524222, y: 15.307703, z: -7.176738}
+  - {x: -0.3524227, y: 14.523572, z: -9.186944}
+  - {x: -0.35242248, y: 14.939478, z: -8.413924}
+  - {x: -0.35242248, y: 15.307701, z: -7.1767397}
+  - {x: 0.44757771, y: 14.523572, z: -9.186944}
+  - {x: 0.44757771, y: 14.939478, z: -8.413924}
+  - {x: 0.44757771, y: 15.307703, z: -7.176738}
+  - {x: 1.2475774, y: 14.523572, z: -9.186944}
+  - {x: 1.2475774, y: 14.939478, z: -8.413924}
+  - {x: 1.2475772, y: 15.307702, z: -7.1767387}
+  - {x: 2.095597, y: 14.356366, z: -12.001905}
+  - {x: 2.0955966, y: 14.356367, z: -11.201906}
+  - {x: 2.095597, y: 14.356366, z: -10.401904}
+  - {x: 2.095597, y: 14.356366, z: -9.601903}
+  - {x: 2.052015, y: 14.521457, z: -9.183424}
+  - {x: 2.1068432, y: 14.652044, z: -8.801907}
+  - {x: 2.0455554, y: 14.937232, z: -8.410023}
+  - {x: 2.0998037, y: 15.123082, z: -8.001909}
+  - {x: 2.069577, y: 15.296392, z: -7.165289}
+  - {x: 2.04519, y: 15.732559, z: -7.2058783}
+  - {x: 2.04519, y: 16.532558, z: -7.2058783}
+  - {x: 2.04519, y: 17.332558, z: -7.2058783}
+  - {x: 2.4475787, y: 14.308524, z: -12.1894455}
+  - {x: 3.247578, y: 14.308524, z: -12.1894455}
+  - {x: 4.047578, y: 14.308525, z: -12.1894455}
+  - {x: -2.3524222, y: 16.132559, z: -19.201904}
+  - {x: -2.3524222, y: 16.132559, z: -9.601904}
+  - {x: 4.9585724, y: 14.363403, z: -31.777231}
+  - {x: 4.9585714, y: 14.363403, z: -30.177233}
+  - {x: 4.9585714, y: 14.363403, z: -28.577232}
+  - {x: 4.9585733, y: 14.363403, z: -22.177233}
+  - {x: 4.9585733, y: 14.363403, z: -20.577232}
+  - {x: 4.808572, y: 16.132559, z: -27.402477}
+  - {x: 4.808572, y: 16.132559, z: -23.639523}
+  - {x: 6.558572, y: 14.363403, z: -31.777231}
+  - {x: 6.558572, y: 14.363403, z: -30.177233}
+  - {x: 6.558572, y: 14.363403, z: -28.577232}
+  - {x: 6.558572, y: 14.363403, z: -22.177233}
+  - {x: 6.558572, y: 14.363403, z: -20.577234}
+  - {x: 6.5585737, y: 17.42513, z: -31.77723}
+  - {x: 6.558573, y: 17.425129, z: -30.177227}
+  - {x: 6.5585737, y: 16.132559, z: -27.402477}
+  - {x: 6.558573, y: 17.425129, z: -28.57723}
+  - {x: 6.558573, y: 17.425129, z: -22.17723}
+  - {x: 6.5585737, y: 16.132559, z: -23.639523}
+  - {x: 6.558573, y: 17.425129, z: -20.577232}
+  - {x: 8.158571, y: 14.363402, z: -31.777231}
+  - {x: 8.158572, y: 14.363403, z: -30.177233}
+  - {x: 8.158572, y: 14.363403, z: -28.577232}
+  - {x: 8.158572, y: 14.363404, z: -22.177233}
+  - {x: 8.158572, y: 14.363403, z: -20.577234}
+  - {x: 8.158573, y: 17.425129, z: -31.777233}
+  - {x: 8.158573, y: 17.425129, z: -30.177227}
+  - {x: 8.158573, y: 16.132559, z: -27.402477}
+  - {x: 8.158573, y: 17.425129, z: -28.577232}
+  - {x: 8.158573, y: 17.425129, z: -22.17723}
+  - {x: 8.158573, y: 16.132559, z: -23.639523}
+  - {x: 8.158573, y: 17.425129, z: -20.577232}
+  - {x: 9.758573, y: 14.363402, z: -31.777231}
+  - {x: 9.758573, y: 14.363403, z: -30.177233}
+  - {x: 9.758573, y: 14.363403, z: -28.577232}
+  - {x: 9.758572, y: 14.363404, z: -22.177233}
+  - {x: 9.758572, y: 14.363403, z: -20.577234}
+  - {x: 10.808578, y: 17.425129, z: -31.777233}
+  - {x: 10.808578, y: 17.425129, z: -30.177227}
+  - {x: 10.808578, y: 17.425129, z: -28.577232}
+  - {x: 10.808578, y: 17.425129, z: -22.17723}
+  - {x: 9.758574, y: 16.132559, z: -23.639523}
+  - {x: 10.808578, y: 17.425129, z: -20.577232}
+  - {x: 4.6566677, y: 14.3413515, z: -26.97723}
+  - {x: 4.6566668, y: 14.3413515, z: -26.177233}
+  - {x: 4.6566677, y: 14.3413515, z: -25.377232}
+  - {x: 4.6566677, y: 14.341352, z: -24.577232}
+  - {x: 4.9585733, y: 14.344173, z: -27.288052}
+  - {x: 4.9585724, y: 14.288204, z: -23.683237}
+  - {x: 5.358572, y: 14.344172, z: -27.288052}
+  - {x: 5.7585707, y: 14.344173, z: -27.288052}
+  - {x: 5.7585707, y: 14.288203, z: -23.683237}
+  - {x: 6.158572, y: 14.288204, z: -23.683237}
+  - {x: 6.558573, y: 14.344173, z: -27.288052}
+  - {x: 6.55857, y: 14.288203, z: -23.683235}
+  - {x: 6.558573, y: 17.453426, z: -27.305038}
+  - {x: 6.558572, y: 17.518175, z: -23.670525}
+  - {x: 6.9585705, y: 14.344172, z: -27.288054}
+  - {x: 7.358572, y: 14.344173, z: -27.288052}
+  - {x: 7.358572, y: 14.288204, z: -23.683237}
+  - {x: 7.358572, y: 17.453426, z: -27.305038}
+  - {x: 7.358571, y: 17.518173, z: -23.670525}
+  - {x: 7.7585716, y: 14.344172, z: -27.288052}
+  - {x: 7.7585707, y: 14.288203, z: -23.683237}
+  - {x: 7.7585745, y: 17.453423, z: -27.305038}
+  - {x: 8.158573, y: 14.344173, z: -27.288052}
+  - {x: 8.158573, y: 17.518173, z: -23.670525}
+  - {x: 8.558572, y: 14.288204, z: -23.683235}
+  - {x: 8.558573, y: 17.453426, z: -27.305038}
+  - {x: 8.958571, y: 14.344173, z: -27.288052}
+  - {x: 8.958573, y: 17.518173, z: -23.670525}
+  - {x: 9.35857, y: 14.288204, z: -23.683237}
+  - {x: 9.358575, y: 17.453423, z: -27.305038}
+  - {x: 9.758574, y: 14.344173, z: -27.288052}
+  - {x: 9.758574, y: 17.518173, z: -23.670525}
+  - {x: 10.158572, y: 14.288204, z: -23.683235}
+  - {x: 10.158572, y: 17.453423, z: -27.305038}
+  - {x: 10.645538, y: 14.34487, z: -28.17723}
+  - {x: 10.652366, y: 14.330143, z: -27.281414}
+  - {x: 10.674072, y: 14.283296, z: -23.689383}
+  - {x: 10.5897255, y: 14.932559, z: -27.345406}
+  - {x: 10.660221, y: 14.932559, z: -23.651949}
+  - {x: 10.5897255, y: 15.732559, z: -27.345406}
+  - {x: 10.660221, y: 15.732559, z: -23.651949}
+  - {x: 10.5897255, y: 16.532558, z: -27.345406}
+  - {x: 10.660221, y: 16.532558, z: -23.651949}
+  - {x: 10.719092, y: 17.486633, z: -31.777227}
+  - {x: 10.719094, y: 17.486633, z: -31.377228}
+  - {x: 10.719095, y: 17.486633, z: -30.97723}
+  - {x: 10.719094, y: 17.486635, z: -30.177227}
+  - {x: 10.719094, y: 17.486633, z: -29.377232}
+  - {x: 10.719091, y: 17.486633, z: -28.577229}
+  - {x: 10.62868, y: 17.45247, z: -28.17723}
+  - {x: 10.639906, y: 17.471668, z: -27.29414}
+  - {x: 10.669942, y: 17.523043, z: -23.678219}
+  - {x: 10.719091, y: 17.486633, z: -22.97723}
+  - {x: 10.719091, y: 17.486633, z: -22.17723}
+  - {x: 10.719094, y: 17.486633, z: -21.377232}
+  - {x: 10.719094, y: 17.486631, z: -20.977232}
+  - {x: 10.719093, y: 17.48663, z: -20.17723}
+  - {x: 10.719094, y: 17.486633, z: -19.377232}
+  - {x: 10.958572, y: 14.319557, z: -28.406765}
+  - {x: 10.958572, y: 14.298503, z: -22.83273}
+  - {x: 10.958572, y: 16.826527, z: -28.389458}
+  - {x: 10.9585705, y: 16.844284, z: -22.848822}
+  - {x: 4.9585733, y: 16.132559, z: -31.777233}
+  - {x: -2.3524222, y: 16.132559, z: -31.93684}
+  - {x: -0.75242233, y: 14.363403, z: -31.777231}
+  - {x: -0.7524222, y: 14.532558, z: -32.49559}
+  - {x: -0.75242233, y: 14.363403, z: -30.177233}
+  - {x: -0.75242233, y: 14.363403, z: -22.177233}
+  - {x: -0.75242233, y: 14.363403, z: -20.577232}
+  - {x: -0.7524222, y: 16.132559, z: -32.49559}
+  - {x: 0.8475778, y: 14.363403, z: -31.777233}
+  - {x: 0.8475778, y: 14.363403, z: -30.177233}
+  - {x: 0.8475778, y: 14.363403, z: -28.577232}
+  - {x: 0.8475778, y: 14.363403, z: -26.977232}
+  - {x: 0.8475778, y: 14.363403, z: -25.377232}
+  - {x: 0.8475778, y: 14.363403, z: -23.777233}
+  - {x: 0.8475778, y: 14.363404, z: -22.177233}
+  - {x: 0.8475778, y: 14.363403, z: -20.577234}
+  - {x: 2.4475782, y: 14.363403, z: -31.777233}
+  - {x: 2.4475777, y: 14.363403, z: -30.177233}
+  - {x: 2.4475782, y: 14.363403, z: -28.577232}
+  - {x: 2.4475782, y: 14.363403, z: -26.977232}
+  - {x: 2.4475777, y: 14.363403, z: -25.377232}
+  - {x: 2.4475782, y: 14.363403, z: -23.777233}
+  - {x: 2.4475782, y: 14.363404, z: -22.177233}
+  - {x: 2.4475782, y: 14.363403, z: -20.577232}
+  - {x: -2.7524223, y: 14.305039, z: -32.00406}
+  - {x: -1.9524224, y: 14.298039, z: -32.034847}
+  - {x: -2.3524222, y: 16.132559, z: -30.177233}
+  - {x: -2.3524222, y: 16.132559, z: -20.577232}
+  - {x: -9.66342, y: 14.363403, z: -19.201904}
+  - {x: -9.71342, y: 16.132559, z: -17.404438}
+  - {x: -8.06342, y: 14.363403, z: -19.201904}
+  - {x: -8.06342, y: 14.363403, z: -17.601904}
+  - {x: -8.063419, y: 14.363403, z: -16.001905}
+  - {x: -9.448361, y: 16.132559, z: -17.151903}
+  - {x: -7.2634163, y: 16.132559, z: -12.234438}
+  - {x: -6.46342, y: 14.363403, z: -19.201904}
+  - {x: -6.46342, y: 14.363403, z: -17.601906}
+  - {x: -6.46342, y: 14.363403, z: -16.001905}
+  - {x: -6.46342, y: 14.363404, z: -14.401905}
+  - {x: -6.46342, y: 14.363403, z: -12.801905}
+  - {x: -6.9681864, y: 16.132559, z: -11.951908}
+  - {x: -6.9681864, y: 16.132559, z: -9.601904}
+  - {x: -6.9681864, y: 16.132559, z: -8.001905}
+  - {x: -4.8634195, y: 14.363403, z: -19.201904}
+  - {x: -4.8634195, y: 14.363403, z: -17.601906}
+  - {x: -4.8634195, y: 14.363403, z: -16.001905}
+  - {x: -4.8634195, y: 14.363403, z: -14.401905}
+  - {x: -4.8634195, y: 14.363403, z: -12.801905}
+  - {x: -4.8634195, y: 14.363403, z: -11.201906}
+  - {x: -4.8634195, y: 16.132559, z: -7.234437}
+  - {x: -10.063419, y: 14.299509, z: -17.35098}
+  - {x: -9.504009, y: 14.301834, z: -17.201906}
+  - {x: -9.50401, y: 14.301834, z: -16.401907}
+  - {x: -9.50401, y: 14.301834, z: -15.601906}
+  - {x: -9.504011, y: 14.301834, z: -14.801906}
+  - {x: -9.50401, y: 14.301835, z: -14.0019045}
+  - {x: -9.50401, y: 14.301834, z: -13.201906}
+  - {x: -9.527683, y: 14.27913, z: -12.1615715}
+  - {x: -9.5004, y: 14.932559, z: -12.186384}
+  - {x: -9.503695, y: 15.732559, z: -12.172537}
+  - {x: -9.503695, y: 16.532558, z: -12.172537}
+  - {x: -9.503695, y: 17.332558, z: -12.172537}
+  - {x: -8.863419, y: 14.308523, z: -12.1894455}
+  - {x: -8.063419, y: 14.308525, z: -12.1894455}
+  - {x: -7.263418, y: 14.308525, z: -12.1894455}
+  - {x: -7.0408564, y: 14.317636, z: -11.601905}
+  - {x: -7.0408564, y: 14.317635, z: -11.201906}
+  - {x: -7.0408564, y: 14.317635, z: -10.401906}
+  - {x: -7.0408564, y: 14.317635, z: -9.601905}
+  - {x: -6.8931913, y: 14.507973, z: -9.160978}
+  - {x: -7.0603743, y: 14.624817, z: -8.801907}
+  - {x: -6.8756657, y: 14.923212, z: -8.38567}
+  - {x: -7.0476503, y: 15.088621, z: -8.001906}
+  - {x: -6.9928417, y: 15.261555, z: -7.601905}
+  - {x: -7.0098925, y: 15.253065, z: -7.121422}
+  - {x: -6.9929037, y: 15.732559, z: -7.1307526}
+  - {x: -6.9929037, y: 16.532558, z: -7.1307526}
+  - {x: -6.9929037, y: 17.332558, z: -7.1307526}
+  - {x: -6.463419, y: 15.307702, z: -7.1767387}
+  - {x: -6.0634193, y: 14.523572, z: -9.186944}
+  - {x: -6.0634193, y: 14.939478, z: -8.413924}
+  - {x: -5.6634192, y: 15.307702, z: -7.1767406}
+  - {x: -5.263419, y: 14.523571, z: -9.186943}
+  - {x: -5.263419, y: 14.939478, z: -8.413924}
+  - {x: -4.8634195, y: 15.307703, z: -7.1767387}
+  - {x: -4.46342, y: 14.523572, z: -9.186944}
+  - {x: -4.46342, y: 14.939478, z: -8.413924}
+  - {x: -4.0634203, y: 15.307703, z: -7.176738}
+  - {x: -3.6634197, y: 14.523572, z: -9.186944}
+  - {x: -3.6634202, y: 14.939478, z: -8.413924}
+  - {x: -3.26342, y: 15.307703, z: -7.1767387}
+  - {x: -9.66342, y: 16.132559, z: -19.201904}
+  - {x: -6.46342, y: 16.132559, z: -14.401905}
+  - {x: -16.974413, y: 16.795078, z: -18.401905}
+  - {x: -15.874408, y: 16.132557, z: -18.510012}
+  - {x: -15.3744135, y: 14.363403, z: -19.201904}
+  - {x: -15.8744135, y: 17.425125, z: -19.201904}
+  - {x: -13.774415, y: 14.363403, z: -19.201904}
+  - {x: -13.774413, y: 17.425129, z: -19.201902}
+  - {x: -13.774415, y: 16.132559, z: -17.404438}
+  - {x: -12.174415, y: 14.363403, z: -19.201904}
+  - {x: -12.174412, y: 17.425125, z: -19.201902}
+  - {x: -12.174415, y: 16.132559, z: -17.404438}
+  - {x: -17.374409, y: 14.283453, z: -18.469978}
+  - {x: -17.37441, y: 16.858074, z: -18.455717}
+  - {x: -16.974413, y: 14.283452, z: -18.469975}
+  - {x: -16.57441, y: 16.858074, z: -18.455717}
+  - {x: -16.174408, y: 14.283453, z: -18.469978}
+  - {x: -15.575172, y: 14.311469, z: -18.001905}
+  - {x: -15.604484, y: 14.279186, z: -17.329102}
+  - {x: -15.580684, y: 14.932559, z: -17.357325}
+  - {x: -15.580684, y: 15.732559, z: -17.357325}
+  - {x: -15.580684, y: 16.532558, z: -17.357325}
+  - {x: -15.558944, y: 17.493612, z: -18.001904}
+  - {x: -15.597252, y: 17.527046, z: -17.33768}
+  - {x: -14.974414, y: 14.299509, z: -17.350983}
+  - {x: -14.974415, y: 17.506565, z: -17.365507}
+  - {x: -14.1744175, y: 14.299509, z: -17.350983}
+  - {x: -14.174415, y: 17.506565, z: -17.365507}
+  - {x: -13.374414, y: 14.299509, z: -17.350983}
+  - {x: -13.3744135, y: 17.506565, z: -17.365507}
+  - {x: -12.574417, y: 14.299509, z: -17.350983}
+  - {x: -12.574414, y: 17.506565, z: -17.365507}
+  - {x: -11.774414, y: 14.299509, z: -17.350983}
+  - {x: -11.774414, y: 17.506565, z: -17.365507}
+  - {x: -10.974414, y: 14.299509, z: -17.350983}
+  - {x: -10.574412, y: 14.299509, z: -17.35098}
+  - {x: -9.66342, y: 14.363402, z: -31.777231}
+  - {x: -9.663419, y: 14.363403, z: -30.177233}
+  - {x: -9.66342, y: 14.363403, z: -28.577232}
+  - {x: -9.66342, y: 14.363403, z: -22.177233}
+  - {x: -9.66342, y: 14.363403, z: -20.577232}
+  - {x: -8.063419, y: 14.363403, z: -31.777233}
+  - {x: -8.063418, y: 14.363403, z: -30.177233}
+  - {x: -8.063419, y: 14.363403, z: -28.577232}
+  - {x: -8.063419, y: 14.363403, z: -26.977232}
+  - {x: -8.063419, y: 14.363403, z: -25.377232}
+  - {x: -8.063419, y: 14.363403, z: -23.777233}
+  - {x: -8.063419, y: 14.363403, z: -22.177233}
+  - {x: -8.063419, y: 14.363403, z: -20.577234}
+  - {x: -9.527964, y: 16.132559, z: -27.127234}
+  - {x: -9.527964, y: 16.132559, z: -23.927227}
+  - {x: -6.4634194, y: 14.363403, z: -31.777231}
+  - {x: -6.46342, y: 14.363403, z: -30.177233}
+  - {x: -6.46342, y: 14.363403, z: -28.577232}
+  - {x: -6.46342, y: 14.363403, z: -26.977232}
+  - {x: -6.46342, y: 14.363403, z: -25.377232}
+  - {x: -6.46342, y: 14.363403, z: -23.777233}
+  - {x: -6.4634194, y: 14.363403, z: -22.177233}
+  - {x: -6.46342, y: 14.363403, z: -20.577232}
+  - {x: -4.8634195, y: 14.363403, z: -31.777231}
+  - {x: -4.8634195, y: 14.532558, z: -33.119976}
+  - {x: -4.8634195, y: 14.363403, z: -30.177233}
+  - {x: -4.8634195, y: 14.363403, z: -28.577232}
+  - {x: -4.8634195, y: 14.363403, z: -26.977234}
+  - {x: -4.8634195, y: 14.363403, z: -25.377232}
+  - {x: -4.8634195, y: 14.363403, z: -23.777233}
+  - {x: -4.8634195, y: 14.363404, z: -22.177233}
+  - {x: -4.8634195, y: 14.363403, z: -20.577234}
+  - {x: -4.8634195, y: 16.132559, z: -33.119976}
+  - {x: -10.063419, y: 14.344173, z: -27.288052}
+  - {x: -10.063419, y: 14.288202, z: -23.683237}
+  - {x: -9.571382, y: 14.287832, z: -26.97723}
+  - {x: -9.571382, y: 14.287832, z: -26.17723}
+  - {x: -9.5713825, y: 14.287832, z: -25.377232}
+  - {x: -9.571382, y: 14.287832, z: -24.577232}
+  - {x: -3.2634194, y: 14.288752, z: -32.08036}
+  - {x: -9.66342, y: 16.132559, z: -31.777233}
+  - {x: -8.06342, y: 16.132559, z: -30.177233}
+  - {x: -16.974413, y: 14.363403, z: -31.777231}
+  - {x: -15.77441, y: 14.532558, z: -32.7802}
+  - {x: -16.974413, y: 14.363403, z: -30.17723}
+  - {x: -16.974413, y: 14.363403, z: -20.577234}
+  - {x: -16.974413, y: 16.795082, z: -32.977234}
+  - {x: -15.77441, y: 16.132559, z: -32.7802}
+  - {x: -18.324417, y: 16.795082, z: -30.177227}
+  - {x: -15.77441, y: 16.132559, z: -28.481796}
+  - {x: -15.77441, y: 16.132559, z: -22.780203}
+  - {x: -18.324417, y: 16.795078, z: -20.577232}
+  - {x: -15.3744135, y: 14.363403, z: -31.777231}
+  - {x: -15.3744135, y: 14.363403, z: -30.177233}
+  - {x: -15.374413, y: 14.363403, z: -22.177233}
+  - {x: -15.374413, y: 14.363403, z: -20.577234}
+  - {x: -15.8744135, y: 17.425129, z: -31.77723}
+  - {x: -15.8744135, y: 17.425129, z: -30.177227}
+  - {x: -15.8744135, y: 17.425129, z: -22.17723}
+  - {x: -15.8744135, y: 17.425129, z: -20.577232}
+  - {x: -13.774415, y: 14.363403, z: -31.777231}
+  - {x: -13.774415, y: 14.363403, z: -30.177233}
+  - {x: -13.774415, y: 14.363403, z: -28.577232}
+  - {x: -13.774415, y: 14.363403, z: -22.177233}
+  - {x: -13.774415, y: 14.363403, z: -20.577232}
+  - {x: -13.774414, y: 17.425129, z: -31.77723}
+  - {x: -13.774413, y: 17.425129, z: -30.177227}
+  - {x: -13.774415, y: 16.132559, z: -27.402477}
+  - {x: -13.774413, y: 17.425129, z: -28.57723}
+  - {x: -13.774414, y: 17.425129, z: -22.17723}
+  - {x: -13.774415, y: 16.132559, z: -23.639523}
+  - {x: -13.774414, y: 17.425129, z: -20.577232}
+  - {x: -12.174415, y: 14.363403, z: -31.777231}
+  - {x: -12.174415, y: 14.363403, z: -30.177233}
+  - {x: -12.174415, y: 14.363403, z: -28.577232}
+  - {x: -12.174415, y: 14.363403, z: -22.177233}
+  - {x: -12.174415, y: 14.363403, z: -20.577234}
+  - {x: -12.174414, y: 17.425129, z: -31.77723}
+  - {x: -12.174412, y: 17.425129, z: -30.177227}
+  - {x: -12.174415, y: 16.132559, z: -27.402477}
+  - {x: -12.174414, y: 17.425129, z: -28.57723}
+  - {x: -12.174414, y: 17.425129, z: -22.17723}
+  - {x: -12.174415, y: 16.132559, z: -23.639523}
+  - {x: -12.174414, y: 17.425129, z: -20.577232}
+  - {x: -17.374414, y: 14.319557, z: -28.406765}
+  - {x: -17.374409, y: 14.298503, z: -22.83273}
+  - {x: -17.374414, y: 16.826527, z: -28.389458}
+  - {x: -17.374413, y: 16.844284, z: -22.84882}
+  - {x: -16.974415, y: 14.298503, z: -22.83273}
+  - {x: -16.574413, y: 14.319557, z: -28.406765}
+  - {x: -16.574413, y: 16.826527, z: -28.389458}
+  - {x: -16.574411, y: 16.844284, z: -22.848822}
+  - {x: -16.174414, y: 14.298503, z: -22.83273}
+  - {x: -15.774415, y: 14.319557, z: -28.406765}
+  - {x: -15.5751705, y: 14.311468, z: -28.17723}
+  - {x: -15.582737, y: 14.303136, z: -27.268627}
+  - {x: -15.611024, y: 14.271986, z: -23.703548}
+  - {x: -15.532277, y: 14.932559, z: -27.294935}
+  - {x: -15.592218, y: 14.932559, z: -23.677608}
+  - {x: -15.532277, y: 15.732559, z: -27.294935}
+  - {x: -15.592218, y: 15.732559, z: -23.677608}
+  - {x: -15.532277, y: 16.532558, z: -27.294935}
+  - {x: -15.592218, y: 16.532558, z: -23.677608}
+  - {x: -15.558942, y: 17.493614, z: -28.177227}
+  - {x: -15.569367, y: 17.502712, z: -27.275595}
+  - {x: -15.605135, y: 17.533928, z: -23.69542}
+  - {x: -14.974415, y: 14.344173, z: -27.288052}
+  - {x: -14.974415, y: 14.288204, z: -23.683237}
+  - {x: -14.974415, y: 17.453423, z: -27.305038}
+  - {x: -14.974415, y: 17.518173, z: -23.670525}
+  - {x: -14.574414, y: 14.288204, z: -23.683235}
+  - {x: -14.174417, y: 14.344173, z: -27.288052}
+  - {x: -14.174415, y: 17.453423, z: -27.305038}
+  - {x: -14.174415, y: 17.518173, z: -23.670525}
+  - {x: -13.774414, y: 14.288204, z: -23.683237}
+  - {x: -13.374414, y: 14.344173, z: -27.288052}
+  - {x: -13.3744135, y: 14.288203, z: -23.683237}
+  - {x: -13.3744135, y: 17.453423, z: -27.305038}
+  - {x: -13.374414, y: 17.518173, z: -23.670525}
+  - {x: -12.574412, y: 14.344173, z: -27.288052}
+  - {x: -12.574416, y: 14.288204, z: -23.683237}
+  - {x: -12.574414, y: 17.453423, z: -27.305038}
+  - {x: -12.574414, y: 17.518173, z: -23.670525}
+  - {x: -12.174413, y: 14.288202, z: -23.683237}
+  - {x: -11.774414, y: 14.344173, z: -27.288052}
+  - {x: -11.774415, y: 17.453426, z: -27.305038}
+  - {x: -11.774414, y: 17.518175, z: -23.670525}
+  - {x: -11.374414, y: 14.288204, z: -23.683237}
+  - {x: -10.974415, y: 14.344173, z: -27.288052}
+  - {x: -10.574411, y: 14.288204, z: -23.683237}
+  - {x: 4.9585724, y: 14.363405, z: -42.752556}
+  - {x: 4.9585733, y: 14.363402, z: -41.152557}
+  - {x: 4.9585733, y: 14.363403, z: -39.55256}
+  - {x: 4.808572, y: 16.132559, z: -43.57505}
+  - {x: 4.808572, y: 16.132559, z: -37.344673}
+  - {x: 4.808572, y: 16.132559, z: -33.61733}
+  - {x: 6.5585737, y: 14.363405, z: -42.752556}
+  - {x: 6.558572, y: 14.363403, z: -41.152557}
+  - {x: 6.558572, y: 14.363403, z: -39.55256}
+  - {x: 6.5585737, y: 16.132559, z: -43.57505}
+  - {x: 6.558573, y: 17.425133, z: -42.752556}
+  - {x: 6.558573, y: 17.425133, z: -41.152554}
+  - {x: 6.558573, y: 17.425133, z: -39.55255}
+  - {x: 6.5585737, y: 16.132559, z: -37.344673}
+  - {x: 6.5585737, y: 16.132559, z: -33.61733}
+  - {x: 8.158573, y: 14.363405, z: -42.752556}
+  - {x: 8.158572, y: 14.363402, z: -41.152557}
+  - {x: 8.158571, y: 14.363403, z: -39.55256}
+  - {x: 8.158573, y: 16.132559, z: -43.57505}
+  - {x: 8.158573, y: 17.425133, z: -42.752556}
+  - {x: 8.158573, y: 17.425133, z: -41.15255}
+  - {x: 8.158573, y: 17.425133, z: -39.55255}
+  - {x: 8.158573, y: 16.132559, z: -37.344673}
+  - {x: 8.158573, y: 16.132559, z: -33.61733}
+  - {x: 9.758574, y: 14.363407, z: -42.752556}
+  - {x: 9.758574, y: 14.363405, z: -41.152557}
+  - {x: 9.758572, y: 14.363403, z: -39.55256}
+  - {x: 10.808578, y: 17.425133, z: -42.752556}
+  - {x: 10.808578, y: 17.425133, z: -41.152554}
+  - {x: 10.808578, y: 17.425133, z: -39.552555}
+  - {x: 4.656455, y: 14.341422, z: -44.35255}
+  - {x: 4.6552954, y: 14.341786, z: -36.752556}
+  - {x: 4.6552944, y: 14.341785, z: -35.952557}
+  - {x: 4.6552944, y: 14.341787, z: -35.152557}
+  - {x: 4.6552925, y: 14.341787, z: -34.35256}
+  - {x: 4.9585724, y: 14.334702, z: -43.671806}
+  - {x: 4.9585733, y: 14.297612, z: -37.29296}
+  - {x: 4.9585724, y: 14.325924, z: -33.700756}
+  - {x: 5.7585735, y: 14.334702, z: -43.671803}
+  - {x: 5.7585726, y: 14.297613, z: -37.29296}
+  - {x: 5.758569, y: 14.325925, z: -33.700756}
+  - {x: 6.158573, y: 14.325926, z: -33.700764}
+  - {x: 6.558573, y: 14.334702, z: -43.671803}
+  - {x: 6.558572, y: 14.297609, z: -37.29296}
+  - {x: 6.55857, y: 14.325924, z: -33.70076}
+  - {x: 6.558573, y: 17.465935, z: -43.653984}
+  - {x: 6.558573, y: 17.50856, z: -37.30719}
+  - {x: 6.558573, y: 17.47684, z: -33.683125}
+  - {x: 6.9585714, y: 14.297613, z: -37.29296}
+  - {x: 7.35857, y: 14.334702, z: -43.671803}
+  - {x: 7.35857, y: 14.325924, z: -33.70076}
+  - {x: 7.358572, y: 17.465935, z: -43.65398}
+  - {x: 7.358571, y: 17.50856, z: -37.30719}
+  - {x: 7.358572, y: 17.47684, z: -33.68313}
+  - {x: 7.7585726, y: 14.297613, z: -37.29296}
+  - {x: 8.158573, y: 14.334702, z: -43.671806}
+  - {x: 8.158572, y: 14.325924, z: -33.70076}
+  - {x: 8.158573, y: 17.465935, z: -43.65398}
+  - {x: 8.158573, y: 17.508556, z: -37.30719}
+  - {x: 8.158573, y: 17.47684, z: -33.68313}
+  - {x: 8.558573, y: 14.297611, z: -37.29296}
+  - {x: 8.958571, y: 14.334702, z: -43.671803}
+  - {x: 8.9585705, y: 14.325924, z: -33.70076}
+  - {x: 8.958573, y: 17.465935, z: -43.65398}
+  - {x: 8.958573, y: 17.50856, z: -37.30719}
+  - {x: 8.958573, y: 17.476837, z: -33.68313}
+  - {x: 9.358573, y: 14.297613, z: -37.29296}
+  - {x: 9.358574, y: 17.47684, z: -33.683125}
+  - {x: 9.7585745, y: 14.334702, z: -43.671806}
+  - {x: 9.758569, y: 14.325925, z: -33.70076}
+  - {x: 9.758574, y: 17.465935, z: -43.65398}
+  - {x: 9.758574, y: 17.50856, z: -37.30719}
+  - {x: 10.15857, y: 14.297613, z: -37.29296}
+  - {x: 10.158573, y: 17.47684, z: -33.683125}
+  - {x: 10.655849, y: 14.322624, z: -43.679085}
+  - {x: 10.645539, y: 14.344873, z: -43.152557}
+  - {x: 10.64554, y: 14.34487, z: -37.952553}
+  - {x: 10.670243, y: 14.291561, z: -37.286278}
+  - {x: 10.659149, y: 14.315504, z: -33.708237}
+  - {x: 10.64554, y: 14.344869, z: -33.152557}
+  - {x: 10.607723, y: 14.932559, z: -43.616493}
+  - {x: 10.652278, y: 14.932559, z: -37.329113}
+  - {x: 10.621093, y: 14.932559, z: -33.649338}
+  - {x: 10.607723, y: 15.732559, z: -43.616493}
+  - {x: 10.652278, y: 15.732559, z: -37.329113}
+  - {x: 10.621093, y: 15.732559, z: -33.649338}
+  - {x: 10.607723, y: 16.532558, z: -43.616493}
+  - {x: 10.652278, y: 16.532558, z: -37.329113}
+  - {x: 10.621093, y: 16.532558, z: -33.649338}
+  - {x: 10.64524, y: 17.480797, z: -43.665283}
+  - {x: 10.628681, y: 17.452475, z: -43.15256}
+  - {x: 10.719092, y: 17.486637, z: -42.75255}
+  - {x: 10.719092, y: 17.486637, z: -41.952553}
+  - {x: 10.719095, y: 17.486637, z: -41.55256}
+  - {x: 10.719092, y: 17.486635, z: -41.15255}
+  - {x: 10.719092, y: 17.486635, z: -40.352554}
+  - {x: 10.719096, y: 17.486633, z: -39.952553}
+  - {x: 10.719092, y: 17.486637, z: -39.552555}
+  - {x: 10.719092, y: 17.486637, z: -38.752556}
+  - {x: 10.62868, y: 17.452475, z: -37.952557}
+  - {x: 10.665113, y: 17.514791, z: -37.298508}
+  - {x: 10.650077, y: 17.48907, z: -33.6942}
+  - {x: 10.62868, y: 17.452475, z: -33.152557}
+  - {x: 10.719091, y: 17.486635, z: -32.35256}
+  - {x: 10.9585705, y: 14.34551, z: -42.837494}
+  - {x: 10.958568, y: 14.356126, z: -38.303543}
+  - {x: 10.9585705, y: 16.807053, z: -42.852043}
+  - {x: 10.95857, y: 16.799818, z: -38.2933}
+  - {x: 10.9585705, y: 16.815638, z: -32.891876}
+  - {x: 11.35857, y: 14.333638, z: -32.87522}
+  - {x: 4.9585733, y: 16.132559, z: -41.152557}
+  - {x: -2.3524222, y: 14.363402, z: -41.152557}
+  - {x: -2.3524222, y: 14.363403, z: -39.55256}
+  - {x: -2.3524222, y: 16.132559, z: -38.26516}
+  - {x: -0.75242233, y: 14.363406, z: -44.352554}
+  - {x: -0.75242233, y: 14.363405, z: -42.752556}
+  - {x: -0.7524222, y: 14.363403, z: -41.152557}
+  - {x: -0.7524222, y: 14.363403, z: -39.55256}
+  - {x: -1.4691638, y: 16.132559, z: -37.95256}
+  - {x: 0.84757733, y: 14.363407, z: -44.352554}
+  - {x: 0.8475776, y: 14.363405, z: -42.752556}
+  - {x: 0.84757805, y: 14.363403, z: -41.152554}
+  - {x: 0.84757805, y: 14.363403, z: -39.55256}
+  - {x: 0.84757805, y: 14.363403, z: -37.952557}
+  - {x: 0.84757733, y: 14.363403, z: -33.152557}
+  - {x: 0.40049624, y: 16.132559, z: -36.352562}
+  - {x: 0.65682244, y: 16.132559, z: -34.75256}
+  - {x: 2.4475777, y: 14.363407, z: -44.352554}
+  - {x: 2.4475777, y: 14.363405, z: -42.752556}
+  - {x: 2.4475782, y: 14.363402, z: -41.152557}
+  - {x: 2.4475782, y: 14.363403, z: -39.55256}
+  - {x: 2.4475782, y: 14.363403, z: -37.95256}
+  - {x: 2.4475782, y: 14.363403, z: -36.352554}
+  - {x: 2.4475782, y: 14.363403, z: -34.752556}
+  - {x: 2.4475782, y: 14.363403, z: -33.152557}
+  - {x: -2.7524223, y: 14.319477, z: -38.181797}
+  - {x: -2.7524223, y: 14.340698, z: -32.052715}
+  - {x: -1.9524224, y: 14.3118515, z: -38.153282}
+  - {x: -1.9524224, y: 14.333114, z: -32.076923}
+  - {x: -1.1524221, y: 14.338701, z: -37.846077}
+  - {x: -1.1524221, y: 14.282249, z: -32.29135}
+  - {x: -0.7524221, y: 14.290902, z: -37.660496}
+  - {x: -0.44243693, y: 14.461212, z: -37.476524}
+  - {x: -0.39480662, y: 14.484229, z: -32.790565}
+  - {x: -0.42034554, y: 15.332559, z: -37.495186}
+  - {x: -0.3594172, y: 15.332559, z: -32.75883}
+  - {x: -0.42034554, y: 16.132559, z: -37.495186}
+  - {x: -0.35941744, y: 16.132559, z: -32.75883}
+  - {x: -0.42034554, y: 16.93256, z: -37.495186}
+  - {x: -0.35941744, y: 16.93256, z: -32.75883}
+  - {x: -0.20021224, y: 14.300207, z: -37.152554}
+  - {x: -0.000054597855, y: 14.473859, z: -33.217804}
+  - {x: 0.022473335, y: 15.332559, z: -33.186943}
+  - {x: 0.022473574, y: 16.132559, z: -33.186943}
+  - {x: 0.022473574, y: 16.93256, z: -33.186943}
+  - {x: 0.13284516, y: 14.286568, z: -36.752556}
+  - {x: 0.3117416, y: 14.32961, z: -36.35256}
+  - {x: 0.34191656, y: 14.338959, z: -33.952557}
+  - {x: 0.5905678, y: 14.298172, z: -35.55256}
+  - {x: 0.60220504, y: 14.30075, z: -34.75256}
+  - {x: -2.3524222, y: 16.132559, z: -44.352562}
+  - {x: -0.7524222, y: 16.132559, z: -39.55256}
+  - {x: 4.9585724, y: 14.363409, z: -49.777878}
+  - {x: 6.3085775, y: 14.532555, z: -50.14314}
+  - {x: 6.3085766, y: 15.53034, z: -52.12789}
+  - {x: 6.3085775, y: 14.829124, z: -50.527893}
+  - {x: 4.808572, y: 16.132559, z: -47.566948}
+  - {x: 4.656455, y: 14.341423, z: -47.32789}
+  - {x: 4.656455, y: 14.341423, z: -46.527885}
+  - {x: 4.656457, y: 14.341422, z: -45.72789}
+  - {x: 4.656455, y: 14.341422, z: -44.92788}
+  - {x: 4.558572, y: 15.493456, z: -53.497528}
+  - {x: 4.9585733, y: 14.306412, z: -47.506775}
+  - {x: 5.358572, y: 15.493456, z: -53.497528}
+  - {x: 6.071147, y: 14.322507, z: -49.72789}
+  - {x: 6.081318, y: 14.285102, z: -49.32789}
+  - {x: 6.086381, y: 14.284189, z: -48.52789}
+  - {x: 6.0889196, y: 14.283737, z: -48.12789}
+  - {x: 6.1134233, y: 14.266841, z: -47.46808}
+  - {x: 6.0630674, y: 14.737145, z: -50.527893}
+  - {x: 6.0901814, y: 14.932559, z: -47.4851}
+  - {x: 6.054942, y: 15.151543, z: -51.32789}
+  - {x: 6.0739527, y: 15.452324, z: -53.52672}
+  - {x: 6.064189, y: 15.462715, z: -52.927895}
+  - {x: 6.069124, y: 15.461779, z: -52.12789}
+  - {x: 6.073847, y: 15.4498825, z: -51.72789}
+  - {x: 6.0901766, y: 15.732558, z: -47.4851}
+  - {x: 6.0400085, y: 16.132559, z: -53.505325}
+  - {x: 6.0901766, y: 16.532558, z: -47.4851}
+  - {x: 6.0400085, y: 16.932556, z: -53.505325}
+  - {x: 6.090187, y: 17.332558, z: -47.4851}
+  - {x: 6.0400085, y: 17.73256, z: -53.505325}
+  - {x: -2.3524222, y: 14.363409, z: -49.777878}
+  - {x: -2.352423, y: 14.532555, z: -50.143143}
+  - {x: -0.75242233, y: 14.363409, z: -49.777878}
+  - {x: -0.7524215, y: 14.532555, z: -50.143143}
+  - {x: -0.75242233, y: 14.363408, z: -47.32789}
+  - {x: -0.75242233, y: 14.363407, z: -45.72789}
+  - {x: 0.37114406, y: 16.132555, z: -50.427895}
+  - {x: 0.8475776, y: 14.363409, z: -49.77787}
+  - {x: 0.84757733, y: 14.363408, z: -47.32789}
+  - {x: 0.84757733, y: 14.363407, z: -45.72789}
+  - {x: 0.69757795, y: 16.132559, z: -50.09755}
+  - {x: 2.4475777, y: 14.363409, z: -49.777878}
+  - {x: 2.4475777, y: 14.363408, z: -47.32789}
+  - {x: 2.4475777, y: 14.363407, z: -45.72789}
+  - {x: 2.3975785, y: 16.132559, z: -50.09755}
+  - {x: -2.7524223, y: 15.493456, z: -53.497528}
+  - {x: -1.9524223, y: 15.493456, z: -53.497528}
+  - {x: -1.1524222, y: 15.493456, z: -53.497528}
+  - {x: -0.35242128, y: 15.493456, z: -53.497528}
+  - {x: 0.49571204, y: 14.814356, z: -50.527893}
+  - {x: 0.4916482, y: 15.230816, z: -51.32789}
+  - {x: 0.44757748, y: 15.493456, z: -53.497528}
+  - {x: 0.5124068, y: 15.5215225, z: -52.527893}
+  - {x: 0.51422, y: 15.508097, z: -51.727882}
+  - {x: 1.2475772, y: 15.493456, z: -53.497528}
+  - {x: 1.2475772, y: 15.4861965, z: -52.729782}
+  - {x: 2.0475771, y: 15.493454, z: -53.497528}
+  - {x: 2.047578, y: 15.486197, z: -52.729782}
+  - {x: 2.6486537, y: 14.760124, z: -50.527893}
+  - {x: 2.6525686, y: 15.173641, z: -51.32789}
+  - {x: 2.8475764, y: 15.493455, z: -53.497528}
+  - {x: 2.628825, y: 15.481205, z: -52.527885}
+  - {x: 2.6262615, y: 15.469427, z: -51.72789}
+  - {x: 3.647578, y: 15.493456, z: -53.497528}
+  - {x: -2.3524222, y: 16.132559, z: -48.927895}
+  - {x: 0.8475778, y: 16.132559, z: -47.327892}
+  - {x: -9.663419, y: 14.363405, z: -42.752556}
+  - {x: -9.66342, y: 14.363402, z: -41.152557}
+  - {x: -9.66342, y: 14.363403, z: -39.55256}
+  - {x: -8.06342, y: 14.363407, z: -44.352554}
+  - {x: -8.06342, y: 14.363405, z: -42.752556}
+  - {x: -8.063419, y: 14.363403, z: -41.152557}
+  - {x: -8.063419, y: 14.363403, z: -39.55256}
+  - {x: -8.063419, y: 14.363403, z: -37.95256}
+  - {x: -8.06342, y: 14.363403, z: -36.352562}
+  - {x: -8.06342, y: 14.363403, z: -34.752556}
+  - {x: -8.063419, y: 14.363403, z: -33.152557}
+  - {x: -9.52762, y: 16.132559, z: -43.85257}
+  - {x: -9.525737, y: 16.132559, z: -37.052547}
+  - {x: -9.525737, y: 16.132559, z: -33.90256}
+  - {x: -6.4634194, y: 14.363407, z: -44.352554}
+  - {x: -6.4634194, y: 14.363405, z: -42.752556}
+  - {x: -6.46342, y: 14.363403, z: -41.152557}
+  - {x: -6.46342, y: 14.363403, z: -39.55256}
+  - {x: -6.46342, y: 14.363403, z: -37.95256}
+  - {x: -6.46342, y: 14.363403, z: -36.352554}
+  - {x: -6.46342, y: 14.363403, z: -34.75256}
+  - {x: -6.4634194, y: 14.363402, z: -33.152557}
+  - {x: -5.380495, y: 16.132559, z: -36.352562}
+  - {x: -5.6368217, y: 16.132559, z: -34.75256}
+  - {x: -4.8634195, y: 14.363407, z: -44.35256}
+  - {x: -4.8634195, y: 14.363405, z: -42.752556}
+  - {x: -4.8634195, y: 14.363402, z: -41.152557}
+  - {x: -4.8634195, y: 14.363403, z: -39.55256}
+  - {x: -10.063419, y: 14.334702, z: -43.671806}
+  - {x: -10.063419, y: 14.297612, z: -37.29296}
+  - {x: -10.063419, y: 14.325924, z: -33.70076}
+  - {x: -9.571083, y: 14.287891, z: -44.35256}
+  - {x: -9.569446, y: 14.288198, z: -36.752556}
+  - {x: -9.569446, y: 14.288198, z: -35.952557}
+  - {x: -9.569446, y: 14.288198, z: -35.152554}
+  - {x: -9.569446, y: 14.288198, z: -34.35256}
+  - {x: -5.33939, y: 14.284864, z: -36.352562}
+  - {x: -5.5320044, y: 14.330952, z: -35.55256}
+  - {x: -5.5414486, y: 14.333853, z: -34.752556}
+  - {x: -5.3789315, y: 14.292412, z: -33.952553}
+  - {x: -5.0834312, y: 14.316942, z: -36.752556}
+  - {x: -5.222599, y: 14.470321, z: -33.635727}
+  - {x: -5.2375717, y: 15.332559, z: -33.605225}
+  - {x: -5.237572, y: 16.132559, z: -33.605225}
+  - {x: -5.237572, y: 16.93256, z: -33.605225}
+  - {x: -4.881655, y: 14.932558, z: -37.173912}
+  - {x: -4.7951736, y: 14.932559, z: -32.821312}
+  - {x: -4.881525, y: 15.732559, z: -37.173897}
+  - {x: -4.922744, y: 15.732559, z: -33.07921}
+  - {x: -4.881525, y: 16.532558, z: -37.173897}
+  - {x: -4.922744, y: 16.532558, z: -33.07921}
+  - {x: -4.881525, y: 17.332558, z: -37.173897}
+  - {x: -4.922744, y: 17.332558, z: -33.07921}
+  - {x: -4.4310846, y: 14.49298, z: -37.527306}
+  - {x: -4.4604263, y: 14.525636, z: -32.755043}
+  - {x: -4.4752674, y: 15.332559, z: -37.561813}
+  - {x: -4.538963, y: 15.732559, z: -32.68988}
+  - {x: -4.475268, y: 16.132559, z: -37.561813}
+  - {x: -4.538963, y: 16.532558, z: -32.68988}
+  - {x: -4.475268, y: 16.93256, z: -37.561813}
+  - {x: -4.5389633, y: 17.332558, z: -32.68988}
+  - {x: -3.992415, y: 14.474929, z: -32.38887}
+  - {x: -4.026157, y: 15.332559, z: -32.371616}
+  - {x: -4.0261574, y: 16.132559, z: -32.371616}
+  - {x: -4.0261574, y: 16.93256, z: -32.371616}
+  - {x: -3.630003, y: 14.497572, z: -37.940613}
+  - {x: -3.6634195, y: 14.294916, z: -32.224827}
+  - {x: -3.6842175, y: 15.332559, z: -37.96}
+  - {x: -3.6842177, y: 16.132559, z: -37.96}
+  - {x: -3.6842177, y: 16.93256, z: -37.96}
+  - {x: -9.66342, y: 16.132559, z: -42.75256}
+  - {x: -8.06342, y: 16.132559, z: -41.152557}
+  - {x: -18.024418, y: 14.363403, z: -41.152557}
+  - {x: -18.024418, y: 14.363403, z: -39.55256}
+  - {x: -18.324417, y: 16.795082, z: -41.15255}
+  - {x: -16.974413, y: 16.795082, z: -38.302574}
+  - {x: -15.3744135, y: 14.363403, z: -41.152557}
+  - {x: -15.3744135, y: 14.363403, z: -39.55256}
+  - {x: -15.874415, y: 17.425133, z: -41.15255}
+  - {x: -15.874415, y: 17.425133, z: -39.552555}
+  - {x: -13.774414, y: 14.363405, z: -42.752556}
+  - {x: -13.774415, y: 14.363403, z: -41.152557}
+  - {x: -13.774415, y: 14.363403, z: -39.55256}
+  - {x: -13.774415, y: 16.132559, z: -43.57505}
+  - {x: -13.774414, y: 17.425133, z: -42.752556}
+  - {x: -13.774413, y: 17.425133, z: -41.152554}
+  - {x: -13.774413, y: 17.425133, z: -39.552555}
+  - {x: -13.774415, y: 16.132559, z: -37.344673}
+  - {x: -13.774415, y: 16.132559, z: -33.61733}
+  - {x: -12.174415, y: 14.363405, z: -42.752556}
+  - {x: -12.174415, y: 14.363402, z: -41.152557}
+  - {x: -12.174415, y: 14.363403, z: -39.55256}
+  - {x: -12.174415, y: 16.132559, z: -43.57505}
+  - {x: -12.174414, y: 17.425133, z: -42.752556}
+  - {x: -12.174414, y: 17.425133, z: -41.152554}
+  - {x: -12.174414, y: 17.425133, z: -39.552555}
+  - {x: -12.174415, y: 16.132559, z: -37.344673}
+  - {x: -12.174415, y: 16.132559, z: -33.61733}
+  - {x: -17.374414, y: 14.343626, z: -42.843468}
+  - {x: -17.37441, y: 14.3561125, z: -38.30354}
+  - {x: -17.37441, y: 14.333639, z: -32.87522}
+  - {x: -17.37441, y: 16.80838, z: -42.858482}
+  - {x: -17.374413, y: 16.799818, z: -38.293297}
+  - {x: -17.374414, y: 16.81564, z: -32.891884}
+  - {x: -16.574411, y: 14.343626, z: -42.84347}
+  - {x: -16.574411, y: 14.356113, z: -38.30354}
+  - {x: -16.57441, y: 14.333639, z: -32.875217}
+  - {x: -16.57441, y: 16.80838, z: -42.85849}
+  - {x: -16.57441, y: 16.799818, z: -38.2933}
+  - {x: -16.574411, y: 16.815638, z: -32.89188}
+  - {x: -15.774415, y: 14.343626, z: -42.84347}
+  - {x: -15.774415, y: 14.353501, z: -38.293983}
+  - {x: -15.774413, y: 14.333638, z: -32.87522}
+  - {x: -15.586837, y: 14.298623, z: -43.693546}
+  - {x: -15.575173, y: 14.311472, z: -43.152557}
+  - {x: -15.5751705, y: 14.311469, z: -37.952553}
+  - {x: -15.605552, y: 14.278009, z: -37.271305}
+  - {x: -15.590872, y: 14.294176, z: -33.72353}
+  - {x: -15.542988, y: 14.932559, z: -43.664448}
+  - {x: -15.58264, y: 14.932559, z: -37.29919}
+  - {x: -15.552675, y: 14.932559, z: -33.69336}
+  - {x: -15.542988, y: 15.732559, z: -43.664448}
+  - {x: -15.58264, y: 15.732559, z: -37.29919}
+  - {x: -15.552675, y: 15.732559, z: -33.69336}
+  - {x: -15.542988, y: 16.532558, z: -43.664448}
+  - {x: -15.58264, y: 16.532558, z: -37.29919}
+  - {x: -15.552675, y: 16.532558, z: -33.69336}
+  - {x: -15.574855, y: 17.507504, z: -43.685596}
+  - {x: -15.558945, y: 17.493618, z: -43.152557}
+  - {x: -15.558939, y: 17.493618, z: -37.952553}
+  - {x: -15.598554, y: 17.528185, z: -37.279827}
+  - {x: -15.580135, y: 17.512115, z: -33.715046}
+  - {x: -15.558942, y: 17.493618, z: -33.152557}
+  - {x: -14.974413, y: 14.334702, z: -43.671803}
+  - {x: -14.974415, y: 14.297613, z: -37.29296}
+  - {x: -14.974416, y: 14.325925, z: -33.70076}
+  - {x: -14.974415, y: 17.465935, z: -43.65398}
+  - {x: -14.974415, y: 17.50856, z: -37.30719}
+  - {x: -14.974415, y: 17.47684, z: -33.683125}
+  - {x: -14.174414, y: 14.334702, z: -43.671806}
+  - {x: -14.174415, y: 14.297613, z: -37.29296}
+  - {x: -14.1744175, y: 14.325925, z: -33.700756}
+  - {x: -14.174415, y: 17.465935, z: -43.65398}
+  - {x: -14.174415, y: 17.50856, z: -37.30719}
+  - {x: -14.174415, y: 17.47684, z: -33.683125}
+  - {x: -13.374412, y: 14.334702, z: -43.671806}
+  - {x: -13.374414, y: 14.297613, z: -37.29296}
+  - {x: -13.374414, y: 14.325924, z: -33.70076}
+  - {x: -13.374412, y: 17.465935, z: -43.65398}
+  - {x: -13.3744135, y: 17.50856, z: -37.30719}
+  - {x: -13.3744135, y: 17.47684, z: -33.68313}
+  - {x: -12.574412, y: 14.334702, z: -43.6718}
+  - {x: -12.574412, y: 14.297613, z: -37.29296}
+  - {x: -12.574417, y: 14.325925, z: -33.700756}
+  - {x: -12.574414, y: 17.465935, z: -43.65398}
+  - {x: -12.574414, y: 17.50856, z: -37.30719}
+  - {x: -12.574414, y: 17.47684, z: -33.68313}
+  - {x: -11.774413, y: 14.334702, z: -43.671806}
+  - {x: -11.774414, y: 14.297613, z: -37.29296}
+  - {x: -11.774414, y: 14.325924, z: -33.70076}
+  - {x: -11.774414, y: 17.465935, z: -43.653984}
+  - {x: -11.774414, y: 17.50856, z: -37.30719}
+  - {x: -11.774414, y: 17.47684, z: -33.683125}
+  - {x: -11.3744135, y: 14.297609, z: -37.29296}
+  - {x: -10.974413, y: 14.334702, z: -43.671806}
+  - {x: -10.974415, y: 14.297613, z: -37.29296}
+  - {x: -10.974415, y: 14.325924, z: -33.70076}
+  - {x: -9.66342, y: 14.363409, z: -49.77787}
+  - {x: -9.663419, y: 14.532555, z: -50.143143}
+  - {x: -11.040903, y: 13.382554, z: -48.927895}
+  - {x: -9.663419, y: 15.53034, z: -52.12789}
+  - {x: -11.040903, y: 16.132559, z: -52.12789}
+  - {x: -9.663419, y: 14.829126, z: -50.527893}
+  - {x: -11.040903, y: 16.132559, z: -50.527893}
+  - {x: -11.040903, y: 16.132559, z: -48.927895}
+  - {x: -8.06342, y: 14.363409, z: -49.777878}
+  - {x: -8.06342, y: 14.363408, z: -47.32789}
+  - {x: -8.06342, y: 14.363407, z: -45.72789}
+  - {x: -7.703926, y: 16.132557, z: -50.427895}
+  - {x: -9.52762, y: 16.132559, z: -47.277893}
+  - {x: -9.52762, y: 16.132559, z: -45.72789}
+  - {x: -6.4634194, y: 14.363409, z: -49.777878}
+  - {x: -5.6134205, y: 14.532558, z: -50.09755}
+  - {x: -6.46342, y: 14.363408, z: -47.32789}
+  - {x: -6.46342, y: 14.363407, z: -45.72789}
+  - {x: -5.6134205, y: 16.132559, z: -50.09755}
+  - {x: -4.8634195, y: 14.363409, z: -49.77787}
+  - {x: -5.362876, y: 16.132559, z: -50.377895}
+  - {x: -10.063419, y: 14.306412, z: -47.506775}
+  - {x: -10.063419, y: 15.493456, z: -53.497528}
+  - {x: -9.571083, y: 14.287891, z: -47.32789}
+  - {x: -9.571082, y: 14.287892, z: -46.92788}
+  - {x: -9.571083, y: 14.287891, z: -46.52789}
+  - {x: -9.571084, y: 14.287891, z: -45.72789}
+  - {x: -9.571084, y: 14.287891, z: -44.927895}
+  - {x: -9.263417, y: 15.493456, z: -53.497528}
+  - {x: -8.46342, y: 15.493456, z: -53.497528}
+  - {x: -7.596981, y: 14.80616, z: -50.527893}
+  - {x: -7.601529, y: 15.221861, z: -51.32789}
+  - {x: -7.663418, y: 15.493456, z: -53.497528}
+  - {x: -7.57765, y: 15.516257, z: -52.527893}
+  - {x: -7.5754967, y: 15.503124, z: -51.727882}
+  - {x: -6.8634186, y: 15.493455, z: -53.497528}
+  - {x: -6.8634186, y: 15.4861965, z: -52.729782}
+  - {x: -6.0634193, y: 15.493456, z: -53.497528}
+  - {x: -6.0634193, y: 15.486197, z: -52.729782}
+  - {x: -5.422387, y: 14.771477, z: -50.527893}
+  - {x: -5.418062, y: 15.185244, z: -51.32789}
+  - {x: -5.263419, y: 15.493454, z: -53.497528}
+  - {x: -5.4434376, y: 15.490761, z: -52.527885}
+  - {x: -5.446067, y: 15.478712, z: -51.72789}
+  - {x: -4.4634194, y: 15.493456, z: -53.497528}
+  - {x: -3.6634192, y: 15.493456, z: -53.497528}
+  - {x: -8.06342, y: 16.132559, z: -48.927895}
+  - {x: -11.113293, y: 14.364956, z: -49.72789}
+  - {x: -11.123839, y: 14.325569, z: -49.32789}
+  - {x: -11.123838, y: 14.32557, z: -48.52789}
+  - {x: -11.123839, y: 14.325569, z: -48.12789}
+  - {x: -11.149162, y: 14.290491, z: -47.491196}
+  - {x: -11.108923, y: 14.779399, z: -50.527893}
+  - {x: -11.122109, y: 14.932559, z: -47.527847}
+  - {x: -11.104368, y: 15.193446, z: -51.327896}
+  - {x: -11.145819, y: 15.47385, z: -53.51145}
+  - {x: -11.130435, y: 15.4970665, z: -52.927887}
+  - {x: -11.130437, y: 15.4970665, z: -52.127884}
+  - {x: -11.133056, y: 15.484801, z: -51.72789}
+  - {x: -11.122109, y: 15.732559, z: -47.527847}
+  - {x: -11.104401, y: 16.132559, z: -53.467094}
+  - {x: -11.122109, y: 16.532558, z: -47.527847}
+  - {x: -11.104401, y: 16.93256, z: -53.467094}
+  - {x: -11.122109, y: 17.332558, z: -47.527847}
+  - {x: -11.104401, y: 17.73256, z: -53.467094}
+  - {x: -10.574412, y: 14.306412, z: -47.506775}
+  - {x: -10.574413, y: 15.493456, z: -53.497528}
+  m_Dering: 1
+--- !u!1 &4878004048519089636
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 5330336715315649412}
+  - component: {fileID: 6842057081229428253}
+  - component: {fileID: 1065727879130675365}
+  m_Layer: 0
+  m_Name: lobby_halloween_floor.001
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 4294967295
+  m_IsActive: 1
+--- !u!4 &5330336715315649412
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4878004048519089636}
+  m_LocalRotation: {x: -0.00000005960463, y: 0, z: -0, w: 1}
+  m_LocalPosition: {x: 0.389845, y: 0.023437014, z: 0.60138685}
+  m_LocalScale: {x: 1.0000004, y: 1, z: 1.000001}
+  m_Children: []
+  m_Father: {fileID: 1571151301987675373}
+  m_RootOrder: 3
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &6842057081229428253
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4878004048519089636}
+  m_Mesh: {fileID: 4300012, guid: 8b471d963a4288f40be104b5e2c8b2c5, type: 3}
+--- !u!23 &1065727879130675365
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 4878004048519089636}
+  m_Enabled: 1
+  m_CastShadows: 0
+  m_ReceiveShadows: 0
+  m_DynamicOccludee: 1
+  m_MotionVectors: 2
+  m_LightProbeUsage: 0
+  m_ReflectionProbeUsage: 0
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: ad20951c7d4fc954da8ecf4c6a94f472, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!1 &5816237316411896870
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 5816237316411896871}
+  m_Layer: 0
+  m_Name: Fake Bounce Cards
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 4294967295
+  m_IsActive: 1
+--- !u!4 &5816237316411896871
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 5816237316411896870}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 4644310040266947290}
+  m_Father: {fileID: 5816237317842038418}
+  m_RootOrder: 1
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &5816237316631983876
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 5816237316631983877}
+  m_Layer: 0
+  m_Name: Light Probe Group - Leave Active
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &5816237316631983877
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 5816237316631983876}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: -0.22151849, y: 9.117853, z: -7.2177863}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 4644310041114672055}
+  m_Father: {fileID: 5816237317524984591}
+  m_RootOrder: 1
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &5816237317524984590
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 5816237317524984591}
+  m_Layer: 0
+  m_Name: p_lobbyHalloween_IndirectLighting
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 65
+  m_IsActive: 1
+--- !u!4 &5816237317524984591
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 5816237317524984590}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 5816237317842038418}
+  - {fileID: 5816237316631983877}
+  m_Father: {fileID: 0}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &5816237317842038417
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 5816237317842038418}
+  m_Layer: 0
+  m_Name: Indirect Lighting Assets - Hide after bake
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 4294967295
+  m_IsActive: 1
+--- !u!4 &5816237317842038418
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 5816237317842038417}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 1571151301987675373}
+  - {fileID: 5816237316411896871}
+  - {fileID: 5816237316957601020}
+  m_Father: {fileID: 5816237317524984591}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!1 &6001443405384865459
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 7613532128838385189}
+  - component: {fileID: 502895765061973463}
+  - component: {fileID: 4447556748215766526}
+  m_Layer: 0
+  m_Name: lobby_halloween_centerpiece.001
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 4294967295
+  m_IsActive: 1
+--- !u!4 &7613532128838385189
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6001443405384865459}
+  m_LocalRotation: {x: 1.4210855e-14, y: 0, z: -0, w: 1}
+  m_LocalPosition: {x: -0, y: -6.6022034e-15, z: 0.5311988}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1571151301987675373}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &502895765061973463
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6001443405384865459}
+  m_Mesh: {fileID: 4300014, guid: 8b471d963a4288f40be104b5e2c8b2c5, type: 3}
+--- !u!23 &4447556748215766526
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6001443405384865459}
+  m_Enabled: 1
+  m_CastShadows: 0
+  m_ReceiveShadows: 0
+  m_DynamicOccludee: 1
+  m_MotionVectors: 2
+  m_LightProbeUsage: 0
+  m_ReflectionProbeUsage: 0
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: f0476de09c36ca046aa06c597a9af78f, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!1 &8131051448207254405
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 3817721655325656216}
+  - component: {fileID: 9162481763980009358}
+  - component: {fileID: 6606532681050816255}
+  m_Layer: 0
+  m_Name: lobby_halloween_entrance.001
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 4294967295
+  m_IsActive: 1
+--- !u!4 &3817721655325656216
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8131051448207254405}
+  m_LocalRotation: {x: 1.4210855e-14, y: 0, z: -0, w: 1}
+  m_LocalPosition: {x: 0.3898447, y: 0.023437476, z: 0.6013869}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1571151301987675373}
+  m_RootOrder: 1
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &9162481763980009358
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8131051448207254405}
+  m_Mesh: {fileID: 4300008, guid: 8b471d963a4288f40be104b5e2c8b2c5, type: 3}
+--- !u!23 &6606532681050816255
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8131051448207254405}
+  m_Enabled: 1
+  m_CastShadows: 0
+  m_ReceiveShadows: 0
+  m_DynamicOccludee: 1
+  m_MotionVectors: 2
+  m_LightProbeUsage: 0
+  m_ReflectionProbeUsage: 0
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: 582bdea2c17bbb94f9c722adeb3228d7, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!1 &8399781240147166605
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 5983657285698611491}
+  - component: {fileID: 7406058502498402362}
+  - component: {fileID: 5077970706745697657}
+  m_Layer: 0
+  m_Name: lobby_halloween_entrancesteps.001
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 4294967295
+  m_IsActive: 1
+--- !u!4 &5983657285698611491
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8399781240147166605}
+  m_LocalRotation: {x: 1.8189894e-14, y: 0, z: -0, w: 1}
+  m_LocalPosition: {x: 0.3898447, y: 0.023437476, z: 0.6013869}
+  m_LocalScale: {x: 100, y: 100, z: 100}
+  m_Children: []
+  m_Father: {fileID: 1571151301987675373}
+  m_RootOrder: 2
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &7406058502498402362
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8399781240147166605}
+  m_Mesh: {fileID: 4300010, guid: 8b471d963a4288f40be104b5e2c8b2c5, type: 3}
+--- !u!23 &5077970706745697657
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8399781240147166605}
+  m_Enabled: 1
+  m_CastShadows: 0
+  m_ReceiveShadows: 0
+  m_DynamicOccludee: 1
+  m_MotionVectors: 2
+  m_LightProbeUsage: 0
+  m_ReflectionProbeUsage: 0
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: bf291f345848121478757ff18e1ed172, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+--- !u!1001 &5816237316957988476
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 5816237317842038418}
+    m_Modifications:
+    - target: {fileID: -4216859302048453862, guid: 94ed071c059f6e04c895dc605415789c,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: -0.01
+      objectReference: {fileID: 0}
+    - target: {fileID: -4216859302048453862, guid: 94ed071c059f6e04c895dc605415789c,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 8.180899
+      objectReference: {fileID: 0}
+    - target: {fileID: -4216859302048453862, guid: 94ed071c059f6e04c895dc605415789c,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: -12.08
+      objectReference: {fileID: 0}
+    - target: {fileID: -4216859302048453862, guid: 94ed071c059f6e04c895dc605415789c,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: -4216859302048453862, guid: 94ed071c059f6e04c895dc605415789c,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: -4216859302048453862, guid: 94ed071c059f6e04c895dc605415789c,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: -4216859302048453862, guid: 94ed071c059f6e04c895dc605415789c,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: -4216859302048453862, guid: 94ed071c059f6e04c895dc605415789c,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: -4216859302048453862, guid: 94ed071c059f6e04c895dc605415789c,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -4216859302048453862, guid: 94ed071c059f6e04c895dc605415789c,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -4216859302048453862, guid: 94ed071c059f6e04c895dc605415789c,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: -1504981713932161579, guid: 94ed071c059f6e04c895dc605415789c,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: 0649fbada42027d498fb1e6c027a1bce, type: 2}
+    - target: {fileID: -1504981713932161579, guid: 94ed071c059f6e04c895dc605415789c,
+        type: 3}
+      propertyPath: m_ReceiveGI
+      value: 2
+      objectReference: {fileID: 0}
+    - target: {fileID: -927199367670048503, guid: 94ed071c059f6e04c895dc605415789c,
+        type: 3}
+      propertyPath: m_Name
+      value: LobbyHalloweenSignage
+      objectReference: {fileID: 0}
+    - target: {fileID: -927199367670048503, guid: 94ed071c059f6e04c895dc605415789c,
+        type: 3}
+      propertyPath: m_StaticEditorFlags
+      value: 4294967295
+      objectReference: {fileID: 0}
+    - target: {fileID: 400000, guid: 94ed071c059f6e04c895dc605415789c, type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 8.180899
+      objectReference: {fileID: 0}
+    - target: {fileID: 400000, guid: 94ed071c059f6e04c895dc605415789c, type: 3}
+      propertyPath: m_RootOrder
+      value: 2
+      objectReference: {fileID: 0}
+    - target: {fileID: 400000, guid: 94ed071c059f6e04c895dc605415789c, type: 3}
+      propertyPath: m_LocalPosition.z
+      value: -12.08
+      objectReference: {fileID: 0}
+    - target: {fileID: 400000, guid: 94ed071c059f6e04c895dc605415789c, type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: 400000, guid: 94ed071c059f6e04c895dc605415789c, type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0.43
+      objectReference: {fileID: 0}
+    - target: {fileID: 2300000, guid: 94ed071c059f6e04c895dc605415789c, type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: cba8e8dd69eb11d4eaa4a72680faf20d, type: 2}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: 94ed071c059f6e04c895dc605415789c, type: 3}
+--- !u!4 &5816237316957601020 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: 400000, guid: 94ed071c059f6e04c895dc605415789c,
+    type: 3}
+  m_PrefabInstance: {fileID: 5816237316957988476}
+  m_PrefabAsset: {fileID: 0}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Prefabs/p_lobbyHalloween_IndirectLighting.prefab.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Prefabs/p_lobbyHalloween_IndirectLighting.prefab.meta
new file mode 100644
index 0000000000000000000000000000000000000000..345df86038a82f4769ea542cae1ca5d5d32c2e23
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Prefabs/p_lobbyHalloween_IndirectLighting.prefab.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 6a7f8143ef3e80845bc4624c6c0a5a50
+PrefabImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Shaders.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Shaders.meta
new file mode 100644
index 0000000000000000000000000000000000000000..e3ae059ff814d602bc27d771367ec4219b3e0ced
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Shaders.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 492e6fc6b8588d74a8b1da7124366058
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Shaders/s_backroundBuildings.shader b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Shaders/s_backroundBuildings.shader
new file mode 100644
index 0000000000000000000000000000000000000000..b2b77057a9a15ba70e92f163d7cd11791606feb2
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Shaders/s_backroundBuildings.shader
@@ -0,0 +1,112 @@
+Shader "Venues/Environment/S_Building"
+{
+    Properties
+    {
+        [Header(Walls)]
+        _WallColor("Wall Color", color) = (1, 1, 1, 1)
+        [Header(Windows)]
+        _WindowTexSize("Window Texture Size", Float) = 1024
+        _WindowSizeAndPadding("Window Number And Padding", Vector) = (12, 12, 10, 40)
+        [Toggle(ENABLE_WINDOW_TEX)] _EnableWindowTex("Enable Window Texture", Float) = 0
+        [HideIf(ENABLE_WINDOW_TEX, false)][NoScaleOffset] _WindowTex("Window Texture", 2D) = "white" {}
+        [HideIf(ENABLE_WINDOW_TEX, true)] _WindowOnColor("Window On Color", color) = (1, 1, 1, 1)
+        _WindowOffColor("Window Off Color", color) = (1, 1, 1, 1)
+        _LightIntensity("Window Light Intensity", Range(0,1)) = 1
+        _WindowThreshold("% Windows", Range(0, 1)) = 0.5
+        _LitThreshold("% Lit Windows", Range(0, 1)) = 0.5
+        [Header(Animation)]
+        _Speed("Speed", Range(0, 0.1)) = 0.01
+        _RandomSeed("Random Seed", Float) = 0
+        [Header(Debug)]
+        [Toggle(DEBUG)] _Debug("Enable Debug", Float) = 0
+    }
+    SubShader
+    {
+        Tags { "RenderType"="Opaque" }
+
+        Pass
+        {
+            CGPROGRAM
+            #pragma vertex vert
+            #pragma fragment frag
+
+            #include "UnityCG.cginc"
+
+            #pragma shader_feature ENABLE_WINDOW_TEX
+            #pragma shader_feature DEBUG 
+
+            struct appdata
+            {
+                float4 vertex : POSITION;
+                float2 uv1 : TEXCOORD0;
+                float2 uv2 : TEXCOORD1;
+                half4 color : COLOR;
+            };
+
+            struct v2f
+            {
+                float2 uv1 : TEXCOORD0;
+                float2 uv2 : TEXCOORD1;
+                float4 vertex : SV_POSITION;
+                half4 color : COLOR;
+            };
+
+            #if ENABLE_WINDOW_TEX
+                sampler2D _WindowTex;
+            #else
+                half4 _WindowOnColor;
+            #endif
+
+            half4 _WindowSizeAndPadding, _WindowOffColor, _WallColor;
+            half _WindowTexSize, _Speed, _LitThreshold, _WindowThreshold, _LightIntensity, _WindowFromTex, _RandomSeed;
+
+            v2f vert (appdata v)
+            {
+                v2f o;
+                o.vertex = UnityObjectToClipPos(v.vertex);
+                o.uv1 = v.uv1;
+                o.uv2 = v.uv2;
+                o.color = v.color;
+                return o;
+            }
+
+            half random(in half2 st) {
+                return frac(sin(dot(st.xy, half2(12.9898, 78.233))) * 43.5453);
+            }
+
+            fixed4 frag (v2f i) : SV_Target
+            {
+                half2 window_size = ((_WindowTexSize - (_WindowSizeAndPadding.xy * _WindowSizeAndPadding.yz)) / _WindowSizeAndPadding.xy) + _WindowSizeAndPadding.yz;
+                half2 size = window_size / _WindowTexSize;
+                half2 st1 = floor((i.uv2 - .5 * _WindowSizeAndPadding.zw / _WindowTexSize) / size) * size;
+                half2 st2 = floor((i.uv2 + .5 * _WindowSizeAndPadding.zw / _WindowTexSize) / size) * size;
+                half2 st = (st2 - st1);
+                half mask = min(1., step(.01, st.r) + step(.01, st.g));
+                st2 *= (1. - mask);
+                half r = random(st2 + _RandomSeed);
+                half val = step(1. - _LitThreshold, frac(_Time.y * r * _Speed + r * 10. + i.color.a * 20.)) * (1. - mask);
+
+                #if ENABLE_WINDOW_TEX
+                    fixed3 window_color = tex2D(_WindowTex, i.uv2) * _LightIntensity;
+                #else
+                    fixed3 window_color = _WindowOnColor * max(.5, r) * _LightIntensity;
+                #endif
+
+                fixed3 col = lerp(lerp(window_color, _WindowOffColor, val), _WallColor.rgb,  min(1., mask + step(_WindowThreshold, r)));
+
+                #if DEBUG
+                    half debug = r;
+                    if (step(.5, i.color.a) > .5) {
+                        col = half3(st2.r, st2.g, 0.);
+                    }
+                    else {
+                        col = half3(r, r, r);
+                    }
+                #endif
+
+                return fixed4(col, 1.);
+            }
+            ENDCG
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Shaders/s_backroundBuildings.shader.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Shaders/s_backroundBuildings.shader.meta
new file mode 100644
index 0000000000000000000000000000000000000000..7ce1c47798d921838f9d9572d94b5cf8315af50f
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Shaders/s_backroundBuildings.shader.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 860976c0d634347459e23b85a6e84611
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Shaders/s_lobbyHalloweenCenterpiece.shader b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Shaders/s_lobbyHalloweenCenterpiece.shader
new file mode 100644
index 0000000000000000000000000000000000000000..3c50946d0ae75f9c665f0c5d6257ce2c2c26a2a4
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Shaders/s_lobbyHalloweenCenterpiece.shader
@@ -0,0 +1,304 @@
+// Upgrade NOTE: upgraded instancing buffer 'customVenuesLobby_HalloweenCenterpiece' to new syntax.
+
+// Made with Amplify Shader Editor
+// Available at the Unity Asset Store - http://u3d.as/y3X 
+Shader "custom/VenuesLobby_HalloweenCenterpiece"
+{
+	Properties
+	{
+		_FlameFlickerSpeed("Flame Flicker Speed", Float) = 0.28
+		_Scale("Scale", Vector) = (8,50,50,0)
+		_hallwayCenterpieceHalloween_bc("hallwayCenterpieceHalloween_bc", 2D) = "white" {}
+		[HideInInspector] _texcoord( "", 2D ) = "white" {}
+
+	}
+	
+	SubShader
+	{
+		
+		
+		Tags { "RenderType"="Opaque" }
+	LOD 100
+
+		CGINCLUDE
+		#pragma target 3.0
+		ENDCG
+		Blend Off
+		AlphaToMask Off
+		Cull Back
+		ColorMask RGBA
+		ZWrite On
+		ZTest LEqual
+		Offset 0 , 0
+		
+		
+		
+		Pass
+		{
+			Name "Unlit"
+
+			// Use this with Universal Rendering Pipeline (URP)
+			// Tags{ "LightMode" = "LightweightForward"}
+			// Use this without Universal Rendering Pipeline (URP)
+			Tags { "LightMode"="ForwardBase" }
+
+			CGPROGRAM
+
+			
+
+			#ifndef UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX
+			//only defining to not throw compilation error over Unity 5.5
+			#define UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input)
+			#endif
+			#pragma vertex vert
+			#pragma fragment frag
+			#pragma multi_compile_instancing
+			#include "UnityCG.cginc"
+			#include "UnityShaderVariables.cginc"
+			#define ASE_NEEDS_VERT_COLOR
+			#define ASE_NEEDS_FRAG_COLOR
+			#define ASE_NEEDS_FRAG_WORLD_POSITION
+
+
+			struct appdata
+			{
+				float4 vertex : POSITION;
+				float4 color : COLOR;
+				float4 ase_texcoord : TEXCOORD0;
+				UNITY_VERTEX_INPUT_INSTANCE_ID
+			};
+			
+			struct v2f
+			{
+				float4 vertex : SV_POSITION;
+				#ifdef ASE_NEEDS_FRAG_WORLD_POSITION
+				float3 worldPos : TEXCOORD0;
+				#endif
+				float4 ase_texcoord1 : TEXCOORD1;
+				float4 ase_color : COLOR;
+				UNITY_VERTEX_INPUT_INSTANCE_ID
+				UNITY_VERTEX_OUTPUT_STEREO
+			};
+
+			uniform sampler2D _hallwayCenterpieceHalloween_bc;
+			SamplerState sampler_hallwayCenterpieceHalloween_bc;
+			UNITY_INSTANCING_BUFFER_START(customVenuesLobby_HalloweenCenterpiece)
+				UNITY_DEFINE_INSTANCED_PROP(float4, _hallwayCenterpieceHalloween_bc_ST)
+#define _hallwayCenterpieceHalloween_bc_ST_arr customVenuesLobby_HalloweenCenterpiece
+				UNITY_DEFINE_INSTANCED_PROP(float3, _Scale)
+#define _Scale_arr customVenuesLobby_HalloweenCenterpiece
+				UNITY_DEFINE_INSTANCED_PROP(float, _FlameFlickerSpeed)
+#define _FlameFlickerSpeed_arr customVenuesLobby_HalloweenCenterpiece
+			UNITY_INSTANCING_BUFFER_END(customVenuesLobby_HalloweenCenterpiece)
+			float3 mod3D289( float3 x ) { return x - floor( x / 289.0 ) * 289.0; }
+			float4 mod3D289( float4 x ) { return x - floor( x / 289.0 ) * 289.0; }
+			float4 permute( float4 x ) { return mod3D289( ( x * 34.0 + 1.0 ) * x ); }
+			float4 taylorInvSqrt( float4 r ) { return 1.79284291400159 - r * 0.85373472095314; }
+			float snoise( float3 v )
+			{
+				const float2 C = float2( 1.0 / 6.0, 1.0 / 3.0 );
+				float3 i = floor( v + dot( v, C.yyy ) );
+				float3 x0 = v - i + dot( i, C.xxx );
+				float3 g = step( x0.yzx, x0.xyz );
+				float3 l = 1.0 - g;
+				float3 i1 = min( g.xyz, l.zxy );
+				float3 i2 = max( g.xyz, l.zxy );
+				float3 x1 = x0 - i1 + C.xxx;
+				float3 x2 = x0 - i2 + C.yyy;
+				float3 x3 = x0 - 0.5;
+				i = mod3D289( i);
+				float4 p = permute( permute( permute( i.z + float4( 0.0, i1.z, i2.z, 1.0 ) ) + i.y + float4( 0.0, i1.y, i2.y, 1.0 ) ) + i.x + float4( 0.0, i1.x, i2.x, 1.0 ) );
+				float4 j = p - 49.0 * floor( p / 49.0 );  // mod(p,7*7)
+				float4 x_ = floor( j / 7.0 );
+				float4 y_ = floor( j - 7.0 * x_ );  // mod(j,N)
+				float4 x = ( x_ * 2.0 + 0.5 ) / 7.0 - 1.0;
+				float4 y = ( y_ * 2.0 + 0.5 ) / 7.0 - 1.0;
+				float4 h = 1.0 - abs( x ) - abs( y );
+				float4 b0 = float4( x.xy, y.xy );
+				float4 b1 = float4( x.zw, y.zw );
+				float4 s0 = floor( b0 ) * 2.0 + 1.0;
+				float4 s1 = floor( b1 ) * 2.0 + 1.0;
+				float4 sh = -step( h, 0.0 );
+				float4 a0 = b0.xzyw + s0.xzyw * sh.xxyy;
+				float4 a1 = b1.xzyw + s1.xzyw * sh.zzww;
+				float3 g0 = float3( a0.xy, h.x );
+				float3 g1 = float3( a0.zw, h.y );
+				float3 g2 = float3( a1.xy, h.z );
+				float3 g3 = float3( a1.zw, h.w );
+				float4 norm = taylorInvSqrt( float4( dot( g0, g0 ), dot( g1, g1 ), dot( g2, g2 ), dot( g3, g3 ) ) );
+				g0 *= norm.x;
+				g1 *= norm.y;
+				g2 *= norm.z;
+				g3 *= norm.w;
+				float4 m = max( 0.6 - float4( dot( x0, x0 ), dot( x1, x1 ), dot( x2, x2 ), dot( x3, x3 ) ), 0.0 );
+				m = m* m;
+				m = m* m;
+				float4 px = float4( dot( x0, g0 ), dot( x1, g1 ), dot( x2, g2 ), dot( x3, g3 ) );
+				return 42.0 * dot( m, px);
+			}
+			
+			//https://www.shadertoy.com/view/XdXGW8
+			float2 GradientNoiseDir( float2 x )
+			{
+				const float2 k = float2( 0.3183099, 0.3678794 );
+				x = x * k + k.yx;
+				return -1.0 + 2.0 * frac( 16.0 * k * frac( x.x * x.y * ( x.x + x.y ) ) );
+			}
+			
+			float GradientNoise( float2 UV, float Scale )
+			{
+				float2 p = UV * Scale;
+				float2 i = floor( p );
+				float2 f = frac( p );
+				float2 u = f * f * ( 3.0 - 2.0 * f );
+				return lerp( lerp( dot( GradientNoiseDir( i + float2( 0.0, 0.0 ) ), f - float2( 0.0, 0.0 ) ),
+						dot( GradientNoiseDir( i + float2( 1.0, 0.0 ) ), f - float2( 1.0, 0.0 ) ), u.x ),
+						lerp( dot( GradientNoiseDir( i + float2( 0.0, 1.0 ) ), f - float2( 0.0, 1.0 ) ),
+						dot( GradientNoiseDir( i + float2( 1.0, 1.0 ) ), f - float2( 1.0, 1.0 ) ), u.x ), u.y );
+			}
+			
+
+			
+			v2f vert ( appdata v )
+			{
+				v2f o;
+				UNITY_SETUP_INSTANCE_ID(v);
+				UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
+				UNITY_TRANSFER_INSTANCE_ID(v, o);
+
+				float _FlameFlickerSpeed_Instance = UNITY_ACCESS_INSTANCED_PROP(_FlameFlickerSpeed_arr, _FlameFlickerSpeed);
+				float2 appendResult47 = (float2(_FlameFlickerSpeed_Instance , _FlameFlickerSpeed_Instance));
+				float3 ase_worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
+				float2 panner46 = ( 1.0 * _Time.y * appendResult47 + ase_worldPos.xy);
+				float3 _Scale_Instance = UNITY_ACCESS_INSTANCED_PROP(_Scale_arr, _Scale);
+				float simplePerlin3D3 = snoise( float3( panner46 ,  0.0 )*_Scale_Instance.x );
+				simplePerlin3D3 = simplePerlin3D3*0.5 + 0.5;
+				float lerpResult60 = lerp( 0.0 , ( simplePerlin3D3 / 150.0 ) , v.color.a);
+				float3 temp_cast_3 = (lerpResult60).xxx;
+				
+				o.ase_texcoord1.xy = v.ase_texcoord.xy;
+				o.ase_color = v.color;
+				
+				//setting value to unused interpolator channels and avoid initialization warnings
+				o.ase_texcoord1.zw = 0;
+				float3 vertexValue = float3(0, 0, 0);
+				#if ASE_ABSOLUTE_VERTEX_POS
+				vertexValue = v.vertex.xyz;
+				#endif
+				vertexValue = temp_cast_3;
+				#if ASE_ABSOLUTE_VERTEX_POS
+				v.vertex.xyz = vertexValue;
+				#else
+				v.vertex.xyz += vertexValue;
+				#endif
+				o.vertex = UnityObjectToClipPos(v.vertex);
+
+				#ifdef ASE_NEEDS_FRAG_WORLD_POSITION
+				o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
+				#endif
+				return o;
+			}
+			
+			fixed4 frag (v2f i ) : SV_Target
+			{
+				UNITY_SETUP_INSTANCE_ID(i);
+				UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i);
+				fixed4 finalColor;
+				#ifdef ASE_NEEDS_FRAG_WORLD_POSITION
+				float3 WorldPosition = i.worldPos;
+				#endif
+				float4 _hallwayCenterpieceHalloween_bc_ST_Instance = UNITY_ACCESS_INSTANCED_PROP(_hallwayCenterpieceHalloween_bc_ST_arr, _hallwayCenterpieceHalloween_bc_ST);
+				float2 uv_hallwayCenterpieceHalloween_bc = i.ase_texcoord1.xy * _hallwayCenterpieceHalloween_bc_ST_Instance.xy + _hallwayCenterpieceHalloween_bc_ST_Instance.zw;
+				float4 tex2DNode70 = tex2D( _hallwayCenterpieceHalloween_bc, uv_hallwayCenterpieceHalloween_bc );
+				float temp_output_99_0 = ( 1.3 * tex2DNode70.a );
+				float4 color88 = IsGammaSpace() ? float4(0.1176471,0.8509804,0.172549,1) : float4(0.01298304,0.6938719,0.02518685,1);
+				float4 color2 = IsGammaSpace() ? float4(0.9339623,0.7375837,0.3392221,0) : float4(0.8562991,0.5033876,0.094183,0);
+				float4 lerpResult89 = lerp( ( temp_output_99_0 * color88 ) , ( color2 * temp_output_99_0 ) , i.ase_color.r);
+				float lerpResult97 = lerp( -20.0 , -12.0 , i.ase_color.r);
+				float2 appendResult98 = (float2(0.0 , lerpResult97));
+				float2 panner73 = ( 1.0 * _Time.y * appendResult98 + WorldPosition.xy);
+				float lerpResult102 = lerp( -0.1 , -0.25 , i.ase_color.r);
+				float gradientNoise74 = GradientNoise(panner73,lerpResult102);
+				gradientNoise74 = gradientNoise74*0.5 + 0.5;
+				float4 lerpResult75 = lerp( tex2DNode70 , ( tex2DNode70 + lerpResult89 ) , gradientNoise74);
+				
+				
+				finalColor = lerpResult75;
+				finalColor.a = 1.0f;
+				return finalColor;
+			}
+			ENDCG
+		}
+	}
+	CustomEditor "ASEMaterialInspector"
+	
+	
+}
+/*ASEBEGIN
+Version=18500
+2.666667;146;1636;671;1284.421;1107.462;2.059661;True;True
+Node;AmplifyShaderEditor.VertexColorNode;59;-520.7559,514.5302;Inherit;False;0;5;COLOR;0;FLOAT;1;FLOAT;2;FLOAT;3;FLOAT;4
+Node;AmplifyShaderEditor.SamplerNode;70;-684.084,-923.1321;Inherit;True;Property;_hallwayCenterpieceHalloween_bc;hallwayCenterpieceHalloween_bc;2;0;Create;True;0;0;False;0;False;-1;765eb242ebe51234f85fcf1ce015d038;6052dd3abb528fd4daca3cce03d9069b;True;0;False;white;Auto;False;Object;-1;Auto;Texture2D;8;0;SAMPLER2D;;False;1;FLOAT2;0,0;False;2;FLOAT;0;False;3;FLOAT2;0,0;False;4;FLOAT2;0,0;False;5;FLOAT;1;False;6;FLOAT;0;False;7;SAMPLERSTATE;;False;5;COLOR;0;FLOAT;1;FLOAT;2;FLOAT;3;FLOAT;4
+Node;AmplifyShaderEditor.RangedFloatNode;100;-635.7979,-687.4479;Inherit;False;Constant;_Float0;Float 0;5;0;Create;True;0;0;False;0;False;1.3;0;0;0;0;1;FLOAT;0
+Node;AmplifyShaderEditor.ColorNode;88;-474.5434,-675.8533;Inherit;False;Constant;_Color1;Color 1;0;0;Create;True;0;0;False;0;False;0.1176471,0.8509804,0.172549,1;0,0,0,0;True;0;5;COLOR;0;FLOAT;1;FLOAT;2;FLOAT;3;FLOAT;4
+Node;AmplifyShaderEditor.SimpleMultiplyOpNode;99;-335.7977,-860.4481;Inherit;False;2;2;0;FLOAT;0.7490196;False;1;FLOAT;0;False;1;FLOAT;0
+Node;AmplifyShaderEditor.ColorNode;2;-614.1295,-1094.349;Inherit;False;Constant;_Color0;Color 0;0;0;Create;True;0;0;False;0;False;0.9339623,0.7375837,0.3392221,0;0,0,0,0;True;0;5;COLOR;0;FLOAT;1;FLOAT;2;FLOAT;3;FLOAT;4
+Node;AmplifyShaderEditor.LerpOp;97;-363.2632,78.53415;Inherit;False;3;0;FLOAT;-20;False;1;FLOAT;-12;False;2;FLOAT;0;False;1;FLOAT;0
+Node;AmplifyShaderEditor.RangedFloatNode;48;-598.8521,993.535;Inherit;False;InstancedProperty;_FlameFlickerSpeed;Flame Flicker Speed;0;0;Create;True;0;0;False;0;False;0.28;0.28;0;0;0;1;FLOAT;0
+Node;AmplifyShaderEditor.SimpleMultiplyOpNode;77;-182.4068,-1003.263;Inherit;True;2;2;0;COLOR;0,0,0,0;False;1;FLOAT;0;False;1;COLOR;0
+Node;AmplifyShaderEditor.WorldPosInputsNode;5;-431.3358,824.8111;Inherit;False;0;4;FLOAT3;0;FLOAT;1;FLOAT;2;FLOAT;3
+Node;AmplifyShaderEditor.SimpleMultiplyOpNode;87;-101.3785,-736.1888;Inherit;True;2;2;0;FLOAT;0;False;1;COLOR;0,0,0,0;False;1;COLOR;0
+Node;AmplifyShaderEditor.DynamicAppendNode;98;-187.0618,34.37659;Inherit;False;FLOAT2;4;0;FLOAT;0;False;1;FLOAT;0;False;2;FLOAT;0;False;3;FLOAT;0;False;1;FLOAT2;0
+Node;AmplifyShaderEditor.DynamicAppendNode;47;-389.7454,981.4828;Inherit;False;FLOAT2;4;0;FLOAT;0;False;1;FLOAT;0;False;2;FLOAT;0;False;3;FLOAT;0;False;1;FLOAT2;0
+Node;AmplifyShaderEditor.WorldPosInputsNode;71;-243.3006,-119.7851;Inherit;False;0;4;FLOAT3;0;FLOAT;1;FLOAT;2;FLOAT;3
+Node;AmplifyShaderEditor.LerpOp;89;189.7045,-728.7795;Inherit;True;3;0;COLOR;0,0,0,0;False;1;COLOR;0,0,0,0;False;2;FLOAT;0;False;1;COLOR;0
+Node;AmplifyShaderEditor.PannerNode;73;-42.35918,-56.64176;Inherit;False;3;0;FLOAT2;0,0;False;2;FLOAT2;0,-5;False;1;FLOAT;1;False;1;FLOAT2;0
+Node;AmplifyShaderEditor.Vector3Node;52;-179.0262,654.6486;Inherit;False;InstancedProperty;_Scale;Scale;1;0;Create;True;0;0;False;0;False;8,50,50;0.2,50,50;0;4;FLOAT3;0;FLOAT;1;FLOAT;2;FLOAT;3
+Node;AmplifyShaderEditor.LerpOp;102;-23.88721,104.3595;Inherit;False;3;0;FLOAT;-0.1;False;1;FLOAT;-0.25;False;2;FLOAT;0;False;1;FLOAT;0
+Node;AmplifyShaderEditor.PannerNode;46;-197.6858,824.0949;Inherit;False;3;0;FLOAT2;0,0;False;2;FLOAT2;50,50;False;1;FLOAT;1;False;1;FLOAT2;0
+Node;AmplifyShaderEditor.NoiseGeneratorNode;74;205.533,-226.8826;Inherit;True;Gradient;True;False;2;0;FLOAT2;1,1;False;1;FLOAT;1;False;1;FLOAT;0
+Node;AmplifyShaderEditor.NoiseGeneratorNode;3;54.56548,621.7202;Inherit;True;Simplex3D;True;False;2;0;FLOAT3;1,1,1;False;1;FLOAT;1;False;1;FLOAT;0
+Node;AmplifyShaderEditor.SimpleAddOpNode;76;616.7811,-606.245;Inherit;True;2;2;0;COLOR;0,0,0,0;False;1;COLOR;0,0,0,0;False;1;COLOR;0
+Node;AmplifyShaderEditor.SimpleDivideOpNode;31;327.3663,590.9765;Inherit;False;2;0;FLOAT;0;False;1;FLOAT;150;False;1;FLOAT;0
+Node;AmplifyShaderEditor.LerpOp;75;859.756,-272.9042;Inherit;True;3;0;COLOR;0,0,0,0;False;1;COLOR;0,0,0,0;False;2;FLOAT;0;False;1;COLOR;0
+Node;AmplifyShaderEditor.LerpOp;60;597.5072,567.2776;Inherit;False;3;0;FLOAT;0;False;1;FLOAT;0;False;2;FLOAT;0;False;1;FLOAT;0
+Node;AmplifyShaderEditor.PowerNode;80;469.007,-215.3068;Inherit;False;False;2;0;FLOAT;0;False;1;FLOAT;2;False;1;FLOAT;0
+Node;AmplifyShaderEditor.SwitchNode;81;1157.219,-440.9215;Inherit;False;1;2;8;0;COLOR;0,0,0,0;False;1;COLOR;0,0,0,0;False;2;FLOAT;0;False;3;FLOAT;0;False;4;FLOAT;0;False;5;FLOAT;0;False;6;FLOAT;0;False;7;FLOAT;0;False;1;COLOR;0
+Node;AmplifyShaderEditor.TemplateMultiPassMasterNode;0;1337.29,-40.08469;Float;False;True;-1;2;ASEMaterialInspector;100;1;custom/VenuesLobby_HalloweenCenterpiece;0770190933193b94aaa3065e307002fa;True;Unlit;0;0;Unlit;2;True;0;1;False;-1;0;False;-1;0;1;False;-1;0;False;-1;True;0;False;-1;0;False;-1;False;False;False;False;False;False;True;0;False;-1;True;0;False;-1;True;True;True;True;True;0;False;-1;False;False;False;True;False;255;False;-1;255;False;-1;255;False;-1;7;False;-1;1;False;-1;1;False;-1;1;False;-1;7;False;-1;1;False;-1;1;False;-1;1;False;-1;True;1;False;-1;True;3;False;-1;True;True;0;False;-1;0;False;-1;True;1;RenderType=Opaque=RenderType;True;2;0;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;False;True;1;LightMode=ForwardBase;False;0;;0;0;Standard;1;Vertex Position,InvertActionOnDeselection;1;0;1;True;False;;False;0
+WireConnection;99;0;100;0
+WireConnection;99;1;70;4
+WireConnection;97;2;59;1
+WireConnection;77;0;2;0
+WireConnection;77;1;99;0
+WireConnection;87;0;99;0
+WireConnection;87;1;88;0
+WireConnection;98;1;97;0
+WireConnection;47;0;48;0
+WireConnection;47;1;48;0
+WireConnection;89;0;87;0
+WireConnection;89;1;77;0
+WireConnection;89;2;59;1
+WireConnection;73;0;71;0
+WireConnection;73;2;98;0
+WireConnection;102;2;59;1
+WireConnection;46;0;5;0
+WireConnection;46;2;47;0
+WireConnection;74;0;73;0
+WireConnection;74;1;102;0
+WireConnection;3;0;46;0
+WireConnection;3;1;52;0
+WireConnection;76;0;70;0
+WireConnection;76;1;89;0
+WireConnection;31;0;3;0
+WireConnection;75;0;70;0
+WireConnection;75;1;76;0
+WireConnection;75;2;74;0
+WireConnection;60;1;31;0
+WireConnection;60;2;59;4
+WireConnection;80;0;74;0
+WireConnection;81;0;70;0
+WireConnection;81;1;75;0
+WireConnection;0;0;81;0
+WireConnection;0;1;60;0
+ASEEND*/
+//CHKSM=9128CB2C5F4AA0697FADB1FAA25559EAEACD4EAA
\ No newline at end of file
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Shaders/s_lobbyHalloweenCenterpiece.shader.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Shaders/s_lobbyHalloweenCenterpiece.shader.meta
new file mode 100644
index 0000000000000000000000000000000000000000..0a46b4fc2a4495e9beaf3c207a59c9eb891322ba
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/Shaders/s_lobbyHalloweenCenterpiece.shader.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: f652a252ca50345e68c98404003292fc
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures:
+  - _hallwayCenterpieceHalloween_bc: {fileID: 2800000, guid: 765eb242ebe51234f85fcf1ce015d038,
+      type: 3}
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/VenuesLobby LightRig.prefab b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/VenuesLobby LightRig.prefab
new file mode 100644
index 0000000000000000000000000000000000000000..0c114d1f11712e5931838723ec6946aa0cb61c26
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/VenuesLobby LightRig.prefab	
@@ -0,0 +1,442 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &6176357634634757532
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 6176357634634757534}
+  - component: {fileID: 6176357634634757535}
+  m_Layer: 0
+  m_Name: Rim Directional Light
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &6176357634634757534
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6176357634634757532}
+  m_LocalRotation: {x: 0.6637632, y: 0.11920266, z: 0.049397115, w: 0.7367286}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 5650585432433268674}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!108 &6176357634634757535
+Light:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6176357634634757532}
+  m_Enabled: 1
+  serializedVersion: 10
+  m_Type: 1
+  m_Shape: 0
+  m_Color: {r: 0.8490566, g: 0.8870806, b: 1, a: 1}
+  m_Intensity: 0.4
+  m_Range: 10
+  m_SpotAngle: 30
+  m_InnerSpotAngle: 21.80208
+  m_CookieSize: 10
+  m_Shadows:
+    m_Type: 0
+    m_Resolution: -1
+    m_CustomResolution: -1
+    m_Strength: 1
+    m_Bias: 0.05
+    m_NormalBias: 0.4
+    m_NearPlane: 0.2
+    m_CullingMatrixOverride:
+      e00: 1
+      e01: 0
+      e02: 0
+      e03: 0
+      e10: 0
+      e11: 1
+      e12: 0
+      e13: 0
+      e20: 0
+      e21: 0
+      e22: 1
+      e23: 0
+      e30: 0
+      e31: 0
+      e32: 0
+      e33: 1
+    m_UseCullingMatrixOverride: 0
+  m_Cookie: {fileID: 0}
+  m_DrawHalo: 0
+  m_Flare: {fileID: 0}
+  m_RenderMode: 0
+  m_CullingMask:
+    serializedVersion: 2
+    m_Bits: 4294967295
+  m_RenderingLayerMask: 1
+  m_Lightmapping: 4
+  m_LightShadowCasterMode: 0
+  m_AreaSize: {x: 1, y: 1}
+  m_BounceIntensity: 1
+  m_ColorTemperature: 6570
+  m_UseColorTemperature: 0
+  m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
+  m_UseBoundingSphereOverride: 0
+  m_UseViewFrustumForShadowCasterCull: 1
+  m_ShadowRadius: 0
+  m_ShadowAngle: 0
+--- !u!1 &6449103579296214519
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 2045496478646130412}
+  - component: {fileID: 1213625666104222948}
+  m_Layer: 0
+  m_Name: Fill Directional Light
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &2045496478646130412
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6449103579296214519}
+  m_LocalRotation: {x: 0.10682808, y: 0.14581934, z: -0.618728, w: 0.7645262}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 5650585432433268674}
+  m_RootOrder: 1
+  m_LocalEulerAnglesHint: {x: 20.108, y: 5.547, z: -76.982}
+--- !u!108 &1213625666104222948
+Light:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 6449103579296214519}
+  m_Enabled: 1
+  serializedVersion: 10
+  m_Type: 1
+  m_Shape: 0
+  m_Color: {r: 1, g: 0.90643334, b: 0.599, a: 1}
+  m_Intensity: 0.5
+  m_Range: 10
+  m_SpotAngle: 30
+  m_InnerSpotAngle: 21.80208
+  m_CookieSize: 10
+  m_Shadows:
+    m_Type: 0
+    m_Resolution: -1
+    m_CustomResolution: -1
+    m_Strength: 1
+    m_Bias: 0.05
+    m_NormalBias: 0.4
+    m_NearPlane: 0.2
+    m_CullingMatrixOverride:
+      e00: 1
+      e01: 0
+      e02: 0
+      e03: 0
+      e10: 0
+      e11: 1
+      e12: 0
+      e13: 0
+      e20: 0
+      e21: 0
+      e22: 1
+      e23: 0
+      e30: 0
+      e31: 0
+      e32: 0
+      e33: 1
+    m_UseCullingMatrixOverride: 0
+  m_Cookie: {fileID: 0}
+  m_DrawHalo: 0
+  m_Flare: {fileID: 0}
+  m_RenderMode: 0
+  m_CullingMask:
+    serializedVersion: 2
+    m_Bits: 4294967295
+  m_RenderingLayerMask: 1
+  m_Lightmapping: 4
+  m_LightShadowCasterMode: 0
+  m_AreaSize: {x: 1, y: 1}
+  m_BounceIntensity: 1
+  m_ColorTemperature: 6570
+  m_UseColorTemperature: 0
+  m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
+  m_UseBoundingSphereOverride: 0
+  m_UseViewFrustumForShadowCasterCull: 1
+  m_ShadowRadius: 0
+  m_ShadowAngle: 0
+--- !u!1 &7322517129512244511
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 2867013423916878268}
+  - component: {fileID: 5273423659756724343}
+  - component: {fileID: 2251684094276761330}
+  - component: {fileID: 5180273014275040591}
+  m_Layer: 0
+  m_Name: EnvironmentCube
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &2867013423916878268
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7322517129512244511}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 100, y: 100, z: 100}
+  m_Children: []
+  m_Father: {fileID: 5650585432433268674}
+  m_RootOrder: 3
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!33 &5273423659756724343
+MeshFilter:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7322517129512244511}
+  m_Mesh: {fileID: 10202, guid: 0000000000000000e000000000000000, type: 0}
+--- !u!23 &2251684094276761330
+MeshRenderer:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7322517129512244511}
+  m_Enabled: 1
+  m_CastShadows: 1
+  m_ReceiveShadows: 1
+  m_DynamicOccludee: 1
+  m_MotionVectors: 1
+  m_LightProbeUsage: 1
+  m_ReflectionProbeUsage: 1
+  m_RayTracingMode: 2
+  m_RayTraceProcedural: 0
+  m_RenderingLayerMask: 1
+  m_RendererPriority: 0
+  m_Materials:
+  - {fileID: 2100000, guid: 67ebeea9c72bc464eacaaae9cfd8b739, type: 2}
+  m_StaticBatchInfo:
+    firstSubMesh: 0
+    subMeshCount: 0
+  m_StaticBatchRoot: {fileID: 0}
+  m_ProbeAnchor: {fileID: 0}
+  m_LightProbeVolumeOverride: {fileID: 0}
+  m_ScaleInLightmap: 1
+  m_ReceiveGI: 1
+  m_PreserveUVs: 0
+  m_IgnoreNormalsForChartDetection: 0
+  m_ImportantGI: 0
+  m_StitchLightmapSeams: 1
+  m_SelectedEditorRenderState: 3
+  m_MinimumChartSize: 4
+  m_AutoUVMaxDistance: 0.5
+  m_AutoUVMaxAngle: 89
+  m_LightmapParameters: {fileID: 0}
+  m_SortingLayerID: 0
+  m_SortingLayer: 0
+  m_SortingOrder: 0
+  m_AdditionalVertexStreams: {fileID: 0}
+--- !u!65 &5180273014275040591
+BoxCollider:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7322517129512244511}
+  m_Material: {fileID: 0}
+  m_IsTrigger: 0
+  m_Enabled: 1
+  serializedVersion: 2
+  m_Size: {x: 1, y: 1, z: 1}
+  m_Center: {x: 0, y: 0, z: 0}
+--- !u!1 &7868569535731771042
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 2986542401637056387}
+  - component: {fileID: 6326507335235603723}
+  m_Layer: 0
+  m_Name: Key Directional Light
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &2986542401637056387
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7868569535731771042}
+  m_LocalRotation: {x: -0.65992695, y: 0.73905295, z: -0.08904281, w: -0.10182609}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 5650585432433268674}
+  m_RootOrder: 2
+  m_LocalEulerAnglesHint: {x: 15.427, y: 181.961, z: -83.26}
+--- !u!108 &6326507335235603723
+Light:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 7868569535731771042}
+  m_Enabled: 1
+  serializedVersion: 10
+  m_Type: 1
+  m_Shape: 0
+  m_Color: {r: 1, g: 0.9064333, b: 0.599, a: 1}
+  m_Intensity: 0.8
+  m_Range: 10
+  m_SpotAngle: 30
+  m_InnerSpotAngle: 21.80208
+  m_CookieSize: 10
+  m_Shadows:
+    m_Type: 0
+    m_Resolution: -1
+    m_CustomResolution: -1
+    m_Strength: 1
+    m_Bias: 0.05
+    m_NormalBias: 0.4
+    m_NearPlane: 0.2
+    m_CullingMatrixOverride:
+      e00: 1
+      e01: 0
+      e02: 0
+      e03: 0
+      e10: 0
+      e11: 1
+      e12: 0
+      e13: 0
+      e20: 0
+      e21: 0
+      e22: 1
+      e23: 0
+      e30: 0
+      e31: 0
+      e32: 0
+      e33: 1
+    m_UseCullingMatrixOverride: 0
+  m_Cookie: {fileID: 0}
+  m_DrawHalo: 0
+  m_Flare: {fileID: 0}
+  m_RenderMode: 0
+  m_CullingMask:
+    serializedVersion: 2
+    m_Bits: 4294967295
+  m_RenderingLayerMask: 1
+  m_Lightmapping: 4
+  m_LightShadowCasterMode: 0
+  m_AreaSize: {x: 1, y: 1}
+  m_BounceIntensity: 1
+  m_ColorTemperature: 6570
+  m_UseColorTemperature: 0
+  m_BoundingSphereOverride: {x: 0, y: 0, z: 0, w: 0}
+  m_UseBoundingSphereOverride: 0
+  m_UseViewFrustumForShadowCasterCull: 1
+  m_ShadowRadius: 0
+  m_ShadowAngle: 0
+--- !u!1 &8423574076275988413
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 5650585432433268674}
+  - component: {fileID: 3623718394477109854}
+  m_Layer: 0
+  m_Name: VenuesLobby LightRig
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &5650585432433268674
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8423574076275988413}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 6176357634634757534}
+  - {fileID: 2045496478646130412}
+  - {fileID: 2986542401637056387}
+  - {fileID: 2867013423916878268}
+  m_Father: {fileID: 0}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &3623718394477109854
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 8423574076275988413}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: cc8b2bc8875ed714f874f31c2183c84b, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  CurrentExposure: 1
+  ExposureShaderParameterName: u_Exposure
+  DiffuseEnvironmentCubeMap: {fileID: 8900000, guid: 651a7e8194498914981dd7211c2ac107,
+    type: 3}
+  DiffuseEnvironmentShaderParameterNames:
+  - u_DiffuseEnvSampler
+  - u_LambertianEnvSampler
+  SpecularEnvironmentCubeMap: {fileID: 8900000, guid: 9a991013884426c40a06843632bed336,
+    type: 3}
+  SpecularEnvironmentShaderParameterNames:
+  - u_SpecularEnvSampler
+  - u_GGXEnvSampler
+  SpecularMapMipCountShaderPameterName: u_MipCount
+  BrdfLutMap: {fileID: 2800000, guid: 918eb5f61eea9f6479953048b19170c0, type: 3}
+  BrdfLutShaderParameterNames:
+  - u_brdfLUT
+  - u_GGXLUT
+  CubeMapMaterial: {fileID: 0}
+  CubeMapShaderParameterName: _Tex
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/VenuesLobby LightRig.prefab.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/VenuesLobby LightRig.prefab.meta
new file mode 100644
index 0000000000000000000000000000000000000000..7b96f1e4006c4fb6b1dcd1bbaa04d198f722504a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/VenuesLobby LightRig.prefab.meta	
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 0fe34e45f675346438d2ee1df9419ff7
+PrefabImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/VenuesLobby Skybox.mat b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/VenuesLobby Skybox.mat
new file mode 100644
index 0000000000000000000000000000000000000000..12263911fcce0d10837ddca789de67aea98724f2
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/VenuesLobby Skybox.mat	
@@ -0,0 +1,85 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!21 &2100000
+Material:
+  serializedVersion: 6
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: VenuesLobby Skybox
+  m_Shader: {fileID: 4800000, guid: 4dda107104e02f9418ce35671143716c, type: 3}
+  m_ShaderKeywords: 
+  m_LightmapFlags: 4
+  m_EnableInstancingVariants: 0
+  m_DoubleSidedGI: 0
+  m_CustomRenderQueue: -1
+  stringTagMap: {}
+  disabledShaderPasses: []
+  m_SavedProperties:
+    serializedVersion: 3
+    m_TexEnvs:
+    - _BumpMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailAlbedoMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailMask:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _DetailNormalMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _EmissionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MainTex:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _MetallicGlossMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _OcclusionMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _ParallaxMap:
+        m_Texture: {fileID: 0}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    - _Tex:
+        m_Texture: {fileID: 8900000, guid: 9a991013884426c40a06843632bed336, type: 3}
+        m_Scale: {x: 1, y: 1}
+        m_Offset: {x: 0, y: 0}
+    m_Floats:
+    - _BumpScale: 1
+    - _Cutoff: 0.5
+    - _DetailNormalMapScale: 1
+    - _DstBlend: 0
+    - _Exposure: 1
+    - _GlossMapScale: 1
+    - _Glossiness: 0.5
+    - _GlossyReflections: 1
+    - _Metallic: 0
+    - _Mode: 0
+    - _OcclusionStrength: 1
+    - _Parallax: 0.02
+    - _Rotation: 0
+    - _SmoothnessTextureChannel: 0
+    - _SpecularHighlights: 1
+    - _SrcBlend: 1
+    - _UVSec: 0
+    - _ZWrite: 1
+    m_Colors:
+    - _Color: {r: 1, g: 1, b: 1, a: 1}
+    - _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
+    - _Tint: {r: 0.5, g: 0.5, b: 0.5, a: 0.5}
+  m_BuildTextureStacks: []
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/VenuesLobby Skybox.mat.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/VenuesLobby Skybox.mat.meta
new file mode 100644
index 0000000000000000000000000000000000000000..ce2a4524b6aa8aa39da001ae0a46cc5cfa3725b9
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/VenuesLobby Skybox.mat.meta	
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 67ebeea9c72bc464eacaaae9cfd8b739
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/VenuesLobbyLightingExample.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/VenuesLobbyLightingExample.meta
new file mode 100644
index 0000000000000000000000000000000000000000..b24926a4afc6a2422f4e0f6b6a6245f4d614e56a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/VenuesLobbyLightingExample.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: d47eb0b4e77b87248ba6010590327688
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/VenuesLobbyLightingExample.unity b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/VenuesLobbyLightingExample.unity
new file mode 100644
index 0000000000000000000000000000000000000000..4e5b2673bc337c0cc260fa3b8d2aab6959d2b49b
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/VenuesLobbyLightingExample.unity
@@ -0,0 +1,961 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!29 &1
+OcclusionCullingSettings:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_OcclusionBakeSettings:
+    smallestOccluder: 5
+    smallestHole: 0.25
+    backfaceThreshold: 100
+  m_SceneGUID: 00000000000000000000000000000000
+  m_OcclusionCullingData: {fileID: 0}
+--- !u!104 &2
+RenderSettings:
+  m_ObjectHideFlags: 0
+  serializedVersion: 9
+  m_Fog: 0
+  m_FogColor: {r: 0.5, g: 0.5, b: 0.5, a: 1}
+  m_FogMode: 3
+  m_FogDensity: 0.01
+  m_LinearFogStart: 0
+  m_LinearFogEnd: 300
+  m_AmbientSkyColor: {r: 0, g: 0, b: 0, a: 1}
+  m_AmbientEquatorColor: {r: 0.114, g: 0.125, b: 0.133, a: 1}
+  m_AmbientGroundColor: {r: 0.047, g: 0.043, b: 0.035, a: 1}
+  m_AmbientIntensity: 1
+  m_AmbientMode: 0
+  m_SubtractiveShadowColor: {r: 0.42, g: 0.478, b: 0.627, a: 1}
+  m_SkyboxMaterial: {fileID: 0}
+  m_HaloStrength: 0.5
+  m_FlareStrength: 1
+  m_FlareFadeSpeed: 3
+  m_HaloTexture: {fileID: 0}
+  m_SpotCookie: {fileID: 10001, guid: 0000000000000000e000000000000000, type: 0}
+  m_DefaultReflectionMode: 0
+  m_DefaultReflectionResolution: 128
+  m_ReflectionBounces: 1
+  m_ReflectionIntensity: 1
+  m_CustomReflection: {fileID: 0}
+  m_Sun: {fileID: 0}
+  m_IndirectSpecularColor: {r: 0, g: 0, b: 0, a: 1}
+  m_UseRadianceAmbientProbe: 0
+--- !u!157 &3
+LightmapSettings:
+  m_ObjectHideFlags: 0
+  serializedVersion: 12
+  m_GIWorkflowMode: 1
+  m_GISettings:
+    serializedVersion: 2
+    m_BounceScale: 1
+    m_IndirectOutputScale: 1
+    m_AlbedoBoost: 1
+    m_EnvironmentLightingMode: 0
+    m_EnableBakedLightmaps: 1
+    m_EnableRealtimeLightmaps: 0
+  m_LightmapEditorSettings:
+    serializedVersion: 12
+    m_Resolution: 2
+    m_BakeResolution: 40
+    m_AtlasSize: 32
+    m_AO: 0
+    m_AOMaxDistance: 1
+    m_CompAOExponent: 1
+    m_CompAOExponentDirect: 0
+    m_ExtractAmbientOcclusion: 0
+    m_Padding: 2
+    m_LightmapParameters: {fileID: 15203, guid: 0000000000000000f000000000000000,
+      type: 0}
+    m_LightmapsBakeMode: 1
+    m_TextureCompression: 1
+    m_FinalGather: 0
+    m_FinalGatherFiltering: 1
+    m_FinalGatherRayCount: 256
+    m_ReflectionCompression: 2
+    m_MixedBakeMode: 0
+    m_BakeBackend: 1
+    m_PVRSampling: 1
+    m_PVRDirectSampleCount: 32
+    m_PVRSampleCount: 512
+    m_PVRBounces: 2
+    m_PVREnvironmentSampleCount: 512
+    m_PVREnvironmentReferencePointCount: 2048
+    m_PVRFilteringMode: 2
+    m_PVRDenoiserTypeDirect: 0
+    m_PVRDenoiserTypeIndirect: 0
+    m_PVRDenoiserTypeAO: 0
+    m_PVRFilterTypeDirect: 0
+    m_PVRFilterTypeIndirect: 0
+    m_PVRFilterTypeAO: 0
+    m_PVREnvironmentMIS: 0
+    m_PVRCulling: 1
+    m_PVRFilteringGaussRadiusDirect: 1
+    m_PVRFilteringGaussRadiusIndirect: 5
+    m_PVRFilteringGaussRadiusAO: 2
+    m_PVRFilteringAtrousPositionSigmaDirect: 0.5
+    m_PVRFilteringAtrousPositionSigmaIndirect: 2
+    m_PVRFilteringAtrousPositionSigmaAO: 1
+    m_ExportTrainingData: 0
+    m_TrainingDataDestination: TrainingData
+    m_LightProbeSampleCountMultiplier: 4
+  m_LightingDataAsset: {fileID: 112000000, guid: 2f421bf54781ede478ba00f104823042,
+    type: 2}
+  m_LightingSettings: {fileID: 498571238}
+--- !u!196 &4
+NavMeshSettings:
+  serializedVersion: 2
+  m_ObjectHideFlags: 0
+  m_BuildSettings:
+    serializedVersion: 2
+    agentTypeID: 0
+    agentRadius: 0.5
+    agentHeight: 2
+    agentSlope: 45
+    agentClimb: 0.4
+    ledgeDropHeight: 0
+    maxJumpAcrossDistance: 0
+    minRegionArea: 2
+    manualCellSize: 0
+    cellSize: 0.16666667
+    manualTileSize: 0
+    tileSize: 256
+    accuratePlacement: 0
+    maxJobWorkers: 0
+    preserveTilesOutsideBounds: 0
+    debug:
+      m_Flags: 0
+  m_NavMeshData: {fileID: 0}
+--- !u!1001 &24603138
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 0}
+    m_Modifications:
+    - target: {fileID: 1396242425075152212, guid: 42b9aeec34cdef84095cf9a474a80fce,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 4
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: 42b9aeec34cdef84095cf9a474a80fce,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0.5
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: 42b9aeec34cdef84095cf9a474a80fce,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: 42b9aeec34cdef84095cf9a474a80fce,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: 42b9aeec34cdef84095cf9a474a80fce,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: 42b9aeec34cdef84095cf9a474a80fce,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: 42b9aeec34cdef84095cf9a474a80fce,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: 42b9aeec34cdef84095cf9a474a80fce,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: 42b9aeec34cdef84095cf9a474a80fce,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: 42b9aeec34cdef84095cf9a474a80fce,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 180
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: 42b9aeec34cdef84095cf9a474a80fce,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152213, guid: 42b9aeec34cdef84095cf9a474a80fce,
+        type: 3}
+      propertyPath: m_Name
+      value: ShowcaseAvatarsGroup2
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152213, guid: 42b9aeec34cdef84095cf9a474a80fce,
+        type: 3}
+      propertyPath: m_IsActive
+      value: 1
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: 42b9aeec34cdef84095cf9a474a80fce, type: 3}
+--- !u!1 &368365074
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 368365076}
+  - component: {fileID: 368365075}
+  m_Layer: 0
+  m_Name: SceneSwitcher
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!114 &368365075
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 368365074}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: ea6e0c6cd67922c48bbb5939267828e9, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  _nextSceneInput:
+    controllerMask: 2
+    buttonMask: 1
+  _prevSceneInput:
+    controllerMask: 1
+    buttonMask: 1
+--- !u!4 &368365076
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 368365074}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 0}
+  m_RootOrder: 6
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!850595691 &498571238
+LightingSettings:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_Name: Settings.lighting
+  serializedVersion: 3
+  m_GIWorkflowMode: 1
+  m_EnableBakedLightmaps: 1
+  m_EnableRealtimeLightmaps: 0
+  m_RealtimeEnvironmentLighting: 1
+  m_BounceScale: 1
+  m_AlbedoBoost: 1
+  m_IndirectOutputScale: 1
+  m_UsingShadowmask: 0
+  m_BakeBackend: 1
+  m_LightmapMaxSize: 32
+  m_BakeResolution: 40
+  m_Padding: 2
+  m_TextureCompression: 1
+  m_AO: 0
+  m_AOMaxDistance: 1
+  m_CompAOExponent: 1
+  m_CompAOExponentDirect: 0
+  m_ExtractAO: 0
+  m_MixedBakeMode: 0
+  m_LightmapsBakeMode: 1
+  m_FilterMode: 1
+  m_LightmapParameters: {fileID: 15203, guid: 0000000000000000f000000000000000, type: 0}
+  m_ExportTrainingData: 0
+  m_TrainingDataDestination: TrainingData
+  m_RealtimeResolution: 2
+  m_ForceWhiteAlbedo: 0
+  m_ForceUpdates: 0
+  m_FinalGather: 0
+  m_FinalGatherRayCount: 256
+  m_FinalGatherFiltering: 1
+  m_PVRCulling: 1
+  m_PVRSampling: 1
+  m_PVRDirectSampleCount: 32
+  m_PVRSampleCount: 512
+  m_PVREnvironmentSampleCount: 512
+  m_PVREnvironmentReferencePointCount: 2048
+  m_LightProbeSampleCountMultiplier: 4
+  m_PVRBounces: 2
+  m_PVRMinBounces: 2
+  m_PVREnvironmentMIS: 0
+  m_PVRFilteringMode: 2
+  m_PVRDenoiserTypeDirect: 0
+  m_PVRDenoiserTypeIndirect: 0
+  m_PVRDenoiserTypeAO: 0
+  m_PVRFilterTypeDirect: 0
+  m_PVRFilterTypeIndirect: 0
+  m_PVRFilterTypeAO: 0
+  m_PVRFilteringGaussRadiusDirect: 1
+  m_PVRFilteringGaussRadiusIndirect: 5
+  m_PVRFilteringGaussRadiusAO: 2
+  m_PVRFilteringAtrousPositionSigmaDirect: 0.5
+  m_PVRFilteringAtrousPositionSigmaIndirect: 2
+  m_PVRFilteringAtrousPositionSigmaAO: 1
+--- !u!1001 &648182179
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 0}
+    m_Modifications:
+    - target: {fileID: 1396242425075152212, guid: cb71a65524741c74d9586cbf034c3051,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: cb71a65524741c74d9586cbf034c3051,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0.369
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: cb71a65524741c74d9586cbf034c3051,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0.876
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: cb71a65524741c74d9586cbf034c3051,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: -0.14
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: cb71a65524741c74d9586cbf034c3051,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: cb71a65524741c74d9586cbf034c3051,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: cb71a65524741c74d9586cbf034c3051,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: cb71a65524741c74d9586cbf034c3051,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: cb71a65524741c74d9586cbf034c3051,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: cb71a65524741c74d9586cbf034c3051,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152212, guid: cb71a65524741c74d9586cbf034c3051,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152213, guid: cb71a65524741c74d9586cbf034c3051,
+        type: 3}
+      propertyPath: m_Name
+      value: TestAvatars
+      objectReference: {fileID: 0}
+    - target: {fileID: 1396242425075152213, guid: cb71a65524741c74d9586cbf034c3051,
+        type: 3}
+      propertyPath: m_IsActive
+      value: 0
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: cb71a65524741c74d9586cbf034c3051, type: 3}
+--- !u!1 &743882950
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 743882951}
+  m_Layer: 0
+  m_Name: Environment - (Offset to 0,0,0)
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &743882951
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 743882950}
+  m_LocalRotation: {x: 0, y: 1, z: 0, w: 0}
+  m_LocalPosition: {x: 0, y: -6.1, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 7521770810441873666}
+  m_Father: {fileID: 0}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 180, z: 0}
+--- !u!1001 &1342837806
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 0}
+    m_Modifications:
+    - target: {fileID: 3157928069488761441, guid: 6df47a28fe7c7c444a658aebcc35ad94,
+        type: 3}
+      propertyPath: m_Name
+      value: AvatarSdkManagerKhronosBabylonMatch
+      objectReference: {fileID: 0}
+    - target: {fileID: 3157928069488761443, guid: 6df47a28fe7c7c444a658aebcc35ad94,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 5
+      objectReference: {fileID: 0}
+    - target: {fileID: 3157928069488761443, guid: 6df47a28fe7c7c444a658aebcc35ad94,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3157928069488761443, guid: 6df47a28fe7c7c444a658aebcc35ad94,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3157928069488761443, guid: 6df47a28fe7c7c444a658aebcc35ad94,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3157928069488761443, guid: 6df47a28fe7c7c444a658aebcc35ad94,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 3157928069488761443, guid: 6df47a28fe7c7c444a658aebcc35ad94,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3157928069488761443, guid: 6df47a28fe7c7c444a658aebcc35ad94,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3157928069488761443, guid: 6df47a28fe7c7c444a658aebcc35ad94,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3157928069488761443, guid: 6df47a28fe7c7c444a658aebcc35ad94,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3157928069488761443, guid: 6df47a28fe7c7c444a658aebcc35ad94,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 3157928069488761443, guid: 6df47a28fe7c7c444a658aebcc35ad94,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: 6df47a28fe7c7c444a658aebcc35ad94, type: 3}
+--- !u!1001 &1568823152
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 0}
+    m_Modifications:
+    - target: {fileID: 5650585432433268674, guid: 0fe34e45f675346438d2ee1df9419ff7,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 2
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 0fe34e45f675346438d2ee1df9419ff7,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 0fe34e45f675346438d2ee1df9419ff7,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 0fe34e45f675346438d2ee1df9419ff7,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 0fe34e45f675346438d2ee1df9419ff7,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 0fe34e45f675346438d2ee1df9419ff7,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 0fe34e45f675346438d2ee1df9419ff7,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 0fe34e45f675346438d2ee1df9419ff7,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 0fe34e45f675346438d2ee1df9419ff7,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 0fe34e45f675346438d2ee1df9419ff7,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 5650585432433268674, guid: 0fe34e45f675346438d2ee1df9419ff7,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 8423574076275988413, guid: 0fe34e45f675346438d2ee1df9419ff7,
+        type: 3}
+      propertyPath: m_Name
+      value: VenuesLobby LightRig
+      objectReference: {fileID: 0}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: 0fe34e45f675346438d2ee1df9419ff7, type: 3}
+--- !u!1 &1659080253
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1659080254}
+  - component: {fileID: 1659080255}
+  m_Layer: 0
+  m_Name: CameraOffset
+  m_TagString: Untagged
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!4 &1659080254
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1659080253}
+  m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+  m_LocalPosition: {x: 0, y: 1.6, z: -4.5}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children:
+  - {fileID: 1716217489}
+  m_Father: {fileID: 0}
+  m_RootOrder: 3
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &1659080255
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1659080253}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 5b0eaccd645fc5641ba1cf708f6fd678, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  movementSpeed: 2.5
+--- !u!1 &1716217486
+GameObject:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  serializedVersion: 6
+  m_Component:
+  - component: {fileID: 1716217489}
+  - component: {fileID: 1716217488}
+  - component: {fileID: 1716217487}
+  - component: {fileID: 1716217490}
+  m_Layer: 0
+  m_Name: Main Camera
+  m_TagString: MainCamera
+  m_Icon: {fileID: 0}
+  m_NavMeshLayer: 0
+  m_StaticEditorFlags: 0
+  m_IsActive: 1
+--- !u!81 &1716217487
+AudioListener:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1716217486}
+  m_Enabled: 1
+--- !u!20 &1716217488
+Camera:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1716217486}
+  m_Enabled: 1
+  serializedVersion: 2
+  m_ClearFlags: 1
+  m_BackGroundColor: {r: 0.19215687, g: 0.3019608, b: 0.4745098, a: 0}
+  m_projectionMatrixMode: 1
+  m_GateFitMode: 2
+  m_FOVAxisMode: 0
+  m_SensorSize: {x: 36, y: 24}
+  m_LensShift: {x: 0, y: 0}
+  m_FocalLength: 50
+  m_NormalizedViewPortRect:
+    serializedVersion: 2
+    x: 0
+    y: 0
+    width: 1
+    height: 1
+  near clip plane: 0.3
+  far clip plane: 1000
+  field of view: 60
+  orthographic: 0
+  orthographic size: 5
+  m_Depth: -1
+  m_CullingMask:
+    serializedVersion: 2
+    m_Bits: 4294967295
+  m_RenderingPath: -1
+  m_TargetTexture: {fileID: 0}
+  m_TargetDisplay: 0
+  m_TargetEye: 3
+  m_HDR: 1
+  m_AllowMSAA: 1
+  m_AllowDynamicResolution: 0
+  m_ForceIntoRT: 0
+  m_OcclusionCulling: 1
+  m_StereoConvergence: 10
+  m_StereoSeparation: 0.022
+--- !u!4 &1716217489
+Transform:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1716217486}
+  m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+  m_LocalPosition: {x: 0, y: 0, z: 0}
+  m_LocalScale: {x: 1, y: 1, z: 1}
+  m_Children: []
+  m_Father: {fileID: 1659080254}
+  m_RootOrder: 0
+  m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &1716217490
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 1716217486}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: 5a2a9c34df4095f47b9ca8f975175f5b, type: 3}
+  m_Name: 
+  m_EditorClassIdentifier: 
+  m_Device: 0
+  m_PoseSource: 2
+  m_PoseProviderComponent: {fileID: 0}
+  m_TrackingType: 0
+  m_UpdateType: 0
+  m_UseRelativeTransform: 0
+--- !u!1001 &1518088931513711573
+PrefabInstance:
+  m_ObjectHideFlags: 0
+  serializedVersion: 2
+  m_Modification:
+    m_TransformParent: {fileID: 743882951}
+    m_Modifications:
+    - target: {fileID: 260900583523212020, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: ea5877ea2c5fdce41b360ae15e6889b0, type: 2}
+    - target: {fileID: 698978750644206358, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: ea5877ea2c5fdce41b360ae15e6889b0, type: 2}
+    - target: {fileID: 795559306866936833, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: ea5877ea2c5fdce41b360ae15e6889b0, type: 2}
+    - target: {fileID: 911182518007812963, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: ea5877ea2c5fdce41b360ae15e6889b0, type: 2}
+    - target: {fileID: 1030230606095263163, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: ea5877ea2c5fdce41b360ae15e6889b0, type: 2}
+    - target: {fileID: 1185725937715576670, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: ea5877ea2c5fdce41b360ae15e6889b0, type: 2}
+    - target: {fileID: 1922188851258417324, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: ea5877ea2c5fdce41b360ae15e6889b0, type: 2}
+    - target: {fileID: 2114154569574501420, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: ea5877ea2c5fdce41b360ae15e6889b0, type: 2}
+    - target: {fileID: 2237011750212248559, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: ea5877ea2c5fdce41b360ae15e6889b0, type: 2}
+    - target: {fileID: 2362798242187133835, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: ea5877ea2c5fdce41b360ae15e6889b0, type: 2}
+    - target: {fileID: 2400249572786858936, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: ea5877ea2c5fdce41b360ae15e6889b0, type: 2}
+    - target: {fileID: 2537780761971518445, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_Name
+      value: LobbyHalloween_Environment
+      objectReference: {fileID: 0}
+    - target: {fileID: 2537780761971518445, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_IsActive
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 2653469132624082816, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: ea5877ea2c5fdce41b360ae15e6889b0, type: 2}
+    - target: {fileID: 2805013115650338979, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: ea5877ea2c5fdce41b360ae15e6889b0, type: 2}
+    - target: {fileID: 4168840732123361907, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: ea5877ea2c5fdce41b360ae15e6889b0, type: 2}
+    - target: {fileID: 4321752368935189132, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: ea5877ea2c5fdce41b360ae15e6889b0, type: 2}
+    - target: {fileID: 4481233501183239601, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: ea5877ea2c5fdce41b360ae15e6889b0, type: 2}
+    - target: {fileID: 5106795222614013207, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: ea5877ea2c5fdce41b360ae15e6889b0, type: 2}
+    - target: {fileID: 5273041491426364691, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: ea5877ea2c5fdce41b360ae15e6889b0, type: 2}
+    - target: {fileID: 5339030895510184727, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: ea5877ea2c5fdce41b360ae15e6889b0, type: 2}
+    - target: {fileID: 5752761045824021755, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: ea5877ea2c5fdce41b360ae15e6889b0, type: 2}
+    - target: {fileID: 5771997397935824442, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: ea5877ea2c5fdce41b360ae15e6889b0, type: 2}
+    - target: {fileID: 6268752020957039124, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: ea5877ea2c5fdce41b360ae15e6889b0, type: 2}
+    - target: {fileID: 6468391960920743402, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: ea5877ea2c5fdce41b360ae15e6889b0, type: 2}
+    - target: {fileID: 6644387240665879152, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: ea5877ea2c5fdce41b360ae15e6889b0, type: 2}
+    - target: {fileID: 6895946935062357399, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: ea5877ea2c5fdce41b360ae15e6889b0, type: 2}
+    - target: {fileID: 6898992166305903773, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: ea5877ea2c5fdce41b360ae15e6889b0, type: 2}
+    - target: {fileID: 7008345515320884223, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: ea5877ea2c5fdce41b360ae15e6889b0, type: 2}
+    - target: {fileID: 7202350489774184804, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: ea5877ea2c5fdce41b360ae15e6889b0, type: 2}
+    - target: {fileID: 7265343296571672608, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: ea5877ea2c5fdce41b360ae15e6889b0, type: 2}
+    - target: {fileID: 7299190905765016631, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: ea5877ea2c5fdce41b360ae15e6889b0, type: 2}
+    - target: {fileID: 7539847625724962300, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: ea5877ea2c5fdce41b360ae15e6889b0, type: 2}
+    - target: {fileID: 7888816099441106069, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: ea5877ea2c5fdce41b360ae15e6889b0, type: 2}
+    - target: {fileID: 7947532065422359403, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: ea5877ea2c5fdce41b360ae15e6889b0, type: 2}
+    - target: {fileID: 8266283065563172921, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: ea5877ea2c5fdce41b360ae15e6889b0, type: 2}
+    - target: {fileID: 8546268317445898475, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: ea5877ea2c5fdce41b360ae15e6889b0, type: 2}
+    - target: {fileID: 9039823294720213719, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_RootOrder
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 9039823294720213719, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_LocalPosition.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 9039823294720213719, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_LocalPosition.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 9039823294720213719, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_LocalPosition.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 9039823294720213719, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_LocalRotation.w
+      value: 1
+      objectReference: {fileID: 0}
+    - target: {fileID: 9039823294720213719, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_LocalRotation.x
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: 9039823294720213719, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_LocalRotation.y
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: 9039823294720213719, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_LocalRotation.z
+      value: -0
+      objectReference: {fileID: 0}
+    - target: {fileID: 9039823294720213719, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.x
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 9039823294720213719, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.y
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 9039823294720213719, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_LocalEulerAnglesHint.z
+      value: 0
+      objectReference: {fileID: 0}
+    - target: {fileID: 9156717729050136268, guid: 328b22dc79cb39c4790ef4d18e578c29,
+        type: 3}
+      propertyPath: m_Materials.Array.data[0]
+      value: 
+      objectReference: {fileID: 2100000, guid: ea5877ea2c5fdce41b360ae15e6889b0, type: 2}
+    m_RemovedComponents: []
+  m_SourcePrefab: {fileID: 100100000, guid: 328b22dc79cb39c4790ef4d18e578c29, type: 3}
+--- !u!4 &7521770810441873666 stripped
+Transform:
+  m_CorrespondingSourceObject: {fileID: 9039823294720213719, guid: 328b22dc79cb39c4790ef4d18e578c29,
+    type: 3}
+  m_PrefabInstance: {fileID: 1518088931513711573}
+  m_PrefabAsset: {fileID: 0}
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/VenuesLobbyLightingExample.unity.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/VenuesLobbyLightingExample.unity.meta
new file mode 100644
index 0000000000000000000000000000000000000000..05b2b454c200316f033475b5a3ba9666ebbcc6c6
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/VenuesLobbyLightingExample.unity.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: a1ee37388aca1fb488b0f431e45eafec
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/VenuesLobbyLightingExample/LightingData.asset b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/VenuesLobbyLightingExample/LightingData.asset
new file mode 100644
index 0000000000000000000000000000000000000000..2e392095a3a30da0ac14a116daefc17dd4a612e8
Binary files /dev/null and b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/VenuesLobbyLightingExample/LightingData.asset differ
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/VenuesLobbyLightingExample/LightingData.asset.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/VenuesLobbyLightingExample/LightingData.asset.meta
new file mode 100644
index 0000000000000000000000000000000000000000..ec3ba5043fcc6dc82e0c12b7cf96dee38ba09481
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/VenuesLobbyLightingExample/LightingData.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 2f421bf54781ede478ba00f104823042
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/diffuse-argb16.DDS b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/diffuse-argb16.DDS
new file mode 100644
index 0000000000000000000000000000000000000000..21c2c62f2d17f16b9bc47c6758164b9ad4b7f3d6
Binary files /dev/null and b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/diffuse-argb16.DDS differ
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/diffuse-argb16.DDS.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/diffuse-argb16.DDS.meta
new file mode 100644
index 0000000000000000000000000000000000000000..43a83432050f5953423a81c324b66e1ac4e1e7ea
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/diffuse-argb16.DDS.meta
@@ -0,0 +1,19 @@
+fileFormatVersion: 2
+guid: 651a7e8194498914981dd7211c2ac107
+IHVImageFormatImporter:
+  externalObjects: {}
+  textureSettings:
+    serializedVersion: 2
+    filterMode: 1
+    aniso: 1
+    mipBias: 0
+    wrapU: 0
+    wrapV: 0
+    wrapW: 0
+  isReadable: 0
+  sRGBTexture: 1
+  streamingMipmaps: 0
+  streamingMipmapsPriority: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/specular-argb16.DDS b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/specular-argb16.DDS
new file mode 100644
index 0000000000000000000000000000000000000000..e91b708d4d96e7a34673834efa1852d289a59b1a
Binary files /dev/null and b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/specular-argb16.DDS differ
diff --git a/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/specular-argb16.DDS.meta b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/specular-argb16.DDS.meta
new file mode 100644
index 0000000000000000000000000000000000000000..60cb85424b00d7c2bf7d36ed1859662662118c1a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Example/Scenes/VenuesLobbyLightingExample/specular-argb16.DDS.meta
@@ -0,0 +1,19 @@
+fileFormatVersion: 2
+guid: 9a991013884426c40a06843632bed336
+IHVImageFormatImporter:
+  externalObjects: {}
+  textureSettings:
+    serializedVersion: 2
+    filterMode: 1
+    aniso: 1
+    mipBias: 0
+    wrapU: 0
+    wrapV: 0
+    wrapW: 0
+  isReadable: 0
+  sRGBTexture: 1
+  streamingMipmaps: 0
+  streamingMipmapsPriority: 0
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/LICENSE.txt b/Assets/Oculus/Avatar2/LICENSE.txt
new file mode 100644
index 0000000000000000000000000000000000000000..132bc697ed5c6e83beb928f2b78c8273c2035d5e
--- /dev/null
+++ b/Assets/Oculus/Avatar2/LICENSE.txt
@@ -0,0 +1,371 @@
+Oculus SDK License Agreement
+
+Effective date: October 29, 2021
+
+Copyright © Facebook Technologies, LLC and its affiliates. All rights reserved.
+
+The text of this may be found at: https://developer.oculus.com/licenses/oculussdk/
+
+This Oculus SDK License Agreement (“Agreement”) is a legal agreement between you and Oculus governing your use of our Oculus
+Software Development Kit. Oculus Software Development Kit means any application programming interfaces (“APIs”), tools, plugins,
+code, technology, specification, documentation, Platform Services, and/or content made available by us to others, including app
+developers and content providers (collectively, the “SDK”).
+
+By downloading or using our SDK, you are agreeing to this Agreement along with other applicable terms and conditions such as the
+additional terms or documents accompanying the SDK and the Terms of Service, and acknowledging our Privacy Policy (collectively,
+the “Terms”). If you use the SDK as an interface to, or in conjunction with other Oculus products or services, then the terms for those
+other products or services also apply.
+
+Here, "Oculus" means Facebook Technologies, LLC, formerly known as Oculus VR, LLC, a Delaware limited liability company with its
+principal place of business at 1 Hacker Way, Menlo Park, California 94025, United States unless set forth otherwise. We may refer to
+"Oculus" as "we", "our", or "us" in this Agreement.
+
+You may not use the SDK and may not accept this Agreement if (1) you are a person with whom Oculus is prohibited from transacting
+business under applicable law, or (2) you are a person barred from using or receiving the SDK by Oculus or under the applicable laws of
+the United States or other countries including the country in which you are resident or from which you use the SDK. If you are using the
+SDK on behalf of an entity, you represent and warrant that you have authority to bind that entity to this Agreement and by accepting
+this Agreement, you are doing so on behalf of that entity (and all references to "you" in this Agreement refer to that entity).
+
+This Agreement requires the resolution of most disputes between you and Oculus by binding arbitration on an individual basis; class
+actions and jury trials are not permitted.
+
+
+1. License Grant
+
+1.1 License. Subject to the Terms and the restrictions set forth in this Agreement, Oculus hereby grants you a limited, royalty-free, non-
+exclusive, non-transferrable, non-sublicensable (except as otherwise set forth in this Agreement), revocable copyright license
+(“License”) during the term of this Agreement to use and reproduce the SDK solely to develop, test, and/or distribute your Application
+(defined below) and to enable you and/or your end users to access Oculus features through your Application. You may only use the
+SDK to develop Applications in connection with Oculus approved hardware and software products (“Oculus Approved Products”)
+unless the documentation accompanying the SDK expressly authorizes broader use such as with other third-party platforms.
+
+1.1.1 If the SDK includes any libraries, sample source code, or other materials that we make available specifically for incorporation in your
+Application (as indicated by applicable documentation), you may incorporate those materials and reproduce and distribute them as
+part of your Application, including by distributing those materials to third parties contributing to your Application.
+
+1.1.2 The SDK may include other content (e.g., sample code) that is for demonstration, reference, or other purposes and is subject to
+terms and conditions included with such materials. Such materials will be clearly marked in the applicable documentation. Absent such
+additional terms and conditions, you may modify, distribute, and sublicense any sample source made available as part of the SDK
+pursuant to this Agreement and the Terms.
+
+1.1.3 The SDK may include Oculus content that is subject to your additional right to display the content to your end users through the use
+of the corresponding SDK, as contemplated by the documentation accompanying such SDK. For example, the SDK may include avatars
+that you may display to your end users.
+
+1.2 General Restrictions. The License grant in this Section is solely for the purpose of developing, testing, and promoting your engines,
+tools, applications, content, games and demos, or other products and features (collectively, “Application”) and providing you and/or
+your end users access to Oculus services and features through your Application as contemplated by applicable documentation
+accompanying the SDK. You may not (or allow those acting on your behalf to):
+
+1.2.1 modify or create derivative works from any SDK or its component (other than sample source code described in this Section or
+expressly authorized by the documents accompanying the SDK);
+
+1.2.2 misrepresent or mask either your identity or your Application's identity when using the SDK or developer accounts;
+
+1.2.3 attempt to circumvent any limitations implemented within or documented with the SDK (e.g., limiting the number of requests you
+may make or end users you may serve);
+
+1.2.4 reverse engineer, decompile, disassemble, or otherwise attempt to extract the source code from the SDK, except to the extent
+that applicable law expressly permits such actions despite this limitation;
+
+1.2.5 alter, restrict, or interfere with the normal operation or functionality of the SDK, the Oculus hardware or software, or Oculus
+Approved Products, including, but not limited to: (a) the behavior of the “Oculus button” and “XBox button” implemented by the Oculus
+system software; (b) any on-screen messages or information; (c) the behavior of the proximity sensor in the Oculus hardware
+implemented by the Oculus system software; (d) any Oculus hardware or software security features; (e) any end user's settings; and
+(f) Health and Safety Warnings;
+
+1.2.6 use the SDK or your Application in a manner that violates: (a) the Oculus Data Use Policy (where applicable); (b) the Oculus
+Content Guidelines, or other applicable terms and policies made available on our Developer Policy portal; (c) any rights of Oculus or
+third parties; (d) applicable laws (such as laws regarding import, export, privacy, health & safety); or (e) other terms of service with
+Oculus or its affiliates;
+
+1.2.7 remove, obscure, or alter any Terms or any links to or notices of those Terms; or
+
+1.2.8 use or redistribute the SDK or any portion thereof in any manner that would cause the SDK (or any portion thereof) or Oculus to
+become subject to the terms of any open source license or other restrictions.
+
+
+1.3 Distribution and Sublicense Restrictions. The redistribution and sublicense rights under this Section are further subject to the
+following restrictions: (1) redistribution of sample source code or other materials must include the following copyright notice: “Copyright
+© Facebook Technologies, LLC and its affiliates. All rights reserved;” and (2) if the sample source code or other materials include a
+"License" or "Notice" text file, you must provide a copy of the License or Notice file with the sample code.
+
+
+1.4 Privacy and Security.
+
+1.4.1 You are responsible for the data collection, processing and disclosure by your Application and agree to comply with all applicable
+privacy and data protection laws, as well as our applicable terms and policies, particularly the Oculus Data Use Policy. You represent and
+warrant that you have provided robust and sufficiently prominent notice to users regarding (i) data processing that includes, at a
+minimum, that third parties, including Oculus and its affiliates, may collect or receive information from your Application, and (ii) any
+other information required to be disclosed to users by applicable privacy and data protection laws. You represent and warrant that you
+will not back up or make available to the Oculus Cloud Backup feature any information that you know or reasonably should know (i) is
+from or about children under the age of 13, or (ii) includes data concerning health, financial information, or other categories of sensitive
+information (including any information defined as special or sensitive under applicable laws, regulations, and applicable industry
+guidelines).
+
+1.4.2 For purposes of the GDPR, you acknowledge and agree that you are a separate and independent controller of the Developer User
+Data (as defined in the Oculus Developer Data Use Policy) and Facebook Ireland Limited (an affiliate of Oculus) is a separate and
+independent controller for any processing of personal data, except as provided in Section 1.4.3, including the Oculus User Data (as
+defined in the Oculus Developer Data Use Policy). The parties do not and will not process Developer User Data or Oculus User Data as
+joint controllers. Each party shall comply with the obligations that apply to it as a controller under the GDPR, and each party shall be
+individually and separately responsible for its own compliance.
+
+1.4.3 Notwithstanding the foregoing, where (a) we process Developer User Data that contains personal data to (i) store, host or
+otherwise backup Developer User Data through Oculus Cloud Backup; (ii) provide and operate the Spatial Audio VoIP API; (iii) or provide
+other services described in the Data Processing Terms (the “Services”), and (b) (i) our processing of such personal data is subject to
+the GDPR, you instruct Facebook Ireland Limited to process such personal data in order to provide the Services pursuant to this
+Agreement and the Data Processing Terms, which are incorporated herein by reference, including for product improvement for your
+benefit and to anonymize the data so that it is no longer Personal Data for the purposes of GDPR, and you understand and agree that we
+may retain such anonymized metadata for our own legitimate purposes, including improvement and development of the Services; or (ii)
+such personal data is considered “personal information,” “personal data,” “personally identifiable information” or similar terms and is
+subject to the California Consumer Privacy Act of 2018 (“CCPA”), or other applicable privacy and data protection laws (excluding the
+GDPR), we will only retain, use and disclose such personal data for the purposes of providing those Services to you and improving those
+Services on your behalf or as otherwise permitted by the CCPA and such other applicable privacy and data protection laws.
+
+1.4.4 “Personal data,” “controller,” “processor,” and “process” in this Section 1.4 have the meanings set out in the Data Processing
+Terms.
+
+1.5 You have no obligations under this Agreement to license or make available your Application to Oculus, its affiliates, or any third
+parties. Nothing in this Agreement obligates Oculus or its affiliates to enable you or any of your Applications to access, interact with, or
+retrieve or publish content to any Oculus platform or service. However, Oculus and/or its affiliates may require you to agree to additional
+terms as a condition of providing you with such platform services in connection with your use of the SDK. You acknowledge and agree
+that Oculus and its affiliates may develop products or services that may compete with your Application or any other products or
+services of yours.
+
+1.6 Experimental Features. From time to time, Oculus may, in its sole discretion, make available to you as part of the Oculus Software
+Development Kit, certain experimental, test or beta software, APIs or features on a limited or test basis (“Experimental Features”).
+Experimental Features can only be used for experimental or testing purposes and cannot be incorporated into a production build unless
+(i) the Experimental Feature has been released or included in Oculus software production builds or (ii) otherwise permitted by Oculus in
+writing. Your use of any Experimental Feature is voluntary. You agree that all use of any Experimental Feature is at your sole risk. You
+agree that once you use an Experiment Feature, your content, data and/or systems may be affected, and you may be unable to revert
+back to a prior version of the same or similar feature. Additionally, if such reversion is possible, you may not be able to return or restore
+data created or transferred using the Experimental Feature back to the prior version. The Experimental Features may not work in the
+same way as a final production version. Oculus and its affiliates make no representations or warranties that the Experimental Features
+will function or be free from errors. The Experimental Features are provided on an “as is” basis and may contain errors or inaccuracies
+that could cause failures, corruption or loss of data and information from any connected device or service. Oculus and its affiliates have
+no obligation to correct bugs, defects, or errors or otherwise support or maintain Experimental Features. Oculus and its affiliates may
+discontinue, update, modify or remove access to any Experimental Feature at any time in its sole discretion, and may not release a final
+version of an Experimental Feature in its sole discretion.
+
+
+2. Oculus Platform Services
+
+Oculus and/or its affiliates makes certain Platform Services (defined below) available to you to include and enable in your Application
+on our Platform. An Application that enables or includes any Platform Service must implement the Oculus Platform Framework (defined
+below) with the Application. Once your Application has been authorized for use of the Platform Services, you are not required to update
+your Application to include new Platform Services that Oculus and/or its affiliates may make available as part of the Oculus Platform
+Framework. For more information, please visit https://developer.oculus.com.
+
+2.1 For the purpose of this Section,
+
+2.1.1 “Application Services” means services provided by Oculus and/or its affiliates associated with the Platform, including, but not
+limited to, in-app purchasing, multiplayer matchmaking, friends, leader boards, achievements, Virtual Reality Real Time Systems
+(“VERTS”), voice over IP and Cloud Backup, which list may be changed from time to time in Oculus' or its affiliates’ sole discretion.
+
+2.1.2 "Oculus Platform Framework" means the suite of Oculus platform services, including, but not limited to, the Oculus file
+distribution and update system (enabling distribution and updates of Applications by Oculus and/or its affiliates, including through
+generated activation Keys), entitlement system, and account authentication, which list may be changed from time to time in Oculus' or
+its affiliates’ sole discretion.
+
+2.1.3 "Platform" means the virtual, mixed, and augmented reality platform made available by Oculus and/or its affiliates, including, but
+not limited to, the user experience, user interface, store, and social features, usable on hardware approved by Oculus or its affiliates or
+any third-party device or operating system, including, but not limited to, iOS, Android, Windows, OS X, Linux, and Windows Mobile.
+
+2.1.4 "Platform Services" means the Oculus Platform Framework and the Application Services.
+
+2.2 Key Provision and Redemption. If you request that Oculus generate activation keys for your Application on the Platform ("Keys")
+and Oculus agrees, you hereby grant Oculus and its affiliates (1) the right to generate Keys for you and (2) a license to make available,
+reproduce, distribute, perform, and display the Application to end users who have submitted a Key to Oculus or its affiliates. Oculus
+agrees to authenticate and make the Application available to any end user supplying a valid Key (or have its affiliates do so) (unless the
+Application has been removed or withdrawn).
+
+2.3 Platform Services Requirements. You will not make any use of any API, software, code or other item or information supplied by
+Oculus or its affiliates in connection with the Platform Services other than to enhance the functionality of your Application. In particular,
+you must not (nor enable others to): (1) defame, abuse, harass, stalk, or threaten others, or to promote or facilitate any prohibited or
+illegal activities; (2) enable any functionality in your Application that would generate excessive traffic over the Oculus network or
+servers that would negatively impact other users' experience, or otherwise interfere with or restrict the operation of the Platform
+Services, or Oculus' or its affiliates’ servers or networks providing the Platform Services; (3) remove, obscure, or alter any license
+terms, policies or terms of service or any links to or notices thereto provided by Oculus or its affiliates; or (4) violate any rights of Oculus,
+its affiliates, or any third parties. Notwithstanding anything to the contrary set forth in this Agreement, you may not sublicense any
+software, firmware or other item or information supplied by Oculus or its affiliates in connection with the Platform Services for use by a
+third party, unless expressly authorized by Oculus or its affiliates to do so. You agree not to use (or encourage the use of) the Platform
+Services for mission critical, life saving or ultra-hazardous activities. Oculus or its affiliates may suspend operation of or remove any
+Application that does not comply with the restrictions in this Agreement.
+
+2.4 Changes to Platform or Platform Services. Oculus and/or its affiliates may change the Platform or the functionality of the Platform
+Services at any time, including discontinuing some of the functionality of the Platform Services, and your continued use of the Platform
+or Platform Services or use of any modified or additional Platform Services is conditioned upon your adherence to the terms of this
+Agreement, as modified by Oculus or its affiliates from time to time.
+
+
+3. Intellectual Property
+
+3.1 Ownership. As between you and Oculus, Oculus and/or its affiliates or licensors own all rights, title, and interest, including all
+Intellectual Property Rights (defined below), in and to the SDK (including associated Oculus content and sample code) and all
+derivatives thereof. Oculus reserves all rights not expressly granted under the License. As between you and Oculus, you and/or your
+licensors own all rights, title, and interest in and to your Application, (excluding our SDK), including all Intellectual Property Rights.
+“Intellectual Property Rights” means any and all worldwide rights under applicable laws of patent, copyright, trade secret, trademark,
+rights of publicity and privacy, and other proprietary rights.
+
+3.2 Third-Party Materials. Our SDK may include third-party software offered under an open source license or third-party content
+subject to a separate third-party agreement. To the extent any of such third-party terms conflicts with this Agreement, such third-party
+terms will control solely with respect to such third-party software or content.
+
+3.3 Feedback. If you provide comments, suggestions, recommendations, ideas, know-how or other feedback about our SDK or any
+other Oculus or affiliate product or service, we (and our affiliates and those we allow) may use such information for any purposes
+without obligation to you and all intellectual property and other proprietary rights in any such feedback are deemed (and hereby)
+licensed to Oculus (with the right to sublicense through multiple tiers) for any purpose on a perpetual, irrevocable, worldwide, paid-
+up, and royalty-free basis and may be used or disclosed for any purpose.
+
+3.4 Brand Attribution. This Agreement does not grant you or any third party permission to use our trade names, trademarks, service
+marks, logos, domain names, and other distinctive brand features (collectively, “Brand Features”) except as required for reasonable and
+customary use in describing the origin of the SDK or reproduction of the copyright notice as required under the License grant. You will
+not use our SDK or make any statement regarding the SDK or your Application which suggests partnership with, sponsorship by, or
+endorsement by Oculus, its affiliates or any of their employees, contractors, contributors, licensors, affiliates, or partners without our
+prior written permission.
+
+
+4. Confidentiality
+
+4.1 Confidentiality. Our communications to you and our SDK may contain Oculus confidential information, which includes information
+that is marked confidential or that would normally be considered confidential under the circumstances. If you receive any such
+information, you will not disclose it to any third party without Oculus' prior written consent. Oculus confidential information does not
+include information that you independently developed, that was rightfully given to you by a third party without a confidentiality
+obligation with regard to such information, or that becomes public through no fault of your own. You may disclose Oculus confidential
+information when compelled to do so by law if you provide us reasonable prior notice, unless a court order prohibits such notice.
+
+
+5. Termination
+
+5.1 Termination. The term of this Agreement will begin on the date on which you click accept, download, or use the SDK or any of its
+components and will continue until terminated as set forth in this Agreement. Oculus reserves the right to terminate this Agreement
+with you, or to discontinue or suspend the SDK or any portion or feature or your access thereto in the event you breach any material
+provisions of this Agreement or the Terms, without liability or other obligation to you.
+
+5.2. Discontinuation of SDK. Oculus reserves the right to discontinue all or part of the SDK at any time, in our sole discretion, without
+notice to you, and without liability or other obligation to you. This Agreement will terminate automatically and without notice to you in
+the event that the SDK is discontinued in its entirety.
+
+5.3 Effect of Termination. Upon termination of this Agreement, you will immediately stop using, distributing, or otherwise making
+available the SDK and all Applications that incorporate the SDK or any of its components, cease all use of the Oculus Brand Features,
+and destroy or return any cached or stored content, software, or other materials obtained through our SDK.
+
+5.4 Surviving Provisions. When this Agreement terminates, those terms that by their nature are intended to continue indefinitely will
+continue to apply, including, but not limited to, Section 3 (Intellectual Property), Section 4 (Confidentiality), Section 5 (Termination),
+Section 6 (Liability) and Section 7 (General Provisions).
+
+
+6. Liability
+
+6.1 Indemnification. Unless prohibited by applicable law, you will indemnify and (at Oculus’s option), defend Oculus, its affiliates and
+subsidiaries, and the agents, licensors, contributors, directors, officers, employees, suppliers, and distributors thereof (collectively,
+“Oculus Parties”) against all liabilities, damages, losses, costs, fees (including legal fees), and expenses relating to any allegation or
+third- party legal proceeding arising from: (1) your use of the SDK, or any negligence or misconduct, by you or your employees, agents,
+vendors, or contractors (collectively “Developer Parties”); (2) any Developer Parties’ violation of this Agreement, Terms, or any
+applicable law and regulation; (3) any use of your Application; or (4) Developer User Data (defined in Developer Data Use Policy).
+
+6.2 WARRANTIES. EXCEPT AS EXPRESSLY SET OUT IN THE TERMS, THE SDK IS PROVIDED “AS IS” WITHOUT ANY SPECIFIC PROMISES,
+REPRESENTATIONS, GUARANTEES OR WARRANTIES, WHETHER EXPRESS, IMPLIED OR STATUTORY, INCLUDING, BUT NOT LIMITED TO,
+ANY COMMITMENTS ABOUT THE CONTENT ACCESSED THROUGH THE SDK, THE SPECIFIC FUNCTIONS OF THE SDK OR OUR PLATFORM
+SERVICES, OR THEIR RELIABILITY, AVAILABILITY, OR ABILITY TO MEET YOUR NEEDS. THE OCULUS PARTIES HEREBY DISCLAIM ANY
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, NON-INFRINGEMENT AND
+FITNESS FOR A PARTICULAR PURPOSE. SOME JURISDICTIONS DO NOT PERMIT THE EXCLUSION OR LIMITATION OF IMPLIED
+WARRANTIES, SO YOU MAY HAVE ADDITIONAL RIGHTS.
+
+6.3 LIMITATION OF LIABILITY. TO THE EXTENT PERMITTED BY APPLICABLE LAW, OCULUS PARTIES WILL NOT BE RESPONSIBLE FOR LOST
+PROFITS, BUSINESS OR GOODWILL, REVENUES, OR DATA; FINANCIAL LOSSES; OR INDIRECT, SPECIAL, CONSEQUENTIAL, EXEMPLARY,
+OR PUNITIVE DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING AS A RESULT OF THIS AGREEMENT, USE OF THE SDK OR ANY MODIFIED
+SAMPLE CODE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. YOU AGREE THAT YOUR REMEDIES UNDER THIS AGREEMENT
+ARE LIMITED SOLELY TO THE RIGHT TO COLLECT MONEY DAMAGES, IF ANY, AND YOU HEREBY WAIVE YOUR RIGHT TO SEEK INJUNCTIVE
+RELIEF OR OTHER EQUITABLE RELIEF. IF YOU ARE A CALIFORNIA RESIDENT, YOU AGREE TO WAIVE CALIFORNIA CIVIL CODE § 1542,
+WHICH SAYS: “A GENERAL RELEASE DOES NOT EXTEND TO CLAIMS THAT THE CREDITOR OR RELEASING PARTY DOES NOT KNOW OR
+SUSPECT TO EXIST IN HIS OR HER FAVOR AT THE TIME OF EXECUTING THE RELEASE, WHICH IF KNOWN BY HIM OR HER WOULD HAVE
+MATERIALLY AFFECTED HIS OR HER SETTLEMENT WITH THE DEBTOR OR RELEASED PARTY.” TO THE EXTENT PERMITTED BY LAW, THE
+CUMULATIVE, AGGREGATE LIABILITY OF OCULUS PARTIES, FOR ANY AND ALL CLAIMS ARISING UNDER THIS AGREEMENT OR ITS
+SUBJECT MATTER SHALL NOT EXCEED THE GREATER OF ONE HUNDRED US DOLLARS ($100) OR THE AMOUNT YOU HAVE PAID US IN
+THE PAST TWELVE MONTHS. IN ALL CASES, OCULUS PARTIES WILL NOT BE LIABLE FOR ANY EXPENSE, LOSS, OR DAMAGE THAT IS NOT
+REASONABLY FORESEEABLE.
+
+
+7. General Provisions
+
+7.1 Updates. We may need to update this Agreement from time to time, including to accurately reflect the access or uses of our SDK,
+and so we encourage you to check this Agreement regularly. By continuing to access or use our SDK after any notice of an update to
+this Agreement, you agree to be bound by them. Any updates to the Disputes section of this Agreement will apply only to disputes that
+arise after notice of the update takes place. If you do not agree to the updated terms, please stop all access or use of our SDK. You
+cannot sidestep your compliance obligations under an updated version of this Agreement by developing against an older release of the
+SDK or relying on the older Agreement and all updates to your application are subject to the modified Agreement.
+
+7.2 Authorization. You hereby grant Oculus and its contractors and affiliates the authorization reasonably necessary for Oculus to
+exercise its rights and perform its obligations under this Agreement, including a limited, royalty-free, non-exclusive license to use,
+perform, and display the Application you provide to Oculus for testing, evaluation, and approval purposes.
+
+7.3 General Provisions. You and Oculus are independent contractors with regard to each other. This Agreement does not create any
+third-party beneficiary rights or any agency, partnership, employment, or joint venture. We are not liable for failure or delay in
+performance to the extent caused by circumstances beyond our reasonable control. If you do not comply with this Agreement, and
+Oculus does not take action right away or does not enforce any provision of this Agreement, this inaction or lack of enforcement will not
+act as a waiver by Oculus of any rights that it may have (such as taking action in the future) or in any way affect the validity of this
+Agreement or parts thereof. If a particular provision of this Agreement is deemed unenforceable, it will be deemed modified to the
+minimum extent necessary to render it enforceable and most nearly reflect the intent of the original provision, and all other provisions in
+this Agreement shall remain in full force and effect. You may not assign or delegate this Agreement or any obligations under this
+Agreement without our advance written consent. Any such prohibited attempted assignment will be void. Oculus may assign or
+delegate this Agreement and any of its rights or obligations under this Agreement without your consent or notice to you. This
+Agreement shall bind the parties and their respective heirs, successors, and permitted assigns. This Agreement is the entire agreement
+between you and Oculus relating to the subject matter herein and supersede any prior or contemporaneous agreements on such
+subject matter.
+
+
+7.4 Dispute Resolution.
+
+7.4.1 If you reside outside the US or your business is located outside the US: You agree that any claim, cause of action, or dispute you
+have against us that arises out of or relates to any access or use of the SDK must be resolved exclusively in the U.S. District Court for the
+Northern District of California or a state court located in San Mateo County, that you submit to the personal jurisdiction of either of
+these courts for the purpose of litigating any such claim, and that the laws of the State of California will govern this Agreement and any
+such claim, without regard to conflict of law provisions.
+
+7.4.2 If you reside in the US or your business is located in the US: You and we agree to arbitrate any claim, cause of action, or dispute
+between you and us that arises out of or relates to any access or use of the SDK for business or commercial purposes (“commercial
+claim”). This provision does not cover any commercial claims relating to violations of your or our intellectual property rights, including,
+but not limited to, copyright infringement, patent infringement, trademark infringement, violations of the brand guidelines, violations of
+your or our confidential information or trade secrets, or efforts to interfere with our products or engage with our products in
+unauthorized ways (for example, automated ways).
+
+7.4.3 We and you agree that, by entering into this arbitration provision all parties are waiving their respective rights to a trial by jury or to
+participate in a class or representative action. THE PARTIES AGREE THAT EACH MAY BRING COMMERCIAL CLAIMS AGAINST THE OTHER
+ONLY IN ITS INDIVIDUAL CAPACITY, AND NOT AS A PLAINTIFF OR CLASS MEMBER IN ANY PURPORTED CLASS, REPRESENTATIVE, OR
+PRIVATE ATTORNEY GENERAL PROCEEDING. You may bring a commercial claim only on your own behalf and cannot seek relief that
+would affect other parties. If there is a final judicial determination that any particular commercial claim (or a request for particular relief)
+cannot be arbitrated in accordance with this paragraph’s limitations, then only that commercial claim (or only that request for relief)
+may be brought in court. All other commercial claims (or requests for relief) remain subject to this paragraph.
+
+7.4.4 The Federal Arbitration Act governs the interpretation and enforcement of this arbitration provision. All issues are for an arbitrator
+to decide, except that only a court may decide issues relating to the scope or enforceability of this arbitration provision or the
+interpretation of the prohibition of class and representative actions.
+
+7.4.5 If any party intends to seek arbitration of a dispute, that party must provide the other party with notice in writing.
+
+7.4.6 The arbitration will be governed by the AAA’s Commercial Arbitration Rules (“AAA Rules”), as modified by this Agreement, and will
+be administered by the AAA. If the AAA is unavailable, the parties will agree to another arbitration provider or the court will appoint a
+substitute. The arbitrator will not be bound by rulings in other arbitrations in which you are not a party. To the fullest extent permitted by
+applicable law, any evidentiary submissions made in arbitration will be maintained as confidential in the absence of good cause for its
+disclosure. The arbitrator’s award will be maintained as confidential only to the extent necessary to protect either party’s trade secrets
+or proprietary business information or to comply with a legal requirement mandating confidentiality. Each party will be responsible for
+paying any AAA filing, administrative and arbitrator fees in accordance with AAA Rules, except that we will pay for your filing,
+administrative, and arbitrator fees if your commercial claim for damages does not exceed $75,000 and is non-frivolous (as measured
+by the standards set forth in Federal Rule of Civil Procedure 11(b)).
+
+7.4.7 If you do not wish to be bound by this provision (including its waiver of class and representative claims), you must notify us as set
+forth below within 30 days of the first acceptance date of any version of this Agreement containing an arbitration provision. Your notice
+to us under this subsection must be submitted to the address here: Facebook Technologies, LLC, 1 Hacker Way, Menlo Park, California
+94025
+
+7.4.8 All commercial claims between us, whether subject to arbitration or not, will be governed by California law, excluding California’s
+conflict of laws rules, except to the extent that California law is contrary to or preempted by federal law.
+
+7.4.9 If a commercial claim between you and us is not subject to arbitration, you agree that the claim must be resolved exclusively in the
+U.S. District Court for the Northern District of California or a state court located in San Mateo County, and that you submit to the
+personal jurisdiction of either of these courts for the purpose of litigating any such claim.
+
+7.4.10 If any provision of this dispute resolution provision is found unenforceable, that provision will be severed and the balance of the
+dispute resolution provision will remain in full force and effect.
diff --git a/Assets/Oculus/Avatar2/LICENSE.txt.meta b/Assets/Oculus/Avatar2/LICENSE.txt.meta
new file mode 100644
index 0000000000000000000000000000000000000000..8abee806134ff34af4d773134bb8abc58dc9b4cb
--- /dev/null
+++ b/Assets/Oculus/Avatar2/LICENSE.txt.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: d211742ab75b5904eb2b7e0a4ca1b374
+TextScriptImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Oculus.AvatarSDK2.asmdef b/Assets/Oculus/Avatar2/Oculus.AvatarSDK2.asmdef
new file mode 100644
index 0000000000000000000000000000000000000000..739b0c28c6c4fb26525637d637f2c052c4a5f661
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Oculus.AvatarSDK2.asmdef
@@ -0,0 +1,25 @@
+{
+    "name": "Oculus.AvatarSDK2",
+    "rootNamespace": "",
+    "references": [],
+    "includePlatforms": [],
+    "excludePlatforms": [],
+    "allowUnsafeCode": true,
+    "overrideReferences": false,
+    "precompiledReferences": [],
+    "autoReferenced": true,
+    "defineConstraints": [],
+    "versionDefines": [
+        {
+            "name": "com.unity.xr.management",
+            "expression": "",
+            "define": "USING_XR_MANAGEMENT"
+        },
+        {
+            "name": "com.unity.xr.oculus",
+            "expression": "",
+            "define": "USING_XR_SDK_OCULUS"
+        }
+    ],
+    "noEngineReferences": false
+}
\ No newline at end of file
diff --git a/Assets/Oculus/Avatar2/Oculus.AvatarSDK2.asmdef.meta b/Assets/Oculus/Avatar2/Oculus.AvatarSDK2.asmdef.meta
new file mode 100644
index 0000000000000000000000000000000000000000..96b205b86b299c4a27361df4ab0c3bc5a8ef8a33
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Oculus.AvatarSDK2.asmdef.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 64a5e78bc3a487e4785840abcc411b13
+AssemblyDefinitionImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Plugins.meta b/Assets/Oculus/Avatar2/Plugins.meta
new file mode 100644
index 0000000000000000000000000000000000000000..7075fb2af8cf64b914b0488f0b675f98b2d75795
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Plugins.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: b6f2aa3d71444e742a0eb09ebf8a9f5b
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Plugins/Android32.meta b/Assets/Oculus/Avatar2/Plugins/Android32.meta
new file mode 100644
index 0000000000000000000000000000000000000000..c611bf0e9a811f4ec8a49c7066f626cc8b96b622
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Plugins/Android32.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 51c89a111ea10a045981e09f7f1fd6f1
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Plugins/Android32/libovravatar2.so b/Assets/Oculus/Avatar2/Plugins/Android32/libovravatar2.so
new file mode 100644
index 0000000000000000000000000000000000000000..8140a64c3145b4a5e6ac4feba2018061c98921bc
Binary files /dev/null and b/Assets/Oculus/Avatar2/Plugins/Android32/libovravatar2.so differ
diff --git a/Assets/Oculus/Avatar2/Plugins/Android32/libovravatar2.so.meta b/Assets/Oculus/Avatar2/Plugins/Android32/libovravatar2.so.meta
new file mode 100644
index 0000000000000000000000000000000000000000..dcc04e68e00b7a7fd214a52fda358d0e5f87d794
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Plugins/Android32/libovravatar2.so.meta
@@ -0,0 +1,96 @@
+fileFormatVersion: 2
+guid: e8a422d9f655c8340be91e4f4bd4f972
+PluginImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  iconMap: {}
+  executionOrder: {}
+  defineConstraints: []
+  isPreloaded: 0
+  isOverridable: 1
+  isExplicitlyReferenced: 0
+  validateReferences: 1
+  platformData:
+  - first:
+      '': Any
+    second:
+      enabled: 0
+      settings:
+        Exclude Android: 0
+        Exclude Editor: 1
+        Exclude Linux: 1
+        Exclude Linux64: 1
+        Exclude LinuxUniversal: 1
+        Exclude OSXUniversal: 1
+        Exclude Win: 1
+        Exclude Win64: 1
+  - first:
+      Android: Android
+    second:
+      enabled: 1
+      settings:
+        CPU: ARMv7
+  - first:
+      Any: 
+    second:
+      enabled: 0
+      settings: {}
+  - first:
+      Editor: Editor
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+        DefaultValueInitialized: true
+        OS: AnyOS
+  - first:
+      Facebook: Win
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  - first:
+      Facebook: Win64
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: Linux
+    second:
+      enabled: 0
+      settings:
+        CPU: x86
+  - first:
+      Standalone: Linux64
+    second:
+      enabled: 0
+      settings:
+        CPU: x86_64
+  - first:
+      Standalone: LinuxUniversal
+    second:
+      enabled: 0
+      settings:
+        CPU: None
+  - first:
+      Standalone: OSXUniversal
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: Win
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: Win64
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Plugins/Android32/libovrbody.so b/Assets/Oculus/Avatar2/Plugins/Android32/libovrbody.so
new file mode 100644
index 0000000000000000000000000000000000000000..1e4659902045dee102f44f2455567cb48f904080
Binary files /dev/null and b/Assets/Oculus/Avatar2/Plugins/Android32/libovrbody.so differ
diff --git a/Assets/Oculus/Avatar2/Plugins/Android32/libovrbody.so.meta b/Assets/Oculus/Avatar2/Plugins/Android32/libovrbody.so.meta
new file mode 100644
index 0000000000000000000000000000000000000000..1fe049dd8192aef273f379cc0463812cf2693143
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Plugins/Android32/libovrbody.so.meta
@@ -0,0 +1,96 @@
+fileFormatVersion: 2
+guid: 8a76e7bd2a1913f4eacf1f500835bcb7
+PluginImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  iconMap: {}
+  executionOrder: {}
+  defineConstraints: []
+  isPreloaded: 1
+  isOverridable: 1
+  isExplicitlyReferenced: 0
+  validateReferences: 1
+  platformData:
+  - first:
+      : Any
+    second:
+      enabled: 0
+      settings:
+        Exclude Android: 0
+        Exclude Editor: 1
+        Exclude Linux: 1
+        Exclude Linux64: 1
+        Exclude LinuxUniversal: 1
+        Exclude OSXUniversal: 1
+        Exclude Win: 1
+        Exclude Win64: 1
+  - first:
+      Android: Android
+    second:
+      enabled: 1
+      settings:
+        CPU: ARMv7
+  - first:
+      Any: 
+    second:
+      enabled: 0
+      settings: {}
+  - first:
+      Editor: Editor
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+        DefaultValueInitialized: true
+        OS: AnyOS
+  - first:
+      Facebook: Win
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  - first:
+      Facebook: Win64
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: Linux
+    second:
+      enabled: 0
+      settings:
+        CPU: x86
+  - first:
+      Standalone: Linux64
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: LinuxUniversal
+    second:
+      enabled: 0
+      settings:
+        CPU: None
+  - first:
+      Standalone: OSXUniversal
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: Win
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: Win64
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Plugins/Android32/libovrgpuskinning.so b/Assets/Oculus/Avatar2/Plugins/Android32/libovrgpuskinning.so
new file mode 100644
index 0000000000000000000000000000000000000000..b8274af63c3a65a220b186f6590731fefcd92e7b
Binary files /dev/null and b/Assets/Oculus/Avatar2/Plugins/Android32/libovrgpuskinning.so differ
diff --git a/Assets/Oculus/Avatar2/Plugins/Android32/libovrgpuskinning.so.meta b/Assets/Oculus/Avatar2/Plugins/Android32/libovrgpuskinning.so.meta
new file mode 100644
index 0000000000000000000000000000000000000000..bfa1a98c1752308e5ed1f39c87c21b62ef8f3371
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Plugins/Android32/libovrgpuskinning.so.meta
@@ -0,0 +1,96 @@
+fileFormatVersion: 2
+guid: 257fe9df90b82aa4691fe4b14b8c3249
+PluginImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  iconMap: {}
+  executionOrder: {}
+  defineConstraints: []
+  isPreloaded: 0
+  isOverridable: 1
+  isExplicitlyReferenced: 0
+  validateReferences: 1
+  platformData:
+  - first:
+      '': Any
+    second:
+      enabled: 0
+      settings:
+        Exclude Android: 0
+        Exclude Editor: 1
+        Exclude Linux: 1
+        Exclude Linux64: 1
+        Exclude LinuxUniversal: 1
+        Exclude OSXUniversal: 1
+        Exclude Win: 1
+        Exclude Win64: 1
+  - first:
+      Android: Android
+    second:
+      enabled: 1
+      settings:
+        CPU: ARMv7
+  - first:
+      Any: 
+    second:
+      enabled: 0
+      settings: {}
+  - first:
+      Editor: Editor
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+        DefaultValueInitialized: true
+        OS: AnyOS
+  - first:
+      Facebook: Win
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  - first:
+      Facebook: Win64
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: Linux
+    second:
+      enabled: 0
+      settings:
+        CPU: x86
+  - first:
+      Standalone: Linux64
+    second:
+      enabled: 0
+      settings:
+        CPU: x86_64
+  - first:
+      Standalone: LinuxUniversal
+    second:
+      enabled: 0
+      settings:
+        CPU: None
+  - first:
+      Standalone: OSXUniversal
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: Win
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: Win64
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Plugins/Android32/libovrplugintracking.so b/Assets/Oculus/Avatar2/Plugins/Android32/libovrplugintracking.so
new file mode 100644
index 0000000000000000000000000000000000000000..b6430a45129c04f07a9150d1b1630a2e30c592b2
Binary files /dev/null and b/Assets/Oculus/Avatar2/Plugins/Android32/libovrplugintracking.so differ
diff --git a/Assets/Oculus/Avatar2/Plugins/Android32/libovrplugintracking.so.meta b/Assets/Oculus/Avatar2/Plugins/Android32/libovrplugintracking.so.meta
new file mode 100644
index 0000000000000000000000000000000000000000..f104269c52b8c9e6a7e82ba46daa354fca4e676d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Plugins/Android32/libovrplugintracking.so.meta
@@ -0,0 +1,96 @@
+fileFormatVersion: 2
+guid: f579738e928d3ac43b41335a28a01a07
+PluginImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  iconMap: {}
+  executionOrder: {}
+  defineConstraints: []
+  isPreloaded: 0
+  isOverridable: 1
+  isExplicitlyReferenced: 0
+  validateReferences: 1
+  platformData:
+  - first:
+      '': Any
+    second:
+      enabled: 0
+      settings:
+        Exclude Android: 0
+        Exclude Editor: 1
+        Exclude Linux: 1
+        Exclude Linux64: 1
+        Exclude LinuxUniversal: 1
+        Exclude OSXUniversal: 1
+        Exclude Win: 1
+        Exclude Win64: 1
+  - first:
+      Android: Android
+    second:
+      enabled: 1
+      settings:
+        CPU: ARMv7
+  - first:
+      Any: 
+    second:
+      enabled: 0
+      settings: {}
+  - first:
+      Editor: Editor
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+        DefaultValueInitialized: true
+        OS: AnyOS
+  - first:
+      Facebook: Win
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  - first:
+      Facebook: Win64
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: Linux
+    second:
+      enabled: 0
+      settings:
+        CPU: x86
+  - first:
+      Standalone: Linux64
+    second:
+      enabled: 0
+      settings:
+        CPU: x86_64
+  - first:
+      Standalone: LinuxUniversal
+    second:
+      enabled: 0
+      settings:
+        CPU: None
+  - first:
+      Standalone: OSXUniversal
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: Win
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: Win64
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Plugins/Android64.meta b/Assets/Oculus/Avatar2/Plugins/Android64.meta
new file mode 100644
index 0000000000000000000000000000000000000000..8ef96948a604f538cf6fe7361f637bf1067a936b
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Plugins/Android64.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: bb886988a58a6bc4bbf0d83430a04c6e
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Plugins/Android64/libovravatar2.so b/Assets/Oculus/Avatar2/Plugins/Android64/libovravatar2.so
new file mode 100644
index 0000000000000000000000000000000000000000..8f18331219084fe7ab1f3052b2daacc6ddbf8585
Binary files /dev/null and b/Assets/Oculus/Avatar2/Plugins/Android64/libovravatar2.so differ
diff --git a/Assets/Oculus/Avatar2/Plugins/Android64/libovravatar2.so.meta b/Assets/Oculus/Avatar2/Plugins/Android64/libovravatar2.so.meta
new file mode 100644
index 0000000000000000000000000000000000000000..80c9439675427f7e8ed2514b8a5c9af162ad31c3
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Plugins/Android64/libovravatar2.so.meta
@@ -0,0 +1,96 @@
+fileFormatVersion: 2
+guid: eb68bc894edba5f4f90feeb7bc2e27aa
+PluginImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  iconMap: {}
+  executionOrder: {}
+  defineConstraints: []
+  isPreloaded: 0
+  isOverridable: 1
+  isExplicitlyReferenced: 0
+  validateReferences: 1
+  platformData:
+  - first:
+      : Any
+    second:
+      enabled: 0
+      settings:
+        Exclude Android: 0
+        Exclude Editor: 1
+        Exclude Linux: 1
+        Exclude Linux64: 1
+        Exclude LinuxUniversal: 1
+        Exclude OSXUniversal: 1
+        Exclude Win: 1
+        Exclude Win64: 1
+  - first:
+      Android: Android
+    second:
+      enabled: 1
+      settings:
+        CPU: ARM64
+  - first:
+      Any: 
+    second:
+      enabled: 0
+      settings: {}
+  - first:
+      Editor: Editor
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+        DefaultValueInitialized: true
+        OS: AnyOS
+  - first:
+      Facebook: Win
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  - first:
+      Facebook: Win64
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: Linux
+    second:
+      enabled: 0
+      settings:
+        CPU: None
+  - first:
+      Standalone: Linux64
+    second:
+      enabled: 0
+      settings:
+        CPU: None
+  - first:
+      Standalone: LinuxUniversal
+    second:
+      enabled: 0
+      settings:
+        CPU: None
+  - first:
+      Standalone: OSXUniversal
+    second:
+      enabled: 0
+      settings:
+        CPU: None
+  - first:
+      Standalone: Win
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: Win64
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Plugins/Android64/libovrbody.so b/Assets/Oculus/Avatar2/Plugins/Android64/libovrbody.so
new file mode 100644
index 0000000000000000000000000000000000000000..34150a4b00958958373cdbce05c522ccb40639e9
Binary files /dev/null and b/Assets/Oculus/Avatar2/Plugins/Android64/libovrbody.so differ
diff --git a/Assets/Oculus/Avatar2/Plugins/Android64/libovrbody.so.meta b/Assets/Oculus/Avatar2/Plugins/Android64/libovrbody.so.meta
new file mode 100644
index 0000000000000000000000000000000000000000..f2113e9fc1165c0b7ede47ce20b48b3f8939b65e
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Plugins/Android64/libovrbody.so.meta
@@ -0,0 +1,96 @@
+fileFormatVersion: 2
+guid: c7bbd4711d939b643980b74029bc7422
+PluginImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  iconMap: {}
+  executionOrder: {}
+  defineConstraints: []
+  isPreloaded: 1
+  isOverridable: 1
+  isExplicitlyReferenced: 0
+  validateReferences: 1
+  platformData:
+  - first:
+      : Any
+    second:
+      enabled: 0
+      settings:
+        Exclude Android: 0
+        Exclude Editor: 1
+        Exclude Linux: 1
+        Exclude Linux64: 1
+        Exclude LinuxUniversal: 1
+        Exclude OSXUniversal: 1
+        Exclude Win: 1
+        Exclude Win64: 1
+  - first:
+      Android: Android
+    second:
+      enabled: 1
+      settings:
+        CPU: ARM64
+  - first:
+      Any: 
+    second:
+      enabled: 0
+      settings: {}
+  - first:
+      Editor: Editor
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+        DefaultValueInitialized: true
+        OS: AnyOS
+  - first:
+      Facebook: Win
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  - first:
+      Facebook: Win64
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: Linux
+    second:
+      enabled: 0
+      settings:
+        CPU: x86
+  - first:
+      Standalone: Linux64
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: LinuxUniversal
+    second:
+      enabled: 0
+      settings:
+        CPU: None
+  - first:
+      Standalone: OSXUniversal
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: Win
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: Win64
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Plugins/Android64/libovrgpuskinning.so b/Assets/Oculus/Avatar2/Plugins/Android64/libovrgpuskinning.so
new file mode 100644
index 0000000000000000000000000000000000000000..ba0e009330a4bf1596389871a77af99c66767b04
Binary files /dev/null and b/Assets/Oculus/Avatar2/Plugins/Android64/libovrgpuskinning.so differ
diff --git a/Assets/Oculus/Avatar2/Plugins/Android64/libovrgpuskinning.so.meta b/Assets/Oculus/Avatar2/Plugins/Android64/libovrgpuskinning.so.meta
new file mode 100644
index 0000000000000000000000000000000000000000..bce583ba2797cfdc2e741ff889b62b92b73e3906
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Plugins/Android64/libovrgpuskinning.so.meta
@@ -0,0 +1,96 @@
+fileFormatVersion: 2
+guid: 92c1e93c3b7ec15459b5c6811601b4cf
+PluginImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  iconMap: {}
+  executionOrder: {}
+  defineConstraints: []
+  isPreloaded: 0
+  isOverridable: 1
+  isExplicitlyReferenced: 0
+  validateReferences: 1
+  platformData:
+  - first:
+      '': Any
+    second:
+      enabled: 0
+      settings:
+        Exclude Android: 0
+        Exclude Editor: 1
+        Exclude Linux: 1
+        Exclude Linux64: 1
+        Exclude LinuxUniversal: 1
+        Exclude OSXUniversal: 1
+        Exclude Win: 1
+        Exclude Win64: 1
+  - first:
+      Android: Android
+    second:
+      enabled: 1
+      settings:
+        CPU: ARM64
+  - first:
+      Any: 
+    second:
+      enabled: 0
+      settings: {}
+  - first:
+      Editor: Editor
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+        DefaultValueInitialized: true
+        OS: AnyOS
+  - first:
+      Facebook: Win
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  - first:
+      Facebook: Win64
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: Linux
+    second:
+      enabled: 0
+      settings:
+        CPU: x86
+  - first:
+      Standalone: Linux64
+    second:
+      enabled: 0
+      settings:
+        CPU: x86_64
+  - first:
+      Standalone: LinuxUniversal
+    second:
+      enabled: 0
+      settings:
+        CPU: None
+  - first:
+      Standalone: OSXUniversal
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: Win
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: Win64
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Plugins/Android64/libovrplugintracking.so b/Assets/Oculus/Avatar2/Plugins/Android64/libovrplugintracking.so
new file mode 100644
index 0000000000000000000000000000000000000000..d28154f8238227ea3e9b3b7c431e66d45d08264f
Binary files /dev/null and b/Assets/Oculus/Avatar2/Plugins/Android64/libovrplugintracking.so differ
diff --git a/Assets/Oculus/Avatar2/Plugins/Android64/libovrplugintracking.so.meta b/Assets/Oculus/Avatar2/Plugins/Android64/libovrplugintracking.so.meta
new file mode 100644
index 0000000000000000000000000000000000000000..0b67373d1abb66cd470917d94e2724fc8d3e75fc
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Plugins/Android64/libovrplugintracking.so.meta
@@ -0,0 +1,96 @@
+fileFormatVersion: 2
+guid: 5731b2f6777438545b61336eca7c67ca
+PluginImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  iconMap: {}
+  executionOrder: {}
+  defineConstraints: []
+  isPreloaded: 0
+  isOverridable: 1
+  isExplicitlyReferenced: 0
+  validateReferences: 1
+  platformData:
+  - first:
+      '': Any
+    second:
+      enabled: 0
+      settings:
+        Exclude Android: 0
+        Exclude Editor: 1
+        Exclude Linux: 1
+        Exclude Linux64: 1
+        Exclude LinuxUniversal: 1
+        Exclude OSXUniversal: 1
+        Exclude Win: 1
+        Exclude Win64: 1
+  - first:
+      Android: Android
+    second:
+      enabled: 1
+      settings:
+        CPU: ARM64
+  - first:
+      Any: 
+    second:
+      enabled: 0
+      settings: {}
+  - first:
+      Editor: Editor
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+        DefaultValueInitialized: true
+        OS: AnyOS
+  - first:
+      Facebook: Win
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  - first:
+      Facebook: Win64
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: Linux
+    second:
+      enabled: 0
+      settings:
+        CPU: x86
+  - first:
+      Standalone: Linux64
+    second:
+      enabled: 0
+      settings:
+        CPU: x86_64
+  - first:
+      Standalone: LinuxUniversal
+    second:
+      enabled: 0
+      settings:
+        CPU: None
+  - first:
+      Standalone: OSXUniversal
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: Win
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: Win64
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Plugins/Win64.meta b/Assets/Oculus/Avatar2/Plugins/Win64.meta
new file mode 100644
index 0000000000000000000000000000000000000000..f62d8aed3c291eca6ee5a50f919cfe1e40bd4cae
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Plugins/Win64.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 36a17712b7a7b83458f44899fdc22adb
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Plugins/Win64/libovravatar2.dll b/Assets/Oculus/Avatar2/Plugins/Win64/libovravatar2.dll
new file mode 100644
index 0000000000000000000000000000000000000000..8daff340741919c96062c735d756b086c813b1f2
Binary files /dev/null and b/Assets/Oculus/Avatar2/Plugins/Win64/libovravatar2.dll differ
diff --git a/Assets/Oculus/Avatar2/Plugins/Win64/libovravatar2.dll.meta b/Assets/Oculus/Avatar2/Plugins/Win64/libovravatar2.dll.meta
new file mode 100644
index 0000000000000000000000000000000000000000..72842a790014d5880d3aac9b6ff9ab378c89bc21
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Plugins/Win64/libovravatar2.dll.meta
@@ -0,0 +1,106 @@
+fileFormatVersion: 2
+guid: bc6ac27601314d7418e96ef9fa31593c
+PluginImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  iconMap: {}
+  executionOrder: {}
+  defineConstraints: []
+  isPreloaded: 0
+  isOverridable: 1
+  isExplicitlyReferenced: 0
+  validateReferences: 1
+  platformData:
+  - first:
+      : Any
+    second:
+      enabled: 0
+      settings:
+        Exclude Android: 1
+        Exclude Editor: 0
+        Exclude Linux: 0
+        Exclude Linux64: 0
+        Exclude LinuxUniversal: 0
+        Exclude OSXUniversal: 0
+        Exclude Win: 1
+        Exclude Win64: 0
+        Exclude iOS: 1
+  - first:
+      Android: Android
+    second:
+      enabled: 0
+      settings:
+        CPU: ARMv7
+  - first:
+      Any: 
+    second:
+      enabled: 0
+      settings: {}
+  - first:
+      Editor: Editor
+    second:
+      enabled: 1
+      settings:
+        CPU: x86_64
+        DefaultValueInitialized: true
+        OS: Windows
+  - first:
+      Facebook: Win
+    second:
+      enabled: 0
+      settings:
+        CPU: None
+  - first:
+      Facebook: Win64
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: Linux
+    second:
+      enabled: 1
+      settings:
+        CPU: x86
+  - first:
+      Standalone: Linux64
+    second:
+      enabled: 1
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: LinuxUniversal
+    second:
+      enabled: 1
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: OSXUniversal
+    second:
+      enabled: 1
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: Win
+    second:
+      enabled: 0
+      settings:
+        CPU: None
+  - first:
+      Standalone: Win64
+    second:
+      enabled: 1
+      settings:
+        CPU: AnyCPU
+  - first:
+      iPhone: iOS
+    second:
+      enabled: 0
+      settings:
+        AddToEmbeddedBinaries: false
+        CPU: AnyCPU
+        CompileFlags: 
+        FrameworkDependencies: 
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Plugins/Win64/libovrbody.dll b/Assets/Oculus/Avatar2/Plugins/Win64/libovrbody.dll
new file mode 100644
index 0000000000000000000000000000000000000000..0a8c91ba6d06e11b87aff3ff934f418e203a0f02
Binary files /dev/null and b/Assets/Oculus/Avatar2/Plugins/Win64/libovrbody.dll differ
diff --git a/Assets/Oculus/Avatar2/Plugins/Win64/libovrbody.dll.meta b/Assets/Oculus/Avatar2/Plugins/Win64/libovrbody.dll.meta
new file mode 100644
index 0000000000000000000000000000000000000000..b7568ba902bd73f88c4edcb62e24c61bb270a834
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Plugins/Win64/libovrbody.dll.meta
@@ -0,0 +1,96 @@
+fileFormatVersion: 2
+guid: e9474f4a8384f1b47b4d662cc53e56b4
+PluginImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  iconMap: {}
+  executionOrder: {}
+  defineConstraints: []
+  isPreloaded: 1
+  isOverridable: 1
+  isExplicitlyReferenced: 0
+  validateReferences: 1
+  platformData:
+  - first:
+      : Any
+    second:
+      enabled: 0
+      settings:
+        Exclude Android: 1
+        Exclude Editor: 0
+        Exclude Linux: 0
+        Exclude Linux64: 0
+        Exclude LinuxUniversal: 0
+        Exclude OSXUniversal: 0
+        Exclude Win: 1
+        Exclude Win64: 0
+  - first:
+      Android: Android
+    second:
+      enabled: 0
+      settings:
+        CPU: ARMv7
+  - first:
+      Any: 
+    second:
+      enabled: 0
+      settings: {}
+  - first:
+      Editor: Editor
+    second:
+      enabled: 1
+      settings:
+        CPU: x86_64
+        DefaultValueInitialized: true
+        OS: Windows
+  - first:
+      Facebook: Win
+    second:
+      enabled: 0
+      settings:
+        CPU: None
+  - first:
+      Facebook: Win64
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: Linux
+    second:
+      enabled: 1
+      settings:
+        CPU: x86
+  - first:
+      Standalone: Linux64
+    second:
+      enabled: 1
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: LinuxUniversal
+    second:
+      enabled: 1
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: OSXUniversal
+    second:
+      enabled: 1
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: Win
+    second:
+      enabled: 0
+      settings:
+        CPU: None
+  - first:
+      Standalone: Win64
+    second:
+      enabled: 1
+      settings:
+        CPU: AnyCPU
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Plugins/Win64/libovrgpuskinning.dll b/Assets/Oculus/Avatar2/Plugins/Win64/libovrgpuskinning.dll
new file mode 100644
index 0000000000000000000000000000000000000000..1dffc5228fbbe80a9a459d5e0a6d0537294de105
Binary files /dev/null and b/Assets/Oculus/Avatar2/Plugins/Win64/libovrgpuskinning.dll differ
diff --git a/Assets/Oculus/Avatar2/Plugins/Win64/libovrgpuskinning.dll.meta b/Assets/Oculus/Avatar2/Plugins/Win64/libovrgpuskinning.dll.meta
new file mode 100644
index 0000000000000000000000000000000000000000..2de97c69b3da1ea03ccf31830f10c05982e60aee
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Plugins/Win64/libovrgpuskinning.dll.meta
@@ -0,0 +1,106 @@
+fileFormatVersion: 2
+guid: 14cb8a960e885b24487cbbc6f270ad96
+PluginImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  iconMap: {}
+  executionOrder: {}
+  defineConstraints: []
+  isPreloaded: 0
+  isOverridable: 1
+  isExplicitlyReferenced: 0
+  validateReferences: 1
+  platformData:
+  - first:
+      : Any
+    second:
+      enabled: 0
+      settings:
+        Exclude Android: 1
+        Exclude Editor: 0
+        Exclude Linux: 0
+        Exclude Linux64: 0
+        Exclude LinuxUniversal: 0
+        Exclude OSXUniversal: 0
+        Exclude Win: 1
+        Exclude Win64: 0
+        Exclude iOS: 1
+  - first:
+      Android: Android
+    second:
+      enabled: 0
+      settings:
+        CPU: ARMv7
+  - first:
+      Any: 
+    second:
+      enabled: 0
+      settings: {}
+  - first:
+      Editor: Editor
+    second:
+      enabled: 1
+      settings:
+        CPU: x86_64
+        DefaultValueInitialized: true
+        OS: Windows
+  - first:
+      Facebook: Win
+    second:
+      enabled: 0
+      settings:
+        CPU: None
+  - first:
+      Facebook: Win64
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: Linux
+    second:
+      enabled: 1
+      settings:
+        CPU: x86
+  - first:
+      Standalone: Linux64
+    second:
+      enabled: 1
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: LinuxUniversal
+    second:
+      enabled: 1
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: OSXUniversal
+    second:
+      enabled: 1
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: Win
+    second:
+      enabled: 0
+      settings:
+        CPU: None
+  - first:
+      Standalone: Win64
+    second:
+      enabled: 1
+      settings:
+        CPU: AnyCPU
+  - first:
+      iPhone: iOS
+    second:
+      enabled: 0
+      settings:
+        AddToEmbeddedBinaries: false
+        CPU: AnyCPU
+        CompileFlags: 
+        FrameworkDependencies: 
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Plugins/Win64/libovrplugintracking.dll b/Assets/Oculus/Avatar2/Plugins/Win64/libovrplugintracking.dll
new file mode 100644
index 0000000000000000000000000000000000000000..67a864550e5c8d67d73499ee454d0f3aeb0dd477
Binary files /dev/null and b/Assets/Oculus/Avatar2/Plugins/Win64/libovrplugintracking.dll differ
diff --git a/Assets/Oculus/Avatar2/Plugins/Win64/libovrplugintracking.dll.meta b/Assets/Oculus/Avatar2/Plugins/Win64/libovrplugintracking.dll.meta
new file mode 100644
index 0000000000000000000000000000000000000000..6bec1e62790209c18a85cb03f01ddc49ce62b9db
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Plugins/Win64/libovrplugintracking.dll.meta
@@ -0,0 +1,106 @@
+fileFormatVersion: 2
+guid: 5100cddc1c144ab418307e61ffb472df
+PluginImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  iconMap: {}
+  executionOrder: {}
+  defineConstraints: []
+  isPreloaded: 0
+  isOverridable: 1
+  isExplicitlyReferenced: 0
+  validateReferences: 1
+  platformData:
+  - first:
+      : Any
+    second:
+      enabled: 0
+      settings:
+        Exclude Android: 1
+        Exclude Editor: 0
+        Exclude Linux: 0
+        Exclude Linux64: 0
+        Exclude LinuxUniversal: 0
+        Exclude OSXUniversal: 0
+        Exclude Win: 1
+        Exclude Win64: 0
+        Exclude iOS: 1
+  - first:
+      Android: Android
+    second:
+      enabled: 0
+      settings:
+        CPU: ARMv7
+  - first:
+      Any: 
+    second:
+      enabled: 0
+      settings: {}
+  - first:
+      Editor: Editor
+    second:
+      enabled: 1
+      settings:
+        CPU: x86_64
+        DefaultValueInitialized: true
+        OS: Windows
+  - first:
+      Facebook: Win
+    second:
+      enabled: 0
+      settings:
+        CPU: None
+  - first:
+      Facebook: Win64
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: Linux
+    second:
+      enabled: 1
+      settings:
+        CPU: x86
+  - first:
+      Standalone: Linux64
+    second:
+      enabled: 1
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: LinuxUniversal
+    second:
+      enabled: 1
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: OSXUniversal
+    second:
+      enabled: 1
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: Win
+    second:
+      enabled: 0
+      settings:
+        CPU: None
+  - first:
+      Standalone: Win64
+    second:
+      enabled: 1
+      settings:
+        CPU: AnyCPU
+  - first:
+      iPhone: iOS
+    second:
+      enabled: 0
+      settings:
+        AddToEmbeddedBinaries: false
+        CPU: AnyCPU
+        CompileFlags: 
+        FrameworkDependencies: 
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts.meta b/Assets/Oculus/Avatar2/Scripts.meta
new file mode 100644
index 0000000000000000000000000000000000000000..78fb9b4d053f1d8bd37e9994f1a3930d6c723ccf
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 5f68bd1e82374754fb1bb16367416c87
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/AssetTypes.meta b/Assets/Oculus/Avatar2/Scripts/AssetTypes.meta
new file mode 100644
index 0000000000000000000000000000000000000000..7cc00cb9c027cf5852cbdbae0633a599d3e58842
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/AssetTypes.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 5feeb678b1ecde148874ef1ec3aabfac
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarAssetBase.cs b/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarAssetBase.cs
new file mode 100644
index 0000000000000000000000000000000000000000..9ce10600ec15ec6803fe1bfd7a5cb9ece20cd85b
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarAssetBase.cs
@@ -0,0 +1,108 @@
+using System;
+using System.Collections;
+
+/// @file OvrAvatarAssetBase.cs
+
+namespace Oculus.Avatar2
+{
+    /**
+     * Parent class for loading avatar assets.
+     * @see OvrAvatarImage
+     * @see OvrAvatarPrimitive
+     */
+    public abstract class OvrAvatarAssetBase : IDisposable
+    {
+        /// Unique global asset ID.
+        public readonly CAPI.ovrAvatar2Id assetId;
+
+        /// Asset type.
+        public abstract string typeName { get; }
+
+        /// Asset name.
+        public abstract string assetName { get; }
+
+        /// True if asset has finished loading, else false.
+        public virtual bool isLoaded { get; protected set; } = false;
+
+        /// True if asset loading was cancelled, else false.
+        public bool isCancelled { get; protected set; } = false;
+
+        /**
+         * Constructs and initializes an avatar asset.
+         * @param assetId   ID to assign to this asset.
+         */
+        protected OvrAvatarAssetBase(CAPI.ovrAvatar2Id assetId)
+        {
+            this.assetId = assetId;
+            if (OvrAvatarManager.initialized)
+            {
+                OvrAvatarManager.AddAsset(this);
+            }
+        }
+
+        // If disposing == true, safe to dispose managed resources. Otherwise only unmanaged resources should be disposed
+        protected abstract void Dispose(bool disposing);
+
+        [System.Diagnostics.CodeAnalysis.SuppressMessage("Design", "CA1063:Implement IDisposable Correctly", Justification = "Bad Linter")]
+        public void Dispose()
+        {
+            isLoaded = false;
+
+            if (!isCancelled)
+            {
+                CancelLoad();
+            }
+
+            if (OvrAvatarManager.initialized)
+            {
+                OvrAvatarManager.RemoveAsset(this);
+            }
+
+            Dispose(true);
+            GC.SuppressFinalize(this);
+        }
+
+        ~OvrAvatarAssetBase()
+        {
+            Dispose(false);
+        }
+
+        /**
+         * Coroutine to wait until an asset has finished loading.
+         * Each frame it checks for load completion or cancellation.
+         * @see isLoaded
+         * @see isCancelled
+         */
+        public IEnumerator WaitForAssetToLoad()
+        {
+            while (!isLoaded && !isCancelled)
+            {
+                yield return null;
+            }
+        }
+
+        /**
+         * Cancel the loading of this asset.
+         * @see isCancelled
+         */
+        public void CancelLoad()
+        {
+            isCancelled = true;
+            isLoaded = false;
+            _ExecuteCancel();
+        }
+
+        abstract protected void _ExecuteCancel();
+    }
+
+    public abstract class OvrAvatarAsset<T> : OvrAvatarAssetBase where T : struct
+    {
+        public readonly T data;
+
+        protected OvrAvatarAsset(CAPI.ovrAvatar2Id assetId, T data) : base(assetId)
+        {
+            this.data = data;
+        }
+    }
+
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarAssetBase.cs.meta b/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarAssetBase.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..ca5ff52170fabb1f86db358d5b38c111eec6a522
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarAssetBase.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 7be50a58b220a4e4e8605502c341b926
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarComputeSkinnedPrimitive.cs b/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarComputeSkinnedPrimitive.cs
new file mode 100644
index 0000000000000000000000000000000000000000..8d8a0f9061ba8e4a0beadcf857eaa05c2cfe2f36
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarComputeSkinnedPrimitive.cs
@@ -0,0 +1,874 @@
+// Due to Unity bug (fixed in version 2021.2), copy to a native array then copy native array to ComputeBuffer in one chunk
+// (ComputeBuffer.SetData erases previously set data)
+// https://issuetracker.unity3d.com/issues/partial-updates-of-computebuffer-slash-graphicsbuffer-using-setdata-dont-preserve-existing-data-when-using-opengl-es
+#if UNITY_2021_2_OR_NEWER
+        #define COMPUTE_BUFFER_PARTIAL_UPDATE_ALLOWED
+#endif
+
+using Oculus.Skinning;
+
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+
+using Unity.Collections;
+using Unity.Collections.LowLevel.Unsafe;
+
+using UnityEngine;
+using UnityEngine.Profiling;
+
+namespace Oculus.Avatar2
+{
+    public sealed class OvrAvatarComputeSkinnedPrimitive : IDisposable
+    {
+        private const string LOG_SCOPE = "OvrAvatarComputeSkinnedPrimitive";
+
+        internal class StaticMetaData
+        {
+            // Number of verts in mesh affected by at least one morph target
+            public int numMorphedVerts;
+            public int numVertsNoJointsOrMorphs;
+
+            public Vector3 positionOutputScale;
+            public Vector3 positionOutputBias;
+
+            public CAPI.ovrGpuSkinningEncodingPrecision jointIndicesPrecision;
+            public CAPI.ovrGpuSkinningEncodingPrecision inputPositionPrecision;
+            public CAPI.ovrGpuSkinningEncodingPrecision morphDeltasPrecision;
+            public GpuSkinningConfiguration.TexturePrecision outputPositionPrecision;
+        }
+
+        [StructLayout(LayoutKind.Sequential)]
+        private struct Vector4UInt
+        {
+            public uint x, y, z, w;
+        }
+
+        [StructLayout(LayoutKind.Sequential)]
+        private struct ComputeBufferStaticMeshHeader {
+            // Static data offsets
+
+            // Offset to an array of positions(x), normals(y), tangents(z), joint weights (w)
+            public Vector4UInt attributesAndJointWeightsOffsetBytes;
+
+            // Morph target deltas offset bytes(x), numMorphs(y), numMorphedVertices(z), joint indices(w)
+            public Vector4UInt morphTargetInfoAndJointIndicesOffsetBytes;
+
+            // Offset of an array of output indices (x) (yzw) - unused
+            public Vector4UInt outputIndexOffset;
+
+            public Vector4 vertexInputPositionBias; // Float4s for alignment, w unused (is this needed?)
+            public Vector4 vertexInputPositionScale;
+            public Vector4 vertexOutputPositionBias; // Float4s for alignment, w unused (is this needed?)
+            public Vector4 vertexOutputPositionScale;
+
+            public Vector4 morphTargetsPosRange;
+            public Vector4 morphTargetsNormRange;
+            public Vector4 morphTargetsTanRange;
+
+            public int PositionsOffset => (int)attributesAndJointWeightsOffsetBytes.x;
+            public int NormalsOffset => (int)attributesAndJointWeightsOffsetBytes.y;
+            public int TangentsOffset => (int)attributesAndJointWeightsOffsetBytes.z;
+            public int JointWeightsOffset => (int)attributesAndJointWeightsOffsetBytes.w;
+            public int MorphTargetDeltasOffset => (int)morphTargetInfoAndJointIndicesOffsetBytes.x;
+            public int JointIndicesOffset => (int)morphTargetInfoAndJointIndicesOffsetBytes.w;
+
+            public int OutputIndicesOffset => (int)outputIndexOffset.x;
+        }
+
+        public ComputeBuffer StaticDataComputeBuffer { get; private set; }
+        internal StaticMetaData SourceMetaData { get; private set; }
+
+        public bool IsLoading => _buildSlice.IsValid;
+
+        private OvrTime.SliceHandle _buildSlice;
+
+        public OvrAvatarComputeSkinnedPrimitive(
+            string name,
+            int vertexCount,
+            IntPtr neutralPositions,
+            IntPtr neutralNormals,
+            IntPtr neutralTangents,
+            int morphTargetCount,
+            IntPtr deltaPosPtr,
+            IntPtr deltaNormPtr,
+            IntPtr deltaTanPtr,
+            BoneWeight[] boneWeights,
+            // TODO: adjust scoping to give direct access to MeshInfo
+            Action neutralPoseCallback,
+            Action finishCallback)
+        {
+            var gpuSkinningConfig = GpuSkinningConfiguration.Instance;
+
+            _buildSlice = OvrTime.Slice(
+                BuildBuffers(
+                    gpuSkinningConfig,
+                    name,
+                    vertexCount,
+                    neutralPositions,
+                    neutralNormals,
+                    neutralTangents,
+                    morphTargetCount,
+                    deltaPosPtr,
+                    deltaNormPtr,
+                    deltaTanPtr,
+                    boneWeights,
+                    neutralPoseCallback,
+                    finishCallback)
+            );
+        }
+
+        private IEnumerator<OvrTime.SliceStep> BuildBuffers(
+            GpuSkinningConfiguration gpuSkinningConfig,
+            string name,
+            int vertexCount,
+            IntPtr neutralPositions,
+            IntPtr neutralNormals,
+            IntPtr neutralTangents,
+            int morphTargetCount,
+            IntPtr deltaPosPtr,
+            IntPtr deltaNormPtr,
+            IntPtr deltaTanPtr,
+            BoneWeight[] boneWeights,
+            Action neutralPoseCallback,
+            Action finishCallback)
+        {
+            // TODO: Some of this work can be moved off the main thread (everything except creating Unity.Objects)
+            yield return OvrTime.SliceStep.Stall;
+
+            bool hasTangents = neutralTangents != IntPtr.Zero;
+
+            // Native arrays for temporary data
+            var jointIndices = new NativeArray<CAPI.ovrAvatar2Vector4us>();
+            var jointWeights = new NativeArray<CAPI.ovrAvatar2Vector4f>();
+            var jointWeightsSourceData = new NativeArray<byte>();
+            var jointIndicesSourceData = new NativeArray<byte>();
+            var morphBufferData = new NativeArray<byte>();
+            var neutralNormalsSourceData = new NativeArray<byte>();
+            var neutralPositionsSourceData = new NativeArray<byte>();
+            var neutralTangentsSourceData = new NativeArray<byte>();
+            var vertexReorderBuffer = new NativeArray<ushort>();
+
+            try
+            {
+                Profiler.BeginSample("OvrAvatarComputeSkinnedPrimitive.GetJointIndicesAndWeights");
+                GetJointIndicesAndWeights(
+                    vertexCount,
+                    boneWeights,
+                    out jointIndices,
+                    out jointWeights);
+                Profiler.EndSample();
+
+                yield return OvrTime.SliceStep.Stall;
+                // Grab the morph target (and vertex reordering) info
+                Profiler.BeginSample("OvrAvatarComputeSkinnedPrimitive.CreateMorphTargetSourceData");
+                CreateMorphTargetSourceData(
+                    name,
+                    vertexCount,
+                    morphTargetCount,
+                    hasTangents,
+                    deltaPosPtr,
+                    deltaNormPtr,
+                    deltaTanPtr,
+                    jointWeights,
+                    gpuSkinningConfig.SourceMorphFormat,
+                    out CAPI.ovrGpuMorphTargetBufferDesc morphBufferDesc,
+                    out morphBufferData,
+                    out vertexReorderBuffer);
+
+                Profiler.EndSample();
+
+                // Grab the neutral pose info
+                yield return OvrTime.SliceStep.Stall;
+                Profiler.BeginSample("OvrAvatarComputeSkinnedPrimitive.CreateNeutralPositionsSourceData");
+                CreateNeutralPositionsSourceData(
+                    vertexCount,
+                    neutralPositions,
+                    vertexReorderBuffer,
+                    GpuSkinningConfiguration.TexturePrecision.Float, // Hard coding float precision
+                    out CAPI.ovrGpuSkinningBufferDesc neutralPositionsDesc,
+                    out neutralPositionsSourceData);
+                Profiler.EndSample();
+
+                yield return OvrTime.SliceStep.Stall;
+                Profiler.BeginSample("OvrAvatarComputeSkinnedPrimitive.CreateNeutralNormalsSourceData");
+                CreateNeutralNormalsSourceData(
+                    vertexCount,
+                    neutralNormals,
+                    vertexReorderBuffer,
+                    GpuSkinningConfiguration.TexturePrecision.Snorm10, // Hard coding 10-10-10-2
+                    out CAPI.ovrGpuSkinningBufferDesc neutralNormalsDesc,
+                    out neutralNormalsSourceData);
+                Profiler.EndSample();
+
+                if (hasTangents)
+                {
+                    yield return OvrTime.SliceStep.Stall;
+                    Profiler.BeginSample("OvrAvatarComputeSkinnedPrimitive.CreateNeutralTangentsSourceData");
+                    CreateNeutralTangentsSourceData(
+                        vertexCount,
+                        neutralTangents,
+                        vertexReorderBuffer,
+                        GpuSkinningConfiguration.TexturePrecision.Snorm10, // Hard coding 10-10-10-2
+                        out CAPI.ovrGpuSkinningBufferDesc neutralTangentsDesc,
+                        out neutralTangentsSourceData);
+                    Profiler.EndSample();
+                }
+
+                neutralPoseCallback?.Invoke();
+
+                // Grab the joint weights and indices info
+                yield return OvrTime.SliceStep.Stall;
+                Profiler.BeginSample("OvrAvatarComputeSkinnedPrimitive.CreateJointWeightsSourceData");
+                CreateJointWeightsSourceData(
+                    vertexCount,
+                    jointWeights,
+                    vertexReorderBuffer,
+                    out jointWeightsSourceData);
+                Profiler.EndSample();
+
+                yield return OvrTime.SliceStep.Stall;
+                Profiler.BeginSample("OvrAvatarComputeSkinnedPrimitive.CreateJointIndicesSourceData");
+                CreateJointIndicesSourceData(
+                    vertexCount,
+                    jointIndices,
+                    vertexReorderBuffer,
+                    CAPI.ovrGpuSkinningEncodingPrecision.ENCODING_PRECISION_UINT8, // hard coding UINT8 for now < 256 joints
+                    out CAPI.ovrGpuSkinningBufferDesc jointIndicesBufferDesc,
+                    out jointIndicesSourceData);
+                Profiler.EndSample();
+
+                // Now pull out relevant meta data and create compute buffer
+                yield return OvrTime.SliceStep.Stall;
+                Profiler.BeginSample("ovrAvatarComputeSkinnedPrimitive.CreateStaticDataComputeBuffer");
+                StaticDataComputeBuffer = CreateStaticDataComputeBuffer(
+                    gpuSkinningConfig,
+                    morphBufferDesc,
+                    morphBufferData,
+                    neutralPositionsSourceData,
+                    neutralNormalsSourceData,
+                    neutralTangentsSourceData,
+                    jointWeightsSourceData,
+                    jointIndicesSourceData,
+                    vertexReorderBuffer,
+                    out Vector3 posOutputScale,
+                    out Vector3 posOutputBias);
+
+                yield return OvrTime.SliceStep.Stall;
+                Profiler.BeginSample("OvrAvatarComputeSkinnedPrimitive.PopulateMetadata");
+                SourceMetaData = CreateExternalMetadata(
+                    morphBufferDesc,
+                    neutralPositionsDesc,
+                    jointIndicesBufferDesc,
+                    gpuSkinningConfig.SkinnerOutputFormat,
+                    posOutputScale,
+                    posOutputBias);
+                Profiler.EndSample();
+
+                Debug.Assert(neutralPositionsSourceData.IsCreated && vertexReorderBuffer.IsCreated);
+                Debug.Assert(neutralNormalsSourceData.IsCreated);
+            } finally {
+                // Clear the temporary native arrays if they were created
+                morphBufferData.Reset();
+                jointWeightsSourceData.Reset();
+                jointIndicesSourceData.Reset();
+                neutralTangentsSourceData.Reset();
+                jointIndices.Reset();
+                jointWeights.Reset();
+                neutralPositionsSourceData.Reset();
+                neutralNormalsSourceData.Reset();
+                vertexReorderBuffer.Reset();
+            }
+
+            finishCallback?.Invoke();
+
+            // Mark loading as finished/cleanup
+            _buildSlice.Clear();
+        }
+
+        public void Dispose()
+        {
+            Dispose(true);
+            GC.SuppressFinalize(this);
+        }
+
+        private void Dispose(bool isDispose)
+        {
+            if (isDispose)
+            {
+                if (_buildSlice.IsValid)
+                {
+                    _buildSlice.Cancel();
+                }
+
+                StaticDataComputeBuffer?.Dispose();
+            }
+            else
+            {
+                if (_buildSlice.IsValid)
+                {
+                    OvrAvatarLog.LogError("Build buffers slice still valid when finalized", LOG_SCOPE);
+
+                    // Prevent OvrTime from stalling
+                    _buildSlice.EmergencyShutdown();
+                }
+
+                if (StaticDataComputeBuffer != null)
+                {
+                    OvrAvatarLog.LogError($"OvrAvatarComputeSkinnedPrimitive was not disposed before being destroyed", LOG_SCOPE);
+                }
+            }
+
+            StaticDataComputeBuffer = null;
+            SourceMetaData = null;
+        }
+
+        ~OvrAvatarComputeSkinnedPrimitive()
+        {
+            Dispose(false);
+        }
+
+        private static int GetUintAlignedLength<T>(int numEntries) where T : struct
+        {
+            // Since this will be writing to a ByteAddressBuffer in the ComputeBuffer,
+            // and a ByteAddressBuffer is basically a "bag of uints", each but if data written to the ComputeBuffer
+            // must have a byte length that is a multiple of the size of a uint. Some arrays may need additional
+            // padding when written to the ComputeBuffer
+            // Pad the vertex count so that the total byte size of vertexReorderBuffer is a multiple of 4
+            const int sizeOfUint = sizeof(uint);
+            int sizeOfType = UnsafeUtility.SizeOf<T>();
+            var byteSize = numEntries * sizeOfType;
+            int numUintsNeeded = (byteSize + sizeOfUint - 1) / sizeOfUint;
+
+            return numUintsNeeded * sizeOfUint / sizeOfType;
+        }
+
+        private static void CreateMorphTargetSourceData(
+            string name,
+            int vertexCount,
+            int morphTargetCount,
+            bool hasTangents,
+            IntPtr deltaPosPtr,
+            IntPtr deltaNormPtr,
+            IntPtr deltaTanPtr,
+            in NativeArray<CAPI.ovrAvatar2Vector4f> jointWeights,
+            GpuSkinningConfiguration.TexturePrecision morphSrcPrecision,
+            out CAPI.ovrGpuMorphTargetBufferDesc bufferDesc,
+            out NativeArray<byte> morphStaticData,
+            out NativeArray<UInt16> vertexReorderBuffer)
+        {
+            // Pad the vertex count so that the total byte size of vertexReorderBuffer is a multiple of 4
+            var paddedLength = GetUintAlignedLength<UInt16>(vertexCount);
+            vertexReorderBuffer = new NativeArray<UInt16>(paddedLength, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
+
+            IntPtr vertexReorderPtr = vertexReorderBuffer.GetIntPtr();
+            IntPtr jointWeightsPtr = jointWeights.GetIntPtr();
+
+            if (hasTangents)
+            {
+                bufferDesc = CAPI.OvrGpuSkinning_MorphTargetGetTextureBufferMetaDataWithTangents(
+                    (uint)vertexCount,
+                    (uint)morphTargetCount,
+                    morphSrcPrecision.GetOvrPrecision(),
+                    deltaPosPtr,
+                    deltaNormPtr,
+                    deltaTanPtr,
+                    jointWeightsPtr,
+                    vertexReorderPtr);
+            }
+            else
+            {
+                bufferDesc = CAPI.OvrGpuSkinning_MorphTargetGetTextureBufferMetaData(
+                    (uint)vertexCount,
+                    (uint)morphTargetCount,
+                    morphSrcPrecision.GetOvrPrecision(),
+                    deltaPosPtr,
+                    deltaNormPtr,
+                    jointWeightsPtr,
+                    vertexReorderPtr);
+            }
+
+            if (bufferDesc.numMorphedVerts <= 0)
+            {
+                OvrAvatarLog.LogDebug($"Primitive ({name}) has morph targets, but no affected verts", LOG_SCOPE);
+                morphStaticData = default;
+                return;
+            }
+
+            // Create a native array and fill it with data
+            paddedLength = GetUintAlignedLength<byte>((int)bufferDesc.bufferDataSize);
+            morphStaticData = new NativeArray<byte>(paddedLength, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
+            IntPtr dataPtr = morphStaticData.GetIntPtr();
+
+            if (hasTangents)
+            {
+                if (!CAPI.OvrGpuSkinning_MorphTargetEncodeBufferDataWithTangents(bufferDesc, vertexReorderPtr,
+                    deltaPosPtr, deltaNormPtr, deltaTanPtr, dataPtr))
+                {
+                    OvrAvatarLog.LogError("failed to get morph data", LOG_SCOPE);
+                }
+            }
+            else
+            {
+                if (!CAPI.OvrGpuSkinning_MorphTargetEncodeBufferData(bufferDesc, vertexReorderPtr, deltaPosPtr,
+                    deltaNormPtr, dataPtr))
+                {
+                    OvrAvatarLog.LogError("failed to get morph data", LOG_SCOPE);
+                }
+            }
+        } // end method
+
+        private static void CreateNeutralPositionsSourceData(
+            int vertexCount,
+            IntPtr neutralPosPtr,
+            in NativeArray<UInt16> vertexReorder,
+            GpuSkinningConfiguration.TexturePrecision precision,
+            out CAPI.ovrGpuSkinningBufferDesc bufferDesc,
+            out NativeArray<byte> neutralPositionsSourceData)
+        {
+            bufferDesc = CAPI.OvrGpuSkinning_NeutralPositionsBufferDesc(
+                (uint)vertexCount,
+                precision.GetOvrPrecision());
+
+            // Create a native array and fill it with data
+            int paddedLength = GetUintAlignedLength<byte>((int)bufferDesc.dataSize);
+            neutralPositionsSourceData = new NativeArray<byte>(paddedLength, Allocator.Persistent);
+
+            IntPtr dataPtr = neutralPositionsSourceData.GetIntPtr();
+            IntPtr vertexReorderPtr = vertexReorder.GetIntPtr();
+
+            if (!CAPI.OvrGpuSkinning_EncodeNeutralPositionsBufferData(bufferDesc, neutralPosPtr, vertexReorderPtr, dataPtr))
+            {
+                OvrAvatarLog.LogError("failed to get neutral pose position data", LOG_SCOPE);
+            }
+        } // end method
+
+        private static void CreateNeutralNormalsSourceData(
+            int vertexCount,
+            IntPtr neutralNormPtr,
+            in NativeArray<UInt16> vertexReorder,
+            GpuSkinningConfiguration.TexturePrecision precision,
+            out CAPI.ovrGpuSkinningBufferDesc bufferDesc,
+            out NativeArray<byte> neutralNormalsSourceData)
+        {
+            bufferDesc = CAPI.OvrGpuSkinning_NeutralNormalsBufferDesc(
+                (uint)vertexCount,
+                precision.GetOvrPrecision());
+
+            // Create a native array and fill it with data
+            int paddedLength = GetUintAlignedLength<byte>((int)bufferDesc.dataSize);
+            neutralNormalsSourceData = new NativeArray<byte>(paddedLength, Allocator.Persistent);
+
+            IntPtr dataPtr = neutralNormalsSourceData.GetIntPtr();
+            IntPtr vertexReorderPtr = vertexReorder.GetIntPtr();
+
+            if (!CAPI.OvrGpuSkinning_EncodeNeutralNormalsBufferData(bufferDesc, neutralNormPtr, vertexReorderPtr, dataPtr))
+            {
+                OvrAvatarLog.LogError("failed to get neutral pose normal data", LOG_SCOPE);
+            }
+        } // end method
+
+        private static void CreateNeutralTangentsSourceData(
+            int vertexCount,
+            IntPtr neutralTanPtr,
+            in NativeArray<UInt16> vertexReorder,
+            GpuSkinningConfiguration.TexturePrecision precision,
+            out CAPI.ovrGpuSkinningBufferDesc bufferDesc,
+            out NativeArray<byte> neutralTangentsSourceData)
+        {
+            bufferDesc = CAPI.OvrGpuSkinning_NeutralTangentsBufferDesc(
+                (uint)vertexCount,
+                precision.GetOvrPrecision());
+
+            // Create a native array and fill it with data
+            int paddedLength = GetUintAlignedLength<byte>((int)bufferDesc.dataSize);
+            neutralTangentsSourceData = new NativeArray<byte>(paddedLength, Allocator.Persistent);
+
+            IntPtr dataPtr = neutralTangentsSourceData.GetIntPtr();
+            IntPtr vertexReorderPtr = vertexReorder.GetIntPtr();
+
+            if (!CAPI.OvrGpuSkinning_EncodeNeutralTangentsBufferData(bufferDesc, neutralTanPtr, vertexReorderPtr, dataPtr))
+            {
+                OvrAvatarLog.LogError("failed to get neutral pose tangent data", LOG_SCOPE);
+            }
+        } // end method
+
+        private static void CreateJointWeightsSourceData(
+            int vertexCount,
+            in NativeArray<CAPI.ovrAvatar2Vector4f> jointWeights,
+            in NativeArray<UInt16> vertexReorder,
+            out NativeArray<byte> jointWeightsSourceData)
+        {
+            CAPI.ovrGpuSkinningBufferDesc desc = CAPI.OvrGpuSkinning_JointWeightsBufferDesc((uint)vertexCount);
+
+            // Create a native array and fill it with data
+            int paddedLength = GetUintAlignedLength<byte>((int)desc.dataSize);
+            jointWeightsSourceData = new NativeArray<byte>(paddedLength, Allocator.Persistent);
+
+            IntPtr dataPtr;
+            IntPtr vertexReorderPtr;
+            IntPtr jointWeightsPtr;
+
+            unsafe
+            {
+                dataPtr = (IntPtr)jointWeightsSourceData.GetUnsafePtr();
+                vertexReorderPtr = (IntPtr)vertexReorder.GetUnsafePtr();
+                jointWeightsPtr = (IntPtr)jointWeights.GetUnsafePtr();
+            }
+
+            if (!CAPI.OvrGpuSkinning_EncodeJointWeightsBufferData(desc,  jointWeightsPtr, vertexReorderPtr, dataPtr))
+            {
+                OvrAvatarLog.LogError("failed to get joint wegihts data", LOG_SCOPE);
+            }
+        } // end method
+
+        private static void CreateJointIndicesSourceData(
+            int vertexCount,
+            in NativeArray<CAPI.ovrAvatar2Vector4us> jointIndices,
+            in NativeArray<UInt16> vertexReorder,
+            CAPI.ovrGpuSkinningEncodingPrecision encodingPrecision,
+            out CAPI.ovrGpuSkinningBufferDesc bufferDesc,
+            out NativeArray<byte> jointIndicesSourceData)
+        {
+            bufferDesc = CAPI.OvrGpuSkinning_JointIndicesBufferDesc((uint)vertexCount, encodingPrecision);
+
+            // Create a native array and fill it with data
+            int paddedLength = GetUintAlignedLength<byte>((int)bufferDesc.dataSize);
+            jointIndicesSourceData = new NativeArray<byte>(paddedLength, Allocator.Persistent);
+
+            IntPtr dataPtr;
+            IntPtr vertexReorderPtr;
+            IntPtr jointIndicesPtr;
+            unsafe
+            {
+                dataPtr = (IntPtr)jointIndicesSourceData.GetUnsafePtr();
+                vertexReorderPtr = (IntPtr)vertexReorder.GetUnsafePtr();
+                jointIndicesPtr = (IntPtr)jointIndices.GetUnsafePtr();
+            }
+
+            if (!CAPI.OvrGpuSkinning_EncodeJointIndicesBufferData(bufferDesc, jointIndicesPtr, vertexReorderPtr, dataPtr))
+            {
+                OvrAvatarLog.LogError("failed to get joint indices data", LOG_SCOPE);
+            }
+        } // end method
+
+
+        private static StaticMetaData CreateExternalMetadata(
+            in CAPI.ovrGpuMorphTargetBufferDesc morphBufferDesc,
+            in CAPI.ovrGpuSkinningBufferDesc neutralPositionBufferDesc,
+            in CAPI.ovrGpuSkinningBufferDesc jointIndicesBufferDesc,
+            GpuSkinningConfiguration.TexturePrecision outputPosPrecision,
+            in Vector3 posOutputScale,
+            in Vector3 posOutputBias)
+        {
+            StaticMetaData externalMeta = new StaticMetaData
+            {
+                numMorphedVerts = (int)morphBufferDesc.numMorphedVerts,
+                numVertsNoJointsOrMorphs = (int)morphBufferDesc.numMorphedVertsNoJoints,
+                morphDeltasPrecision = morphBufferDesc.encodingPrecision,
+                inputPositionPrecision = neutralPositionBufferDesc.precision,
+                outputPositionPrecision = outputPosPrecision,
+                jointIndicesPrecision = jointIndicesBufferDesc.precision,
+                positionOutputBias = posOutputBias,
+                positionOutputScale = posOutputScale,
+            };
+
+            return externalMeta;
+        }
+
+        private static void FillStaticDataComputeBufferViaBackingBuffer(
+            ComputeBufferStaticMeshHeader header,
+            in NativeArray<byte> rawMorphData,
+            in NativeArray<byte> rawNeutralPosData,
+            in NativeArray<byte> rawNeutralNormData,
+            in NativeArray<byte> rawNeutralTanData,
+            in NativeArray<byte> rawJointWeightsData,
+            in NativeArray<byte> rawJointIndicesData,
+            in NativeArray<UInt16> vertexReorderBuffer,
+            ComputeBuffer bufferToFill,
+            int bufferSizeBytes)
+        {
+            using(var backingBuffer = new NativeArray<byte>(bufferSizeBytes, Allocator.Temp, NativeArrayOptions.UninitializedMemory))
+            {
+                // Data layout is header -> neutral pose -> morphs deltas (if applicable) -> joint weights -> joint indices (if applicable)
+                backingBuffer.ReinterpretStore(0, header);
+                CopyNativeArrayToBackingBuffer(backingBuffer, rawNeutralPosData, header.PositionsOffset);
+                CopyNativeArrayToBackingBuffer(backingBuffer, rawNeutralNormData, header.NormalsOffset);
+
+                if (rawNeutralTanData.Length > 0)
+                {
+                    CopyNativeArrayToBackingBuffer(backingBuffer, rawNeutralTanData, header.TangentsOffset);
+                }
+
+                if (rawMorphData.Length > 0)
+                {
+                    CopyNativeArrayToBackingBuffer(backingBuffer, rawMorphData, header.MorphTargetDeltasOffset);
+                }
+
+                if (rawJointIndicesData.Length > 0)
+                {
+                    CopyNativeArrayToBackingBuffer(backingBuffer, rawJointWeightsData, header.JointWeightsOffset);
+                    CopyNativeArrayToBackingBuffer(backingBuffer, rawJointIndicesData, header.JointIndicesOffset);
+                }
+
+                CopyNativeArrayToBackingBuffer(backingBuffer, vertexReorderBuffer, header.OutputIndicesOffset);
+
+                // Now copy over to compute buffer as one whole copy
+                SetComputeBufferDataFromNativeArray(bufferToFill, backingBuffer, 0);
+            }
+        }
+
+         private static void FillStaticDataComputeBufferViaPartialUpdates(
+            ComputeBufferStaticMeshHeader header,
+            in NativeArray<byte> rawMorphData,
+            in NativeArray<byte> rawNeutralPosData,
+            in NativeArray<byte> rawNeutralNormData,
+            in NativeArray<byte> rawNeutralTanData,
+            in NativeArray<byte> rawJointWeightsData,
+            in NativeArray<byte> rawJointIndicesData,
+            in NativeArray<UInt16> vertexReorderBuffer,
+            int sizeOfHeaderBytes,
+            ComputeBuffer bufferToFill)
+        {
+            // Convert header to native array
+            NativeArray<byte> headerAsBytes = new NativeArray<byte>(
+                sizeOfHeaderBytes,
+                Allocator.Temp,
+                NativeArrayOptions.UninitializedMemory);
+            headerAsBytes.ReinterpretStore(0, header);
+
+            try
+            {
+                // Data layout is header -> neutral pose -> morphs deltas (if applicable) -> joint weights -> joint indices (if applicable)
+                SetComputeBufferDataFromNativeArray(bufferToFill, headerAsBytes, 0);
+                SetComputeBufferDataFromNativeArray(bufferToFill, rawNeutralPosData, header.PositionsOffset);
+                SetComputeBufferDataFromNativeArray(bufferToFill, rawNeutralNormData, header.NormalsOffset);
+
+                if (rawNeutralTanData.Length > 0)
+                {
+                    SetComputeBufferDataFromNativeArray(bufferToFill, rawNeutralTanData, header.TangentsOffset);
+                }
+
+                if (rawMorphData.Length > 0)
+                {
+                    SetComputeBufferDataFromNativeArray(bufferToFill, rawMorphData, header.MorphTargetDeltasOffset);
+                }
+
+                if (rawJointIndicesData.Length > 0)
+                {
+                    SetComputeBufferDataFromNativeArray(bufferToFill, rawJointWeightsData, header.JointWeightsOffset);
+                    SetComputeBufferDataFromNativeArray(bufferToFill, rawJointIndicesData, header.JointIndicesOffset);
+                }
+
+                SetComputeBufferDataFromNativeArray(bufferToFill, vertexReorderBuffer, header.OutputIndicesOffset);            }
+            finally
+            {
+                headerAsBytes.Dispose();
+            }
+        }
+
+        private static ComputeBuffer CreateStaticDataComputeBuffer(
+            GpuSkinningConfiguration gpuSkinningConfig,
+            in CAPI.ovrGpuMorphTargetBufferDesc morphBufferDesc,
+            in NativeArray<byte> rawMorphData,
+            in NativeArray<byte> rawNeutralPosData,
+            in NativeArray<byte> rawNeutralNormData,
+            in NativeArray<byte> rawNeutralTanData,
+            in NativeArray<byte> rawJointWeightsData,
+            in NativeArray<byte> rawJointIndicesData,
+            in NativeArray<UInt16> vertexReorderBuffer,
+            out Vector3 positionOutputScale,
+            out Vector3 positionOutputBias)
+        {
+            const int BAG_OF_UINTS_BUFFER_STRIDE_BYTES = sizeof(UInt32);
+
+            // Data layout is header -> neutral pose -> morphs deltas (if applicable) -> joints (if applicable)
+
+            // Create a "header" of the metadata to describe the rest of the data in the buffer
+            ComputeBufferStaticMeshHeader header = new ComputeBufferStaticMeshHeader();
+
+            // Calculate offsets into the buffer for the neutral attribute and the joints and store in the header
+            int sizeOfHeaderBytes = UnsafeUtility.SizeOf<ComputeBufferStaticMeshHeader>();
+            int neutralPosePosOffset = sizeOfHeaderBytes;
+            int neutralPoseNormOffset = neutralPosePosOffset + rawNeutralPosData.Length;
+            int neutralPoseTanOffset = neutralPoseNormOffset + rawNeutralNormData.Length;
+            int morphDeltasOffset = neutralPoseTanOffset + rawNeutralTanData.Length;
+            int jointWeightsOffset = morphDeltasOffset + rawMorphData.Length;
+            int jointIndicesOffset = jointWeightsOffset + rawJointWeightsData.Length;
+            int outputIndicesOffset = jointIndicesOffset + rawJointIndicesData.Length;
+
+            header.attributesAndJointWeightsOffsetBytes = new Vector4UInt
+            {
+                x = (uint)neutralPosePosOffset,
+                y = (uint)neutralPoseNormOffset,
+                z = (uint)neutralPoseTanOffset,
+                w = (uint)jointWeightsOffset,
+            };
+
+            // Calculate offset for the morph target deltas and store other info in the header
+            header.morphTargetInfoAndJointIndicesOffsetBytes = new Vector4UInt
+            {
+                x = (uint)morphDeltasOffset,
+                y = morphBufferDesc.numMorphTargets,
+                z = morphBufferDesc.numMorphedVerts,
+                w = (uint)jointIndicesOffset,
+            };
+
+            header.outputIndexOffset = new Vector4UInt
+            {
+                x = (uint)outputIndicesOffset,
+            };
+
+            // Store the misc. offsets and scales for normalized data into the header
+            header.morphTargetsPosRange = new Vector4(morphBufferDesc.positionScale.x, morphBufferDesc.positionScale.y, morphBufferDesc.positionScale.z, 0.0f);
+            header.morphTargetsNormRange = new Vector4(morphBufferDesc.normalScale.x, morphBufferDesc.normalScale.y, morphBufferDesc.normalScale.z, 0.0f);
+            header.morphTargetsTanRange = new Vector4(
+                morphBufferDesc.tangentScale.x,
+                morphBufferDesc.tangentScale.y,
+                morphBufferDesc.tangentScale.z, 0.0f);
+
+            // For now, since position input  is only supporting float values, just set offset and scale to be 0 and 1
+            header.vertexInputPositionBias = new Vector4(0.0f, 0.0f, 0.0f, 0.0f);
+            header.vertexInputPositionScale = new Vector4(1.0f, 1.0f, 1.0f, 1.0f);
+
+            // Set output position scale/bias based on gpu skinning config
+            switch (gpuSkinningConfig.SkinnerOutputFormat)
+            {
+                case GpuSkinningConfiguration.TexturePrecision.Unorm16:
+                    // TODO*: Read in a normalization scale and bias from GpuSkinningConfiguration when
+                    // available. For now, just hard code
+                    float scale = 8.0f;
+                    float bias = -0.75f;
+                    positionOutputBias = new Vector3(bias, bias, bias);
+                    positionOutputScale = new Vector3(scale, scale, scale);
+                    break;
+                default:
+                    positionOutputBias = Vector3.zero;
+                    positionOutputScale = Vector3.one;
+                    break;
+            }
+            header.vertexOutputPositionBias = positionOutputBias;
+            header.vertexOutputPositionScale = positionOutputScale;
+
+            int totalSizeBytes =
+                sizeOfHeaderBytes +
+                rawMorphData.Length +
+                rawNeutralPosData.Length +
+                rawNeutralNormData.Length +
+                rawNeutralTanData.Length +
+                rawJointIndicesData.Length +
+                rawJointWeightsData.Length +
+                (vertexReorderBuffer.Length * sizeof(UInt16));
+
+            // Unity requires compute buffers to have a minimum stride of 4 even for "raw" buffers because
+            // ByteAddressBuffers are really bags of uints instead of bags of bytes.
+            // All of the data should be aligned to 4 byte boundaries anyway, so, just need to convert the sizes/offsets
+            // when setting compute buffer data to be divided by 4...sigh
+            var result = new ComputeBuffer(
+                totalSizeBytes / BAG_OF_UINTS_BUFFER_STRIDE_BYTES,
+                 BAG_OF_UINTS_BUFFER_STRIDE_BYTES,
+                 ComputeBufferType.Raw); // Raw for ByteAddressBuffer
+
+            try
+            {
+#if COMPUTE_BUFFER_PARTIAL_UPDATE_ALLOWED
+                FillStaticDataComputeBufferViaPartialUpdates(
+                    header,
+                    rawMorphData,
+                    rawNeutralPosData,
+                    rawNeutralNormData,
+                    rawNeutralTanData,
+                    rawJointWeightsData,
+                    rawJointIndicesData,
+                    vertexReorderBuffer,
+                    sizeOfHeaderBytes,
+                    result);
+#else
+                FillStaticDataComputeBufferViaBackingBuffer(
+                    header,
+                    rawMorphData,
+                    rawNeutralPosData,
+                    rawNeutralNormData,
+                    rawNeutralTanData,
+                    rawJointWeightsData,
+                    rawJointIndicesData,
+                    vertexReorderBuffer,
+                    result,
+                    totalSizeBytes);
+#endif
+            }
+            catch
+            {
+                // Don't leak memory here if there is an exception filling the compute buffer
+                result.Dispose();
+                throw;
+            }
+
+            return result;
+        }
+
+        // ASSUMPTIONS: Starting an byte array index 0 and copying whole array
+        private static void SetComputeBufferDataFromNativeArray<T>(
+            ComputeBuffer computeBuffer,
+            NativeArray<T> nativeArr,
+            int byteOffsetInComputeBuffer) where T : struct
+        {
+            int stride = computeBuffer.stride;
+            var arrayOfUints = nativeArr.Reinterpret<uint>(UnsafeUtility.SizeOf<T>());
+            computeBuffer.SetData(
+                arrayOfUints,
+                0,
+                byteOffsetInComputeBuffer / stride,
+                arrayOfUints.Length);
+        }
+
+        private static void CopyNativeArrayToBackingBuffer<T>(
+            NativeArray<byte> backingBuffer,
+            NativeArray<T> nativeArr,
+            int byteOffset) where T : struct
+        {
+            var arrayOfBytes = nativeArr.Reinterpret<byte>(UnsafeUtility.SizeOf<T>());
+            var sourceSlice = arrayOfBytes.Slice();
+            var destSlice = backingBuffer.Slice(byteOffset, arrayOfBytes.Length);
+            destSlice.CopyFrom(sourceSlice);
+        }
+
+        void GetJointIndicesAndWeights(
+            int vertexCount,
+            BoneWeight[] boneWeights,
+            out NativeArray<CAPI.ovrAvatar2Vector4us> jointIndices,
+            out NativeArray<CAPI.ovrAvatar2Vector4f> jointWeights)
+        {
+            // TODO: get these two arrays directly! See RetrieveBoneWeights()
+            jointIndices = new NativeArray<CAPI.ovrAvatar2Vector4us>(
+                vertexCount,
+                Allocator.Persistent,
+                NativeArrayOptions.UninitializedMemory);
+            jointWeights = new NativeArray<CAPI.ovrAvatar2Vector4f>(
+                vertexCount,
+                Allocator.Persistent,
+                NativeArrayOptions.UninitializedMemory);
+
+            for (int i = 0; i < boneWeights.Length; i++)
+            {
+                BoneWeight bw = boneWeights[i];
+                jointIndices[i] = new CAPI.ovrAvatar2Vector4us
+                {
+                    x = (ushort)bw.boneIndex0,
+                    y = (ushort)bw.boneIndex1,
+                    z = (ushort)bw.boneIndex2,
+                    w = (ushort)bw.boneIndex3,
+                };
+
+                jointWeights[i] = new CAPI.ovrAvatar2Vector4f
+                {
+                    x = bw.weight0,
+                    y = bw.weight1,
+                    z = bw.weight2,
+                    w = bw.weight3,
+                };
+            }
+        }
+
+    } // end class OvrAvatarComputeSkinnedPrimitive
+} // end namespace
diff --git a/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarComputeSkinnedPrimitive.cs.meta b/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarComputeSkinnedPrimitive.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..9483767ea2ab37d2cde7d598ebfad2b8840b70b0
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarComputeSkinnedPrimitive.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 8f65bccfd5b3a5b45b94e6e4dbea34f2
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarGpuSkinnedPrimitive.cs b/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarGpuSkinnedPrimitive.cs
new file mode 100644
index 0000000000000000000000000000000000000000..cbf93080b0d6c0c69c6160c50354c21e0bda307e
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarGpuSkinnedPrimitive.cs
@@ -0,0 +1,401 @@
+using Oculus.Skinning;
+using Oculus.Skinning.GpuSkinning;
+
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+
+using Unity.Collections;
+using Unity.Collections.LowLevel.Unsafe;
+
+using UnityEngine;
+using UnityEngine.Experimental.Rendering;
+using UnityEngine.Profiling;
+
+namespace Oculus.Avatar2
+{
+    public sealed class OvrAvatarGpuSkinnedPrimitive : IDisposable
+    {
+        private const string LOG_SCOPE = "OvrAvatarGPUSkinnedPrimitive";
+        private const NativeArrayOptions NATIVE_ARRAY_INIT = NativeArrayOptions.UninitializedMemory;
+
+        public bool IsLoading => _buildTextureSlice.IsValid;
+
+        public class SourceTextureMetaData
+        {
+            public CAPI.ovrTextureLayoutResult LayoutInMorphTargetsTex;
+            public uint NumMorphTargetAffectedVerts;
+            public int[] MeshVertexToAffectedIndex;
+            public CAPI.ovrTextureLayoutResult LayoutInNeutralPoseTex;
+            public CAPI.ovrTextureLayoutResult LayoutInJointsTex;
+            public Vector3 PositionRange;
+            public Vector3 NormalRange;
+            public Vector3 TangentRange;
+        }
+
+        public OvrExpandableTextureArray NeutralPoseTex { get; private set; }
+        public OvrExpandableTextureArray MorphTargetSourceTex { get; private set; }
+        public OvrExpandableTextureArray JointsTex { get; private set; }
+        public SourceTextureMetaData MetaData { get; private set; }
+
+        private OvrTime.SliceHandle _buildTextureSlice;
+
+        public OvrAvatarGpuSkinnedPrimitive(string name,
+            uint vertexCount, IntPtr neutralPositions, IntPtr neutralNormals, IntPtr neutralTangents,
+            uint morphTargetCount, IntPtr deltaPosPtr, IntPtr deltaNormPtr, IntPtr deltaTanPtr,
+            uint jointsCount, BoneWeight[] boneWeights,
+            // TODO: adjust scoping to give direct access to MeshInfo
+            Action neutralPoseCallback, Action finishCallback)
+        {
+            var gpuSkinningConfig = GpuSkinningConfiguration.Instance;
+
+            _buildTextureSlice = OvrTime.Slice(
+                BuildTextures(gpuSkinningConfig, name, vertexCount, neutralPositions, neutralNormals, neutralTangents,
+                morphTargetCount, deltaPosPtr, deltaNormPtr, deltaTanPtr,
+                jointsCount, boneWeights,
+                neutralPoseCallback, finishCallback)
+            );
+        }
+
+        private IEnumerator<OvrTime.SliceStep> BuildTextures(GpuSkinningConfiguration gpuSkinningConfig, string name,
+            uint vertexCount, IntPtr neutralPositions, IntPtr neutralNormals, IntPtr neutralTangents,
+            uint morphTargetCount, IntPtr deltaPosPtr, IntPtr deltaNormPtr, IntPtr deltaTanPtr,
+            uint jointsCount, BoneWeight[] boneWeights,
+            // TODO: adjust scoping to give direct access to MeshInfo
+            Action neutralPoseCallback, Action finishCallback)
+        {
+            // TODO: Some of this work can be moved off the main thread (everything except creating Unity.Objects)
+            var result = new SourceTextureMetaData();
+
+            yield return OvrTime.SliceStep.Stall;
+            Profiler.BeginSample("OvrAvatarGPUSkinnedPrimitive.CreateNeutralPoseTex");
+            NeutralPoseTex = CreateNeutralPoseTex(name, vertexCount, neutralPositions, neutralNormals, neutralTangents,
+                gpuSkinningConfig.NeutralPoseFormat, ref result);
+            Profiler.EndSample();
+            neutralPoseCallback?.Invoke();
+
+            if (morphTargetCount > 0)
+            {
+                yield return OvrTime.SliceStep.Stall;
+                Profiler.BeginSample("OvrAvatarGPUSkinnedPrimitive.CreateMorphTargetSourceTex");
+                MorphTargetSourceTex = CreateMorphTargetSourceTex(name, vertexCount, morphTargetCount,
+                    deltaPosPtr, deltaNormPtr, deltaTanPtr,
+                    gpuSkinningConfig.SourceMorphFormat, ref result);
+                Profiler.EndSample();
+            }
+
+            if (jointsCount > 0)
+            {
+                yield return OvrTime.SliceStep.Stall;
+                Profiler.BeginSample("OvrAvatarGPUSkinnedPrimitive.CreateJointsTex");
+                JointsTex = CreateJointsTex(name, vertexCount, boneWeights, gpuSkinningConfig.JointsFormat, ref result);
+                Profiler.EndSample();
+            }
+
+            MetaData = result;
+            finishCallback?.Invoke();
+
+            _buildTextureSlice.Clear();
+        }
+
+        public void Dispose()
+        {
+            Dispose(true);
+            GC.SuppressFinalize(this);
+        }
+
+        private void Dispose(bool isDispose)
+        {
+            if (isDispose)
+            {
+                if (_buildTextureSlice.IsValid)
+                {
+                    _buildTextureSlice.Cancel();
+                }
+
+                JointsTex?.Destroy();
+                MorphTargetSourceTex?.Destroy();
+                NeutralPoseTex?.Destroy();
+            }
+            else
+            {
+                if (_buildTextureSlice.IsValid)
+                {
+                    OvrAvatarLog.LogError("Build texture slice still valid when finalized", LOG_SCOPE);
+
+                    // Prevent OvrTime from stalling
+                    _buildTextureSlice.EmergencyShutdown();
+                }
+                if (NeutralPoseTex != null || MorphTargetSourceTex != null || JointsTex != null)
+                {
+                    OvrAvatarLog.LogError($"OvrAvatarGPUSkinnedPrimitive was not disposed before being destroyed", LOG_SCOPE);
+                }
+            }
+            JointsTex = null;
+            MorphTargetSourceTex = null;
+            NeutralPoseTex = null;
+        }
+
+        ~OvrAvatarGpuSkinnedPrimitive()
+        {
+            Dispose(false);
+        }
+
+        private static OvrExpandableTextureArray CreateNeutralPoseTex(string name, uint vertexCount, IntPtr positions,
+            IntPtr normals, IntPtr tangents,
+            GraphicsFormat neutralTexFormat, ref SourceTextureMetaData metaData)
+        {
+            var hasTangents = tangents != IntPtr.Zero;
+
+            CAPI.ovrGpuSkinningTextureDesc texDesc = CAPI.OvrGpuSkinning_NeutralPoseTextureDesc(
+                OvrGpuSkinningUtils.MAX_TEXTURE_DIMENSION,
+                vertexCount,
+                hasTangents);
+
+            // Create expandable texture array and fill with data
+            var output = new OvrExpandableTextureArray(
+                "neutral(" + name + ")",
+                texDesc.width,
+                texDesc.height,
+                neutralTexFormat);
+
+            OvrSkinningTypes.Handle handle = output.AddEmptyBlock(texDesc.width, texDesc.height);
+            CAPI.ovrTextureLayoutResult layout = output.GetLayout(handle);
+
+            {
+                Texture2D tempTex = new Texture2D(
+                    layout.w,
+                    layout.h,
+                    output.Format,
+                    output.HasMips,
+                    output.IsLinear);
+
+                var texData = tempTex.GetRawTextureData<byte>();
+
+                // This will validate the sizes match
+                Debug.Assert(texData.Length == texDesc.dataSize);
+
+                IntPtr dataPtr = texData.GetIntPtr();
+
+                CAPI.OvrGpuSkinning_NeutralPoseEncodeTextureData(in texDesc, vertexCount,
+                        positions, normals, tangents, dataPtr,
+                        texData.GetBufferSize());
+
+                tempTex.Apply(false, true);
+
+                output.CopyFromTexture(layout, tempTex);
+
+                Texture2D.Destroy(tempTex);
+            }
+
+            metaData.LayoutInNeutralPoseTex = layout;
+
+            return output;
+        }
+
+        private static OvrExpandableTextureArray CreateMorphTargetSourceTex(string name,
+            uint vertexCount, uint morphTargetCount, IntPtr deltaPosPtr, IntPtr deltaNormPtr, IntPtr deltaTanPtr,
+            GpuSkinningConfiguration.TexturePrecision morphSrcPrecision, ref SourceTextureMetaData metaData)
+        {
+            var hasTangents = deltaTanPtr != IntPtr.Zero;
+
+            CAPI.ovrGpuMorphTargetTextureDesc morphTexDesc;
+            OvrExpandableTextureArray output;
+
+            using (var mVtoAVBuffer = new NativeArray<Int32>((int)vertexCount, Allocator.Temp, NATIVE_ARRAY_INIT))
+            {
+                IntPtr mVtoAVPtr;
+                unsafe
+                {
+                    mVtoAVPtr = (IntPtr)mVtoAVBuffer.GetUnsafePtr();
+                }
+
+                if (hasTangents)
+                {
+                    morphTexDesc = CAPI.OvrGpuSkinning_MorphTargetEncodeMeshVertToAffectedVertWithTangents(
+                        OvrGpuSkinningUtils.MAX_TEXTURE_DIMENSION,
+                        vertexCount,
+                        morphTargetCount,
+                        morphSrcPrecision.GetOvrPrecision(),
+                        deltaPosPtr,
+                        deltaNormPtr,
+                        deltaTanPtr,
+                        mVtoAVPtr);
+                }
+                else
+                {
+                    morphTexDesc = CAPI.OvrGpuSkinning_MorphTargetEncodeMeshVertToAffectedVert(
+                        OvrGpuSkinningUtils.MAX_TEXTURE_DIMENSION,
+                        vertexCount,
+                        morphTargetCount,
+                        morphSrcPrecision.GetOvrPrecision(),
+                        deltaPosPtr,
+                        deltaNormPtr,
+                        mVtoAVPtr);
+                }
+
+                metaData.NumMorphTargetAffectedVerts = morphTexDesc.numAffectedVerts;
+
+                if (metaData.NumMorphTargetAffectedVerts <= 0)
+                {
+                    // TODO: Could we catch this much, much earlier?
+                    //HasMorphTargets = false;
+                    metaData.MeshVertexToAffectedIndex = Array.Empty<int>();
+                    metaData.LayoutInMorphTargetsTex = CAPI.ovrTextureLayoutResult.INVALID_LAYOUT;
+
+                    OvrAvatarLog.LogDebug($"Primitive ({name}) has morph target, but no affected verts", LOG_SCOPE);
+                    return null;
+                }
+
+                var morphTargetTexels = morphTexDesc.texWidth * morphTexDesc.texHeight;
+                Debug.Assert(morphTargetTexels > 0);
+
+                // Create expandable texture array and fill with data
+                output = new OvrExpandableTextureArray(
+                    "morphSrc(" + name + ")",
+                     morphTexDesc.texWidth,
+                     morphTexDesc.texHeight,
+                    morphSrcPrecision.GetGraphicsFormat());
+
+                OvrSkinningTypes.Handle handle = output.AddEmptyBlock(
+                     morphTexDesc.texWidth,
+                     morphTexDesc.texHeight);
+                CAPI.ovrTextureLayoutResult layout = output.GetLayout(handle);
+
+                Texture2D tempTex = new Texture2D(
+                    layout.w,
+                    layout.h,
+                    output.Format,
+                    output.HasMips,
+                    output.IsLinear);
+
+                var texData = tempTex.GetRawTextureData<byte>();
+
+                // This will validate the sizes match
+                Debug.Assert(texData.Length == morphTexDesc.textureDataSize);
+
+                IntPtr dataPtr;
+                unsafe
+                {
+                    dataPtr = (IntPtr)texData.GetUnsafePtr();
+                }
+
+                if (hasTangents)
+                {
+                    if (!CAPI.OvrGpuSkinning_MorphTargetEncodeTextureDataWithTangents(morphTexDesc, mVtoAVPtr, morphSrcPrecision.GetOvrPrecision(), deltaPosPtr, deltaNormPtr, deltaTanPtr, dataPtr))
+                    {
+                        OvrAvatarLog.LogError("failed to get morph data", LOG_SCOPE);
+                    }
+                }
+                else
+                {
+                    if (!CAPI.OvrGpuSkinning_MorphTargetEncodeTextureData(morphTexDesc, mVtoAVPtr, morphSrcPrecision.GetOvrPrecision(), deltaPosPtr, deltaNormPtr, dataPtr))
+                    {
+                        OvrAvatarLog.LogError("failed to get morph data", LOG_SCOPE);
+                    }
+                }
+
+                tempTex.Apply(false, true);
+
+                output.CopyFromTexture(layout, tempTex);
+
+                Texture2D.Destroy(tempTex);
+
+                metaData.LayoutInMorphTargetsTex = layout;
+
+                metaData.MeshVertexToAffectedIndex = mVtoAVBuffer.ToArray();
+
+                metaData.PositionRange = morphTexDesc.positionRange;
+                metaData.NormalRange = morphTexDesc.normalRange;
+                metaData.TangentRange = morphTexDesc.tangentRange;
+            }
+
+            return output;
+        }
+
+        private static OvrExpandableTextureArray CreateJointsTex(string name, uint vertexCount, BoneWeight[] boneWeights,
+            GraphicsFormat jointsTexFormat, ref SourceTextureMetaData metaData)
+        {
+            // TODO: get these two arrays directly! See RetrieveBoneWeights()
+            var jointIndices = new CAPI.ovrAvatar2Vector4us[vertexCount];
+            var jointWeights = new CAPI.ovrAvatar2Vector4f[vertexCount];
+
+            for (int i = 0; i < boneWeights.Length; i++)
+            {
+                BoneWeight bw = boneWeights[i];
+                jointIndices[i] = new CAPI.ovrAvatar2Vector4us
+                {
+                    x = (ushort)bw.boneIndex0,
+                    y = (ushort)bw.boneIndex1,
+                    z = (ushort)bw.boneIndex2,
+                    w = (ushort)bw.boneIndex3,
+                };
+
+                jointWeights[i] = new CAPI.ovrAvatar2Vector4f
+                {
+                    x = bw.weight0,
+                    y = bw.weight1,
+                    z = bw.weight2,
+                    w = bw.weight3,
+                };
+            }
+
+            var texDesc = CAPI.OvrGpuSkinning_JointTextureDesc(OvrGpuSkinningUtils.MAX_TEXTURE_DIMENSION, vertexCount);
+
+            var output = new OvrExpandableTextureArray(
+                "joints(" + name + ")",
+                texDesc.width,
+                texDesc.height,
+                jointsTexFormat);
+
+            OvrSkinningTypes.Handle handle = output.AddEmptyBlock(texDesc.width, texDesc.height);
+            CAPI.ovrTextureLayoutResult layout = output.GetLayout(handle);
+
+            OvrAvatarLog.AssertConstMessage(layout.IsValid, "invalid texture layout detected", LOG_SCOPE);
+            {
+                var tempTex = new Texture2D(
+                    layout.w,
+                    layout.h,
+                    output.Format,
+                    output.HasMips,
+                    output.IsLinear);
+
+                var texData = tempTex.GetRawTextureData<byte>();
+
+                Debug.Assert(texData.Length == texDesc.dataSize);
+
+                IntPtr dataPtr;
+                unsafe
+                {
+                    dataPtr = (IntPtr)texData.GetUnsafePtr();
+                }
+
+                var dataIndicesPtr = GCHandle.Alloc(jointIndices, GCHandleType.Pinned);
+                var dataWeightsPtr = GCHandle.Alloc(jointWeights, GCHandleType.Pinned);
+
+                bool didEncode = CAPI.OvrGpuSkinning_JointEncodeTextureData(
+                        in texDesc,
+                        vertexCount,
+                        dataIndicesPtr.AddrOfPinnedObject(),
+                        dataWeightsPtr.AddrOfPinnedObject(),
+                        dataPtr,
+                        texData.GetBufferSize());
+
+                dataIndicesPtr.Free();
+                dataWeightsPtr.Free();
+
+                OvrAvatarLog.AssertConstMessage(didEncode, "get skinning data failure", LOG_SCOPE);
+
+                tempTex.Apply(false, true);
+
+                output.CopyFromTexture(layout, tempTex);
+
+                Texture2D.Destroy(tempTex);
+            }
+
+            metaData.LayoutInJointsTex = layout;
+
+            return output;
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarGpuSkinnedPrimitive.cs.meta b/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarGpuSkinnedPrimitive.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..9d1f8f8f25944a1362f49d987d3032b253fff1f2
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarGpuSkinnedPrimitive.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 38ad29ab672b4cf385a2d92f82b82289
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarImage.cs b/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarImage.cs
new file mode 100644
index 0000000000000000000000000000000000000000..08708803f1c292773d89df87a7f2f590b3bc3114
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarImage.cs
@@ -0,0 +1,242 @@
+using System;
+using System.Collections.Generic;
+
+using Unity.Collections.LowLevel.Unsafe;
+
+using UnityEngine;
+
+/// @file OvrAvatarImage.cs
+
+namespace Oculus.Avatar2
+{
+    ///
+    /// Contains a 2D image used to texture a 3D mesh.
+    /// The pixels of the texture come in a variety of formats.
+    /// Mobile applications use ASTC compressed texture formats
+    /// which are decompressed by hardware. PC applications
+    /// need DXT compressed textures.
+    ///
+    /// The texture data begins loading asynchronously when
+    /// the image is created.
+    ///
+    /// @see OvrAvatarPrimitive
+    ///
+    public class OvrAvatarImage : OvrAvatarAsset<CAPI.ovrAvatar2Image>
+    {
+        private const string avatarImageLogScope = "ovrAvatarImage";
+
+        internal const FilterMode defaultFilterMode = FilterMode.Trilinear;
+        internal const int defaultAnisoLevel = 1;
+
+        public readonly TextureFormat format;
+        public Texture2D texture { get; private set; } = null;
+
+        private OvrTime.SliceHandle _textureLoadSliceHandle = default;
+
+        public override string typeName => avatarImageLogScope;
+        public override string assetName => texture != null ? texture.name : "disposed";
+
+        public bool hasCopiedAllResourceData { get; private set; } = default;
+
+        ///
+        /// Create an image from the given image properties.
+        /// The image begins asynchronously loading upon return.
+        ///
+        /// @param resourceId unique resource ID
+        /// @param imageIndex index of image within material
+        /// @param data       image properties (data format, height, width)
+        /// @param srgb       true for images using SRGB color space,
+        ///                   False for linear color space
+        /// @see CAPI.ovrAvatar2Image
+        ///
+        public OvrAvatarImage(CAPI.ovrAvatar2Id resourceId, UInt32 imageIndex, CAPI.ovrAvatar2Image data, bool srgb) : base(data.id, data)
+        {
+            bool compressed = true;
+            switch (data.format)
+            {
+                case CAPI.ovrAvatar2ImageFormat.RGBA32:
+                    format = TextureFormat.RGBA32;
+                    compressed = false;
+                    break;
+                case CAPI.ovrAvatar2ImageFormat.DXT1:
+                    format = TextureFormat.DXT1;
+                    break;
+                case CAPI.ovrAvatar2ImageFormat.DXT5:
+                    format = TextureFormat.DXT5;
+                    break;
+                case CAPI.ovrAvatar2ImageFormat.BC5S:
+                    format = TextureFormat.BC5;
+                    break;
+                case CAPI.ovrAvatar2ImageFormat.BC7U:
+                    format = TextureFormat.BC7;
+                    break;
+                case CAPI.ovrAvatar2ImageFormat.ASTC_RGBA_4x4:
+                    format = TextureFormat.ASTC_4x4;
+                    break;
+                case CAPI.ovrAvatar2ImageFormat.ASTC_RGBA_6x6:
+                    format = TextureFormat.ASTC_6x6;
+                    break;
+                case CAPI.ovrAvatar2ImageFormat.ASTC_RGBA_8x8:
+                    format = TextureFormat.ASTC_8x8;
+                    break;
+
+//                 case CAPI.ovrAvatar2ImageFormat.ASTC_RGBA_10x10:
+//                     format = TextureFormat.ASTC_10x10;
+//                     break;
+
+                case CAPI.ovrAvatar2ImageFormat.ASTC_RGBA_12x12:
+                    format = TextureFormat.ASTC_12x12;
+                    break;
+
+                case CAPI.ovrAvatar2ImageFormat.Invalid:
+                    OvrAvatarLog.LogError(
+                        $"Invalid image format for image {assetId}",
+                        avatarImageLogScope);
+                    // Can't load invalid format, all valid data has been copied (unblock load)
+                    hasCopiedAllResourceData = true;
+                    return;
+
+                case CAPI.ovrAvatar2ImageFormat.BC5U:
+                    const string BC5UErrorString = "BC5U is currently unsupported in Unity";
+                    // Appears to be unsupported
+                    OvrAvatarLog.LogError(
+                        BC5UErrorString,
+                        avatarImageLogScope);
+                    // Can't load format, proceed w/ other assets
+                    hasCopiedAllResourceData = true;
+                    throw new ArgumentException(BC5UErrorString);
+
+                default:
+                    // Exception will end loading sequence, no opportunity to copy data
+                    hasCopiedAllResourceData = true;
+                    throw new ArgumentOutOfRangeException($"Unrecognized format {data.format}");
+            }
+            Debug.Assert(SystemInfo.SupportsTextureFormat(format));
+
+            bool hasMipMaps = data.mipCount > 1;
+            var buildTexture = new Texture2D((int)data.sizeX, (int)data.sizeY, format, hasMipMaps, !srgb);
+
+            var manager = OvrAvatarManager.Instance;
+            var filterMode = defaultFilterMode;
+            int anisoLevel = defaultAnisoLevel;
+            if (manager != null)
+            {
+                filterMode = manager.TextureFilterMode;
+                anisoLevel = manager.TextureAnisoLevel;
+            }
+            buildTexture.filterMode = filterMode;
+            buildTexture.anisoLevel = anisoLevel;
+
+            // Oh Unity...
+            if (!buildTexture)
+            {
+                // TODO: Fall back to a lower mip level?
+                OvrAvatarLog.LogError(
+                    $"Unable to create texture with size ({data.sizeX}, {data.sizeY}) and formats ({data.format}, {format})",
+                    avatarImageLogScope);
+                // Failed to allocate texture, likely near-OOM condition - cease load and proceed
+                hasCopiedAllResourceData = true;
+                return;
+            }
+
+            buildTexture.name = $"{assetId}:{imageIndex}-{format}";
+            texture = buildTexture;
+            _textureLoadSliceHandle = OvrTime.Slice(LoadTextureAsync(buildTexture, resourceId, imageIndex, srgb, compressed));
+        }
+
+        private IEnumerator<OvrTime.SliceStep> LoadTextureAsync(Texture2D buildTexture, CAPI.ovrAvatar2Id resourceId, UInt32 imageIndex, bool srgb, bool compressed)
+        {
+            var result = LoadTextureData(buildTexture, resourceId, imageIndex);
+
+            if (result.IsSuccess())
+            {
+                bool generateMips = AllowMipGeneration && !compressed && data.mipCount == 1;
+
+                // TODO: Should perhaps `Stall` instead?
+                if (OvrTime.ShouldHold) { yield return OvrTime.SliceStep.Hold; }
+                buildTexture.Apply(generateMips, true);
+
+                texture = buildTexture;
+                isLoaded = true;
+            }
+            else
+            {
+                OvrAvatarLog.LogError($"MeshPrimitive Error: GetImageDataByIndex ({imageIndex}) {result}", avatarImageLogScope, buildTexture);
+                texture = null;
+                Texture2D.Destroy(buildTexture);
+            }
+
+            _textureLoadSliceHandle.Clear();
+        }
+
+        private CAPI.ovrAvatar2Result LoadTextureData(Texture2D buildTexture, CAPI.ovrAvatar2Id resourceId, UInt32 imageIndex)
+        {
+            var textureData = buildTexture.GetRawTextureData<byte>();
+            // AvatarSDK will catch this at runtime, this just provides a more useful error when developing in Unity
+            Debug.Assert(textureData.Length == data.imageDataSize,
+                $"Texture data arrays are different sizes! Texture is {textureData.Length} but image is {data.imageDataSize}");
+
+
+            IntPtr textureDataPtr;
+            unsafe
+            {
+                textureDataPtr = (IntPtr)textureData.GetUnsafePtr();
+            }
+
+            var result = CAPI.ovrAvatar2Asset_GetImageDataByIndex(resourceId, imageIndex, textureDataPtr, (UInt32)textureData.Length);
+            hasCopiedAllResourceData = result.IsSuccess();
+            return result;
+        }
+
+        protected override void _ExecuteCancel()
+        {
+            if (_textureLoadSliceHandle.IsValid)
+            {
+                OvrAvatarLog.LogVerbose("Cancelled image during load", avatarImageLogScope);
+
+                _textureLoadSliceHandle.Cancel();
+            }
+
+            // We will not load any more resource data, so we have effectively loaded all of it
+            hasCopiedAllResourceData = true;
+        }
+
+        protected override void Dispose(bool disposing)
+        {
+            if (_textureLoadSliceHandle.IsValid)
+            {
+                if (disposing)
+                {
+                    _textureLoadSliceHandle.Cancel();
+                }
+                else
+                {
+                    OvrAvatarLog.LogWarning("Finalized image w/ in progress loading slice", avatarImageLogScope);
+
+                    var cpyHandle = _textureLoadSliceHandle;
+                    OvrTime.PostCleanupToUnityMainThread(() => cpyHandle.Cancel());
+                }
+            }
+
+            if (!(texture is null))
+            {
+                if (disposing)
+                {
+                    Texture2D.Destroy(texture);
+                }
+                else
+                {
+                    OvrAvatarLog.LogError(
+                        $"Texture2D asset was not destroyed before OvrAvatarImage ({assetId}) was finalized"
+                        , avatarImageLogScope);
+
+                    var holdTex = texture;
+                    OvrTime.PostCleanupToUnityMainThread(() => Texture2D.Destroy(holdTex));
+                }
+                texture = null;
+            }
+        }
+
+        private const bool AllowMipGeneration = false;
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarImage.cs.meta b/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarImage.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..654272446639676c3578a7d8b57242cc7c655b11
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarImage.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 999ec155a8222314d86ec3c7e146364a
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarPrimitive.cs b/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarPrimitive.cs
new file mode 100644
index 0000000000000000000000000000000000000000..77146ddfb9f5655800f9396d6ea63c7e848c3541
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarPrimitive.cs
@@ -0,0 +1,2868 @@
+#define OVR_ENABLE_VERTEX_REPACKING
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Diagnostics.Contracts;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Threading.Tasks;
+
+using Unity.Collections;
+using Unity.Collections.LowLevel.Unsafe;
+
+using UnityEngine;
+using UnityEngine.Assertions;
+using UnityEngine.Rendering;
+using UnityEngine.Profiling;
+
+/// @file OvrAvatarPrimitive.cs
+
+namespace Oculus.Avatar2
+{
+    /**
+     * Encapsulates a mesh associated with an avatar asset.
+     * Asynchronously loads the mesh and its material and
+     * converts it to a Unity Mesh and Material.
+     * A primitive may be shared across avatar levels of detail
+     * and across avatar renderables.
+     * @see OvrAvatarRenderable
+     */
+    public sealed class OvrAvatarPrimitive : OvrAvatarAsset<CAPI.ovrAvatar2Primitive>
+    {
+        private const string primitiveLogScope = "ovrAvatarPrimitive";
+        //:: Internal
+
+        private const int LOD_INVALID = -1;
+
+        /// Name of the asset this mesh belongs to.
+        /// The asset name is established when the asset is loaded.
+        public override string assetName => shortName;
+
+        /// Type of asset (e.e. "OvrAvatarPrimitive", "OvrAvatarImage")
+        public override string typeName => primitiveLogScope;
+
+        /// Name of this primitive.
+        public readonly string name = null;
+
+        ///
+        /// Short name of this primitive.
+        /// Defaults to the asset name.
+        /// @see assetName
+        ///
+        public readonly string shortName = null;
+
+        /// Unity Material used by this primitive.
+        public Material material { get; private set; } = null;
+
+        /// Unity Mesh used by this primitive.
+        public Mesh mesh { get; private set; } = null;
+
+        /// True if this primitive has a computed bounding volume.
+        public bool hasBounds { get; private set; }
+
+        /// Triangle and vertex counts for this primitive.
+        public ref readonly AvatarLODCostData CostData => ref _costData;
+
+        /// Gets the GPU skinning version of this primitive.
+        public OvrAvatarGpuSkinnedPrimitive gpuPrimitive { get; private set; } = null;
+
+        public OvrAvatarComputeSkinnedPrimitive computePrimitive { get; private set; }
+#pragma warning disable CA2213 // Disposable fields should be disposed - it is, but the linter is confused
+        private OvrAvatarGpuSkinnedPrimitiveBuilder gpuPrimitiveBuilder = null;
+#pragma warning restore CA2213 // Disposable fields should be disposed
+
+        ///
+        /// Index of highest quality level of detail this primitive belongs to.
+        /// One primitive may be used by more than one level of detail.
+        /// This is the lowest set bit in @ref CAPI.ovrAvatar2EntityLODFlags provided from native SDK.
+        ///
+        public uint HighestQualityLODIndex => (uint)lod;
+
+        ///
+        /// LOD bit flags for this primitive.
+        /// These flags indicate which levels of detail this primitive is used by.
+        /// @see HighestQualityLODIndex
+        ///
+        public CAPI.ovrAvatar2EntityLODFlags lodFlags { get; private set; }
+
+        ///
+        /// Type of shader being used by this primitive.
+        /// The shader type depends on what part of the avatar is being shaded.
+        ///
+        public OvrAvatarShaderManagerMultiple.ShaderType shaderType { get; private set; }
+
+        private OvrAvatarShaderConfiguration _shaderConfig;
+
+        // MeshInfo, only tracked for cleanup on cancellation
+        private MeshInfo _meshInfo;
+
+        // NOTE: Once this is initialized, it should not be "reset" even if the Primitive is disposed
+        // Other systems may need to reference this data during shutdown, and it's a PITA if they each have to make copies
+        private AvatarLODCostData _costData = default;
+
+        // TODO: A primitive can technically belong to any number of LODs with gaps in between.
+        private int lod = LOD_INVALID;
+
+        // TODO: Make this debug only
+        public Int32[] joints;
+
+        ///
+        /// Get which body parts of the avatar this primitive is used by.
+        /// These are established when the primitive is loaded.
+        ///
+        public CAPI.ovrAvatar2EntityManifestationFlags manifestationFlags { get; private set; }
+
+        ///
+        /// Get which view(s) (first person, third person) this primitive applies to.
+        /// These are established when the primitive is loaded.
+        ///
+        public CAPI.ovrAvatar2EntityViewFlags viewFlags { get; private set; }
+
+        ///
+        /// If the user wants only a subset of the mesh, as specified by
+        /// indices, these flags will control which submeshes are included.
+        /// NOTE: In the current implementation all verts are downloaded,
+        /// but the indices referencing them are excluded.
+        ///
+        public CAPI.ovrAvatar2EntitySubMeshInclusionFlags subMeshInclusionFlags { get; private set; }
+
+        ///
+        /// If the user wants to load and render advanced features, they can specify them in the
+        /// high quality flags.
+        ///
+        public CAPI.ovrAvatar2EntityHighQualityFlags highQualityFlags { get; private set; }
+
+        /// True if this primitive has joints (is skinned).
+        public bool HasJoints => JointCount > 0;
+
+        /// True if this primitive has blend shapes (morph targets).
+        public bool HasMorphs => morphTargetCount > 0;
+
+        /// Number of joints affecting this primitive.
+        public UInt32 JointCount => joints != null ? (uint)joints.Length : 0;
+
+        /// Number of vertices in this primitive's mesh.
+        public UInt32 meshVertexCount => _meshVertexCount;
+
+        /// Number of vertices affected by morph targets.
+        // TODO: Accurate count of vertices affected by morph targets
+        // Assumes that if there are morph targets, all verts are affected by morphs
+        public UInt32 morphVertexCount => HasMorphs ? meshVertexCount : 0;
+
+        /// Number of triangles in this primitive.
+        public UInt32 triCount { get; private set; }
+
+        /// Number of morph targets affecting this primitive.
+        public UInt32 morphTargetCount => _morphTargetCount;
+
+        /// True if this primitive has tangents for each vertex.
+        public bool hasTangents { get; private set; }
+
+        private UInt32 bufferVertexCount => _bufferVertexCount;
+
+        /// True if this primitive has finished loading.
+        public override bool isLoaded
+        {
+            get => base.isLoaded && meshLoaded && materialLoaded && gpuSkinningLoaded;
+        }
+
+        // Indicates that this Primitive no longer needs access to CAPI asset data and the resource can be released
+        internal bool hasCopiedAllResourceData => !(_needsMeshData || _needsMorphData || _needsImageData);
+
+        // Vertex count for the entire asset buffer, may include data for multiple primitives
+        private UInt32 _bufferVertexCount = UInt32.MaxValue;
+
+        // Vertex count for this mesh's primitive
+        private UInt32 _meshVertexCount = UInt32.MaxValue;
+        private UInt32 _morphTargetCount = UInt32.MaxValue;
+
+        // Task thread completion checks
+        private bool meshLoaded = false;
+        private bool materialLoaded = false;
+        private bool gpuSkinningLoaded = false;
+
+        // Resource copy status
+        private bool _needsMeshData = true;
+        private bool _needsMorphData = true;
+        private bool _needsImageData = true;
+
+        // TODO: Remove via better state management
+        private bool _hasCancelled = false;
+
+        // Cancellation token for Tasks
+#pragma warning disable CA2213 // Disposable fields should be disposed -> It is, but the linter is confused
+        private CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource();
+#pragma warning restore CA2213 // Disposable fields should be disposed
+
+        // Async load coroutines for cancellation
+        private OvrTime.SliceHandle _loadMeshAsyncSliceHandle;
+        private OvrTime.SliceHandle _loadMaterialAsyncSliceHandle;
+
+        [Flags]
+        private enum VertexFormat : uint
+        {
+            VF_POSITION = 1,
+            VF_NORMAL = 2,
+            VF_TANGENT = 4,
+            VF_COLOR = 8,
+            VF_TEXCOORD0 = 16,
+            VF_COLOR_ORMT = 32,
+            VF_BONE_WEIGHTS = 64,
+            VF_BONE_INDICES = 128,
+        }
+
+        // Unity 2022 requires skinned mesh attributes be on specific streams. We create separate NativeArrays and
+        // strides for each stream.
+        // Stream 0: Position, Normal, Tangent
+        // Stream 1: Color, TexCoord0, TextCoord1
+        // Stream 2: BlendWeight, BlendIndices
+        private const int VF_STREAM_COUNT = 3;
+
+        // These aren't really necessary but make it clear which stream is being used and where.
+        private const int VF_STREAM_0 = 0;
+        private const int VF_STREAM_1 = 1;
+        private const int VF_STREAM_2 = 2;
+
+        private struct VertexBuffer
+        {
+            public VertexFormat vertexFormat;
+            public int vertexCount;
+            public int[] vertexStrides;
+            public List<VertexAttributeDescriptor> vertexLayout;
+
+            public NativeArray<byte>[] vertexStreams;
+        }
+
+        // Data shared across threads
+        private sealed class MeshInfo : IDisposable
+        {
+            // Core Mesh
+            public NativeArray<Color> colors;
+            public NativeArray<Color> colorsORMT;
+            public NativeArray<float> subMeshTypes;
+
+            public NativeArray<Color32> meshColors;
+
+            public NativeArray<UInt16> triangles;
+
+            private NativeArray<Vector3> verts_;
+
+            // New vertex format.
+            public VertexBuffer vertexBuffer;
+
+            // NOTE: Held during GPUPrimitiveBuilding
+            public NativeArray<Vector3> verts
+            {
+                get => verts_;
+                set
+                {
+                    verts_ = value;
+                    pendingMeshVerts_ = true;
+                    vertexCount = value.IsCreated ? (uint)value.Length : 0U;
+                }
+            }
+
+            // NOTE: Held during GPUPrimitiveBuilding
+            private NativeArray<Vector3> normals_;
+
+            public NativeArray<Vector3> normals
+            {
+                get => normals_;
+                set
+                {
+                    normals_ = value;
+                    pendingMeshNormals_ = true;
+                }
+            }
+
+            // NOTE: Held during GPUPrimitiveBuilding
+            private NativeArray<Vector4> tangents_;
+
+            public NativeArray<Vector4> tangents
+            {
+                get => tangents_;
+                set
+                {
+                    tangents_ = value;
+                    pendingMeshTangents_ = true;
+                    hasTangents = value.IsCreated && value.Length > 0;
+                }
+            }
+
+            public NativeArray<Vector2> texCoords;
+
+            // Documentation for `SetBoneWeights(NativeArray)` is... lacking
+            // - https://docs.unity3d.com/ScriptReference/Mesh.SetBoneWeights.html
+            private BoneWeight[] boneWeights_;
+
+            public BoneWeight[] boneWeights
+            {
+                get => boneWeights_;
+                set
+                {
+                    boneWeights_ = value;
+                    pendingMeshBoneWeights_ = true;
+                }
+            }
+
+            // Skin
+            // As of 2020.3, no NativeArray bindPoses setter
+            public Matrix4x4[] bindPoses;
+
+            // Track vertex count after verts has been freed
+            public uint vertexCount { get; private set; }
+            public bool hasTangents { get; private set; }
+
+            public void WillBuildGpuPrimitive()
+            {
+                pendingGpuPrimitive_ = true;
+                pendingNeutralPoseTex_ = vertexCount > 0;
+            }
+
+            public void WillBuildComputePrimitive()
+            {
+                _pendingComputePrimitive = true;
+                _pendingNeutralPoseBuffers = vertexCount > 0;
+            }
+
+            public void DidBuildGpuPrimitive()
+            {
+                pendingGpuPrimitive_ = false;
+                if (CanResetVerts) { verts_.Reset(); }
+                if (CanResetNormals) { normals_.Reset(); }
+                if (CanResetTangents) { tangents_.Reset(); }
+                if (CanResetBoneWeights) { boneWeights = null; }
+            }
+
+            public void DidBuildComputePrimitive()
+            {
+                _pendingComputePrimitive = false;
+                if (CanResetVerts) { verts_.Reset(); }
+                if (CanResetNormals) { normals_.Reset(); }
+                if (CanResetTangents) { tangents_.Reset(); }
+                if (CanResetBoneWeights) { boneWeights = null; }
+            }
+
+            public void NeutralPoseTexComplete()
+            {
+                pendingNeutralPoseTex_ = false;
+                if (CanResetVerts) { verts_.Reset(); }
+                if (CanResetNormals) { normals_.Reset(); }
+                if (CanResetTangents) { tangents_.Reset(); }
+            }
+
+            public void NeutralPoseBuffersComplete()
+            {
+                _pendingNeutralPoseBuffers = false;
+                if (CanResetVerts) { verts_.Reset(); }
+                if (CanResetNormals) { normals_.Reset(); }
+                if (CanResetTangents) { tangents_.Reset(); }
+            }
+
+            public void CancelledBuildPrimitives()
+            {
+                DidBuildGpuPrimitive();
+                DidBuildComputePrimitive();
+                NeutralPoseTexComplete();
+                NeutralPoseBuffersComplete();
+            }
+
+            public void MeshVertsComplete()
+            {
+                pendingMeshVerts_ = false;
+                if (CanResetVerts) { verts_.Reset(); }
+            }
+
+            public void MeshNormalsComplete()
+            {
+                pendingMeshNormals_ = false;
+                if (CanResetNormals) { normals_.Reset(); }
+            }
+
+            public void MeshTangentsComplete()
+            {
+                pendingMeshTangents_ = false;
+                if (CanResetTangents) { tangents_.Reset(); }
+            }
+
+            public void MeshBoneWeightsComplete()
+            {
+                pendingMeshBoneWeights_ = false;
+                if (CanResetBoneWeights) { boneWeights = null; }
+            }
+
+            public bool HasPendingPrimitives => pendingGpuPrimitive_ || _pendingComputePrimitive;
+            private bool HasPendingNeutralPoses => pendingNeutralPoseTex_ || _pendingNeutralPoseBuffers;
+
+            private bool CanResetVerts => !HasPendingPrimitives && !HasPendingNeutralPoses && !pendingMeshVerts_;
+            private bool CanResetNormals => !HasPendingPrimitives && !HasPendingNeutralPoses && !pendingMeshNormals_;
+            private bool CanResetTangents => !HasPendingPrimitives && !HasPendingNeutralPoses && !pendingMeshTangents_;
+            private bool CanResetBoneWeights => !HasPendingPrimitives && !pendingMeshBoneWeights_;
+
+            private bool pendingMeshVerts_ = false;
+            private bool pendingMeshTangents_ = false;
+            private bool pendingMeshNormals_ = false;
+            private bool pendingMeshBoneWeights_ = false;
+
+            private bool pendingGpuPrimitive_ = false;
+
+            private bool pendingNeutralPoseTex_ = false;
+
+            private bool _pendingComputePrimitive = false;
+            private bool _pendingNeutralPoseBuffers = false;
+
+            public void Dispose()
+            {
+                Dispose(true);
+                GC.SuppressFinalize(this);
+            }
+
+            private void Dispose(bool isDispose)
+            {
+                boneWeights = null;
+                bindPoses = null;
+
+                triangles.Reset();
+
+                verts_.Reset();
+                normals_.Reset();
+                tangents_.Reset();
+                texCoords.Reset();
+
+                colors.Reset();
+                colorsORMT.Reset();
+                subMeshTypes.Reset();
+
+                OvrAvatarLog.Assert(isDispose, primitiveLogScope);
+            }
+
+            ~MeshInfo()
+            {
+                OvrAvatarLog.LogError("Finalized MeshInfo", primitiveLogScope);
+                Dispose(false);
+            }
+        }
+
+        private class MaterialInfo
+        {
+            public CAPI.ovrAvatar2MaterialTexture[] texturesData = null;
+            public CAPI.ovrAvatar2Image[] imageData = null;
+            public bool hasMetallic = false;
+        }
+
+        // TODO: Look into readonly struct, this doesn't appear to be shared across threads
+        private struct MorphTargetInfo
+        {
+            public readonly string name;
+
+            // TODO: Maybe make these NativeArrays too?
+            public readonly Vector3[] targetPositions;
+            public readonly Vector3[] targetNormals;
+            public readonly Vector3[] targetTangents;
+
+            public MorphTargetInfo(string nameIn, Vector3[] posIn, Vector3[] normIn, Vector3[] tanIn)
+            {
+                this.name = nameIn;
+                this.targetPositions = posIn;
+                this.targetNormals = normIn;
+                this.targetTangents = tanIn;
+            }
+        }
+
+        internal OvrAvatarPrimitive(OvrAvatarResourceLoader loader, in CAPI.ovrAvatar2Primitive primitive) : base(
+            primitive.id, primitive)
+        {
+            // TODO: Can we defer this until later as well?
+            mesh = new Mesh();
+
+            // Name
+
+            unsafe
+            {
+                const int bufferSize = 1024;
+                byte* nameBuffer = stackalloc byte[bufferSize];
+                var result = CAPI.ovrAvatar2Asset_GetPrimitiveName(assetId, nameBuffer, bufferSize);
+                if (result.IsSuccess())
+                {
+                    string meshPrimitiveName = Marshal.PtrToStringAnsi((IntPtr)nameBuffer);
+                    if (!string.IsNullOrEmpty(meshPrimitiveName)) { name = meshPrimitiveName; }
+                }
+                else { OvrAvatarLog.LogWarning($"GetPrimitiveName {result}", primitiveLogScope); }
+            }
+
+            if (name == null) { name = "Mesh" + primitive.id; }
+
+            mesh.name = name;
+            shortName = name.Replace("Primitive", "p");
+        }
+
+        // Must *not* be called more than once
+        private bool _startedLoad = false;
+
+        internal void StartLoad(OvrAvatarResourceLoader loader)
+        {
+            Debug.Assert(!_startedLoad);
+            Debug.Assert(!_loadMeshAsyncSliceHandle.IsValid);
+            Debug.Assert(!_loadMaterialAsyncSliceHandle.IsValid);
+
+            _startedLoad = true;
+
+            var vertCountResult =
+                CAPI.ovrAvatar2VertexBuffer_GetVertexCount(data.vertexBufferId, out _bufferVertexCount);
+            if (!vertCountResult.EnsureSuccess("ovrAvatar2VertexBuffer_GetVertexCount", primitiveLogScope))
+            {
+                _bufferVertexCount = 0;
+                _needsMeshData = false;
+            }
+
+            var morphResult = CAPI.ovrAvatar2Result.Unknown;
+            //primitives might not have a morph target
+            if (data.morphTargetBufferId != CAPI.ovrAvatar2MorphTargetBufferId.Invalid)
+            {
+                morphResult =
+                    CAPI.ovrAvatar2VertexBuffer_GetMorphTargetCount(data.morphTargetBufferId, out _morphTargetCount);
+                //but if they do, then getting the ocunt shouldn't fail
+                morphResult.EnsureSuccess("ovrAvatar2VertexBuffer_GetMorphTargetCount", primitiveLogScope);
+            }
+
+            if (morphResult.IsFailure())
+            {
+                _morphTargetCount = 0;
+                _needsMorphData = false;
+            }
+
+            _loadMeshAsyncSliceHandle = OvrTime.Slice(LoadMeshAsync());
+            _loadMaterialAsyncSliceHandle = OvrTime.Slice(LoadMaterialAsync(loader, loader.resourceId));
+
+            // there are additional flags which will be set before publicly reporting `isLoaded`
+            base.isLoaded = true;
+        }
+
+        private bool _CanCleanupCancellationToken =>
+            !_loadMeshAsyncSliceHandle.IsValid && !_loadMaterialAsyncSliceHandle.IsValid;
+
+        private void _TryCleanupCancellationToken()
+        {
+            if (!_CanCleanupCancellationToken)
+            {
+                // Cancellation called while timesliced operations in progress
+                return;
+            }
+
+            _cancellationTokenSource?.Dispose();
+            _cancellationTokenSource = null;
+        }
+
+        private bool AreAllTasksCancelled()
+        {
+            bool allCancelled = true;
+
+            for (int idx = 0; idx < _apiTasks.Length; ++idx)
+            {
+                ref Task task = ref _apiTasks[idx];
+                if (task != null)
+                {
+                    if (task.IsCompleted)
+                    {
+                        task.Dispose();
+                        task = null;
+                    }
+                    else
+                    {
+                        OvrAvatarLog.LogDebug(
+                            $"Cancelled Task {task} is still running",
+                            primitiveLogScope);
+                        allCancelled = false;
+                    }
+                }
+            }
+
+            if (allCancelled && _apiTasks.Length > 0) { _apiTasks = Array.Empty<Task>(); }
+
+            if (_texturesDataTask != null)
+            {
+                if (_texturesDataTask.IsCompleted)
+                {
+                    _texturesDataTask.Dispose();
+                    _texturesDataTask = null;
+                }
+                else
+                {
+                    OvrAvatarLog.LogError(
+                        $"Cancelled Task {_texturesDataTask} is still running",
+                        primitiveLogScope);
+                    allCancelled = false;
+                }
+            }
+
+            return allCancelled;
+        }
+
+        protected override void _ExecuteCancel()
+        {
+            OvrAvatarLog.Assert(!_hasCancelled);
+            // TODO: Remove this check, this should not be possible
+            if (_hasCancelled)
+            {
+                OvrAvatarLog.LogError($"Double cancelled primitive {name}", primitiveLogScope);
+                return;
+            }
+
+            // TODO: We can probably skip all of this if cancellation token is null
+            _cancellationTokenSource?.Cancel();
+
+            if (_loadMeshAsyncSliceHandle.IsValid)
+            {
+                OvrAvatarLog.LogDebug($"Stopping LoadMeshAsync slice {shortName}", primitiveLogScope);
+                bool didCancel = _loadMeshAsyncSliceHandle.Cancel();
+                OvrAvatarLog.Assert(didCancel, primitiveLogScope);
+            }
+
+            if (_loadMaterialAsyncSliceHandle.IsValid)
+            {
+                OvrAvatarLog.LogDebug($"Stopping LoadMaterialAsync slice {shortName}", primitiveLogScope);
+                bool didCancel = _loadMaterialAsyncSliceHandle.Cancel();
+                OvrAvatarLog.Assert(didCancel, primitiveLogScope);
+            }
+
+            if (AreAllTasksCancelled())
+            {
+                _FinishCancel();
+            }
+            else
+            {
+                OvrTime.Slice(_WaitForCancellation());
+            }
+
+            _hasCancelled = true;
+        }
+
+        private IEnumerator<OvrTime.SliceStep> _WaitForCancellation()
+        {
+            // Wait for all tasks to complete before proceeding with cleanup
+            while (!AreAllTasksCancelled()) { yield return OvrTime.SliceStep.Delay; }
+
+            // Finish cancellation, Dispose of Tasks and Tokens
+            _FinishCancel();
+
+            // Ensure any misc assets created during cancellation window are properly disposed
+            Dispose(true);
+        }
+
+        private void _FinishCancel()
+        {
+            if (gpuPrimitiveBuilder != null)
+            {
+                OvrAvatarLog.LogDebug($"Stopping gpuPrimitiveBuilder {shortName}", primitiveLogScope);
+
+                gpuPrimitiveBuilder.Dispose();
+                gpuPrimitiveBuilder = null;
+            }
+
+            _needsImageData = _needsMeshData = _needsMorphData = false;
+            _TryCleanupCancellationToken();
+        }
+
+        protected override void Dispose(bool disposing)
+        {
+            _loadMeshAsyncSliceHandle.Clear();
+            _loadMaterialAsyncSliceHandle.Clear();
+
+            if (!(mesh is null))
+            {
+                if (disposing) { Mesh.Destroy(mesh); }
+                else
+                {
+                    OvrAvatarLog.LogError(
+                        $"Mesh asset was not destroyed before OvrAvatarPrimitive ({name}, {assetId}) was finalized",
+                        primitiveLogScope);
+                }
+
+                mesh = null;
+            }
+
+            if (!(material is null))
+            {
+                if (disposing) { Material.Destroy(material); }
+                else
+                {
+                    OvrAvatarLog.LogError(
+                        $"Material asset was not destroyed before OvrAvatarPrimitive ({name}, {assetId}) was finalized",
+                        primitiveLogScope);
+                }
+
+                material = null;
+            }
+
+            if (!(gpuPrimitive is null))
+            {
+                if (disposing) { gpuPrimitive.Dispose(); }
+                else
+                {
+                    OvrAvatarLog.LogError(
+                        $"OvrAvatarGPUSkinnedPrimitive asset was not destroyed before OvrAvatarPrimitive ({name}, {assetId}) was finalized"
+                        ,
+                        primitiveLogScope);
+                }
+
+                gpuPrimitive = null;
+            }
+
+            if (!(computePrimitive is null))
+            {
+                if (disposing) { computePrimitive.Dispose(); }
+                else
+                {
+                    OvrAvatarLog.LogError(
+                        $"OvrAvatarComputeSkinnedPrimitive asset was not destroyed before OvrAvatarPrimitive ({name}, {assetId}) was finalized"
+                        ,
+                        primitiveLogScope);
+                }
+
+                computePrimitive = null;
+            }
+
+            _meshInfo?.Dispose();
+
+            joints = null;
+            _shaderConfig = null;
+
+            meshLoaded = false;
+            materialLoaded = false;
+        }
+
+        //:: Main Thread Loading
+
+        #region Main Thread Loading
+
+        private Task[] _apiTasks = Array.Empty<Task>();
+
+        private IEnumerator<OvrTime.SliceStep> LoadMeshAsync()
+        {
+            GetLodInfo();
+            GetManifestationInfo();
+            GetViewInfo();
+            GetSubMeshInclusionInfo();
+            GetHighQualityInfo();
+
+            // load triangles
+            // load mesh & morph targets
+            // create unity mesh and/or gpu skinning resources
+
+            var meshLodInfo = new MeshLodInfo();
+
+            var gpuSkinning = OvrAvatarManager.Instance.OvrGPUSkinnerSupported;
+            var computeSkinning = OvrAvatarManager.Instance.OvrComputeSkinnerSupported;
+            var setupSkin = data.jointCount > 0;
+
+            _meshInfo = new MeshInfo();
+            var morphTargetInfo = new MorphTargetInfo[morphTargetCount];
+            const int alwaysPerformedTasks = 2;
+            var taskCount = alwaysPerformedTasks + (setupSkin ? 1 : 0);
+            var tasks = new Task[taskCount];
+            // for now, all tasks are "CAPI" tasks
+            _apiTasks = tasks;
+
+            tasks[0] = Task.Run(() => { RetrieveTriangles(meshLodInfo, _meshInfo); });
+            if (OvrTime.ShouldHold) { yield return OvrTime.SliceStep.Hold; }
+
+            var tasksAfterTriangles = new Task[morphTargetCount > 0 ? 2 : 1];
+            tasksAfterTriangles[0] =
+                tasks[0].ContinueWith(antecedent => RetrieveMeshData(_meshInfo, in meshLodInfo.repacker));
+            if (morphTargetCount > 0)
+            {
+                tasksAfterTriangles[1] = tasks[0].ContinueWith(
+                    antecedent =>
+                        SetupMorphTargets(morphTargetInfo, in meshLodInfo.repacker));
+            }
+
+            tasks[1] = Task.WhenAll(tasksAfterTriangles);
+            if (OvrTime.ShouldHold) { yield return OvrTime.SliceStep.Hold; }
+
+            if (setupSkin)
+            {
+                tasks[2] = Task.Run(() => SetupSkin(ref _meshInfo));
+            }
+            else
+            {
+                joints = Array.Empty<int>();
+            }
+
+            if (gpuSkinning || computeSkinning)
+            {
+                if (OvrTime.ShouldHold) { yield return OvrTime.SliceStep.Hold; }
+
+                gpuPrimitiveBuilder = new OvrAvatarGpuSkinnedPrimitiveBuilder(shortName, morphTargetCount);
+            }
+            else
+            {
+                // Don't need to wait for gpu skinning
+                gpuSkinningLoaded = true;
+            }
+
+            if (OvrTime.ShouldHold) { yield return OvrTime.SliceStep.Hold; }
+
+            CAPI.ovrAvatar2Vector3f minPos;
+            CAPI.ovrAvatar2Vector3f maxPos;
+            CAPI.ovrAvatar2Result result;
+            if (setupSkin)
+            {
+                result = CAPI.ovrAvatar2Primitive_GetSkinnedMinMaxPosition(data.id, out minPos, out maxPos);
+            }
+            else
+            {
+                result = CAPI.ovrAvatar2Primitive_GetMinMaxPosition(data.id, out minPos, out maxPos);
+            }
+
+            hasBounds = false;
+            Bounds? sdkBounds = null;
+            if (result.IsSuccess())
+            {
+                Vector3 unityMin = minPos;
+                Vector3 unityMax = maxPos;
+                sdkBounds = new Bounds(Vector3.zero, unityMax - unityMin);
+                hasBounds = true;
+            }
+
+            if (OvrTime.ShouldHold) { yield return OvrTime.SliceStep.Hold; }
+
+            while (!AllTasksFinished(tasks))
+            {
+                if (AnyTasksFaulted(tasks))
+                {
+                    // Allow Slicer to cancel before CancelLoad is called or we will cancel during slice!
+                    OvrAvatarLog.LogError("Task fault detected! Disposing resource.", primitiveLogScope);
+                    OvrTime.PostCleanupToUnityMainThread(Dispose);
+                    yield return OvrTime.SliceStep.Cancel;
+                }
+
+                yield return OvrTime.SliceStep.Delay;
+            }
+
+            _needsMeshData = false;
+            _needsMorphData = false;
+
+            if (AllTasksSucceeded(tasks))
+            {
+                _apiTasks = Array.Empty<Task>();
+
+                hasTangents = _meshInfo.hasTangents;
+
+                // TODO: Better way to setup this dependency, we need all preprocessing completed to build GPU resources though :/
+                if (gpuPrimitiveBuilder != null)
+                {
+                    Array.Resize(ref tasks, 1);
+                    tasks[0] = gpuPrimitiveBuilder.CreateSkinningPrimitivesHelperTask(
+                        _meshInfo,
+                        morphTargetInfo,
+                        hasTangents,
+                        gpuSkinning,
+                        computeSkinning);
+
+                    if (OvrTime.ShouldHold) { yield return OvrTime.SliceStep.Hold; }
+                }
+                else
+                {
+                    tasks = Array.Empty<Task>();
+                }
+
+                // TODO: It would be ideal to pull this directly from nativeSDK - requires LOD buffer split
+                _meshVertexCount = _meshInfo.vertexCount;
+
+                // Apply mesh info on main thread
+                if (OvrTime.ShouldHold) { yield return OvrTime.SliceStep.Hold; }
+
+                // Create a vertex buffer using the format and stride.
+                CreateVertexBuffer(_meshInfo);
+
+                if (OvrTime.ShouldHold) { yield return OvrTime.SliceStep.Hold; }
+
+                // Set the mesh vertex buffer parameters from the vertex buffer created above.
+                // Note: final vertex buffer data is not set until the finalized below.
+                var vertexBuffer = _meshInfo.vertexBuffer;
+                mesh.SetVertexBufferParams(vertexBuffer.vertexCount, vertexBuffer.vertexLayout.ToArray());
+
+                // if we extracted colors or submesh types from the model put them into the final mesh
+                // pack the submesh type into the vertex color alpha because no parts of the avatars use alpha
+                if (_meshInfo.colors.Length > 0 || _meshInfo.subMeshTypes.Length > 0)
+                {
+                    if (OvrTime.ShouldHold) { yield return OvrTime.SliceStep.Hold; }
+
+                    // TODO: Move into Task, `AssembleMeshColors` can all be done async
+                    AssembleMeshColors(_meshInfo);
+                }
+
+                if (OvrTime.ShouldHold) { yield return OvrTime.SliceStep.Hold; }
+                StripExcludedSubMeshes(ref _meshInfo.triangles);
+
+                // get number of submeshes
+                // foreach submesh, check to see if it is included
+                // if it is not, then remove this range from the index buffer
+
+                if (OvrTime.ShouldHold) { yield return OvrTime.SliceStep.Hold; }
+                mesh.SetIndices(_meshInfo.triangles, MeshTopology.Triangles, 0, !hasBounds, 0);
+                _meshInfo.triangles.Reset();
+
+                // When UnitySMR is supported, include extra animation data
+                if (OvrAvatarManager.Instance.UnitySMRSupported)
+                {
+                    if (setupSkin)
+                    {
+                        if (OvrTime.ShouldHold) { yield return OvrTime.SliceStep.Hold; }
+
+                        mesh.bindposes = _meshInfo.bindPoses;
+                        _meshInfo.bindPoses = null;
+                    }
+
+                    foreach (var morphTarget in morphTargetInfo)
+                    {
+                        if (OvrTime.ShouldHold) { yield return OvrTime.SliceStep.Hold; }
+
+                        mesh.AddBlendShapeFrame(
+                            morphTarget.name,
+                            1,
+                            morphTarget.targetPositions,
+                            morphTarget.targetNormals,
+                            morphTarget.targetTangents);
+                    }
+                }
+
+                if (OvrTime.ShouldHold) { yield return OvrTime.SliceStep.Hold; }
+
+                // Vertex buffer data.
+                // Copy the vertex data into the vertex buffer array.
+                CopyMeshDataIntoVertexBuffer(_meshInfo);
+                if (OvrTime.ShouldHold) { yield return OvrTime.SliceStep.Hold; }
+
+                // Call the mesh loaded callback before the native arrays are reset.
+                InvokeOnMeshLoaded(mesh, _meshInfo);
+                if (OvrTime.ShouldHold) { yield return OvrTime.SliceStep.Hold; }
+
+                // Resets, these were peppered through the code above, but we needed the data to persist until the
+                // copy above was called.
+                _meshInfo.MeshVertsComplete();
+                _meshInfo.meshColors.Reset();
+                _meshInfo.texCoords.Reset();
+                _meshInfo.colorsORMT.Reset();
+                _meshInfo.MeshTangentsComplete();
+                _meshInfo.MeshNormalsComplete();
+                _meshInfo.MeshBoneWeightsComplete();
+
+                // Upload vertex data to the mesh.
+                for (int i = 0; i < VF_STREAM_COUNT; ++i)
+                {
+                    mesh.SetVertexBufferData(vertexBuffer.vertexStreams[i], 0, 0,
+                        vertexBuffer.vertexCount * vertexBuffer.vertexStrides[i], i);
+                }
+                mesh.UploadMeshData(true);
+
+                // Mark the CPU vertex data as disposable.
+                DisposeVertexBuffer(_meshInfo);
+
+                // It seems that almost every vert data assignment will recalculate (and override) bounds - excellent engine...
+                // So, we must delay this to the very end for no logical reason
+                if (sdkBounds.HasValue)
+                {
+                    if (OvrTime.ShouldHold) { yield return OvrTime.SliceStep.Hold; }
+
+                    mesh.bounds = sdkBounds.Value;
+                }
+
+                if (gpuPrimitiveBuilder != null)
+                {
+                    if (OvrTime.ShouldHold) { yield return OvrTime.SliceStep.Hold; }
+
+                    // TODO: This is not ideal timing for this operation, but it does minimize disruption in this file which is key right now 3/3/2021
+                    // - As of now, there really isn't any meaningful work that can be done off the main thread - pending D26787881
+                    while (!AllTasksFinished(tasks)) { yield return OvrTime.SliceStep.Delay; }
+
+                    // Main thread operations (currently almost all of it), sliced as best possible
+                    if (gpuSkinning)
+                    {
+                        Profiler.BeginSample("Build GPUPrimitive");
+                        gpuPrimitive = gpuPrimitiveBuilder.BuildPrimitive(_meshInfo, joints, hasTangents);
+                        Profiler.EndSample();
+                    }
+
+                    if (computeSkinning)
+                    {
+                        Profiler.BeginSample("Build ComputePrimitive");
+                        computePrimitive = gpuPrimitiveBuilder.BuildComputePrimitive(_meshInfo, hasTangents);
+                        Profiler.EndSample();
+                    }
+
+                    while (gpuPrimitive != null && gpuPrimitive.IsLoading)
+                    {
+                        yield return OvrTime.SliceStep.Delay;
+                    }
+
+                    while (computePrimitive != null && computePrimitive.IsLoading)
+                    {
+                        yield return OvrTime.SliceStep.Delay;
+                    }
+
+                    if (gpuPrimitive != null && gpuPrimitive.MetaData.NumMorphTargetAffectedVerts == 0)
+                    {
+                        _morphTargetCount = 0;
+                    }
+                    else if (computePrimitive != null && computePrimitive.SourceMetaData.numMorphedVerts == 0)
+                    {
+                        _morphTargetCount = 0;
+                    }
+
+                    gpuSkinningLoaded = true;
+
+                    gpuPrimitiveBuilder.Dispose();
+                    gpuPrimitiveBuilder = null;
+                }
+
+                _costData = new AvatarLODCostData(this);
+                meshLoaded = true;
+            }
+            else if (isCancelled)
+            {
+                // Ignore cancellation related exceptions
+                OvrAvatarLog.LogDebug($"LoadMeshAsync was cancelled", primitiveLogScope);
+            }
+            else
+            {
+                // Log errors from Tasks
+                foreach (var task in tasks)
+                {
+                    if (task.Status == TaskStatus.Faulted) { LogTaskErrors(task); }
+                }
+            }
+
+            _loadMeshAsyncSliceHandle.Clear();
+            _TryCleanupCancellationToken();
+        }
+
+        private Task _texturesDataTask = null;
+
+        private IEnumerator<OvrTime.SliceStep> LoadMaterialAsync(OvrAvatarResourceLoader loader,
+            CAPI.ovrAvatar2Id resourceId)
+        {
+            // Info to pass between threads
+            var materialInfo = new MaterialInfo();
+
+            // Marshal texture data on separate thread
+            _texturesDataTask = Task.Run(() => Material_GetTexturesData(resourceId, ref materialInfo));
+            while (WaitForTask(_texturesDataTask, out var step))
+            {
+                yield return step;
+            }
+
+            _texturesDataTask = null;
+
+            // The rest, unfortunately, must be on the main thread
+            _needsImageData = !(materialInfo.texturesData is null) && !(materialInfo.imageData is null);
+            if (_needsImageData)
+            {
+                // Check for images needed by this material. Request image loads on main thread and wait for them.
+                uint numImages = (uint)materialInfo.imageData.Length;
+                var images = new OvrAvatarImage[(int)numImages];
+
+                for (uint imgIdx = 0; imgIdx < numImages; ++imgIdx)
+                {
+                    if (OvrTime.ShouldHold) { yield return OvrTime.SliceStep.Hold; }
+
+                    FindTexture(loader, materialInfo, images, imgIdx, resourceId);
+                }
+
+                _needsImageData = false;
+
+                // Image load wait loop.
+
+                // Wait until all images are fully loaded
+                foreach (var image in images)
+                {
+                    if (image == null) { continue; }
+
+                    while (!image.isLoaded)
+                    {
+                        if (!image.isCancelled)
+                        {
+                            // Loading in progress, delay next slice
+                            yield return OvrTime.SliceStep.Delay;
+                        }
+                        else // isCancelled
+                        {
+                            OvrAvatarLog.LogVerbose(
+                                $"Image {image} cancelled during resource load.",
+                                primitiveLogScope);
+
+                            // Resume checking next frame
+                            // TODO: Switch to Wait, but currently no unit test - use Delay for now
+                            // yield return OvrTime.SliceStep.Wait;
+                            yield return OvrTime.SliceStep.Delay;
+
+                            break; // move to next images
+                        }
+                    }
+                }
+            }
+
+            if (OvrTime.ShouldHold) { yield return OvrTime.SliceStep.Hold; }
+
+            // Configure shader manager and create material
+            if (OvrAvatarManager.Instance == null || OvrAvatarManager.Instance.ShaderManager == null)
+            {
+                OvrAvatarLog.LogError(
+                    $"ShaderManager must be initialized so that a shader can be specified to generate Avatar primitive materials.");
+            }
+            else
+            {
+                bool hasTextures = materialInfo.texturesData != null && materialInfo.texturesData.Length > 0;
+                shaderType =
+                    OvrAvatarManager.Instance.ShaderManager.DetermineConfiguration(
+                        name, materialInfo.hasMetallic,
+                        false, hasTextures);
+                _shaderConfig = OvrAvatarManager.Instance.ShaderManager.GetConfiguration(shaderType);
+            }
+
+            if (_shaderConfig == null)
+            {
+                OvrAvatarLog.LogError($"Could not find config for shaderType {shaderType}", primitiveLogScope);
+                yield break;
+            }
+
+            if (OvrTime.ShouldHold) { yield return OvrTime.SliceStep.Hold; }
+
+            if (_shaderConfig.Material != null) { material = new Material(_shaderConfig.Material); }
+            else
+            {
+                var shader = _shaderConfig.Shader;
+
+                if (shader == null)
+                {
+                    OvrAvatarLog.LogError($"Could not find shader for shaderType {shaderType}", primitiveLogScope);
+                    yield break;
+                }
+
+                material = new Material(shader);
+            }
+
+            material.name = name;
+
+            // Create and apply textures
+            foreach (var textureData in materialInfo.texturesData)
+            {
+                if (OvrTime.ShouldHold) { yield return OvrTime.SliceStep.Hold; }
+
+                // Find corresponding image
+                if (OvrAvatarManager.GetOvrAvatarAsset(textureData.imageId, out OvrAvatarImage image))
+                {
+                    ApplyTexture(image.texture, textureData);
+                }
+                else
+                {
+                    OvrAvatarLog.LogError($"Could not find image {textureData.imageId}", primitiveLogScope);
+                }
+            }
+
+            // TODO: Should this happen before applying textures?
+            if (material != null)
+            {
+                // Finalize dynamically created material
+                _shaderConfig.ApplyKeywords(material);
+                _shaderConfig.ApplyFloatConstants(material);
+
+                bool enableNormalMap = (highQualityFlags & CAPI.ovrAvatar2EntityHighQualityFlags.NormalMaps) != 0;
+                bool enablePropertyHairMap = (highQualityFlags & CAPI.ovrAvatar2EntityHighQualityFlags.PropertyHairMap) != 0;
+                if (enableNormalMap)
+                {
+                    material.EnableKeyword("HAS_NORMAL_MAP_ON");
+                    material.SetFloat("HAS_NORMAL_MAP", 1.0f);
+                }
+                else
+                {
+                    material.DisableKeyword("HAS_NORMAL_MAP_ON");
+                    material.SetFloat("HAS_NORMAL_MAP", 0.0f);
+                }
+
+                if (enablePropertyHairMap)
+                {
+                    material.EnableKeyword("ENABLE_HAIR_ON");
+                    material.SetFloat("ENABLE_HAIR", 1.0f);
+                }
+                else
+                {
+                    material.DisableKeyword("ENABLE_HAIR_ON");
+                    material.SetFloat("ENABLE_HAIR", 0.0f);
+                }
+            }
+
+            LoadAndApplyExtensions();
+
+            materialLoaded = true;
+            _loadMaterialAsyncSliceHandle.Clear();
+            _TryCleanupCancellationToken();
+        }
+
+        private bool LoadAndApplyExtensions()
+        {
+            var result = CAPI.ovrAvatar2Primitive_GetNumMaterialExtensions(assetId, out uint numExtensions);
+            if (!result.IsSuccess())
+            {
+                OvrAvatarLog.LogError(
+                    $"GetNumMaterialExtensions assetId:{assetId}, result:{result}"
+                    , primitiveLogScope);
+                return false;
+            }
+
+            bool success = true;
+            for (uint extensionIdx = 0; extensionIdx < numExtensions; extensionIdx++)
+            {
+                if (OvrAvatarMaterialExtension.LoadExtension(
+                        assetId,
+                        extensionIdx,
+                        out var extension))
+                {
+                    extension.ApplyEntriesToMaterial(material, _shaderConfig.ExtensionConfiguration);
+                }
+                else
+                {
+                    OvrAvatarLog.LogWarning(
+                        $"Unable to load material extension at index {extensionIdx} for assetId:{assetId}",
+                        primitiveLogScope);
+                    success = false;
+                }
+            }
+
+            return success;
+        }
+
+        private bool WaitForTask(Task task, out OvrTime.SliceStep step)
+        {
+            // TODO: isCancelled should be mostly unnecessary here.... mostly.
+            if (isCancelled || task.Status == TaskStatus.Faulted)
+            {
+                step = OvrTime.SliceStep.Cancel;
+                LogTaskErrors(task);
+                return true;
+            }
+
+            if (!task.IsCompleted)
+            {
+                step = OvrTime.SliceStep.Delay;
+                return true;
+            }
+
+            step = OvrTime.SliceStep.Continue;
+            return false;
+        }
+
+        private bool AllTasksFinished(Task[] tasks)
+        {
+            foreach (Task task in tasks)
+            {
+                if (!task.IsCompleted) return false;
+            }
+
+            return true;
+        }
+
+        private bool AllTasksSucceeded(Task[] tasks)
+        {
+            foreach (Task task in tasks)
+            {
+                if (!task.IsCompleted || task.Status == TaskStatus.Faulted) return false;
+            }
+
+            return true;
+        }
+
+        private bool AnyTasksFaulted(Task[] tasks)
+        {
+            foreach (Task task in tasks)
+            {
+                if (task.Status == TaskStatus.Faulted) { return true; }
+            }
+
+            return false;
+        }
+
+        private void LogTaskErrors(Task task)
+        {
+            foreach (var e in task.Exception.InnerExceptions)
+            {
+                OvrAvatarLog.LogError($"{e.Message}\n{e.StackTrace}", primitiveLogScope);
+            }
+        }
+
+        // Helper method for matching imageData to textureData, allows use of local references
+        private void FindTexture(OvrAvatarResourceLoader loader, MaterialInfo materialInfo, OvrAvatarImage[] images,
+            uint imageIndex, CAPI.ovrAvatar2Id resourceId)
+        {
+            ref readonly var imageData = ref materialInfo.imageData[imageIndex];
+
+            for (var texIdx = 0; texIdx < materialInfo.texturesData.Length; ++texIdx)
+            {
+                ref readonly var textureData = ref materialInfo.texturesData[texIdx];
+
+                if (textureData.imageId == imageData.id)
+                {
+                    OvrAvatarLog.LogVerbose(
+                        $"Found match for image index {imageIndex} to texture index {texIdx}",
+                        primitiveLogScope);
+                    // Resolve the image now.
+                    OvrAvatarImage image;
+                    if (!OvrAvatarManager.GetOvrAvatarAsset(imageData.id, out image))
+                    {
+                        OvrAvatarLog.LogDebug($"Created image for id {imageData.id}", primitiveLogScope);
+                        image = loader.CreateImage(in textureData, in imageData, imageIndex, resourceId);
+                    }
+
+                    OvrAvatarLog.Assert(image != null, primitiveLogScope);
+                    images[imageIndex] = image;
+
+                    break;
+                }
+            }
+
+            if (images[imageIndex] == null)
+            {
+                OvrAvatarLog.LogWarning($"Failed to find textures data for image {imageData.id}", primitiveLogScope);
+                // TODO: Assign some sort of fallback image?
+            }
+        }
+
+        #endregion
+
+        private void StripExcludedSubMeshes(ref NativeArray<ushort> triangles)
+        {
+            var ct = _cancellationTokenSource.Token;
+            ct.ThrowIfCancellationRequested();
+
+            if (subMeshInclusionFlags != CAPI.ovrAvatar2EntitySubMeshInclusionFlags.All)
+            {
+                uint subMeshCount = 0;
+                var countResult = CAPI.ovrAvatar2Primitive_GetSubMeshCount(assetId, out subMeshCount);
+                ct.ThrowIfCancellationRequested();
+                if (countResult.IsSuccess())
+                {
+                    unsafe
+                    {
+                        for (uint subMeshIndex = 0; subMeshIndex < subMeshCount; subMeshIndex++)
+                        {
+                            CAPI.ovrAvatar2PrimitiveSubmesh subMesh;
+                            var subMeshResult =
+                                CAPI.ovrAvatar2Primitive_GetSubMeshByIndex(assetId, subMeshIndex, out subMesh);
+                            ct.ThrowIfCancellationRequested();
+                            if (subMeshResult.IsSuccess())
+                            {
+                                // TODO this should honor the _activeSubMeshesIncludeUntyped flag
+                                // ^ This is not possible as that is an OvrAvatarEntity flag
+                                var inclusionType = subMesh.inclusionFlags;
+                                if ((inclusionType & subMeshInclusionFlags) == 0 &&
+                                    inclusionType != CAPI.ovrAvatar2EntitySubMeshInclusionFlags.None)
+                                {
+                                    uint triangleIndex = subMesh.indexStart;
+                                    for (uint triangleCount = 0; triangleCount < subMesh.indexCount; triangleCount++)
+                                    {
+                                        // current strategy is to degenerate the triangle...
+                                        int triangleBase = (int)(triangleIndex + triangleCount);
+                                        triangles[triangleBase] = 0;
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        private void GetLodInfo()
+        {
+            lod = LOD_INVALID;
+
+            var result = CAPI.ovrAvatar2Asset_GetLodFlags(assetId, out var lodFlag);
+            if (result.IsSuccess())
+            {
+                lodFlags = lodFlag;
+
+                // TODO: Handle lods as flags, not a single int. Until then, take the highest quality lod available (lowest bit)
+                const UInt32 highBit = (UInt32)CAPI.ovrAvatar2EntityLODFlags.LOD_4;
+                UInt32 flagValue = (UInt32)lodFlag;
+
+                int i = 0, maskValue = 1 << 0;
+                do
+                {
+                    if ((flagValue & maskValue) != 0)
+                    {
+                        lod = i;
+                        break;
+                    }
+
+                    maskValue = 1 << ++i;
+                } while (maskValue <= highBit);
+            }
+        }
+
+        private void GetViewInfo()
+        {
+            var result = CAPI.ovrAvatar2Asset_GetViewFlags(assetId, out var flags);
+            if (result.IsSuccess())
+            {
+                viewFlags = flags;
+            }
+            else
+            {
+                OvrAvatarLog.LogWarning($"GetViewFlags Failed: {result}", primitiveLogScope);
+            }
+        }
+
+        private void GetManifestationInfo()
+        {
+            var result = CAPI.ovrAvatar2Asset_GetManifestationFlags(assetId, out var flags);
+            if (result.IsSuccess())
+            {
+                manifestationFlags = flags;
+            }
+            else
+            {
+                OvrAvatarLog.LogWarning($"GetManifestationFlags Failed: {result}", primitiveLogScope);
+            }
+        }
+
+        private void GetSubMeshInclusionInfo()
+        {
+            // sub mesh inclusion flags used at this stage will work as load filters,
+            // they must be specified in the creationInfo of the AvatarEntity before loading.
+            var result = CAPI.ovrAvatar2Asset_GetSubMeshInclusionFlags(assetId, out var flags);
+            if (result.IsSuccess())
+            {
+                subMeshInclusionFlags = flags;
+            }
+            else
+            {
+                OvrAvatarLog.LogWarning($"GetSubMeshInclusionInfo Failed: {result}", primitiveLogScope);
+            }
+        }
+
+        private void GetHighQualityInfo()
+        {
+            var result = CAPI.ovrAvatar2Asset_GetHighQualityFlags(assetId, out var flags);
+            if (result.IsSuccess())
+            {
+                highQualityFlags = flags;
+            }
+            else
+            {
+                OvrAvatarLog.LogWarning($"GetHighQualityFlags Failed: {result}", primitiveLogScope);
+            }
+        }
+
+        /////////////////////////////////////////////////
+        //:: Vertex Buffer API
+        // TODO: Factor out into its own file, currently meshInfo access is required for that.
+
+        private void CreateVertexBuffer(MeshInfo meshInfo)
+        {
+            // Apply mesh info on main thread
+            // Get the vertex format information from the fetched vertex data.
+            // We need to build a dynamic layout based on the actual data present.
+            var vertexCount = (int)meshInfo.vertexCount;
+            var vertexFormat = GetVertexFormat(meshInfo, out var vertexStrides);
+
+            var vertices = new NativeArray<byte>[VF_STREAM_COUNT];
+            for (int i = 0; i < VF_STREAM_COUNT; ++i)
+            {
+                var vertexStreamSizeInBytes = vertexStrides[i] * vertexCount;
+                vertices[i] = new NativeArray<byte>(vertexStreamSizeInBytes, _nativeAllocator, _nativeArrayInit);
+            }
+
+            // Create a vertex buffer using the format and stride.
+            meshInfo.vertexBuffer = new VertexBuffer
+            {
+                vertexFormat = vertexFormat,
+                vertexLayout = CreateVertexLayout(vertexFormat),
+                vertexCount = vertexCount,
+                vertexStrides = vertexStrides,
+                vertexStreams = vertices,
+            };
+        }
+
+        private VertexFormat GetVertexFormat(MeshInfo meshInfo, out int[] vertexStrides)
+        {
+            // TODO: Support different attribute formats rather than hardcoding them. This will be useful for quantizing
+            // vertex data to reduce vertex shader read bandwidth.
+            // TODO: Use constants for vector and color sizes.
+            VertexFormat vertexFormat = VertexFormat.VF_POSITION;
+            vertexStrides = new int[VF_STREAM_COUNT];
+            vertexStrides[VF_STREAM_0] = 3 * sizeof(float);   // assume that all vertex formats have a position.
+            if (OvrAvatarManager.Instance.UnitySMRSupported)
+            {
+                if (meshInfo.normals.Length > 0)
+                {
+                    vertexFormat |= VertexFormat.VF_NORMAL;
+                    vertexStrides[VF_STREAM_0] += 3 * sizeof(float);
+                }
+                if (meshInfo.hasTangents && meshInfo.tangents.Length > 0)
+                {
+                    vertexFormat |= VertexFormat.VF_TANGENT;
+                    vertexStrides[VF_STREAM_0] += 4 * sizeof(float);
+                }
+            }
+
+            if (meshInfo.colors.Length > 0 || meshInfo.subMeshTypes.Length > 0)
+            {
+                vertexFormat |= VertexFormat.VF_COLOR;
+                vertexStrides[VF_STREAM_1] += 4;
+            }
+            if (meshInfo.texCoords.Length > 0)
+            {
+                vertexFormat |= VertexFormat.VF_TEXCOORD0;
+                vertexStrides[VF_STREAM_1] += 2 * sizeof(float);
+            }
+            if (meshInfo.colorsORMT.Length > 0)
+            {
+                vertexFormat |= VertexFormat.VF_COLOR_ORMT;
+                vertexStrides[VF_STREAM_1] += 4 * sizeof(float);
+            }
+            if (data.jointCount > 0 && OvrAvatarManager.Instance.UnitySMRSupported)
+            {
+                vertexFormat |= (VertexFormat.VF_BONE_WEIGHTS | VertexFormat.VF_BONE_INDICES);
+                vertexStrides[VF_STREAM_2] += 4 * sizeof(float);    // weights
+                vertexStrides[VF_STREAM_2] += 4;    // bone indices
+            }
+
+            OvrAvatarLog.LogVerbose($"Vertex Format = {vertexFormat}, Strides = [{vertexStrides[VF_STREAM_0]}, {vertexStrides[VF_STREAM_1]}, {vertexStrides[VF_STREAM_2]}]", primitiveLogScope);
+            return vertexFormat;
+        }
+
+        private List<VertexAttributeDescriptor> CreateVertexLayout(VertexFormat format)
+        {
+            // TODO: Support different attribute formats rather than hardcoding them. This will be useful for quantizing
+            // vertex data to reduce vertex shader read bandwidth.
+            var vertexLayout = new List<VertexAttributeDescriptor>();
+            // Note: Unity expects vertex attributes to exist in a specific order, any deviation causes an error.
+            // Order: Position, Normal, Tangent, Color, TexCoord0, TexCoord1, ..., BlendWeights, BlendIndices
+            if ((format & VertexFormat.VF_POSITION) == VertexFormat.VF_POSITION)
+            {
+                vertexLayout.Add(new VertexAttributeDescriptor(VertexAttribute.Position, VertexAttributeFormat.Float32,
+                    3, VF_STREAM_0));
+            }
+            if ((format & VertexFormat.VF_NORMAL) == VertexFormat.VF_NORMAL)
+            {
+                vertexLayout.Add(new VertexAttributeDescriptor(VertexAttribute.Normal, VertexAttributeFormat.Float32, 3,
+                    VF_STREAM_0));
+            }
+            if ((format & VertexFormat.VF_TANGENT) == VertexFormat.VF_TANGENT)
+            {
+                vertexLayout.Add(new VertexAttributeDescriptor(VertexAttribute.Tangent, VertexAttributeFormat.Float32,
+                    4, VF_STREAM_0));
+            }
+            if ((format & VertexFormat.VF_COLOR) == VertexFormat.VF_COLOR)
+            {
+                vertexLayout.Add(new VertexAttributeDescriptor(VertexAttribute.Color, VertexAttributeFormat.UNorm8, 4,
+                    VF_STREAM_1));
+            }
+            if ((format & VertexFormat.VF_TEXCOORD0) == VertexFormat.VF_TEXCOORD0)
+            {
+                vertexLayout.Add(new VertexAttributeDescriptor(VertexAttribute.TexCoord0, VertexAttributeFormat.Float32,
+                    2, VF_STREAM_1));
+            }
+            if ((format & VertexFormat.VF_COLOR_ORMT) == VertexFormat.VF_COLOR_ORMT)
+            {
+                vertexLayout.Add(new VertexAttributeDescriptor(VertexAttribute.TexCoord1, VertexAttributeFormat.Float32,
+                    4, VF_STREAM_1));
+            }
+            if ((format & VertexFormat.VF_BONE_WEIGHTS) == VertexFormat.VF_BONE_WEIGHTS)
+            {
+                vertexLayout.Add(new VertexAttributeDescriptor(VertexAttribute.BlendWeight,
+                    VertexAttributeFormat.Float32, 4, VF_STREAM_2));
+            }
+            if ((format & VertexFormat.VF_BONE_INDICES) == VertexFormat.VF_BONE_INDICES)
+            {
+                vertexLayout.Add(new VertexAttributeDescriptor(VertexAttribute.BlendIndices,
+                    VertexAttributeFormat.UInt8, 4, VF_STREAM_2));
+            }
+
+            return vertexLayout;
+        }
+
+        private void CopyMeshDataIntoVertexBuffer(MeshInfo meshInfo)
+        {
+            var vertexBuffer = meshInfo.vertexBuffer;
+            var vertexFormat = vertexBuffer.vertexFormat;
+
+            unsafe
+            {
+                var vertices = vertexBuffer.vertexStreams[VF_STREAM_0].GetPtr();
+                var vertexStride = vertexBuffer.vertexStrides[VF_STREAM_0];
+                for (int i = 0; i < vertexBuffer.vertexCount; i++)
+                {
+                    byte* outBuffer = &vertices[vertexStride * i];
+                    int offset = 0;
+                    if ((vertexFormat & VertexFormat.VF_POSITION) == VertexFormat.VF_POSITION)
+                    {
+                        Vector3* outPos = (Vector3*)&outBuffer[offset];
+                        *outPos = meshInfo.verts[i];
+                        offset += 3 * sizeof(float);
+                    }
+
+                    if ((vertexFormat & VertexFormat.VF_NORMAL) == VertexFormat.VF_NORMAL)
+                    {
+                        Vector3* outNrm = (Vector3*)&outBuffer[offset];
+                        *outNrm = meshInfo.normals[i];
+                        offset += 3 * sizeof(float);
+                    }
+
+                    if ((vertexFormat & VertexFormat.VF_TANGENT) == VertexFormat.VF_TANGENT)
+                    {
+                        Vector4* outTangent = (Vector4*)&outBuffer[offset];
+                        *outTangent = meshInfo.tangents[i];
+                        offset += 4 * sizeof(float);
+                    }
+                }
+
+                vertices = vertexBuffer.vertexStreams[VF_STREAM_1].GetPtr();
+                vertexStride = vertexBuffer.vertexStrides[VF_STREAM_1];
+                for (int i = 0; i < vertexBuffer.vertexCount; i++)
+                {
+                    byte* outBuffer = &vertices[vertexStride * i];
+                    int offset = 0;
+                    if ((vertexFormat & VertexFormat.VF_COLOR) == VertexFormat.VF_COLOR)
+                    {
+                        Color32* outColor = (Color32*)&outBuffer[offset];
+                        *outColor = meshInfo.meshColors[i];
+                        offset += 4;
+                    }
+
+                    if ((vertexFormat & VertexFormat.VF_TEXCOORD0) == VertexFormat.VF_TEXCOORD0)
+                    {
+                        Vector2* outUv = (Vector2*)&outBuffer[offset];
+                        *outUv = meshInfo.texCoords[i];
+                        offset += 2 * sizeof(float);
+                    }
+
+                    if ((vertexFormat & VertexFormat.VF_COLOR_ORMT) == VertexFormat.VF_COLOR_ORMT)
+                    {
+                        Vector4* outColor = (Vector4*)&outBuffer[offset];
+                        *outColor = meshInfo.colorsORMT[i];
+                        offset += 4 * sizeof(float);
+                    }
+                }
+
+                vertices = vertexBuffer.vertexStreams[VF_STREAM_2].GetPtr();
+                vertexStride = vertexBuffer.vertexStrides[VF_STREAM_2];
+                for (int i = 0; i < vertexBuffer.vertexCount; i++)
+                {
+                    byte* outBuffer = &vertices[vertexStride * i];
+                    int offset = 0;
+                    if ((vertexFormat & VertexFormat.VF_BONE_WEIGHTS) == VertexFormat.VF_BONE_WEIGHTS)
+                    {
+                        Vector4* outWeights = (Vector4*)&outBuffer[offset];
+                        *outWeights = new Vector4(
+                            meshInfo.boneWeights[i].weight0, meshInfo.boneWeights[i].weight1,
+                            meshInfo.boneWeights[i].weight2, meshInfo.boneWeights[i].weight3);
+                        offset += 4 * sizeof(float);
+                    }
+
+                    if ((vertexFormat & VertexFormat.VF_BONE_INDICES) == VertexFormat.VF_BONE_INDICES)
+                    {
+                        Color32* outIndices = (Color32*)&outBuffer[offset];
+                        outIndices->r = (byte)meshInfo.boneWeights[i].boneIndex0;
+                        outIndices->g = (byte)meshInfo.boneWeights[i].boneIndex1;
+                        outIndices->b = (byte)meshInfo.boneWeights[i].boneIndex2;
+                        outIndices->a = (byte)meshInfo.boneWeights[i].boneIndex3;
+                        offset += 4;
+                    }
+                }
+            }
+        }
+
+        private static void DisposeVertexBuffer(MeshInfo meshInfo)
+        {
+            for (int i = 0; i < VF_STREAM_COUNT; ++i)
+            {
+                meshInfo.vertexBuffer.vertexStreams[i].Reset();
+            }
+        }
+
+        /////////////////////////////////////////////////
+        //:: Build Mesh
+
+        private void RetrieveTriangles(MeshLodInfo meshMetaInfo, MeshInfo meshInfo)
+        {
+            // Get index buffer, we will use this to strip out data for other LODs
+            meshInfo.triangles = CreateIndexBuffer(data, out meshMetaInfo.repacker);
+            if (!meshInfo.triangles.IsCreated)
+            {
+                throw new Exception("RetrieveTriangles failed");
+            }
+
+            // TODO: Confirm topology - we only currently support triangle
+            triCount = (uint)(meshInfo.triangles.Length / 3);
+        }
+
+        private void RetrieveMeshData(MeshInfo meshInfo, in VertexRepacker vertexRepacker)
+        {
+            var ct = _cancellationTokenSource.Token;
+            ct.ThrowIfCancellationRequested();
+
+            // Apply Data
+            meshInfo.verts = CreateVertexPositions(in vertexRepacker, ct);
+            meshInfo.normals = CreateVertexNormals(in vertexRepacker, ct);
+            meshInfo.tangents = CreateVertexTangents(in vertexRepacker, ct);
+            meshInfo.texCoords = CreateVertexTexCoords(in vertexRepacker, ct);
+
+            meshInfo.colors = CreateVertexColors(in vertexRepacker, ct);
+            meshInfo.colorsORMT = CreateVertexColorsORMT(in vertexRepacker, ct);
+            meshInfo.subMeshTypes = CreateVertexSubMeshTypes(in vertexRepacker, ct);
+
+            meshInfo.boneWeights = data.jointCount > 0 ? RetrieveBoneWeights(in vertexRepacker, ct) : null;
+        }
+
+        private void InvokeOnMeshLoaded(Mesh sourceMesh, MeshInfo meshInfo)
+        {
+            OvrAvatarLog.LogInfo($"InvokeOnAvatarMeshLoaded", "", OvrAvatarManager.Instance);
+            Profiler.BeginSample("OvrAvatarManager::InvokeOnAvatarMeshLoaded Callbacks");
+            try
+            {
+                if (OvrAvatarManager.Instance.HasMeshLoadListener)
+                {
+                    var destMesh = new OvrAvatarManager.MeshData(
+                        sourceMesh.name,
+                        sourceMesh.triangles,
+                        // We want to decouple the GPU vertex representation from the CPU representation.
+                        // So instead of reading from the mesh directly, we read from the internal mesh info.
+                        (meshInfo.verts.IsCreated) ? meshInfo.verts.ToArray() : Array.Empty<Vector3>(),
+                        (meshInfo.normals.IsCreated) ? meshInfo.normals.ToArray() : Array.Empty<Vector3>(),
+                        (meshInfo.meshColors.IsCreated) ? meshInfo.meshColors.ToArray() : Array.Empty<Color32>(),
+                        (meshInfo.texCoords.IsCreated) ? meshInfo.texCoords.ToArray() : Array.Empty<Vector2>(),
+                        (meshInfo.tangents.IsCreated) ? meshInfo.tangents.ToArray() : Array.Empty<Vector4>(),
+                        meshInfo.boneWeights,
+                        // Bind poses are not part of the vertex data.
+                        sourceMesh.bindposes
+                    );
+                    OvrAvatarManager.Instance.InvokeMeshLoadEvent(this, destMesh);
+                }
+            }
+            catch (Exception e)
+            {
+                OvrAvatarLog.LogException(
+                    "OnAvatarMeshLoaded user callback", e, primitiveLogScope,
+                    OvrAvatarManager.Instance);
+            }
+            finally { Profiler.EndSample(); }
+        }
+
+        private void AssembleMeshColors(MeshInfo meshInfo)
+        {
+            uint vertCount = meshInfo.vertexCount;
+            int colorsCount = meshInfo.colors.Length;
+            int subMeshCount = meshInfo.subMeshTypes.Length;
+            if (vertCount > 0 && (colorsCount > 0 || subMeshCount > 0))
+            {
+                var finalMeshColors
+                    = new NativeArray<Color32>((int)vertCount, _nativeAllocator, _nativeArrayInit);
+
+                try
+                {
+                    unsafe
+                    {
+                        Color* meshColors = colorsCount > 0 ? meshInfo.colors.GetPtr() : null;
+                        float* subMeshTypes = subMeshCount > 0 ? meshInfo.subMeshTypes.GetPtr() : null;
+
+                        Color32* finalColors = finalMeshColors.GetPtr();
+                        Debug.Assert(finalColors != null);
+
+                        for (uint vertIdx = 0; vertIdx < vertCount; vertIdx++)
+                        {
+                            var finalMeshColor = new Color32(255, 255, 255, 0);
+                            if (vertIdx < colorsCount)
+                            {
+                                Debug.Assert(meshColors != null);
+
+                                var originalColor = meshColors + vertIdx;
+                                finalMeshColor.r = (byte)(originalColor->r * 255f);
+                                finalMeshColor.g = (byte)(originalColor->g * 255f);
+                                finalMeshColor.b = (byte)(originalColor->b * 255f);
+                            }
+
+                            if (vertIdx < subMeshCount)
+                            {
+                                Debug.Assert(subMeshTypes != null);
+
+                                finalMeshColor.a = (byte)subMeshTypes[vertIdx];
+                            }
+
+                            finalColors[vertIdx] = finalMeshColor;
+                        }
+                    }
+                    meshInfo.meshColors = finalMeshColors;
+                }
+                catch (Exception e)
+                {
+                    OvrAvatarLog.LogException("AssembleMeshColors", e, primitiveLogScope);
+
+                    finalMeshColors.Reset();
+                }
+            }
+
+            meshInfo.colors.Reset();
+            meshInfo.subMeshTypes.Reset();
+        }
+
+        #region Retrieve Primitive Data
+
+        private delegate CAPI.ovrAvatar2Result VertexBufferAccessor(
+            CAPI.ovrAvatar2VertexBufferId vertexBufferId, IntPtr buffer, UInt32 bytes,
+            UInt32 stride);
+
+        private delegate CAPI.ovrAvatar2Result VertexBufferAccessorWithPrimId(
+            CAPI.ovrAvatar2Id primitiveId, CAPI.ovrAvatar2VertexBufferId vertexBufferId, IntPtr buffer, UInt32 bytes,
+            UInt32 stride);
+
+        private NativeArray<T> CreateVertexData<T>(in VertexRepacker repacker
+            , VertexBufferAccessor accessor
+            , string accessorName, CancellationToken ct) where T : unmanaged
+        {
+            ct.ThrowIfCancellationRequested();
+
+            NativeArray<T> vertsBufferArray = default;
+            try
+            {
+                vertsBufferArray = new NativeArray<T>((int)bufferVertexCount, _nativeAllocator, _nativeArrayInit);
+                IntPtr vertsBuffer = vertsBufferArray.GetIntPtr();
+
+                var elementSize = UnsafeUtility.SizeOf<T>();
+                UInt32 stride = (UInt32)elementSize;
+                UInt32 bufferSize = vertsBufferArray.GetBufferSize(elementSize);
+                var result = accessor(
+                    data.vertexBufferId, vertsBuffer, bufferSize, stride);
+
+                if (repacker.NeedsRepacking)
+                {
+                    return CreateProcessResult(repacker, vertsBufferArray, accessorName, result, ct);
+                }
+                else
+                {
+                    switch (result)
+                    {
+                        case CAPI.ovrAvatar2Result.Success:
+                            var resultBuffer = vertsBufferArray;
+                            vertsBufferArray = default;
+                            return resultBuffer;
+
+                        case CAPI.ovrAvatar2Result.DataNotAvailable:
+                            return default;
+
+                        default:
+                            OvrAvatarLog.LogError($"{accessorName} {result}", primitiveLogScope);
+                            return default;
+                    }
+                }
+            }
+            finally { vertsBufferArray.Reset(); }
+        }
+
+        private NativeArray<T> CreateVertexDataWithPrimId<T>(in VertexRepacker repacker
+            , VertexBufferAccessorWithPrimId accessor
+            , string accessorName, CancellationToken ct) where T : unmanaged
+        {
+            ct.ThrowIfCancellationRequested();
+
+            NativeArray<T> vertsBufferArray = default;
+            try
+            {
+                vertsBufferArray = new NativeArray<T>((int)bufferVertexCount, _nativeAllocator, _nativeArrayInit);
+                {
+                    var elementSize = UnsafeUtility.SizeOf<T>();
+                    var vertsBuffer = vertsBufferArray.GetIntPtr();
+                    var bufferSize = vertsBufferArray.GetBufferSize(elementSize);
+                    var stride = (UInt32)elementSize;
+
+                    var result = accessor(data.id, data.vertexBufferId, vertsBuffer, bufferSize, stride);
+                    if (repacker.NeedsRepacking)
+                    {
+                        return CreateProcessResult(repacker, vertsBufferArray, accessorName, result, ct);
+                    }
+                    else
+                    {
+                        var resultBuffer = vertsBufferArray;
+                        vertsBufferArray = default;
+                        return resultBuffer;
+                    }
+                }
+            }
+            finally { vertsBufferArray.Reset(); }
+        }
+
+        private NativeArray<Vector3> CreateVertexPositions(in VertexRepacker repacker, CancellationToken ct)
+        {
+            return CreateVertexData<Vector3>(
+                in repacker
+                , CAPI.ovrAvatar2VertexBuffer_GetPositions, "GetVertexPositions", ct);
+        }
+
+        private NativeArray<Vector3> CreateVertexNormals(in VertexRepacker repacker, CancellationToken ct)
+        {
+            return CreateVertexData<Vector3>(
+                in repacker
+                , CAPI.ovrAvatar2VertexBuffer_GetNormals, "GetVertexNormals", ct);
+        }
+
+        private NativeArray<Vector4> CreateVertexTangents(in VertexRepacker repacker, CancellationToken ct)
+        {
+            return CreateVertexData<Vector4>(
+                in repacker
+                , CAPI.ovrAvatar2VertexBuffer_GetTangents, "GetVertexTangents", ct);
+        }
+
+        private NativeArray<Color> CreateVertexColors(in VertexRepacker repacker, CancellationToken ct)
+        {
+            return CreateVertexData<Color>(
+                in repacker
+                , CAPI.ovrAvatar2VertexBuffer_GetColors, "GetVertexColors", ct);
+        }
+
+        private NativeArray<Color> CreateVertexColorsORMT(in VertexRepacker repacker, CancellationToken ct)
+        {
+            return CreateVertexData<Color>(
+                in repacker
+                , CAPI.ovrAvatar2VertexBuffer_GetColorsORMT, "GetVertexColorsORMT", ct);
+        }
+
+        private NativeArray<Vector2> CreateVertexTexCoords(in VertexRepacker repacker, CancellationToken ct)
+        {
+            return CreateVertexData<Vector2>(
+                in repacker
+                , CAPI.ovrAvatar2VertexBuffer_GetTexCoord0, "GetVertexTexCoord0", ct);
+        }
+
+        private NativeArray<float> CreateVertexSubMeshTypes(in VertexRepacker repacker, CancellationToken ct)
+        {
+            return CreateVertexDataWithPrimId<float>(
+                in repacker
+                , CAPI.ovrAvatar2VertexBuffer_GetSubMeshTypesFloat, "GetSubMeshTypesFloat", ct);
+        }
+
+        private BoneWeight[] RetrieveBoneWeights(in VertexRepacker repacker, CancellationToken ct)
+        {
+            ct.ThrowIfCancellationRequested();
+
+            var vec4usStride = (UInt32)UnsafeUtility.SizeOf<CAPI.ovrAvatar2Vector4us>();
+            var vec4fStride = (UInt32)UnsafeUtility.SizeOf<CAPI.ovrAvatar2Vector4f>();
+
+            var indicesBuffer =
+                new NativeArray<CAPI.ovrAvatar2Vector4us>((int)bufferVertexCount, _nativeAllocator, _nativeArrayInit);
+            var weightsBuffer =
+                new NativeArray<CAPI.ovrAvatar2Vector4f>((int)bufferVertexCount, _nativeAllocator, _nativeArrayInit);
+
+            try
+            {
+                IntPtr indicesPtr = indicesBuffer.GetIntPtr();
+                IntPtr weightsPtr = weightsBuffer.GetIntPtr();
+
+                var indicesBufferSize = indicesBuffer.GetBufferSize(vec4usStride);
+                var weightsBufferSize = weightsBuffer.GetBufferSize(vec4fStride);
+
+                var result = CAPI.ovrAvatar2VertexBuffer_GetJointIndices(
+                    data.vertexBufferId, indicesPtr, indicesBufferSize, vec4usStride);
+                ct.ThrowIfCancellationRequested();
+                if (result == CAPI.ovrAvatar2Result.DataNotAvailable) { return Array.Empty<BoneWeight>(); }
+                else if (result != CAPI.ovrAvatar2Result.Success)
+                {
+                    OvrAvatarLog.LogError($"GetVertexJointIndices {result}", primitiveLogScope);
+                    return null;
+                }
+
+                result = CAPI.ovrAvatar2VertexBuffer_GetJointWeights(
+                    data.vertexBufferId, weightsPtr,
+                    weightsBufferSize, vec4fStride);
+                ct.ThrowIfCancellationRequested();
+                if (result == CAPI.ovrAvatar2Result.DataNotAvailable) { return Array.Empty<BoneWeight>(); }
+                else if (result != CAPI.ovrAvatar2Result.Success)
+                {
+                    OvrAvatarLog.LogError($"GetVertexJointWeights {result}", primitiveLogScope);
+                    return null;
+                }
+
+                ct.ThrowIfCancellationRequested();
+
+                using (var boneWeightsSrc =
+                       new NativeArray<BoneWeight>((int)bufferVertexCount, _nativeAllocator, _nativeArrayInit))
+                {
+                    var boneWeights = boneWeightsSrc.Slice();
+
+                    unsafe
+                    {
+                        var indices = (CAPI.ovrAvatar2Vector4us*)indicesBuffer.GetUnsafePtr();
+                        var weights = (CAPI.ovrAvatar2Vector4f*)weightsBuffer.GetUnsafePtr();
+                        for (int i = 0; i < bufferVertexCount; ++i)
+                        {
+                            ref CAPI.ovrAvatar2Vector4us jointIndex = ref indices[i];
+                            ref CAPI.ovrAvatar2Vector4f jointWeight = ref weights[i];
+
+                            var boneWeight = new BoneWeight
+                            {
+                                boneIndex0 = jointIndex.x,
+                                boneIndex1 = jointIndex.y,
+                                boneIndex2 = jointIndex.z,
+                                boneIndex3 = jointIndex.w,
+                                weight0 = jointWeight.x,
+                                weight1 = jointWeight.y,
+                                weight2 = jointWeight.z,
+                                weight3 = jointWeight.w
+                            };
+                            boneWeights[i] = boneWeight;
+                        }
+                    }
+
+                    ct.ThrowIfCancellationRequested();
+
+                    return repacker.RepackAttribute(boneWeightsSrc);
+                }
+            }
+            finally
+            {
+                indicesBuffer.Dispose();
+                weightsBuffer.Dispose();
+            }
+        }
+
+        private NativeArray<UInt16> CreateIndexBuffer(in CAPI.ovrAvatar2Primitive prim, out VertexRepacker repacker)
+        {
+            var ct = _cancellationTokenSource.Token;
+            ct.ThrowIfCancellationRequested();
+
+            NativeArray<UInt16> triBuffer = default;
+            try
+            {
+                triBuffer = new NativeArray<UInt16>((int)data.indexCount, _nativeAllocator, _nativeArrayInit);
+
+                UInt32 bufferSize = triBuffer.GetBufferSize(sizeof(UInt16));
+                bool result = CAPI.OvrAvatar2Primitive_GetIndexData(assetId, in triBuffer, bufferSize);
+                if (!result)
+                {
+                    repacker = default;
+                    return default;
+                }
+
+                ct.ThrowIfCancellationRequested();
+                // may steal triBuffer, pass it back as packedIndices, and set triBuffer to default
+                repacker = VertexRepacker.Create(
+                    ref triBuffer, in prim, _bufferVertexCount, out var packedIndices,
+                    ct);
+                return packedIndices;
+            }
+            finally { triBuffer.Reset(); }
+        }
+
+        #endregion
+
+        /////////////////////////////////////////////////
+        //:: Build Material
+
+        #region Build Material
+
+        private void Material_GetTexturesData(CAPI.ovrAvatar2Id resourceId, ref MaterialInfo materialInfo)
+        {
+            var ct = _cancellationTokenSource.Token;
+            ct.ThrowIfCancellationRequested();
+
+            CAPI.ovrAvatar2Result result;
+
+            // Get data for all textures
+            materialInfo.texturesData = new CAPI.ovrAvatar2MaterialTexture[data.textureCount];
+            for (UInt32 i = 0; i < data.textureCount; ++i)
+            {
+                ref var materialTexture = ref materialInfo.texturesData[i];
+                result = CAPI.ovrAvatar2Primitive_GetMaterialTextureByIndex(assetId, i, out materialTexture);
+                ct.ThrowIfCancellationRequested();
+                if (result != CAPI.ovrAvatar2Result.Success)
+                {
+                    OvrAvatarLog.LogError($"GetMaterialTextureByIndex ({i}) {result}", primitiveLogScope);
+
+                    materialInfo.texturesData[i] = default;
+                    continue;
+                }
+
+                if (materialTexture.type == CAPI.ovrAvatar2MaterialTextureType.MetallicRoughness)
+                {
+                    materialInfo.hasMetallic = true;
+                }
+            }
+
+            // Get data for all images
+            result = CAPI.ovrAvatar2Asset_GetImageCount(resourceId, out UInt32 imageCount);
+            ct.ThrowIfCancellationRequested();
+            if (result != CAPI.ovrAvatar2Result.Success)
+            {
+                OvrAvatarLog.LogError($"GetImageCount {result}", primitiveLogScope);
+                return;
+            }
+
+            materialInfo.imageData = new CAPI.ovrAvatar2Image[imageCount];
+
+            for (UInt32 i = 0; i < imageCount; ++i)
+            {
+                ref var imageData = ref materialInfo.imageData[i];
+                result = CAPI.ovrAvatar2Asset_GetImageByIndex(resourceId, i, out imageData);
+                ct.ThrowIfCancellationRequested();
+                if (result != CAPI.ovrAvatar2Result.Success)
+                {
+                    OvrAvatarLog.LogError($"GetImageByIndex ({i}) {result}", primitiveLogScope);
+
+                    materialInfo.imageData[i] = default;
+                    continue;
+                }
+            }
+        }
+
+        private void ApplyTexture(Texture2D texture, CAPI.ovrAvatar2MaterialTexture textureData)
+        {
+            switch (textureData.type)
+            {
+                case CAPI.ovrAvatar2MaterialTextureType.BaseColor:
+                    if (!string.IsNullOrEmpty(_shaderConfig.NameTextureParameter_baseColorTexture))
+                        material.SetTexture(_shaderConfig.NameTextureParameter_baseColorTexture, texture);
+                    material.SetColor(
+                        _shaderConfig.NameColorParameter_BaseColorFactor,
+                        _shaderConfig.UseColorParameter_BaseColorFactor ? textureData.factor : Color.white);
+                    material.mainTexture = texture;
+                    break;
+
+                case CAPI.ovrAvatar2MaterialTextureType.Normal:
+                    if (!string.IsNullOrEmpty(_shaderConfig.NameTextureParameter_normalTexture))
+                        material.SetTexture(_shaderConfig.NameTextureParameter_normalTexture, texture);
+                    break;
+
+                case CAPI.ovrAvatar2MaterialTextureType.Emissive:
+                    if (!string.IsNullOrEmpty(_shaderConfig.NameTextureParameter_emissiveTexture))
+                        material.SetTexture(_shaderConfig.NameTextureParameter_emissiveTexture, texture);
+                    break;
+
+                case CAPI.ovrAvatar2MaterialTextureType.Occulusion:
+                    if (!string.IsNullOrEmpty(_shaderConfig.NameTextureParameter_occlusionTexture))
+                        material.SetTexture(_shaderConfig.NameTextureParameter_occlusionTexture, texture);
+                    break;
+
+                case CAPI.ovrAvatar2MaterialTextureType.MetallicRoughness:
+                    if (!string.IsNullOrEmpty(_shaderConfig.NameTextureParameter_metallicRoughnessTexture))
+                        material.SetTexture(_shaderConfig.NameTextureParameter_metallicRoughnessTexture, texture);
+                    material.SetFloat(
+                        _shaderConfig.NameFloatParameter_MetallicFactor,
+                        _shaderConfig.UseFloatParameter_MetallicFactor ? textureData.factor.x : 1f);
+                    material.SetFloat(
+                        _shaderConfig.NameFloatParameter_RoughnessFactor,
+                        _shaderConfig.UseFloatParameter_RoughnessFactor ? textureData.factor.y : 1f);
+                    break;
+
+                case CAPI.ovrAvatar2MaterialTextureType.UsedInExtension:
+                    // Let extensions handle it
+                    break;
+                default:
+                    throw new ArgumentOutOfRangeException();
+            }
+        }
+
+        #endregion
+
+        /////////////////////////////////////////////////
+        //:: Build Other Data
+
+        private void SetupMorphTargets(MorphTargetInfo[] morphTargetInfo, in VertexRepacker repacker)
+        {
+            var ct = _cancellationTokenSource.Token;
+            ct.ThrowIfCancellationRequested();
+
+            var sizeOfOvrVector3 = Marshal.SizeOf<CAPI.ovrAvatar2Vector3f>();
+            UInt32 bufferSize = (UInt32)(sizeOfOvrVector3 * bufferVertexCount);
+            UInt32 stride = (UInt32)sizeOfOvrVector3;
+
+            if (morphTargetInfo.Length != morphTargetCount)
+            {
+                throw new Exception(
+                    $"Incorrect morphTargetInfo[] size. Was {morphTargetInfo.Length}, but expected {morphTargetCount}");
+            }
+
+            unsafe
+            {
+                const int nameBufferLength = 255;
+                byte* nameBuffer = stackalloc byte[nameBufferLength];
+                for (UInt32 iMorphTarget = 0; iMorphTarget < morphTargetCount; ++iMorphTarget)
+                {
+                    // Would be nice if we had a single simple CAPI that returned what attributes were available, one call to get all 3
+                    // we want to figure out which are available before we spend time allocating giant buffers.
+                    var positionsResult =
+                        CAPI.ovrAvatar2MorphTarget_GetVertexPositions(
+                            data.morphTargetBufferId, iMorphTarget,
+                            null, 0, stride);
+                    if (!positionsResult.IsSuccess())
+                    {
+                        OvrAvatarLog.LogError(
+                            $"MorphTarget_GetVertexPositions ({iMorphTarget}) {positionsResult}",
+                            primitiveLogScope);
+                        continue;
+                    }
+
+                    var normalsResult =
+                        CAPI.ovrAvatar2MorphTarget_GetVertexNormals(
+                            data.morphTargetBufferId, iMorphTarget,
+                            null, 0, stride);
+                    bool normalsAvailable = normalsResult.IsSuccess();
+                    if (!normalsAvailable && normalsResult != CAPI.ovrAvatar2Result.DataNotAvailable)
+                    {
+                        OvrAvatarLog.LogError(
+                            $"MorphTarget_GetVertexNormals ({iMorphTarget}) {normalsResult}",
+                            primitiveLogScope);
+                        continue;
+                    }
+
+                    var tangentsResult =
+                        CAPI.ovrAvatar2MorphTarget_GetVertexTangents(
+                            data.morphTargetBufferId, iMorphTarget,
+                            null, 0, stride);
+                    bool tangentsAvailable = tangentsResult.IsSuccess();
+                    if (!tangentsAvailable && tangentsResult != CAPI.ovrAvatar2Result.DataNotAvailable)
+                    {
+                        OvrAvatarLog.LogError(
+                            $"MorphTarget_GetVertexTangents ({iMorphTarget}) {tangentsResult}",
+                            primitiveLogScope);
+                        continue;
+                    }
+
+                    ct.ThrowIfCancellationRequested();
+
+                    NativeArray<Vector3> positionsArray = default;
+                    NativeArray<Vector3> normalsArray = default;
+                    NativeArray<Vector3> tangentsArray = default;
+                    try
+                    {
+                        // Positions
+                        positionsArray =
+                            new NativeArray<Vector3>((int)bufferVertexCount, _nativeAllocator, _nativeArrayInit);
+
+                        positionsResult =
+                            CAPI.ovrAvatar2MorphTarget_GetVertexPositions(
+                                data.morphTargetBufferId, iMorphTarget,
+                                positionsArray.CastOvrPtr(), bufferSize, stride);
+                        ct.ThrowIfCancellationRequested();
+                        if (!positionsResult.IsSuccess())
+                        {
+                            OvrAvatarLog.LogError(
+                                $"MorphTarget_GetVertexPositions ({iMorphTarget}) {positionsResult}",
+                                primitiveLogScope);
+                            continue;
+                        }
+
+                        // Normals
+                        if (normalsAvailable)
+                        {
+                            normalsArray = new NativeArray<Vector3>(
+                                (int)bufferVertexCount, _nativeAllocator,
+                                _nativeArrayInit);
+
+                            normalsResult =
+                                CAPI.ovrAvatar2MorphTarget_GetVertexNormals(
+                                    data.morphTargetBufferId, iMorphTarget,
+                                    normalsArray.CastOvrPtr(), bufferSize, stride);
+                            ct.ThrowIfCancellationRequested();
+                            normalsAvailable = normalsResult.IsSuccess();
+                            if (!normalsAvailable && normalsResult != CAPI.ovrAvatar2Result.DataNotAvailable)
+                            {
+                                OvrAvatarLog.LogError(
+                                    $"MorphTarget_GetVertexNormals ({iMorphTarget}) {normalsResult}",
+                                    primitiveLogScope);
+                                continue;
+                            }
+                        }
+
+                        // Tangents
+                        if (tangentsAvailable)
+                        {
+                            tangentsArray = new NativeArray<Vector3>(
+                                (int)bufferVertexCount, _nativeAllocator,
+                                _nativeArrayInit);
+
+                            tangentsResult =
+                                CAPI.ovrAvatar2MorphTarget_GetVertexTangents(
+                                    data.morphTargetBufferId, iMorphTarget,
+                                    tangentsArray.CastOvrPtr(),
+                                    bufferSize, stride);
+                            ct.ThrowIfCancellationRequested();
+                            tangentsAvailable = tangentsResult.IsSuccess();
+                            if (!tangentsAvailable && tangentsResult != CAPI.ovrAvatar2Result.DataNotAvailable)
+                            {
+                                OvrAvatarLog.LogError(
+                                    $"MorphTarget_GetVertexTangents ({iMorphTarget}) {tangentsResult}",
+                                    primitiveLogScope);
+                                continue;
+                            }
+                        }
+
+                        var nameResult =
+                            CAPI.ovrAvatar2Asset_GetMorphTargetName(
+                                data.morphTargetBufferId, iMorphTarget,
+                                nameBuffer, nameBufferLength);
+                        ct.ThrowIfCancellationRequested();
+
+                        var name = string.Empty;
+                        if (nameResult.IsSuccess())
+                        {
+                            name = Marshal.PtrToStringAnsi((IntPtr)nameBuffer);
+                        }
+                        else if (nameResult != CAPI.ovrAvatar2Result.NotFound)
+                        {
+                            OvrAvatarLog.LogError($"ovrAvatar2MorphTarget_GetName failed with {nameResult}"
+                                , primitiveLogScope);
+                        }
+
+                        // If we failed to query the name, use the index
+                        if (string.IsNullOrEmpty(name)) { name = "morphTarget" + iMorphTarget; }
+
+                        // Add Morph Target
+                        morphTargetInfo[iMorphTarget] = new MorphTargetInfo(
+                            name,
+                            repacker.RepackAttribute(positionsArray),
+                            normalsAvailable ? repacker.RepackAttribute(normalsArray) : null,
+                            tangentsAvailable ? repacker.RepackAttribute(tangentsArray) : null
+                        );
+                    }
+                    finally
+                    {
+                        positionsArray.Reset();
+                        normalsArray.Reset();
+                        tangentsArray.Reset();
+                    }
+                }
+            }
+        }
+
+        private void SetupSkin(ref MeshInfo meshInfo)
+        {
+            var ct = _cancellationTokenSource.Token;
+            ct.ThrowIfCancellationRequested();
+
+            var bindPoses = Array.Empty<Matrix4x4>();
+            var buildJoints = Array.Empty<int>();
+
+            var jointCount = data.jointCount;
+            if (jointCount > 0)
+            {
+                using var jointsInfoArray =
+                    new NativeArray<CAPI.ovrAvatar2JointInfo>(
+                        (int)jointCount, _nativeAllocator,
+                        _nativeArrayInit);
+                unsafe
+                {
+                    var jointInfoBuffer = jointsInfoArray.GetPtr();
+                    var result = CAPI.ovrAvatar2Primitive_GetJointInfo(
+                        assetId, jointInfoBuffer,
+                        jointsInfoArray.GetBufferSize());
+                    ct.ThrowIfCancellationRequested();
+
+                    if (result.EnsureSuccess("ovrAvatar2Primitive_GetJointInfo", primitiveLogScope))
+                    {
+                        buildJoints = new int[jointCount];
+                        bindPoses = new Matrix4x4[jointCount];
+                        for (int i = 0; i < jointCount; ++i)
+                        {
+                            var jointInfoPtr = jointInfoBuffer + i;
+                            ref var bindPose = ref bindPoses[i];
+
+                            buildJoints[i] = jointInfoPtr->jointIndex;
+                            jointInfoPtr->inverseBind.CopyToUnityMatrix(out bindPose); //Convert to Matrix4x4
+                        }
+                    }
+                } // unsafe
+            }
+
+            ct.ThrowIfCancellationRequested();
+            meshInfo.bindPoses = bindPoses;
+            joints = buildJoints;
+        }
+
+        private static NativeArray<T> CreateProcessResult<T>(in VertexRepacker repacker, in NativeArray<T> buffer,
+            string capiCallName, CAPI.ovrAvatar2Result result, CancellationToken ct) where T : struct
+        {
+            ct.ThrowIfCancellationRequested();
+            switch (result)
+            {
+                case CAPI.ovrAvatar2Result.Success:
+                    return repacker.CreateRepackedAttributes(in buffer);
+
+                case CAPI.ovrAvatar2Result.DataNotAvailable:
+                    return default;
+
+                default:
+                    OvrAvatarLog.LogError($"{capiCallName} {result}", primitiveLogScope);
+                    return default;
+            }
+        }
+
+        private class MeshLodInfo
+        {
+            public VertexRepacker repacker;
+        }
+
+        // Unity doesn't currently have access to the buffer ranges for individual LODs, so we deduce them based on the index buffer
+        private readonly struct VertexRepacker
+        {
+            private readonly BufferRange[] ranges;
+            private readonly int totalVerts;
+            private readonly bool needsRepacking;
+
+            public bool NeedsRepacking => needsRepacking;
+
+            public static VertexRepacker Create(ref NativeArray<UInt16> indices, in CAPI.ovrAvatar2Primitive prim,
+                in uint vertexBufferSize, out NativeArray<UInt16> finalIndices, CancellationToken ct)
+            {
+                if (indices.Length > 0)
+                {
+                    int totalVerts;
+                    bool needsRepacking;
+                    var ranges = ProcessIndices(
+                        ref indices, out totalVerts, out finalIndices, in prim,
+                        in vertexBufferSize, out needsRepacking, ct);
+                    return new VertexRepacker(ranges, totalVerts, needsRepacking);
+                }
+                else
+                {
+                    finalIndices = default;
+                    return new VertexRepacker(null, 0, false);
+                }
+            }
+
+            private VertexRepacker(BufferRange[] _ranges, int _totalVerts, bool _needsRepacking)
+            {
+                ranges = _ranges;
+                totalVerts = _totalVerts;
+                needsRepacking = _needsRepacking;
+            }
+
+            [Pure]
+            public T[] RepackAttribute<T>(in NativeArray<T> buffer) where T : unmanaged
+            {
+                if (buffer.Length == 0) { return Array.Empty<T>(); }
+
+                if (needsRepacking)
+                {
+                    T[] attrBuffer;
+                    try { attrBuffer = new T[totalVerts]; }
+                    catch (Exception e)
+                    {
+                        OvrAvatarLog.LogException("allocate primitive buffer (new T[len])", e, primitiveLogScope);
+                        attrBuffer = null;
+                    }
+
+                    if (attrBuffer == null)
+                    {
+                        // Run GC to hopefully free memory for dynamic log strings
+                        System.GC.Collect();
+
+                        OvrAvatarLog.LogError(
+                            $"Failed to allocate primitive buffer of type {typeof(T)} with length {totalVerts}"
+                            , primitiveLogScope);
+                        return Array.Empty<T>();
+                    }
+
+                    int packedOffset = 0;
+                    for (var idx = 0; idx < ranges.Length; idx++)
+                    {
+                        ref readonly var range = ref ranges[idx];
+                        range.CopyRelevantSlice(in buffer, attrBuffer, attrBuffer.Length, ref packedOffset);
+                    }
+
+                    return attrBuffer;
+                }
+                else { return buffer.ToArray(); }
+            }
+
+            [Pure]
+            public NativeArray<T> CreateRepackedAttributes<T>(in NativeArray<T> buffer) where T : struct
+            {
+                Assert.IsTrue(needsRepacking);
+
+                var packedResult = new NativeArray<T>(totalVerts, _nativeAllocator, _nativeArrayInit);
+
+                int packedOffset = 0;
+                for (int idx = 0; idx < ranges.Length; idx++)
+                {
+                    ref readonly var range = ref ranges[idx];
+                    range.CopyRelevantSlice(in buffer, in packedResult, ref packedOffset);
+                }
+
+                return packedResult;
+            }
+
+
+            private static BufferRange[] ProcessIndices(ref NativeArray<UInt16> indices, out int totalVerts,
+                out NativeArray<UInt16> convertedIndices, in CAPI.ovrAvatar2Primitive prim, in uint vertexBufferSize,
+                out bool needsRepacking, CancellationToken ct)
+            {
+                Debug.Assert(indices.Length > 0);
+
+                // TODO: prim.minIndexValue and prim.maxIndexValue dont seem to be working at the moment.
+                // If they worked could save a bit of memory in code below, albeit not much.
+                // Just have to make a max sized bit array for now.
+                // I see some documentation online about a NativeBitArray in beta as well, might be interesting.
+                var indicesUsed = new BitArray(UInt16.MaxValue + 1);
+                int minIndex = UInt16.MaxValue;
+                int maxIndex = 0;
+                unsafe
+                {
+                    // Use C# unsafe pointers to avoid NativeArray indexer overhead
+                    var indicesPtr = indices.GetPtr();
+                    for (int idx = 0; idx < indices.Length; idx++)
+                    {
+                        var index = indicesPtr[idx];
+                        indicesUsed.Set(index, true);
+                        minIndex = Mathf.Min(minIndex, index);
+                        maxIndex = Mathf.Max(maxIndex, index);
+                    }
+                }
+                ct.ThrowIfCancellationRequested();
+
+                var ranges = new List<BufferRange>();
+                int rangeStart = minIndex;
+                bool inRange = true;
+                for (int idx = minIndex + 1; idx < maxIndex; idx++)
+                {
+                    if (inRange && !indicesUsed[idx])
+                    {
+                        // -1 because BufferRange is closed on both ends. current idx is first not in range
+                        ranges.Add(new BufferRange(rangeStart, idx - 1));
+                        inRange = false;
+                    }
+                    else if (!inRange && indicesUsed[idx])
+                    {
+                        rangeStart = idx;
+                        inRange = true;
+                    }
+                }
+
+                if (inRange) { ranges.Add(new BufferRange(rangeStart, maxIndex)); }
+                ct.ThrowIfCancellationRequested();
+
+                var rangesArray = ranges.ToArray();
+                ranges.Clear();
+                ct.ThrowIfCancellationRequested();
+
+                needsRepacking = !(rangesArray.Length == 1 && rangesArray[0].FirstIndex == 0 &&
+                                   rangesArray[0].LastIndex == (vertexBufferSize - 1));
+                if (needsRepacking)
+                {
+                    // Shift indices to match repacked vert data
+                    var indicesLen = indices.Length;
+                    convertedIndices = new NativeArray<UInt16>(indicesLen, _nativeAllocator, _nativeArrayInit);
+
+                    unsafe
+                    {
+                        var indicesPtr = indices.GetPtr();
+                        var convertedPtr = convertedIndices.GetPtr();
+                        totalVerts = 0;
+                        int packIdx = 0;
+                        for (int idx = 0; idx < rangesArray.Length; ++idx)
+                        {
+                            ref readonly var range = ref rangesArray[idx];
+                            range.ShiftIndexBuffer(indicesPtr, convertedPtr
+                                , ref packIdx, ref totalVerts
+                                , indicesLen);
+                        }
+                    }
+                }
+                else
+                {
+                    totalVerts = (int)vertexBufferSize;
+                    convertedIndices = indices;
+                    indices = default;
+                }
+
+                return rangesArray;
+            }
+
+            private struct BufferRange
+            {
+                private readonly Int32 _firstIndex;
+                private readonly Int32 _lastIndex;
+
+                private readonly Int32 _sliceLength;
+
+                public Int32 FirstIndex => _firstIndex;
+                public Int32 LastIndex => _lastIndex;
+
+                public BufferRange(Int32 vertexCount)
+                {
+                    _firstIndex = vertexCount;
+                    _lastIndex = _sliceLength = 0;
+                }
+
+                public BufferRange(Int32 firstIndex, Int32 lastIndex)
+                {
+                    _firstIndex = firstIndex;
+                    _lastIndex = lastIndex;
+                    // TODO: replace _lastIndex w/ _stopIndex
+                    _sliceLength = lastIndex - firstIndex + 1;
+                }
+
+                // JetBrains didn't read the docs for System.Diagnostics.Contracts.Pure :/
+                // ReSharper disable once PureAttributeOnVoidMethod
+                [Pure]
+                public void CopyRelevantSlice<T>(in NativeArray<T> input, in NativeArray<T> output,
+                    ref int outputOffset) where T : struct
+                {
+                    NativeArray<T>.Copy(input, _firstIndex, output, outputOffset, _sliceLength);
+                    outputOffset += _sliceLength;
+                }
+
+                // JetBrains didn't read the docs for System.Diagnostics.Contracts.Pure :/
+                // ReSharper disable once PureAttributeOnVoidMethod
+                [Pure]
+                public void CopyRelevantSlice<T>(in NativeArray<T> input, T[] output, int outputLength,
+                    ref int outputOffset) where T : unmanaged
+                {
+                    NativeArray<T>.Copy(input, _firstIndex, output, outputOffset, _sliceLength);
+                    outputOffset += _sliceLength;
+                }
+
+                // JetBrains didn't read the docs for System.Diagnostics.Contracts.Pure :/
+                // ReSharper disable once PureAttributeOnVoidMethod
+                [Pure]
+                public unsafe void ShiftIndexBuffer(UInt16* srcPtr, UInt16* dstPtr,
+                    ref Int32 destOffset, ref int totalVerts, int srcLen)
+                {
+                    Debug.Assert(srcLen > 0);
+
+                    // TODO: Something less dumb, though this is extremely thorough.
+                    unsafe
+                    {
+                        var srcEnd = srcPtr + srcLen;
+
+                        Int32 offset = totalVerts - _firstIndex;
+                        do
+                        {
+                            Int32 vertIdx = *srcPtr;
+                            ++srcPtr;
+                            if (IsInRange(vertIdx))
+                            {
+                                *dstPtr = (UInt16)(vertIdx + offset);
+                            }
+
+                            ++dstPtr;
+                        } while (srcPtr < srcEnd);
+                    }
+
+                    totalVerts += _sliceLength;
+                }
+
+                // TODO: Remove - caller should determine range
+                [Pure]
+                private bool IsInRange(Int32 vertIdx)
+                {
+                    return _firstIndex <= vertIdx && vertIdx <= _lastIndex;
+                }
+            }
+        }
+
+        private sealed class OvrAvatarGpuSkinnedPrimitiveBuilder : IDisposable
+        {
+            NativeArray<IntPtr> deltaPositions;
+            NativeArray<IntPtr> deltaNormals;
+            NativeArray<IntPtr> deltaTangents;
+
+            GCHandle[] morphPosHandles;
+            GCHandle[] morphNormalHandles;
+            GCHandle[] morphTangentHandles;
+
+            Task createPrimitivesTask = null;
+
+            private MeshInfo _meshInfo;
+
+            readonly string shortName;
+            readonly uint morphTargetCount;
+
+            public OvrAvatarGpuSkinnedPrimitiveBuilder(string name, uint morphTargetCnt)
+            {
+                shortName = name;
+                morphTargetCount = morphTargetCnt;
+            }
+
+            public Task CreateSkinningPrimitivesHelperTask(
+                MeshInfo meshInfo,
+                MorphTargetInfo[] morphTargetInfo,
+                bool hasTangents,
+                bool gpuSkinning,
+                bool computeSkinning)
+            {
+                OvrAvatarLog.AssertConstMessage(
+                    createPrimitivesTask == null
+                    , "recreating gpu and/or compute primitive",
+                    primitiveLogScope);
+
+                _meshInfo = meshInfo;
+
+                if (gpuSkinning) { _meshInfo.WillBuildGpuPrimitive(); }
+                if (computeSkinning) { _meshInfo.WillBuildComputePrimitive(); }
+
+                createPrimitivesTask = Task.Run(
+                    () =>
+                    {
+                        // TODO: should get pointers to morph target data directly from Native
+
+                        deltaPositions = new NativeArray<IntPtr>(
+                            (int)morphTargetCount, _nativeAllocator, _nativeArrayInit);
+                        deltaNormals = new NativeArray<IntPtr>(
+                            (int)morphTargetCount, _nativeAllocator, _nativeArrayInit);
+                        if (hasTangents)
+                        {
+                            deltaTangents =
+                                new NativeArray<IntPtr>((int)morphTargetCount, _nativeAllocator, _nativeArrayInit);
+                        }
+
+                        morphPosHandles = new GCHandle[morphTargetCount];
+                        morphNormalHandles = new GCHandle[morphTargetCount];
+                        if (hasTangents) { morphTangentHandles = new GCHandle[morphTargetCount]; }
+
+                        for (var i = 0; i < morphTargetCount; ++i)
+                        {
+                            morphPosHandles[i] = GCHandle.Alloc(
+                                morphTargetInfo[i].targetPositions, GCHandleType.Pinned);
+                            morphNormalHandles[i] = GCHandle.Alloc(
+                                morphTargetInfo[i].targetNormals, GCHandleType.Pinned);
+
+                            deltaPositions[i] = morphPosHandles[i].AddrOfPinnedObject();
+                            deltaNormals[i] = morphNormalHandles[i].AddrOfPinnedObject();
+
+                            if (hasTangents)
+                            {
+                                morphTangentHandles[i] =
+                                    GCHandle.Alloc(morphTargetInfo[i].targetTangents, GCHandleType.Pinned);
+                                deltaTangents[i] = morphTangentHandles[i].AddrOfPinnedObject();
+                            }
+                        }
+
+                        createPrimitivesTask = null;
+                    });
+                return createPrimitivesTask;
+            }
+
+            public OvrAvatarGpuSkinnedPrimitive BuildPrimitive(MeshInfo meshInfo, Int32[] joints, bool hasTangents)
+            {
+                OvrAvatarLog.Assert(meshInfo == _meshInfo, primitiveLogScope);
+
+                IntPtr neutralPositions = _meshInfo.verts.GetIntPtr();
+                IntPtr neutralNormals = _meshInfo.normals.GetIntPtr();
+
+                IntPtr deltaPosPtr = deltaPositions.GetIntPtr();
+                IntPtr deltaNormPtr = deltaNormals.GetIntPtr();
+
+                IntPtr neutralTangents = IntPtr.Zero;
+                IntPtr deltaTanPtr = IntPtr.Zero;
+                if (hasTangents)
+                {
+                    neutralTangents = _meshInfo.tangents.GetIntPtr();
+                    deltaTanPtr = deltaTangents.GetIntPtr();
+                }
+
+                var primitive = new OvrAvatarGpuSkinnedPrimitive(
+                    shortName, _meshInfo.vertexCount,
+                    neutralPositions, neutralNormals, neutralTangents,
+                    morphTargetCount, deltaPosPtr, deltaNormPtr, deltaTanPtr,
+                    (uint)joints.Length, _meshInfo.boneWeights,
+                    () => { _meshInfo.NeutralPoseTexComplete(); },
+                    () =>
+                    {
+                        _meshInfo.DidBuildGpuPrimitive();
+                        if (!_meshInfo.HasPendingPrimitives) { _meshInfo = null; }
+                    });
+
+                return primitive;
+            }
+
+            public OvrAvatarComputeSkinnedPrimitive BuildComputePrimitive(MeshInfo meshInfo, bool hasTangents)
+            {
+                OvrAvatarLog.Assert(meshInfo == _meshInfo, primitiveLogScope);
+
+                IntPtr neutralPositions = _meshInfo.verts.GetIntPtr();
+                IntPtr neutralNormals = _meshInfo.normals.GetIntPtr();
+
+                IntPtr deltaPosPtr = deltaPositions.GetIntPtr();
+                IntPtr deltaNormPtr = deltaNormals.GetIntPtr();
+
+                IntPtr neutralTangents = IntPtr.Zero;
+                IntPtr deltaTanPtr = IntPtr.Zero;
+                if (hasTangents)
+                {
+                    neutralTangents = _meshInfo.tangents.GetIntPtr();
+                    deltaTanPtr = deltaTangents.GetIntPtr();
+                }
+
+                var primitive = new OvrAvatarComputeSkinnedPrimitive(
+                    shortName,
+                    (int)_meshInfo.vertexCount,
+                    neutralPositions,
+                    neutralNormals,
+                    neutralTangents,
+                    (int)morphTargetCount,
+                    deltaPosPtr,
+                    deltaNormPtr,
+                    deltaTanPtr,
+                    _meshInfo.boneWeights,
+                    () => { _meshInfo.NeutralPoseBuffersComplete(); },
+                    () =>
+                    {
+                        _meshInfo.DidBuildComputePrimitive();
+                        if (!_meshInfo.HasPendingPrimitives) { _meshInfo = null; }
+                    });
+
+                return primitive;
+            }
+
+            public void Dispose()
+            {
+                Dispose(true);
+                GC.SuppressFinalize(this);
+            }
+
+            private void Dispose(bool isDisposing)
+            {
+                if (createPrimitivesTask != null)
+                {
+                    createPrimitivesTask.Wait();
+                    createPrimitivesTask = null;
+                }
+
+                deltaPositions.Reset();
+                deltaNormals.Reset();
+                deltaTangents.Reset();
+
+                FreeHandles(ref morphPosHandles);
+                FreeHandles(ref morphNormalHandles);
+                FreeHandles(ref morphTangentHandles);
+
+                if (_meshInfo != null)
+                {
+                    _meshInfo.CancelledBuildPrimitives();
+                    _meshInfo = null;
+                }
+            }
+
+            private static void FreeHandles(ref GCHandle[] handles)
+            {
+                if (handles != null)
+                {
+                    foreach (var handle in handles)
+                    {
+                        if (handle.IsAllocated) { handle.Free(); }
+                    }
+
+                    handles = null;
+                }
+            }
+
+            ~OvrAvatarGpuSkinnedPrimitiveBuilder()
+            {
+                Dispose(false);
+            }
+        }
+
+        private const Allocator _nativeAllocator = Allocator.Persistent;
+        private const NativeArrayOptions _nativeArrayInit = NativeArrayOptions.UninitializedMemory;
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarPrimitive.cs.meta b/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarPrimitive.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..de3248539fc74feaef0425c2fa52bc529ac09b8a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarPrimitive.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: af1ead3f2590d2a46b7aac07ef58e338
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarResourceLoader.cs b/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarResourceLoader.cs
new file mode 100644
index 0000000000000000000000000000000000000000..a5210260fe4891d064b002f7d5c936afc876838a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarResourceLoader.cs
@@ -0,0 +1,373 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace Oculus.Avatar2
+{
+    ///
+    /// Represents a request to load a textured mesh.
+    /// This will spawn requests to load one or more
+    /// primitives which will in turn load one or more textures.
+    /// This class accumulates the loaded meshes and textures
+    /// as lists of @ref OvrAvatarPrimitive and @ref OvrAvatarImage instances.
+    ///
+    /// Resource loading is asynchronous and begins when
+    /// @ref StartLoad is called.
+    ///
+    public sealed class OvrAvatarResourceLoader : IDisposable
+    {
+        private const string logScope = "resourceLoader";
+
+        private OvrAvatarResourceTimer resourceTimer = null; // Should be only present in debug builds
+
+        internal static void ResourceCallbackHandler(
+            in CAPI.ovrAvatar2Asset_Resource resource,
+            Dictionary<CAPI.ovrAvatar2Id,
+            OvrAvatarResourceLoader> resourceMap,
+            out OvrAvatarResourceLoader queueLoad)
+        {
+            OvrAvatarLog.LogVerbose(
+                $"Received resource callback {resource.assetID} with status {resource.status}"
+                , logScope);
+
+            queueLoad = null;
+
+            switch (resource.status)
+            {
+                case CAPI.ovrAvatar2AssetStatus.ovrAvatar2AssetStatus_LoadFailed:
+                    if (!OvrAvatarManager.shuttingDown)
+                    {
+                        OvrAvatarLog.LogError($"Failed to load resource");
+
+                        // Try unload, just in case something has gone whacky
+                        if (resourceMap.TryGetValue(resource.assetID
+                            , out OvrAvatarResourceLoader failedLoader))
+                        {
+                            failedLoader.Unload();
+                            resourceMap.Remove(resource.assetID);
+                            failedLoader.resourceTimer?.TrackStatusEvent(OvrAvatarResourceTimer.AssetLifeTimeStatus.LoadFailed);
+                        }
+                    }
+                    break;
+
+                case CAPI.ovrAvatar2AssetStatus.ovrAvatar2AssetStatus_Loaded:
+
+                    if (!resourceMap.TryGetValue(resource.assetID, out var current))
+                    {
+                        var newLoader = new OvrAvatarResourceLoader(resource.assetID);
+                        resourceMap.Add(resource.assetID, newLoader);
+                        queueLoad = newLoader;
+                        OvrAvatarLog.LogDebug($"Mapped resource id:{resource.assetID}", logScope);
+                    }
+                    else
+                    {
+                        OvrAvatarLog.LogDebug(
+                            $"Resource id: {resource.assetID} already loaded {current}", logScope);
+                    }
+
+                    break;
+
+                case CAPI.ovrAvatar2AssetStatus.ovrAvatar2AssetStatus_Unloaded:
+                    if (resourceMap.TryGetValue(resource.assetID, out var loader))
+                    {
+                        loader.Unload();
+                        loader.resourceTimer?.TrackStatusEvent(OvrAvatarResourceTimer.AssetLifeTimeStatus.Unloaded);
+                        resourceMap.Remove(resource.assetID);
+                    }
+                    else
+                    {
+                        OvrAvatarLog.LogWarning(
+                            $"Unable to unloading resource id:{resource.assetID}, not found", logScope);
+                    }
+                    break;
+
+                case CAPI.ovrAvatar2AssetStatus.ovrAvatar2AssetStatus_Updated:
+                    throw new InvalidOperationException("Resource updates are not currently supported");
+
+                default:
+                    throw new InvalidOperationException($"Unexpected resource status {resource.status}");
+            }
+        }
+
+        private readonly List<OvrAvatarPrimitive> _primitives = new List<OvrAvatarPrimitive>();
+        private readonly List<OvrAvatarImage> _images = new List<OvrAvatarImage>();
+
+        // Effectively indicates whether load is in progress
+        private OvrTime.SliceHandle _loadResourceAsyncCoroutine = default;
+
+#if UNITY_EDITOR
+        internal List<OvrAvatarPrimitive> Primitives => _primitives;
+
+        internal List<OvrAvatarImage> Images => _images;
+#endif
+
+        private bool _hasReleasedNativeResources = false;
+        public bool isCancelled { get; private set; } = false;
+        public bool isDisposed { get; private set; } = false;
+
+        public bool CanLoad => !(isCancelled || isDisposed);
+
+        public readonly CAPI.ovrAvatar2Id resourceId;
+
+        internal OvrAvatarImage CreateImage(in CAPI.ovrAvatar2MaterialTexture textureData
+            , in CAPI.ovrAvatar2Image imageData, uint imageIndex, CAPI.ovrAvatar2Id resourceId)
+        {
+            bool srgb = (textureData.type == CAPI.ovrAvatar2MaterialTextureType.BaseColor);
+            var newImage = new OvrAvatarImage(resourceId, imageIndex, imageData, srgb);
+            _images.Add(newImage);
+            return newImage;
+        }
+
+        public OvrAvatarResourceLoader(CAPI.ovrAvatar2Id resourceIdentifier)
+        {
+            OvrAvatarLog.Assert(resourceIdentifier != CAPI.ovrAvatar2Id.Invalid);
+            this.resourceId = resourceIdentifier;
+            // only allow for load time tracking overhead if logging level is high enough
+            if (Debug.isDebugBuild)
+            {
+                resourceTimer = new OvrAvatarResourceTimer(this);
+            }
+
+            resourceTimer?.TrackStatusEvent(OvrAvatarResourceTimer.AssetLifeTimeStatus.Created);
+            CreateResourcePrimitives();
+        }
+
+        private void CreateResourcePrimitives()
+        {
+            // Get Primitive Count
+            var result = CAPI.ovrAvatar2Asset_GetPrimitiveCount(resourceId, out UInt32 primitiveCount);
+            if (result != CAPI.ovrAvatar2Result.Success)
+            {
+                OvrAvatarLog.LogError($"LoadResource Error: GetMeshPrimitiveCount {result}", logScope);
+
+                ReleaseNativeResource();
+                return;
+            }
+
+            // Load Primitives
+            _primitives.Capacity = (int)primitiveCount;
+            for (UInt32 i = 0; i < primitiveCount; ++i)
+            {
+                result = CAPI.ovrAvatar2Asset_GetPrimitiveByIndex(resourceId, i,
+                    out CAPI.ovrAvatar2Primitive primitiveData);
+                if (result != CAPI.ovrAvatar2Result.Success)
+                {
+                    OvrAvatarLog.LogError(
+                        $"LoadResource Error: GetPrimitiveByIndex {result}", logScope);
+
+                    ReleaseNativeResource();
+                    return;
+                }
+
+                if (OvrAvatarManager.IsOvrAvatarAssetLoaded(primitiveData.id))
+                {
+                    OvrAvatarLog.LogWarning(
+                        $"Mesh primitive with id {primitiveData.id} already exists.", logScope);
+                    continue;
+                }
+
+                OvrAvatarLog.LogVerbose($"Mapped primitive id:{primitiveData.id}", logScope);
+                var newPrimitive = new OvrAvatarPrimitive(this, in primitiveData);
+
+                _primitives.Add(newPrimitive);
+            }
+        }
+
+        internal void StartLoad()
+        {
+            _loadResourceAsyncCoroutine = OvrTime.Slice(LoadResourceAsync());
+        }
+
+        private bool CheckCancel(OvrAvatarAssetBase asset, out OvrTime.SliceStep step)
+        {
+            bool cancelled = asset.isCancelled;
+            if (cancelled)
+            {
+                OvrAvatarLog.LogVerbose(
+                    $"{asset.typeName} {asset.assetName} cancelled during resource load."
+                    , logScope);
+
+                // Resume checking next frame
+                // TODO: Switch to Wait, but currently no unit test - use Delay for now
+                // yield return OvrTime.SliceStep.Wait;
+                step = OvrTime.SliceStep.Delay;
+            }
+            else // !isCancelled
+            {
+                // Loading in progress, delay next slice
+                step = OvrTime.SliceStep.Delay;
+            }
+            return cancelled;
+        }
+
+        private IEnumerator<OvrTime.SliceStep> LoadResourceAsync()
+        {
+            resourceTimer?.TrackStatusEvent(OvrAvatarResourceTimer.AssetLifeTimeStatus.LoadStarted);
+
+            foreach (var primitive in _primitives)
+            {
+                primitive.StartLoad(this);
+
+                if (OvrTime.ShouldHold) { yield return OvrTime.SliceStep.Hold; }
+            }
+
+            foreach (var newPrimitive in _primitives)
+            {
+                while (!newPrimitive.hasCopiedAllResourceData)
+                {
+                    yield return OvrTime.SliceStep.Delay;
+                }
+            }
+            foreach (var newImage in _images)
+            {
+                while (!newImage.hasCopiedAllResourceData)
+                {
+                    yield return OvrTime.SliceStep.Delay;
+                }
+            }
+
+            resourceTimer?.TrackStatusEvent(OvrAvatarResourceTimer.AssetLifeTimeStatus.Loaded);
+
+            // Notify nativeSDK that we no longer will access the resource data
+            ReleaseNativeResource();
+
+            OvrAvatarLog.LogVerbose($"Releasing native resources for resourceId {resourceId}", logScope);
+
+            if (OvrTime.ShouldHold) { yield return OvrTime.SliceStep.Hold; }
+
+            // Wait until all primitives are fully loaded
+            foreach (var newPrimitive in _primitives)
+            {
+                while (!newPrimitive.isLoaded && !CheckCancel(newPrimitive, out var step))
+                {
+                    yield return step;
+                }
+            }
+            foreach (var newImage in _images)
+            {
+                while (!newImage.isLoaded && !CheckCancel(newImage, out var step))
+                {
+                    yield return step;
+                }
+            }
+
+            _loadResourceAsyncCoroutine.Clear();
+
+            if (OvrAvatarManager.hasInstance)
+            {
+                OvrAvatarManager.Instance.ResourceLoadComplete(this);
+            }
+
+            MarkResourceReadyToRender();
+
+            // If we reach here, we have finished w/out being (directly) cancelled - congratulations!
+            OvrAvatarLog.LogVerbose($"LoadResourceAsync completed for resourceId {resourceId}", logScope);
+        }
+
+        public void CancelLoad()
+        {
+            if (!_hasReleasedNativeResources)
+            {
+                ReleaseNativeResource();
+            }
+
+            if (_loadResourceAsyncCoroutine.IsValid)
+            {
+                OvrAvatarLog.LogVerbose($"Stopping LoadResourceAsync coroutine for resourceId {resourceId}");
+
+                if (!_loadResourceAsyncCoroutine.Cancel())
+                {
+                    // If cancellation fails w/out crashing - something gnarly has happened
+                    OvrAvatarLog.LogError($"Slice for resourceId {resourceId} failed to Cancel");
+                }
+
+                if (OvrAvatarManager.hasInstance)
+                {
+                    OvrAvatarManager.Instance.ResourceLoadCancelled(this);
+                }
+            }
+
+            isCancelled = true;
+        }
+
+        // Unity no longer needs access to the native resouce, release it
+        private void ReleaseNativeResource()
+        {
+            // TODO: Good use case for "ensure"
+            OvrAvatarLog.Assert(resourceId != CAPI.ovrAvatar2Id.Invalid);
+
+            if (resourceId != CAPI.ovrAvatar2Id.Invalid)
+            {
+                _hasReleasedNativeResources = CAPI.OvrAvatarAsset_ReleaseResource(resourceId);
+                OvrAvatarLog.Assert(_hasReleasedNativeResources);
+            }
+        }
+
+        private void MarkResourceReadyToRender()
+        {
+            // TODO: Good use case for "ensure"
+            OvrAvatarLog.Assert(resourceId != CAPI.ovrAvatar2Id.Invalid);
+
+            if (resourceId != CAPI.ovrAvatar2Id.Invalid)
+            {
+                bool readyToRender = CAPI.OvrAvatarAsset_ResourceReadyToRender(resourceId);
+                OvrAvatarLog.Assert(readyToRender);
+                resourceTimer?.TrackStatusEvent(OvrAvatarResourceTimer.AssetLifeTimeStatus.ReadyToRender);
+            }
+        }
+
+        private void Unload()
+        {
+            OvrAvatarLog.LogDebug($"Unloading resource id: {resourceId}", logScope);
+            Dispose();
+        }
+
+        // IDisposable interface
+        private void Dispose(bool disposing)
+        {
+            if (disposing)
+            {
+                if (_primitives.Count > 0)
+                {
+                    OvrAvatarLog.LogDebug(
+                        $"Disposing {_primitives.Count} OvrAvatarPrimitive instances for resourceId {resourceId}");
+                    foreach (var primitive in _primitives)
+                    {
+                        primitive.Dispose();
+                    }
+                    _primitives.Clear();
+                }
+
+                if (_images.Count > 0)
+                {
+                    OvrAvatarLog.LogDebug(
+                        $"Disposing {_images.Count} OvrAvatarImage instances for resourceId {resourceId}");
+                    foreach (var image in _images)
+                    {
+                        image.Dispose();
+                    }
+                    _images.Clear();
+                }
+
+                CancelLoad();
+            }
+            else
+            {
+                OvrAvatarLog.LogError($"Finalized {resourceId} w/out main thread dispose", logScope);
+            }
+            isDisposed = true;
+        }
+
+        // Called from `OvrAvatarManager.Shutdown`
+        public void Dispose()
+        {
+            Dispose(true);
+            GC.SuppressFinalize(this);
+        }
+
+        ~OvrAvatarResourceLoader()
+        {
+            Dispose(false);
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarResourceLoader.cs.meta b/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarResourceLoader.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..a1a39165c40c9ac0f755a1d8a4c1c828c48dfe94
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarResourceLoader.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 097127100e57a4b44b6fe8f418a3e468
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarResourceTimer.cs b/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarResourceTimer.cs
new file mode 100644
index 0000000000000000000000000000000000000000..e74ee12619cac44ab1466038979dda7f07e0e35d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarResourceTimer.cs
@@ -0,0 +1,156 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+using Oculus.Avatar2;
+
+namespace Oculus.Avatar2
+{
+    internal sealed class OvrAvatarResourceTimer
+    {
+        internal enum AssetLifeTimeStatus
+        {
+            Created,
+            LoadStarted,
+            LoadFailed,
+            Loaded,
+            Unloaded,
+            ReadyToRender
+        }
+
+        private const string logScope = "ResourceTimers";
+
+        private OvrAvatarResourceLoader parentLoader = null;
+
+        private float _resourceCreatedTime = 0;
+        internal float resourceCreatedTime
+        {
+            get { return _resourceCreatedTime; }
+            private set
+            {
+                _resourceCreatedTime = value;
+            }
+        }
+
+        private float _resourceLoadStartedTime = 0;
+        internal float resourceLoadStartedTime
+        {
+            get { return _resourceLoadStartedTime; }
+            private set
+            {
+                _resourceLoadStartedTime = value;
+            }
+        }
+
+        private float _resourceLoadedTime;
+        internal float resourceLoadedTime
+        {
+            get { return _resourceLoadedTime; }
+            private set
+            {
+                _resourceLoadedTime = value;
+                if (resourceCreatedTime != 0)
+                {
+                    float loadingTime = _resourceLoadedTime - resourceCreatedTime;
+                    OvrAvatarLog.LogDebug($"Resource {parentLoader.resourceId} asset loading time: {loadingTime}", logScope);
+                    OvrAvatarStatsTracker.Instance.TrackLoadDuration(parentLoader.resourceId, loadingTime);
+                }
+            }
+        }
+
+
+        private float _resourceLoadFailedTime;
+        internal float resourceLoadFailedTime
+        {
+            get { return _resourceLoadFailedTime; }
+            private set
+            {
+                _resourceLoadFailedTime = value;
+                if (resourceCreatedTime != 0)
+                {
+                    float loadingTime = _resourceLoadFailedTime - resourceCreatedTime;
+                    OvrAvatarLog.LogDebug($"Resource {parentLoader.resourceId} had a failure loading after: {loadingTime}", logScope);
+                    OvrAvatarStatsTracker.Instance.TrackFailedDuration(parentLoader.resourceId, loadingTime);
+                }
+            }
+        }
+
+        private float _resourceReadyToRenderTime;
+        internal float resourceReadyToRenderTime
+        {
+            get { return _resourceReadyToRenderTime; }
+            private set
+            {
+                _resourceReadyToRenderTime = value;
+                if (resourceCreatedTime != 0)
+                {
+                    float totalTime = _resourceReadyToRenderTime - resourceCreatedTime;
+                    OvrAvatarLog.LogDebug($"Resource {parentLoader.resourceId} total creation time: {totalTime}", logScope);
+                    OvrAvatarStatsTracker.Instance.TrackReadyDuration(parentLoader.resourceId, totalTime);
+                }
+            }
+        }
+        private float _resourceUnloadedTime;
+        internal float resourceUnloadedTime
+        {
+            get { return _resourceUnloadedTime; }
+            private set
+            {
+                _resourceUnloadedTime = value;
+                if (resourceCreatedTime != 0)
+                {
+                    float totalTime = _resourceUnloadedTime - resourceCreatedTime;
+                    OvrAvatarLog.LogDebug($"Resource {parentLoader.resourceId} unloaded after a lifetime of {totalTime}", logScope);
+                }
+            }
+        }
+
+        // TODO (jsepulveda, 8/25/21)
+        // For now we're tracking these status changes from direct calls to this function
+        // but in the future we should recieve asynchronous callbacks from the SDK.
+        internal void TrackStatusEvent(AssetLifeTimeStatus status)
+        {
+            float currentTime = Time.realtimeSinceStartup;
+
+            switch(status) {
+                case AssetLifeTimeStatus.LoadFailed:
+                    {
+                        resourceLoadFailedTime = currentTime;
+                    } break;
+                case AssetLifeTimeStatus.Loaded:
+                    {
+                        resourceLoadedTime = currentTime;
+                    }
+                    break;
+                case AssetLifeTimeStatus.Unloaded:
+                    {
+                        resourceUnloadedTime = currentTime;
+                    }
+                    break;
+                case AssetLifeTimeStatus.Created:
+                    {
+                        resourceCreatedTime = currentTime;
+                    }
+                    break;
+                case AssetLifeTimeStatus.LoadStarted:
+                    {
+                        resourceLoadStartedTime = currentTime;
+                    }
+                    break;
+                case AssetLifeTimeStatus.ReadyToRender:
+                    {
+                        resourceReadyToRenderTime = currentTime;
+                    }
+                    break;
+
+            }
+        }
+
+        private OvrAvatarResourceTimer() { }
+        public OvrAvatarResourceTimer(OvrAvatarResourceLoader loader)
+        {
+            parentLoader = loader;
+        }
+    }
+
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarResourceTimer.cs.meta b/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarResourceTimer.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..1c797b8df0b96e7c2192b9ebf3ae974484e888cc
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarResourceTimer.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 9f37ff725c15423408e1e9fb5705ad59
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarStatsTracker.cs b/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarStatsTracker.cs
new file mode 100644
index 0000000000000000000000000000000000000000..969d08cd8ba37133ecc917651a40a9b10df3f7f8
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarStatsTracker.cs
@@ -0,0 +1,116 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace Oculus.Avatar2
+{
+    public sealed class OvrAvatarStatsTracker
+    {
+        #region Singleton (Lazy Initialize)
+        private static OvrAvatarStatsTracker _instance;
+
+        public static OvrAvatarStatsTracker Instance
+        {
+            get
+            {
+                if (_instance == null)
+                {
+                    _instance = new OvrAvatarStatsTracker();
+                }
+
+                return _instance;
+            }
+        }
+
+        void Awake()
+        {
+            _instance = this;
+        }
+        #endregion
+
+        private readonly List<CAPI.ovrAvatar2Id> loadedResourceIds = new List<CAPI.ovrAvatar2Id>();
+        private readonly List<CAPI.ovrAvatar2Id> failedResourceIds = new List<CAPI.ovrAvatar2Id>();
+
+        public int numberPrimitivesLoaded => loadedResourceIds.Count;
+
+        public int numberPrimitivesFailed => failedResourceIds.Count;
+
+        private float _maxLoadTime = 0;
+        // max load time, period between files requested and files ready
+        public float maxLoadTime
+        {
+            get { return _maxLoadTime; }
+            private set { _maxLoadTime = value; }
+        }
+        private float _cumulativeLoadTime = 0;
+        // average load time, period between files requested and files ready
+        public float averageLoadTime => _cumulativeLoadTime / numberPrimitivesLoaded;
+
+        private float _maxFailedTime = 0;
+        // max load time, period between files requested and files ready
+        public float maxFailedTime
+        {
+            get { return _maxFailedTime; }
+            private set { _maxFailedTime = value; }
+        }
+        private float _cumulativeFailedTime = 0;
+        // average load time, period between files requested and files ready
+        public float averageFailedTime => _cumulativeFailedTime / numberPrimitivesFailed;
+
+        private float _maxReadyTime = 0;
+        // max ready time, period between construction and ready to render
+        public float maxReadyTime
+        {
+            get { return _maxReadyTime; }
+            private set { _maxReadyTime = value; }
+        }
+        private float _cumulativeReadyTime = 0;
+        // average ready time, period between construction and ready to render
+        public float averageReadyTime => _cumulativeReadyTime / numberPrimitivesLoaded;
+
+        private void ResolveLoadedId(CAPI.ovrAvatar2Id resourceId)
+        {
+            if (!loadedResourceIds.Contains(resourceId))
+            {
+                loadedResourceIds.Add(resourceId);
+            }
+        }
+        private void ResolveFailedId(CAPI.ovrAvatar2Id resourceId)
+        {
+            if (!failedResourceIds.Contains(resourceId))
+            {
+                failedResourceIds.Add(resourceId);
+            }
+        }
+
+        internal void TrackLoadDuration(CAPI.ovrAvatar2Id resourceId, float time)
+        {
+            ResolveLoadedId(resourceId);
+            _cumulativeLoadTime += time;
+            if (time > _maxLoadTime)
+            {
+                _maxLoadTime = time;
+            }
+        }
+
+        internal void TrackFailedDuration(CAPI.ovrAvatar2Id resourceId, float time)
+        {
+            ResolveFailedId(resourceId);
+            _cumulativeFailedTime += time;
+            if (time > _maxFailedTime)
+            {
+                _maxFailedTime = time;
+            }
+        }
+
+        internal void TrackReadyDuration(CAPI.ovrAvatar2Id resourceId, float time)
+        {
+            ResolveLoadedId(resourceId);
+            _cumulativeReadyTime += time;
+            if (time > _maxReadyTime)
+            {
+                _maxReadyTime = time;
+            }
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarStatsTracker.cs.meta b/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarStatsTracker.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..0bb9b59ab8ecfce3c238e09d2e0ec28eb655f1ab
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/AssetTypes/OvrAvatarStatsTracker.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: fa343ad2ded78994c94844d9f1f16311
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink.meta b/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink.meta
new file mode 100644
index 0000000000000000000000000000000000000000..933813e9897c4237590cfcb36f7638bac44dd3b9
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: a7eaf81f17fe72a40962346946b4eaec
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink/AvatarEditorDeeplink.cs b/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink/AvatarEditorDeeplink.cs
new file mode 100644
index 0000000000000000000000000000000000000000..8dd7989e316618b31e888da5237becd3302fa305
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink/AvatarEditorDeeplink.cs
@@ -0,0 +1,126 @@
+#if USING_XR_MANAGEMENT && USING_XR_SDK_OCULUS && !OVRPLUGIN_UNSUPPORTED_PLATFORM
+#define USING_XR_SDK
+#endif
+
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using Oculus.Avatar2;
+using UnityEngine;
+#if UNITY_STANDALONE_WIN || UNITY_EDITOR
+using Daybreak.IPC;
+#endif
+
+public static class AvatarEditorDeeplink
+{
+    private const string logscope = "deeplink";
+
+    public static void LaunchAvatarEditor()
+    {
+        OvrAvatarLog.LogInfo("Launching Avatar Editor", logscope);
+
+#if UNITY_STANDALONE_WIN || UNITY_EDITOR
+        if (!OvrAvatarEntitlement.AccessTokenIsValid())
+        {
+            OvrAvatarLog.LogError(
+                "Launching the Avatar Editor requires app entitlement. Set a valid access token in OvrAvatarEntitlement");
+            return;
+        }
+
+        if (LoadLibrary() == LoadLibraryResult.Success)
+        {
+            // Unity 2020 seems to have issues with multiple requests using the same connector,
+            // so we must recreate the connector every time
+            if (!IpcOafConnector.Recreate().IsConnected())
+            {
+                OvrAvatarLog.LogError("LaunchAvatarEditor failed: Couldn't connect IpcOafConnector", logscope);
+            }
+
+            IpcOafConnector.RequestWork(
+                "/social/avatar/start",
+                new Dictionary<string, object>()
+                {
+                    {
+                        "source",
+                        System.Convert.ToBase64String(
+                            System.Text.Encoding.UTF8.GetBytes(
+                                "\"avatar_2_sdk\""))
+                    }
+                },
+                (dataSent, dataReceived, error) =>
+                {
+                    if (error != null)
+                    {
+                        error.PopulateFromErrorCode("Unknown error");
+                        if (dataReceived.payloadType == "NOTIFICATION")
+                        {
+                            RecvNotificationInnerData payload = (RecvNotificationInnerData) dataReceived.payload;
+                            OvrAvatarLog.LogError(
+                                $"Failed to launch avatar editor: {payload.notificationType} ({payload.errorCode}) - {payload.title},\n{payload.description}\n{payload.stackTrace}");
+                        }
+                        else
+                        {
+                            OvrAvatarLog.LogError(
+                                $"Failed to launch avatar editor: {(Code)error.ErrorCode} - {error.Title}\n{error.Description}\n{error.StackTrace}",
+                                logscope);
+                        }
+                    }
+                }
+            );
+        }
+#else
+        string deeplinkUri = $"/?version=V2&returnUrl=apk://{Application.identifier}";
+
+        AndroidJavaObject activityClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
+        AndroidJavaObject currentActivity = activityClass.GetStatic<AndroidJavaObject>("currentActivity");
+        var intent = new AndroidJavaObject("android.content.Intent");
+
+        intent.Call<AndroidJavaObject>("setPackage", "com.oculus.vrshell");
+        intent.Call<AndroidJavaObject>("setAction", "com.oculus.vrshell.intent.action.LAUNCH");
+        intent.Call<AndroidJavaObject>("putExtra", "intent_data", "systemux://avatareditor");
+        intent.Call<AndroidJavaObject>("putExtra", "uri", deeplinkUri);
+
+        // Broadcast instead of starting activity, so that it goes to overlay
+        currentActivity.Call("sendBroadcast", intent);
+#endif
+    }
+
+    // TODO: We've had to copy this code for a number of dlls now. Make a generic class to handle this case?
+    private enum LoadLibraryResult : Int32
+    {
+        Success = 0,
+        Failure = 1,
+        Unknown = 2
+    }
+
+    private static LoadLibraryResult LoadLibrary()
+    {
+        LoadLibraryResult loadResult;
+        try
+        {
+            ovrAvatar2_forceLibraryLoad();
+            // This call should have failed
+            loadResult = LoadLibraryResult.Unknown;
+        }
+        catch (Exception e)
+        {
+            loadResult = !(e is DllNotFoundException) ? LoadLibraryResult.Success : LoadLibraryResult.Failure;
+            if (!(e is EntryPointNotFoundException))
+            {
+                OvrAvatarLog.LogError($"Unexpected exception, {e.ToString()}", logscope);
+            }
+        }
+        if (loadResult != LoadLibraryResult.Success)
+        {
+            OvrAvatarLog.LogError("Unable to find OafIpc.dll!", logscope);
+        }
+        return loadResult;
+    }
+
+    private const string OafIpcLibFile = "OafIpc";
+
+    // This method *should* not exist -
+    // we are using it to trigger an expected exception and force DLL load in older Unity versions
+    [DllImport(OafIpcLibFile, CallingConvention = CallingConvention.Cdecl)]
+    private static extern void ovrAvatar2_forceLibraryLoad();
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink/AvatarEditorDeeplink.cs.meta b/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink/AvatarEditorDeeplink.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..22ecebd5dd4c76b187e95c55a1f58b14c7610a86
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink/AvatarEditorDeeplink.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: dc3fa64c21c96374bb87e3ecd57c9eca
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink/IPC.dll b/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink/IPC.dll
new file mode 100644
index 0000000000000000000000000000000000000000..b55cd8bffc0a64033d8a5bc0515111ab39716d74
Binary files /dev/null and b/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink/IPC.dll differ
diff --git a/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink/IPC.dll.meta b/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink/IPC.dll.meta
new file mode 100644
index 0000000000000000000000000000000000000000..19acdc474f57ae99118ae6577ff34384485e6940
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink/IPC.dll.meta
@@ -0,0 +1,101 @@
+fileFormatVersion: 2
+guid: 650a4c54dfce3d246bacb01297b44a2c
+PluginImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  iconMap: {}
+  executionOrder: {}
+  defineConstraints: []
+  isPreloaded: 0
+  isOverridable: 0
+  isExplicitlyReferenced: 0
+  validateReferences: 1
+  platformData:
+  - first:
+      '': Any
+    second:
+      enabled: 0
+      settings:
+        Exclude Android: 1
+        Exclude Editor: 0
+        Exclude Linux: 0
+        Exclude Linux64: 0
+        Exclude LinuxUniversal: 0
+        Exclude OSXUniversal: 0
+        Exclude Win: 0
+        Exclude Win64: 0
+  - first:
+      Android: Android
+    second:
+      enabled: 0
+      settings:
+        CPU: ARMv7
+  - first:
+      Any: 
+    second:
+      enabled: 1
+      settings: {}
+  - first:
+      Editor: Editor
+    second:
+      enabled: 1
+      settings:
+        CPU: AnyCPU
+        DefaultValueInitialized: true
+        OS: AnyOS
+  - first:
+      Facebook: Win
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  - first:
+      Facebook: Win64
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: Linux
+    second:
+      enabled: 1
+      settings:
+        CPU: x86
+  - first:
+      Standalone: Linux64
+    second:
+      enabled: 1
+      settings:
+        CPU: x86_64
+  - first:
+      Standalone: LinuxUniversal
+    second:
+      enabled: 1
+      settings: {}
+  - first:
+      Standalone: OSXUniversal
+    second:
+      enabled: 1
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: Win
+    second:
+      enabled: 1
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: Win64
+    second:
+      enabled: 1
+      settings:
+        CPU: AnyCPU
+  - first:
+      Windows Store Apps: WindowsStoreApps
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink/IPCConnector.cs b/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink/IPCConnector.cs
new file mode 100644
index 0000000000000000000000000000000000000000..a654dc37b1ec21834b89db0d2006942056e2f17f
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink/IPCConnector.cs
@@ -0,0 +1,80 @@
+#if UNITY_EDITOR || UNITY_STANDALONE_WIN
+#if !DISABLE_IPC_CONNECTOR
+using System.Collections.Generic;
+using Daybreak.IPC;
+using Newtonsoft.Json;
+
+internal class IpcOafConnector : OafConnector
+{
+    private static IpcOafConnector _instance;
+    public static IpcOafConnector Instance
+    {
+        get
+        {
+            if (_instance == null)
+            {
+                _instance = new IpcOafConnector();
+            }
+
+            return _instance;
+        }
+    }
+
+    public static bool hasInstance => _instance != null;
+
+    protected override string LogChannel => "OafIpc";
+
+    private IpcOafConnector()
+    {
+        Init();
+    }
+
+    // Unity 2020 seems to have issues with multiple requests using the same connector,
+    // so we need a way to recreate the connector
+    public static IpcOafConnector Recreate()
+    {
+        if (_instance != null)
+        {
+            _instance.Destroy();
+        }
+
+        return Instance;
+    }
+
+    public new void Destroy()
+    {
+        _instance = null;
+        base.Destroy();
+    }
+
+    public static void RequestWork(
+        string endPoint,
+        Dictionary<string, object> dict,
+        SendPayload.SendCallbackCoroutine callbackCoroutine)
+    {
+        Instance.SendPayloadToOaf(
+            new SendPayload
+            {
+                requestName = endPoint,
+                requestData = new DummyPayload(dict),
+                OnRecvResponse = callbackCoroutine
+            });
+    }
+    
+    private class DummyPayload : SendInnerDataBase
+    {
+        public DummyPayload(Dictionary<string, object> dict)
+        {
+            json = JsonConvert.SerializeObject(dict);
+        }
+
+        public override string PayloadToString()
+        {
+            return json;
+        }
+
+        private string json;
+    }
+}
+#endif // !DISABLE_IPC_CONNECTOR
+#endif //UNITY_EDITOR || UNITY_STANDALONE_WIN
diff --git a/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink/IPCConnector.cs.meta b/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink/IPCConnector.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..52af82063804c2ece833f1f2200d14c4ff4abc34
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink/IPCConnector.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a31e92139eef9af42b453f252023bc08
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink/METADATA.bzl b/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink/METADATA.bzl
new file mode 100644
index 0000000000000000000000000000000000000000..0187d5db22ca4d5b42f87de152c719737d61e7c3
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink/METADATA.bzl
@@ -0,0 +1,7 @@
+METADATA = {
+    "maintainers": [
+        "avatar_integrations",
+    ],
+    "name": "AvatarEditorDeeplink",
+    "owner": "avatar_integrations",
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink/METADATA.bzl.meta b/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink/METADATA.bzl.meta
new file mode 100644
index 0000000000000000000000000000000000000000..3465c445d4df990743e6f487b86f85add3220c7a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink/METADATA.bzl.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: af2d941369dc14d4aa66c7cfab1cdc42
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink/Newtonsoft.Json.XML b/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink/Newtonsoft.Json.XML
new file mode 100644
index 0000000000000000000000000000000000000000..ed0eec5fc55837cade9cc46223bae582cfcc0acc
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink/Newtonsoft.Json.XML
@@ -0,0 +1,7977 @@
+<?xml version="1.0"?>
+<doc>
+    <assembly>
+        <name>Newtonsoft.Json</name>
+    </assembly>
+    <members>
+        <member name="T:Newtonsoft.Json.Bson.BsonObjectId">
+            <summary>
+            Represents a BSON Oid (object id).
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.Bson.BsonObjectId.Value">
+            <summary>
+            Gets or sets the value of the Oid.
+            </summary>
+            <value>The value of the Oid.</value>
+        </member>
+        <member name="M:Newtonsoft.Json.Bson.BsonObjectId.#ctor(System.Byte[])">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Bson.BsonObjectId"/> class.
+            </summary>
+            <param name="value">The Oid value.</param>
+        </member>
+        <member name="T:Newtonsoft.Json.Bson.BsonReader">
+            <summary>
+            Represents a reader that provides fast, non-cached, forward-only access to serialized JSON data.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.Bson.BsonReader.JsonNet35BinaryCompatibility">
+            <summary>
+            Gets or sets a value indicating whether binary data reading should compatible with incorrect Json.NET 3.5 written binary.
+            </summary>
+            <value>
+            	<c>true</c> if binary data reading will be compatible with incorrect Json.NET 3.5 written binary; otherwise, <c>false</c>.
+            </value>
+        </member>
+        <member name="P:Newtonsoft.Json.Bson.BsonReader.ReadRootValueAsArray">
+            <summary>
+            Gets or sets a value indicating whether the root object will be read as a JSON array.
+            </summary>
+            <value>
+            	<c>true</c> if the root object will be read as a JSON array; otherwise, <c>false</c>.
+            </value>
+        </member>
+        <member name="P:Newtonsoft.Json.Bson.BsonReader.DateTimeKindHandling">
+            <summary>
+            Gets or sets the <see cref="T:System.DateTimeKind" /> used when reading <see cref="T:System.DateTime"/> values from BSON.
+            </summary>
+            <value>The <see cref="T:System.DateTimeKind" /> used when reading <see cref="T:System.DateTime"/> values from BSON.</value>
+        </member>
+        <member name="M:Newtonsoft.Json.Bson.BsonReader.#ctor(System.IO.Stream)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Bson.BsonReader"/> class.
+            </summary>
+            <param name="stream">The stream.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Bson.BsonReader.#ctor(System.IO.BinaryReader)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Bson.BsonReader"/> class.
+            </summary>
+            <param name="reader">The reader.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Bson.BsonReader.#ctor(System.IO.Stream,System.Boolean,System.DateTimeKind)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Bson.BsonReader"/> class.
+            </summary>
+            <param name="stream">The stream.</param>
+            <param name="readRootValueAsArray">if set to <c>true</c> the root object will be read as a JSON array.</param>
+            <param name="dateTimeKindHandling">The <see cref="T:System.DateTimeKind" /> used when reading <see cref="T:System.DateTime"/> values from BSON.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Bson.BsonReader.#ctor(System.IO.BinaryReader,System.Boolean,System.DateTimeKind)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Bson.BsonReader"/> class.
+            </summary>
+            <param name="reader">The reader.</param>
+            <param name="readRootValueAsArray">if set to <c>true</c> the root object will be read as a JSON array.</param>
+            <param name="dateTimeKindHandling">The <see cref="T:System.DateTimeKind" /> used when reading <see cref="T:System.DateTime"/> values from BSON.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Bson.BsonReader.Read">
+            <summary>
+            Reads the next JSON token from the stream.
+            </summary>
+            <returns>
+            true if the next token was read successfully; false if there are no more tokens to read.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Bson.BsonReader.Close">
+            <summary>
+            Changes the <see cref="T:Newtonsoft.Json.JsonReader.State"/> to Closed.
+            </summary>
+        </member>
+        <member name="T:Newtonsoft.Json.Bson.BsonWriter">
+            <summary>
+            Represents a writer that provides a fast, non-cached, forward-only way of generating JSON data.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.Bson.BsonWriter.DateTimeKindHandling">
+            <summary>
+            Gets or sets the <see cref="T:System.DateTimeKind" /> used when writing <see cref="T:System.DateTime"/> values to BSON.
+            When set to <see cref="F:System.DateTimeKind.Unspecified" /> no conversion will occur.
+            </summary>
+            <value>The <see cref="T:System.DateTimeKind" /> used when writing <see cref="T:System.DateTime"/> values to BSON.</value>
+        </member>
+        <member name="M:Newtonsoft.Json.Bson.BsonWriter.#ctor(System.IO.Stream)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Bson.BsonWriter"/> class.
+            </summary>
+            <param name="stream">The stream.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Bson.BsonWriter.#ctor(System.IO.BinaryWriter)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Bson.BsonWriter"/> class.
+            </summary>
+            <param name="writer">The writer.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Bson.BsonWriter.Flush">
+            <summary>
+            Flushes whatever is in the buffer to the underlying streams and also flushes the underlying stream.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Bson.BsonWriter.WriteEnd(Newtonsoft.Json.JsonToken)">
+            <summary>
+            Writes the end.
+            </summary>
+            <param name="token">The token.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Bson.BsonWriter.WriteComment(System.String)">
+            <summary>
+            Writes out a comment <code>/*...*/</code> containing the specified text.
+            </summary>
+            <param name="text">Text to place inside the comment.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Bson.BsonWriter.WriteStartConstructor(System.String)">
+            <summary>
+            Writes the start of a constructor with the given name.
+            </summary>
+            <param name="name">The name of the constructor.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Bson.BsonWriter.WriteRaw(System.String)">
+            <summary>
+            Writes raw JSON.
+            </summary>
+            <param name="json">The raw JSON to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Bson.BsonWriter.WriteRawValue(System.String)">
+            <summary>
+            Writes raw JSON where a value is expected and updates the writer's state.
+            </summary>
+            <param name="json">The raw JSON to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Bson.BsonWriter.WriteStartArray">
+            <summary>
+            Writes the beginning of a JSON array.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Bson.BsonWriter.WriteStartObject">
+            <summary>
+            Writes the beginning of a JSON object.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Bson.BsonWriter.WritePropertyName(System.String)">
+            <summary>
+            Writes the property name of a name/value pair on a JSON object.
+            </summary>
+            <param name="name">The name of the property.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Bson.BsonWriter.Close">
+            <summary>
+            Closes this stream and the underlying stream.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Bson.BsonWriter.WriteValue(System.Object)">
+            <summary>
+            Writes a <see cref="T:System.Object"/> value.
+            An error will raised if the value cannot be written as a single JSON token.
+            </summary>
+            <param name="value">The <see cref="T:System.Object"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Bson.BsonWriter.WriteNull">
+            <summary>
+            Writes a null value.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Bson.BsonWriter.WriteUndefined">
+            <summary>
+            Writes an undefined value.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Bson.BsonWriter.WriteValue(System.String)">
+            <summary>
+            Writes a <see cref="T:System.String"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.String"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Bson.BsonWriter.WriteValue(System.Int32)">
+            <summary>
+            Writes a <see cref="T:System.Int32"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Int32"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Bson.BsonWriter.WriteValue(System.UInt32)">
+            <summary>
+            Writes a <see cref="T:System.UInt32"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.UInt32"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Bson.BsonWriter.WriteValue(System.Int64)">
+            <summary>
+            Writes a <see cref="T:System.Int64"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Int64"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Bson.BsonWriter.WriteValue(System.UInt64)">
+            <summary>
+            Writes a <see cref="T:System.UInt64"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.UInt64"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Bson.BsonWriter.WriteValue(System.Single)">
+            <summary>
+            Writes a <see cref="T:System.Single"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Single"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Bson.BsonWriter.WriteValue(System.Double)">
+            <summary>
+            Writes a <see cref="T:System.Double"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Double"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Bson.BsonWriter.WriteValue(System.Boolean)">
+            <summary>
+            Writes a <see cref="T:System.Boolean"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Boolean"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Bson.BsonWriter.WriteValue(System.Int16)">
+            <summary>
+            Writes a <see cref="T:System.Int16"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Int16"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Bson.BsonWriter.WriteValue(System.UInt16)">
+            <summary>
+            Writes a <see cref="T:System.UInt16"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.UInt16"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Bson.BsonWriter.WriteValue(System.Char)">
+            <summary>
+            Writes a <see cref="T:System.Char"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Char"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Bson.BsonWriter.WriteValue(System.Byte)">
+            <summary>
+            Writes a <see cref="T:System.Byte"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Byte"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Bson.BsonWriter.WriteValue(System.SByte)">
+            <summary>
+            Writes a <see cref="T:System.SByte"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.SByte"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Bson.BsonWriter.WriteValue(System.Decimal)">
+            <summary>
+            Writes a <see cref="T:System.Decimal"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Decimal"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Bson.BsonWriter.WriteValue(System.DateTime)">
+            <summary>
+            Writes a <see cref="T:System.DateTime"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.DateTime"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Bson.BsonWriter.WriteValue(System.DateTimeOffset)">
+            <summary>
+            Writes a <see cref="T:System.DateTimeOffset"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.DateTimeOffset"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Bson.BsonWriter.WriteValue(System.Byte[])">
+            <summary>
+            Writes a <see cref="T:System.Byte"/>[] value.
+            </summary>
+            <param name="value">The <see cref="T:System.Byte"/>[] value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Bson.BsonWriter.WriteValue(System.Guid)">
+            <summary>
+            Writes a <see cref="T:System.Guid"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Guid"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Bson.BsonWriter.WriteValue(System.TimeSpan)">
+            <summary>
+            Writes a <see cref="T:System.TimeSpan"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.TimeSpan"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Bson.BsonWriter.WriteValue(System.Uri)">
+            <summary>
+            Writes a <see cref="T:System.Uri"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Uri"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Bson.BsonWriter.WriteObjectId(System.Byte[])">
+            <summary>
+            Writes a <see cref="T:System.Byte"/>[] value that represents a BSON object id.
+            </summary>
+            <param name="value">The Object ID value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Bson.BsonWriter.WriteRegex(System.String,System.String)">
+            <summary>
+            Writes a BSON regex.
+            </summary>
+            <param name="pattern">The regex pattern.</param>
+            <param name="options">The regex options.</param>
+        </member>
+        <member name="T:Newtonsoft.Json.ConstructorHandling">
+            <summary>
+            Specifies how constructors are used when initializing objects during deserialization by the <see cref="T:Newtonsoft.Json.JsonSerializer"/>.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.ConstructorHandling.Default">
+            <summary>
+            First attempt to use the public default constructor, then fall back to single paramatized constructor, then the non-public default constructor.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.ConstructorHandling.AllowNonPublicDefaultConstructor">
+            <summary>
+            Json.NET will use a non-public default constructor before falling back to a paramatized constructor.
+            </summary>
+        </member>
+        <member name="T:Newtonsoft.Json.Converters.BsonObjectIdConverter">
+            <summary>
+            Converts a <see cref="T:Newtonsoft.Json.Bson.BsonObjectId"/> to and from JSON and BSON.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Converters.BsonObjectIdConverter.WriteJson(Newtonsoft.Json.JsonWriter,System.Object,Newtonsoft.Json.JsonSerializer)">
+            <summary>
+            Writes the JSON representation of the object.
+            </summary>
+            <param name="writer">The <see cref="T:Newtonsoft.Json.JsonWriter"/> to write to.</param>
+            <param name="value">The value.</param>
+            <param name="serializer">The calling serializer.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Converters.BsonObjectIdConverter.ReadJson(Newtonsoft.Json.JsonReader,System.Type,System.Object,Newtonsoft.Json.JsonSerializer)">
+            <summary>
+            Reads the JSON representation of the object.
+            </summary>
+            <param name="reader">The <see cref="T:Newtonsoft.Json.JsonReader"/> to read from.</param>
+            <param name="objectType">Type of the object.</param>
+            <param name="existingValue">The existing value of object being read.</param>
+            <param name="serializer">The calling serializer.</param>
+            <returns>The object value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Converters.BsonObjectIdConverter.CanConvert(System.Type)">
+            <summary>
+            Determines whether this instance can convert the specified object type.
+            </summary>
+            <param name="objectType">Type of the object.</param>
+            <returns>
+            	<c>true</c> if this instance can convert the specified object type; otherwise, <c>false</c>.
+            </returns>
+        </member>
+        <member name="T:Newtonsoft.Json.Converters.CustomCreationConverter`1">
+            <summary>
+            Create a custom object
+            </summary>
+            <typeparam name="T">The object type to convert.</typeparam>
+        </member>
+        <member name="M:Newtonsoft.Json.Converters.CustomCreationConverter`1.WriteJson(Newtonsoft.Json.JsonWriter,System.Object,Newtonsoft.Json.JsonSerializer)">
+            <summary>
+            Writes the JSON representation of the object.
+            </summary>
+            <param name="writer">The <see cref="T:Newtonsoft.Json.JsonWriter"/> to write to.</param>
+            <param name="value">The value.</param>
+            <param name="serializer">The calling serializer.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Converters.CustomCreationConverter`1.ReadJson(Newtonsoft.Json.JsonReader,System.Type,System.Object,Newtonsoft.Json.JsonSerializer)">
+            <summary>
+            Reads the JSON representation of the object.
+            </summary>
+            <param name="reader">The <see cref="T:Newtonsoft.Json.JsonReader"/> to read from.</param>
+            <param name="objectType">Type of the object.</param>
+            <param name="existingValue">The existing value of object being read.</param>
+            <param name="serializer">The calling serializer.</param>
+            <returns>The object value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Converters.CustomCreationConverter`1.Create(System.Type)">
+            <summary>
+            Creates an object which will then be populated by the serializer.
+            </summary>
+            <param name="objectType">Type of the object.</param>
+            <returns>The created object.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Converters.CustomCreationConverter`1.CanConvert(System.Type)">
+            <summary>
+            Determines whether this instance can convert the specified object type.
+            </summary>
+            <param name="objectType">Type of the object.</param>
+            <returns>
+            	<c>true</c> if this instance can convert the specified object type; otherwise, <c>false</c>.
+            </returns>
+        </member>
+        <member name="P:Newtonsoft.Json.Converters.CustomCreationConverter`1.CanWrite">
+            <summary>
+            Gets a value indicating whether this <see cref="T:Newtonsoft.Json.JsonConverter"/> can write JSON.
+            </summary>
+            <value>
+            	<c>true</c> if this <see cref="T:Newtonsoft.Json.JsonConverter"/> can write JSON; otherwise, <c>false</c>.
+            </value>
+        </member>
+        <member name="T:Newtonsoft.Json.Converters.DateTimeConverterBase">
+            <summary>
+            Provides a base class for converting a <see cref="T:System.DateTime"/> to and from JSON.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Converters.DateTimeConverterBase.CanConvert(System.Type)">
+            <summary>
+            Determines whether this instance can convert the specified object type.
+            </summary>
+            <param name="objectType">Type of the object.</param>
+            <returns>
+            	<c>true</c> if this instance can convert the specified object type; otherwise, <c>false</c>.
+            </returns>
+        </member>
+        <member name="T:Newtonsoft.Json.Converters.DiscriminatedUnionConverter">
+            <summary>
+            Converts a F# discriminated union type to and from JSON.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Converters.DiscriminatedUnionConverter.WriteJson(Newtonsoft.Json.JsonWriter,System.Object,Newtonsoft.Json.JsonSerializer)">
+            <summary>
+            Writes the JSON representation of the object.
+            </summary>
+            <param name="writer">The <see cref="T:Newtonsoft.Json.JsonWriter"/> to write to.</param>
+            <param name="value">The value.</param>
+            <param name="serializer">The calling serializer.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Converters.DiscriminatedUnionConverter.ReadJson(Newtonsoft.Json.JsonReader,System.Type,System.Object,Newtonsoft.Json.JsonSerializer)">
+            <summary>
+            Reads the JSON representation of the object.
+            </summary>
+            <param name="reader">The <see cref="T:Newtonsoft.Json.JsonReader"/> to read from.</param>
+            <param name="objectType">Type of the object.</param>
+            <param name="existingValue">The existing value of object being read.</param>
+            <param name="serializer">The calling serializer.</param>
+            <returns>The object value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Converters.DiscriminatedUnionConverter.CanConvert(System.Type)">
+            <summary>
+            Determines whether this instance can convert the specified object type.
+            </summary>
+            <param name="objectType">Type of the object.</param>
+            <returns>
+            	<c>true</c> if this instance can convert the specified object type; otherwise, <c>false</c>.
+            </returns>
+        </member>
+        <member name="T:Newtonsoft.Json.Converters.EnumerableVectorConverter`1">
+            <summary>
+            
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Converters.EnumerableVectorConverter`1.WriteJson(Newtonsoft.Json.JsonWriter,System.Object,Newtonsoft.Json.JsonSerializer)">
+            <summary>
+            
+            </summary>
+            <param name="writer"></param>
+            <param name="value"></param>
+            <param name="serializer"></param>
+        </member>
+        <member name="T:Newtonsoft.Json.Converters.ExpandoObjectConverter">
+            <summary>
+            Converts an ExpandoObject to and from JSON.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Converters.ExpandoObjectConverter.WriteJson(Newtonsoft.Json.JsonWriter,System.Object,Newtonsoft.Json.JsonSerializer)">
+            <summary>
+            Writes the JSON representation of the object.
+            </summary>
+            <param name="writer">The <see cref="T:Newtonsoft.Json.JsonWriter"/> to write to.</param>
+            <param name="value">The value.</param>
+            <param name="serializer">The calling serializer.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Converters.ExpandoObjectConverter.ReadJson(Newtonsoft.Json.JsonReader,System.Type,System.Object,Newtonsoft.Json.JsonSerializer)">
+            <summary>
+            Reads the JSON representation of the object.
+            </summary>
+            <param name="reader">The <see cref="T:Newtonsoft.Json.JsonReader"/> to read from.</param>
+            <param name="objectType">Type of the object.</param>
+            <param name="existingValue">The existing value of object being read.</param>
+            <param name="serializer">The calling serializer.</param>
+            <returns>The object value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Converters.ExpandoObjectConverter.CanConvert(System.Type)">
+            <summary>
+            Determines whether this instance can convert the specified object type.
+            </summary>
+            <param name="objectType">Type of the object.</param>
+            <returns>
+            	<c>true</c> if this instance can convert the specified object type; otherwise, <c>false</c>.
+            </returns>
+        </member>
+        <member name="P:Newtonsoft.Json.Converters.ExpandoObjectConverter.CanWrite">
+            <summary>
+            Gets a value indicating whether this <see cref="T:Newtonsoft.Json.JsonConverter"/> can write JSON.
+            </summary>
+            <value>
+            	<c>true</c> if this <see cref="T:Newtonsoft.Json.JsonConverter"/> can write JSON; otherwise, <c>false</c>.
+            </value>
+        </member>
+        <member name="M:Newtonsoft.Json.Converters.HashSetConverter.CanConvert(System.Type)">
+            <summary>
+            
+            </summary>
+            <param name="objectType"></param>
+            <returns></returns>
+        </member>
+        <member name="T:Newtonsoft.Json.Converters.IsoDateTimeConverter">
+            <summary>
+            Converts a <see cref="T:System.DateTime"/> to and from the ISO 8601 date format (e.g. 2008-04-12T12:53Z).
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.Converters.IsoDateTimeConverter.DateTimeStyles">
+            <summary>
+            Gets or sets the date time styles used when converting a date to and from JSON.
+            </summary>
+            <value>The date time styles used when converting a date to and from JSON.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Converters.IsoDateTimeConverter.DateTimeFormat">
+            <summary>
+            Gets or sets the date time format used when converting a date to and from JSON.
+            </summary>
+            <value>The date time format used when converting a date to and from JSON.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Converters.IsoDateTimeConverter.Culture">
+            <summary>
+            Gets or sets the culture used when converting a date to and from JSON.
+            </summary>
+            <value>The culture used when converting a date to and from JSON.</value>
+        </member>
+        <member name="M:Newtonsoft.Json.Converters.IsoDateTimeConverter.WriteJson(Newtonsoft.Json.JsonWriter,System.Object,Newtonsoft.Json.JsonSerializer)">
+            <summary>
+            Writes the JSON representation of the object.
+            </summary>
+            <param name="writer">The <see cref="T:Newtonsoft.Json.JsonWriter"/> to write to.</param>
+            <param name="value">The value.</param>
+            <param name="serializer">The calling serializer.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Converters.IsoDateTimeConverter.ReadJson(Newtonsoft.Json.JsonReader,System.Type,System.Object,Newtonsoft.Json.JsonSerializer)">
+            <summary>
+            Reads the JSON representation of the object.
+            </summary>
+            <param name="reader">The <see cref="T:Newtonsoft.Json.JsonReader"/> to read from.</param>
+            <param name="objectType">Type of the object.</param>
+            <param name="existingValue">The existing value of object being read.</param>
+            <param name="serializer">The calling serializer.</param>
+            <returns>The object value.</returns>
+        </member>
+        <member name="T:Newtonsoft.Json.Converters.JavaScriptDateTimeConverter">
+            <summary>
+            Converts a <see cref="T:System.DateTime"/> to and from a JavaScript date constructor (e.g. new Date(52231943)).
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Converters.JavaScriptDateTimeConverter.WriteJson(Newtonsoft.Json.JsonWriter,System.Object,Newtonsoft.Json.JsonSerializer)">
+            <summary>
+            Writes the JSON representation of the object.
+            </summary>
+            <param name="writer">The <see cref="T:Newtonsoft.Json.JsonWriter"/> to write to.</param>
+            <param name="value">The value.</param>
+            <param name="serializer">The calling serializer.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Converters.JavaScriptDateTimeConverter.ReadJson(Newtonsoft.Json.JsonReader,System.Type,System.Object,Newtonsoft.Json.JsonSerializer)">
+            <summary>
+            Reads the JSON representation of the object.
+            </summary>
+            <param name="reader">The <see cref="T:Newtonsoft.Json.JsonReader"/> to read from.</param>
+            <param name="objectType">Type of the object.</param>
+            <param name="existingValue">The existing property value of the JSON that is being converted.</param>
+            <param name="serializer">The calling serializer.</param>
+            <returns>The object value.</returns>
+        </member>
+        <member name="T:Newtonsoft.Json.Converters.KeyValuePairConverter">
+            <summary>
+            Converts a <see cref="T:System.Collections.Generic.KeyValuePair`2"/> to and from JSON.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Converters.KeyValuePairConverter.WriteJson(Newtonsoft.Json.JsonWriter,System.Object,Newtonsoft.Json.JsonSerializer)">
+            <summary>
+            Writes the JSON representation of the object.
+            </summary>
+            <param name="writer">The <see cref="T:Newtonsoft.Json.JsonWriter"/> to write to.</param>
+            <param name="value">The value.</param>
+            <param name="serializer">The calling serializer.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Converters.KeyValuePairConverter.ReadJson(Newtonsoft.Json.JsonReader,System.Type,System.Object,Newtonsoft.Json.JsonSerializer)">
+            <summary>
+            Reads the JSON representation of the object.
+            </summary>
+            <param name="reader">The <see cref="T:Newtonsoft.Json.JsonReader"/> to read from.</param>
+            <param name="objectType">Type of the object.</param>
+            <param name="existingValue">The existing value of object being read.</param>
+            <param name="serializer">The calling serializer.</param>
+            <returns>The object value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Converters.KeyValuePairConverter.CanConvert(System.Type)">
+            <summary>
+            Determines whether this instance can convert the specified object type.
+            </summary>
+            <param name="objectType">Type of the object.</param>
+            <returns>
+            	<c>true</c> if this instance can convert the specified object type; otherwise, <c>false</c>.
+            </returns>
+        </member>
+        <member name="T:Newtonsoft.Json.Converters.RegexConverter">
+            <summary>
+            Converts a <see cref="T:System.Text.RegularExpressions.Regex"/> to and from JSON and BSON.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Converters.RegexConverter.WriteJson(Newtonsoft.Json.JsonWriter,System.Object,Newtonsoft.Json.JsonSerializer)">
+            <summary>
+            Writes the JSON representation of the object.
+            </summary>
+            <param name="writer">The <see cref="T:Newtonsoft.Json.JsonWriter"/> to write to.</param>
+            <param name="value">The value.</param>
+            <param name="serializer">The calling serializer.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Converters.RegexConverter.ReadJson(Newtonsoft.Json.JsonReader,System.Type,System.Object,Newtonsoft.Json.JsonSerializer)">
+            <summary>
+            Reads the JSON representation of the object.
+            </summary>
+            <param name="reader">The <see cref="T:Newtonsoft.Json.JsonReader"/> to read from.</param>
+            <param name="objectType">Type of the object.</param>
+            <param name="existingValue">The existing value of object being read.</param>
+            <param name="serializer">The calling serializer.</param>
+            <returns>The object value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Converters.RegexConverter.CanConvert(System.Type)">
+            <summary>
+            Determines whether this instance can convert the specified object type.
+            </summary>
+            <param name="objectType">Type of the object.</param>
+            <returns>
+            	<c>true</c> if this instance can convert the specified object type; otherwise, <c>false</c>.
+            </returns>
+        </member>
+        <member name="T:Newtonsoft.Json.Converters.StringEnumConverter">
+            <summary>
+            Converts an <see cref="T:System.Enum"/> to and from its name string value.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.Converters.StringEnumConverter.CamelCaseText">
+            <summary>
+            Gets or sets a value indicating whether the written enum text should be camel case.
+            </summary>
+            <value><c>true</c> if the written enum text will be camel case; otherwise, <c>false</c>.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Converters.StringEnumConverter.AllowIntegerValues">
+            <summary>
+            Gets or sets a value indicating whether integer values are allowed.
+            </summary>
+            <value><c>true</c> if integers are allowed; otherwise, <c>false</c>.</value>
+        </member>
+        <member name="M:Newtonsoft.Json.Converters.StringEnumConverter.#ctor">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Converters.StringEnumConverter"/> class.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Converters.StringEnumConverter.#ctor(System.Boolean)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Converters.StringEnumConverter"/> class.
+            </summary>
+            <param name="camelCaseText"><c>true</c> if the written enum text will be camel case; otherwise, <c>false</c>.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Converters.StringEnumConverter.WriteJson(Newtonsoft.Json.JsonWriter,System.Object,Newtonsoft.Json.JsonSerializer)">
+            <summary>
+            Writes the JSON representation of the object.
+            </summary>
+            <param name="writer">The <see cref="T:Newtonsoft.Json.JsonWriter"/> to write to.</param>
+            <param name="value">The value.</param>
+            <param name="serializer">The calling serializer.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Converters.StringEnumConverter.ReadJson(Newtonsoft.Json.JsonReader,System.Type,System.Object,Newtonsoft.Json.JsonSerializer)">
+            <summary>
+            Reads the JSON representation of the object.
+            </summary>
+            <param name="reader">The <see cref="T:Newtonsoft.Json.JsonReader"/> to read from.</param>
+            <param name="objectType">Type of the object.</param>
+            <param name="existingValue">The existing value of object being read.</param>
+            <param name="serializer">The calling serializer.</param>
+            <returns>The object value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Converters.StringEnumConverter.CanConvert(System.Type)">
+            <summary>
+            Determines whether this instance can convert the specified object type.
+            </summary>
+            <param name="objectType">Type of the object.</param>
+            <returns>
+            <c>true</c> if this instance can convert the specified object type; otherwise, <c>false</c>.
+            </returns>
+        </member>
+        <member name="T:Newtonsoft.Json.Converters.VectorConverter">
+            <summary>
+            Json Converter for Vector2, Vector3 and Vector4.  Only serializes x, y, (z) and (w) properties.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Converters.VectorConverter.#ctor">
+            <summary>
+            Default Constructor - All Vector types enabled by default
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Converters.VectorConverter.#ctor(System.Boolean,System.Boolean,System.Boolean)">
+            <summary>
+            Selectively enable Vector types
+            </summary>
+            <param name="enableVector2">Use for Vector2 objects</param>
+            <param name="enableVector3">Use for Vector3 objects</param>
+            <param name="enableVector4">Use for Vector4 objects</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Converters.VectorConverter.WriteJson(Newtonsoft.Json.JsonWriter,System.Object,Newtonsoft.Json.JsonSerializer)">
+            <summary>
+            
+            </summary>
+            <param name="writer"></param>
+            <param name="value"></param>
+            <param name="serializer"></param>
+        </member>
+        <member name="M:Newtonsoft.Json.Converters.VectorConverter.ReadJson(Newtonsoft.Json.JsonReader,System.Type,System.Object,Newtonsoft.Json.JsonSerializer)">
+            <summary>
+            
+            </summary>
+            <param name="reader"></param>
+            <param name="objectType"></param>
+            <param name="existingValue"></param>
+            <param name="serializer"></param>
+            <returns></returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Converters.VectorConverter.CanConvert(System.Type)">
+            <summary>
+            
+            </summary>
+            <param name="objectType"></param>
+            <returns></returns>
+        </member>
+        <member name="T:Newtonsoft.Json.Converters.VersionConverter">
+            <summary>
+            Converts a <see cref="T:System.Version"/> to and from a string (e.g. "1.2.3.4").
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Converters.VersionConverter.WriteJson(Newtonsoft.Json.JsonWriter,System.Object,Newtonsoft.Json.JsonSerializer)">
+            <summary>
+            Writes the JSON representation of the object.
+            </summary>
+            <param name="writer">The <see cref="T:Newtonsoft.Json.JsonWriter"/> to write to.</param>
+            <param name="value">The value.</param>
+            <param name="serializer">The calling serializer.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Converters.VersionConverter.ReadJson(Newtonsoft.Json.JsonReader,System.Type,System.Object,Newtonsoft.Json.JsonSerializer)">
+            <summary>
+            Reads the JSON representation of the object.
+            </summary>
+            <param name="reader">The <see cref="T:Newtonsoft.Json.JsonReader"/> to read from.</param>
+            <param name="objectType">Type of the object.</param>
+            <param name="existingValue">The existing property value of the JSON that is being converted.</param>
+            <param name="serializer">The calling serializer.</param>
+            <returns>The object value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Converters.VersionConverter.CanConvert(System.Type)">
+            <summary>
+            Determines whether this instance can convert the specified object type.
+            </summary>
+            <param name="objectType">Type of the object.</param>
+            <returns>
+            	<c>true</c> if this instance can convert the specified object type; otherwise, <c>false</c>.
+            </returns>
+        </member>
+        <member name="T:Newtonsoft.Json.Converters.XmlNodeConverter">
+            <summary>
+            Converts XML to and from JSON.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.Converters.XmlNodeConverter.DeserializeRootElementName">
+            <summary>
+            Gets or sets the name of the root element to insert when deserializing to XML if the JSON structure has produces multiple root elements.
+            </summary>
+            <value>The name of the deserialize root element.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Converters.XmlNodeConverter.WriteArrayAttribute">
+            <summary>
+            Gets or sets a flag to indicate whether to write the Json.NET array attribute.
+            This attribute helps preserve arrays when converting the written XML back to JSON.
+            </summary>
+            <value><c>true</c> if the array attibute is written to the XML; otherwise, <c>false</c>.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Converters.XmlNodeConverter.OmitRootObject">
+            <summary>
+            Gets or sets a value indicating whether to write the root JSON object.
+            </summary>
+            <value><c>true</c> if the JSON root object is omitted; otherwise, <c>false</c>.</value>
+        </member>
+        <member name="M:Newtonsoft.Json.Converters.XmlNodeConverter.WriteJson(Newtonsoft.Json.JsonWriter,System.Object,Newtonsoft.Json.JsonSerializer)">
+            <summary>
+            Writes the JSON representation of the object.
+            </summary>
+            <param name="writer">The <see cref="T:Newtonsoft.Json.JsonWriter"/> to write to.</param>
+            <param name="serializer">The calling serializer.</param>
+            <param name="value">The value.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Converters.XmlNodeConverter.ReadJson(Newtonsoft.Json.JsonReader,System.Type,System.Object,Newtonsoft.Json.JsonSerializer)">
+            <summary>
+            Reads the JSON representation of the object.
+            </summary>
+            <param name="reader">The <see cref="T:Newtonsoft.Json.JsonReader"/> to read from.</param>
+            <param name="objectType">Type of the object.</param>
+            <param name="existingValue">The existing value of object being read.</param>
+            <param name="serializer">The calling serializer.</param>
+            <returns>The object value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Converters.XmlNodeConverter.IsNamespaceAttribute(System.String,System.String@)">
+            <summary>
+            Checks if the attributeName is a namespace attribute.
+            </summary>
+            <param name="attributeName">Attribute name to test.</param>
+            <param name="prefix">The attribute name prefix if it has one, otherwise an empty string.</param>
+            <returns>True if attribute name is for a namespace attribute, otherwise false.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Converters.XmlNodeConverter.CanConvert(System.Type)">
+            <summary>
+            Determines whether this instance can convert the specified value type.
+            </summary>
+            <param name="valueType">Type of the value.</param>
+            <returns>
+            	<c>true</c> if this instance can convert the specified value type; otherwise, <c>false</c>.
+            </returns>
+        </member>
+        <member name="T:Newtonsoft.Json.DateFormatHandling">
+            <summary>
+            Specifies how dates are formatted when writing JSON text.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.DateFormatHandling.IsoDateFormat">
+            <summary>
+            Dates are written in the ISO 8601 format, e.g. "2012-03-21T05:40Z".
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.DateFormatHandling.MicrosoftDateFormat">
+            <summary>
+            Dates are written in the Microsoft JSON format, e.g. "\/Date(1198908717056)\/".
+            </summary>
+        </member>
+        <member name="T:Newtonsoft.Json.DateParseHandling">
+            <summary>
+            Specifies how date formatted strings, e.g. "\/Date(1198908717056)\/" and "2012-03-21T05:40Z", are parsed when reading JSON text.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.DateParseHandling.None">
+            <summary>
+            Date formatted strings are not parsed to a date type and are read as strings.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.DateParseHandling.DateTime">
+            <summary>
+            Date formatted strings, e.g. "\/Date(1198908717056)\/" and "2012-03-21T05:40Z", are parsed to <see cref="F:Newtonsoft.Json.DateParseHandling.DateTime"/>.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.DateParseHandling.DateTimeOffset">
+            <summary>
+            Date formatted strings, e.g. "\/Date(1198908717056)\/" and "2012-03-21T05:40Z", are parsed to <see cref="F:Newtonsoft.Json.DateParseHandling.DateTimeOffset"/>.
+            </summary>
+        </member>
+        <member name="T:Newtonsoft.Json.DateTimeZoneHandling">
+            <summary>
+            Specifies how to treat the time value when converting between string and <see cref="T:System.DateTime"/>.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.DateTimeZoneHandling.Local">
+            <summary>
+            Treat as local time. If the <see cref="T:System.DateTime"/> object represents a Coordinated Universal Time (UTC), it is converted to the local time.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.DateTimeZoneHandling.Utc">
+            <summary>
+            Treat as a UTC. If the <see cref="T:System.DateTime"/> object represents a local time, it is converted to a UTC.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.DateTimeZoneHandling.Unspecified">
+            <summary>
+            Treat as a local time if a <see cref="T:System.DateTime"/> is being converted to a string.
+            If a string is being converted to <see cref="T:System.DateTime"/>, convert to a local time if a time zone is specified.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.DateTimeZoneHandling.RoundtripKind">
+            <summary>
+            Time zone information should be preserved when converting.
+            </summary>
+        </member>
+        <member name="T:Newtonsoft.Json.DefaultValueHandling">
+            <summary>
+            Specifies default value handling options for the <see cref="T:Newtonsoft.Json.JsonSerializer"/>.
+            </summary>
+            <example>
+              <code lang="cs" source="..\Src\Newtonsoft.Json.Tests\Documentation\SerializationTests.cs" region="ReducingSerializedJsonSizeDefaultValueHandlingObject" title="DefaultValueHandling Class" />
+              <code lang="cs" source="..\Src\Newtonsoft.Json.Tests\Documentation\SerializationTests.cs" region="ReducingSerializedJsonSizeDefaultValueHandlingExample" title="DefaultValueHandling Ignore Example" />
+            </example>
+        </member>
+        <member name="F:Newtonsoft.Json.DefaultValueHandling.Include">
+            <summary>
+            Include members where the member value is the same as the member's default value when serializing objects.
+            Included members are written to JSON. Has no effect when deserializing.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.DefaultValueHandling.Ignore">
+            <summary>
+            Ignore members where the member value is the same as the member's default value when serializing objects
+            so that is is not written to JSON.
+            This option will ignore all default values (e.g. <c>null</c> for objects and nullable types; <c>0</c> for integers,
+            decimals and floating point numbers; and <c>false</c> for booleans). The default value ignored can be changed by
+            placing the <see cref="T:System.ComponentModel.DefaultValueAttribute"/> on the property.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.DefaultValueHandling.Populate">
+            <summary>
+            Members with a default value but no JSON will be set to their default value when deserializing.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.DefaultValueHandling.IgnoreAndPopulate">
+            <summary>
+            Ignore members where the member value is the same as the member's default value when serializing objects
+            and sets members to their default value when deserializing.
+            </summary>
+        </member>
+        <member name="T:Newtonsoft.Json.FloatFormatHandling">
+            <summary>
+            Specifies float format handling options when writing special floating point numbers, e.g. <see cref="F:System.Double.NaN"/>,
+            <see cref="F:System.Double.PositiveInfinity"/> and <see cref="F:System.Double.NegativeInfinity"/> with <see cref="T:Newtonsoft.Json.JsonWriter"/>.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.FloatFormatHandling.String">
+            <summary>
+            Write special floating point values as strings in JSON, e.g. "NaN", "Infinity", "-Infinity".
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.FloatFormatHandling.Symbol">
+            <summary>
+            Write special floating point values as symbols in JSON, e.g. NaN, Infinity, -Infinity.
+            Note that this will produce non-valid JSON.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.FloatFormatHandling.DefaultValue">
+            <summary>
+            Write special floating point values as the property's default value in JSON, e.g. 0.0 for a <see cref="T:System.Double"/> property, null for a <see cref="T:System.Nullable`1"/> property.
+            </summary>
+        </member>
+        <member name="T:Newtonsoft.Json.FloatParseHandling">
+            <summary>
+            Specifies how floating point numbers, e.g. 1.0 and 9.9, are parsed when reading JSON text.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.FloatParseHandling.Double">
+            <summary>
+            Floating point numbers are parsed to <see cref="F:Newtonsoft.Json.FloatParseHandling.Double"/>.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.FloatParseHandling.Decimal">
+            <summary>
+            Floating point numbers are parsed to <see cref="F:Newtonsoft.Json.FloatParseHandling.Decimal"/>.
+            </summary>
+        </member>
+        <member name="T:Newtonsoft.Json.Formatting">
+            <summary>
+            Specifies formatting options for the <see cref="T:Newtonsoft.Json.JsonTextWriter"/>.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.Formatting.None">
+            <summary>
+            No special formatting is applied. This is the default.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.Formatting.Indented">
+            <summary>
+            Causes child objects to be indented according to the <see cref="P:Newtonsoft.Json.JsonTextWriter.Indentation"/> and <see cref="P:Newtonsoft.Json.JsonTextWriter.IndentChar"/> settings.
+            </summary>
+        </member>
+        <member name="T:Newtonsoft.Json.IArrayPool`1">
+            <summary>
+            Provides an interface for using pooled arrays.
+            </summary>
+            <typeparam name="T">The array type content.</typeparam>
+        </member>
+        <member name="M:Newtonsoft.Json.IArrayPool`1.Rent(System.Int32)">
+            <summary>
+            Rent a array from the pool. This array must be returned when it is no longer needed.
+            </summary>
+            <param name="minimumLength">The minimum required length of the array. The returned array may be longer.</param>
+            <returns>The rented array from the pool. This array must be returned when it is no longer needed.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.IArrayPool`1.Return(`0[])">
+            <summary>
+            Return an array to the pool.
+            </summary>
+            <param name="array">The array that is being returned.</param>
+        </member>
+        <member name="T:Newtonsoft.Json.IJsonLineInfo">
+            <summary>
+            Provides an interface to enable a class to return line and position information.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.IJsonLineInfo.HasLineInfo">
+            <summary>
+            Gets a value indicating whether the class can return line information.
+            </summary>
+            <returns>
+            	<c>true</c> if LineNumber and LinePosition can be provided; otherwise, <c>false</c>.
+            </returns>
+        </member>
+        <member name="P:Newtonsoft.Json.IJsonLineInfo.LineNumber">
+            <summary>
+            Gets the current line number.
+            </summary>
+            <value>The current line number or 0 if no line information is available (for example, HasLineInfo returns false).</value>
+        </member>
+        <member name="P:Newtonsoft.Json.IJsonLineInfo.LinePosition">
+            <summary>
+            Gets the current line position.
+            </summary>
+            <value>The current line position or 0 if no line information is available (for example, HasLineInfo returns false).</value>
+        </member>
+        <member name="T:Newtonsoft.Json.JsonArrayAttribute">
+            <summary>
+            Instructs the <see cref="T:Newtonsoft.Json.JsonSerializer"/> how to serialize the collection.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonArrayAttribute.AllowNullItems">
+            <summary>
+            Gets or sets a value indicating whether null items are allowed in the collection.
+            </summary>
+            <value><c>true</c> if null items are allowed in the collection; otherwise, <c>false</c>.</value>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonArrayAttribute.#ctor">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonArrayAttribute"/> class.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonArrayAttribute.#ctor(System.Boolean)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonObjectAttribute"/> class with a flag indicating whether the array can contain null items
+            </summary>
+            <param name="allowNullItems">A flag indicating whether the array can contain null items.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonArrayAttribute.#ctor(System.String)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonArrayAttribute"/> class with the specified container Id.
+            </summary>
+            <param name="id">The container Id.</param>
+        </member>
+        <member name="T:Newtonsoft.Json.JsonConstructorAttribute">
+            <summary>
+            Instructs the <see cref="T:Newtonsoft.Json.JsonSerializer"/> to use the specified constructor when deserializing that object.
+            </summary>
+        </member>
+        <member name="T:Newtonsoft.Json.JsonContainerAttribute">
+            <summary>
+            Instructs the <see cref="T:Newtonsoft.Json.JsonSerializer"/> how to serialize the object.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonContainerAttribute.Id">
+            <summary>
+            Gets or sets the id.
+            </summary>
+            <value>The id.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonContainerAttribute.Title">
+            <summary>
+            Gets or sets the title.
+            </summary>
+            <value>The title.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonContainerAttribute.Description">
+            <summary>
+            Gets or sets the description.
+            </summary>
+            <value>The description.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonContainerAttribute.ItemConverterType">
+            <summary>
+            Gets the collection's items converter.
+            </summary>
+            <value>The collection's items converter.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonContainerAttribute.ItemConverterParameters">
+            <summary>
+            The parameter list to use when constructing the JsonConverter described by ItemConverterType.
+            If null, the default constructor is used.
+            When non-null, there must be a constructor defined in the JsonConverter that exactly matches the number,
+            order, and type of these parameters.
+            </summary>
+            <example>
+            [JsonContainer(ItemConverterType = typeof(MyContainerConverter), ItemConverterParameters = new object[] { 123, "Four" })]
+            </example>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonContainerAttribute.IsReference">
+            <summary>
+            Gets or sets a value that indicates whether to preserve object references.
+            </summary>
+            <value>
+            	<c>true</c> to keep object reference; otherwise, <c>false</c>. The default is <c>false</c>.
+            </value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonContainerAttribute.ItemIsReference">
+            <summary>
+            Gets or sets a value that indicates whether to preserve collection's items references.
+            </summary>
+            <value>
+            	<c>true</c> to keep collection's items object references; otherwise, <c>false</c>. The default is <c>false</c>.
+            </value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonContainerAttribute.ItemReferenceLoopHandling">
+            <summary>
+            Gets or sets the reference loop handling used when serializing the collection's items.
+            </summary>
+            <value>The reference loop handling.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonContainerAttribute.ItemTypeNameHandling">
+            <summary>
+            Gets or sets the type name handling used when serializing the collection's items.
+            </summary>
+            <value>The type name handling.</value>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonContainerAttribute.#ctor">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonContainerAttribute"/> class.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonContainerAttribute.#ctor(System.String)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonContainerAttribute"/> class with the specified container Id.
+            </summary>
+            <param name="id">The container Id.</param>
+        </member>
+        <member name="T:Newtonsoft.Json.JsonConvert">
+            <summary>
+            Provides methods for converting between common language runtime types and JSON types.
+            </summary>
+            <example>
+              <code lang="cs" source="..\Src\Newtonsoft.Json.Tests\Documentation\SerializationTests.cs" region="SerializeObject" title="Serializing and Deserializing JSON with JsonConvert" />
+            </example>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonConvert.DefaultSettings">
+            <summary>
+            Gets or sets a function that creates default <see cref="T:Newtonsoft.Json.JsonSerializerSettings"/>.
+            Default settings are automatically used by serialization methods on <see cref="T:Newtonsoft.Json.JsonConvert"/>,
+            and <see cref="M:Newtonsoft.Json.Linq.JToken.ToObject``1"/> and <see cref="M:Newtonsoft.Json.Linq.JToken.FromObject(System.Object)"/> on <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            To serialize without using any default settings create a <see cref="T:Newtonsoft.Json.JsonSerializer"/> with
+            <see cref="M:Newtonsoft.Json.JsonSerializer.Create"/>.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.JsonConvert.True">
+            <summary>
+            Represents JavaScript's boolean value true as a string. This field is read-only.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.JsonConvert.False">
+            <summary>
+            Represents JavaScript's boolean value false as a string. This field is read-only.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.JsonConvert.Null">
+            <summary>
+            Represents JavaScript's null as a string. This field is read-only.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.JsonConvert.Undefined">
+            <summary>
+            Represents JavaScript's undefined as a string. This field is read-only.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.JsonConvert.PositiveInfinity">
+            <summary>
+            Represents JavaScript's positive infinity as a string. This field is read-only.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.JsonConvert.NegativeInfinity">
+            <summary>
+            Represents JavaScript's negative infinity as a string. This field is read-only.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.JsonConvert.NaN">
+            <summary>
+            Represents JavaScript's NaN as a string. This field is read-only.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.ToString(System.DateTime)">
+            <summary>
+            Converts the <see cref="T:System.DateTime"/> to its JSON string representation.
+            </summary>
+            <param name="value">The value to convert.</param>
+            <returns>A JSON string representation of the <see cref="T:System.DateTime"/>.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.ToString(System.DateTime,Newtonsoft.Json.DateFormatHandling,Newtonsoft.Json.DateTimeZoneHandling)">
+            <summary>
+            Converts the <see cref="T:System.DateTime"/> to its JSON string representation using the <see cref="T:Newtonsoft.Json.DateFormatHandling"/> specified.
+            </summary>
+            <param name="value">The value to convert.</param>
+            <param name="format">The format the date will be converted to.</param>
+            <param name="timeZoneHandling">The time zone handling when the date is converted to a string.</param>
+            <returns>A JSON string representation of the <see cref="T:System.DateTime"/>.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.ToString(System.DateTimeOffset)">
+            <summary>
+            Converts the <see cref="T:System.DateTimeOffset"/> to its JSON string representation.
+            </summary>
+            <param name="value">The value to convert.</param>
+            <returns>A JSON string representation of the <see cref="T:System.DateTimeOffset"/>.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.ToString(System.DateTimeOffset,Newtonsoft.Json.DateFormatHandling)">
+            <summary>
+            Converts the <see cref="T:System.DateTimeOffset"/> to its JSON string representation using the <see cref="T:Newtonsoft.Json.DateFormatHandling"/> specified.
+            </summary>
+            <param name="value">The value to convert.</param>
+            <param name="format">The format the date will be converted to.</param>
+            <returns>A JSON string representation of the <see cref="T:System.DateTimeOffset"/>.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.ToString(System.Boolean)">
+            <summary>
+            Converts the <see cref="T:System.Boolean"/> to its JSON string representation.
+            </summary>
+            <param name="value">The value to convert.</param>
+            <returns>A JSON string representation of the <see cref="T:System.Boolean"/>.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.ToString(System.Char)">
+            <summary>
+            Converts the <see cref="T:System.Char"/> to its JSON string representation.
+            </summary>
+            <param name="value">The value to convert.</param>
+            <returns>A JSON string representation of the <see cref="T:System.Char"/>.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.ToString(System.Enum)">
+            <summary>
+            Converts the <see cref="T:System.Enum"/> to its JSON string representation.
+            </summary>
+            <param name="value">The value to convert.</param>
+            <returns>A JSON string representation of the <see cref="T:System.Enum"/>.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.ToString(System.Int32)">
+            <summary>
+            Converts the <see cref="T:System.Int32"/> to its JSON string representation.
+            </summary>
+            <param name="value">The value to convert.</param>
+            <returns>A JSON string representation of the <see cref="T:System.Int32"/>.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.ToString(System.Int16)">
+            <summary>
+            Converts the <see cref="T:System.Int16"/> to its JSON string representation.
+            </summary>
+            <param name="value">The value to convert.</param>
+            <returns>A JSON string representation of the <see cref="T:System.Int16"/>.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.ToString(System.UInt16)">
+            <summary>
+            Converts the <see cref="T:System.UInt16"/> to its JSON string representation.
+            </summary>
+            <param name="value">The value to convert.</param>
+            <returns>A JSON string representation of the <see cref="T:System.UInt16"/>.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.ToString(System.UInt32)">
+            <summary>
+            Converts the <see cref="T:System.UInt32"/> to its JSON string representation.
+            </summary>
+            <param name="value">The value to convert.</param>
+            <returns>A JSON string representation of the <see cref="T:System.UInt32"/>.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.ToString(System.Int64)">
+            <summary>
+            Converts the <see cref="T:System.Int64"/>  to its JSON string representation.
+            </summary>
+            <param name="value">The value to convert.</param>
+            <returns>A JSON string representation of the <see cref="T:System.Int64"/>.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.ToString(System.UInt64)">
+            <summary>
+            Converts the <see cref="T:System.UInt64"/> to its JSON string representation.
+            </summary>
+            <param name="value">The value to convert.</param>
+            <returns>A JSON string representation of the <see cref="T:System.UInt64"/>.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.ToString(System.Single)">
+            <summary>
+            Converts the <see cref="T:System.Single"/> to its JSON string representation.
+            </summary>
+            <param name="value">The value to convert.</param>
+            <returns>A JSON string representation of the <see cref="T:System.Single"/>.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.ToString(System.Double)">
+            <summary>
+            Converts the <see cref="T:System.Double"/> to its JSON string representation.
+            </summary>
+            <param name="value">The value to convert.</param>
+            <returns>A JSON string representation of the <see cref="T:System.Double"/>.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.ToString(System.Byte)">
+            <summary>
+            Converts the <see cref="T:System.Byte"/> to its JSON string representation.
+            </summary>
+            <param name="value">The value to convert.</param>
+            <returns>A JSON string representation of the <see cref="T:System.Byte"/>.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.ToString(System.SByte)">
+            <summary>
+            Converts the <see cref="T:System.SByte"/> to its JSON string representation.
+            </summary>
+            <param name="value">The value to convert.</param>
+            <returns>A JSON string representation of the <see cref="T:System.SByte"/>.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.ToString(System.Decimal)">
+            <summary>
+            Converts the <see cref="T:System.Decimal"/> to its JSON string representation.
+            </summary>
+            <param name="value">The value to convert.</param>
+            <returns>A JSON string representation of the <see cref="T:System.SByte"/>.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.ToString(System.Guid)">
+            <summary>
+            Converts the <see cref="T:System.Guid"/> to its JSON string representation.
+            </summary>
+            <param name="value">The value to convert.</param>
+            <returns>A JSON string representation of the <see cref="T:System.Guid"/>.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.ToString(System.TimeSpan)">
+            <summary>
+            Converts the <see cref="T:System.TimeSpan"/> to its JSON string representation.
+            </summary>
+            <param name="value">The value to convert.</param>
+            <returns>A JSON string representation of the <see cref="T:System.TimeSpan"/>.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.ToString(System.Uri)">
+            <summary>
+            Converts the <see cref="T:System.Uri"/> to its JSON string representation.
+            </summary>
+            <param name="value">The value to convert.</param>
+            <returns>A JSON string representation of the <see cref="T:System.Uri"/>.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.ToString(System.String)">
+            <summary>
+            Converts the <see cref="T:System.String"/> to its JSON string representation.
+            </summary>
+            <param name="value">The value to convert.</param>
+            <returns>A JSON string representation of the <see cref="T:System.String"/>.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.ToString(System.String,System.Char)">
+            <summary>
+            Converts the <see cref="T:System.String"/> to its JSON string representation.
+            </summary>
+            <param name="value">The value to convert.</param>
+            <param name="delimiter">The string delimiter character.</param>
+            <returns>A JSON string representation of the <see cref="T:System.String"/>.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.ToString(System.String,System.Char,Newtonsoft.Json.StringEscapeHandling)">
+            <summary>
+            Converts the <see cref="T:System.String"/> to its JSON string representation.
+            </summary>
+            <param name="value">The value to convert.</param>
+            <param name="delimiter">The string delimiter character.</param>
+            <param name="stringEscapeHandling">The string escape handling.</param>
+            <returns>A JSON string representation of the <see cref="T:System.String"/>.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.ToString(System.Object)">
+            <summary>
+            Converts the <see cref="T:System.Object"/> to its JSON string representation.
+            </summary>
+            <param name="value">The value to convert.</param>
+            <returns>A JSON string representation of the <see cref="T:System.Object"/>.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.SerializeObject(System.Object)">
+            <summary>
+            Serializes the specified object to a JSON string.
+            </summary>
+            <param name="value">The object to serialize.</param>
+            <returns>A JSON string representation of the object.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.SerializeObject(System.Object,Newtonsoft.Json.Formatting)">
+            <summary>
+            Serializes the specified object to a JSON string using formatting.
+            </summary>
+            <param name="value">The object to serialize.</param>
+            <param name="formatting">Indicates how the output is formatted.</param>
+            <returns>
+            A JSON string representation of the object.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.SerializeObject(System.Object,Newtonsoft.Json.JsonConverter[])">
+            <summary>
+            Serializes the specified object to a JSON string using a collection of <see cref="T:Newtonsoft.Json.JsonConverter"/>.
+            </summary>
+            <param name="value">The object to serialize.</param>
+            <param name="converters">A collection converters used while serializing.</param>
+            <returns>A JSON string representation of the object.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.SerializeObject(System.Object,Newtonsoft.Json.Formatting,Newtonsoft.Json.JsonConverter[])">
+            <summary>
+            Serializes the specified object to a JSON string using formatting and a collection of <see cref="T:Newtonsoft.Json.JsonConverter"/>.
+            </summary>
+            <param name="value">The object to serialize.</param>
+            <param name="formatting">Indicates how the output is formatted.</param>
+            <param name="converters">A collection converters used while serializing.</param>
+            <returns>A JSON string representation of the object.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.SerializeObject(System.Object,Newtonsoft.Json.JsonSerializerSettings)">
+            <summary>
+            Serializes the specified object to a JSON string using <see cref="T:Newtonsoft.Json.JsonSerializerSettings"/>.
+            </summary>
+            <param name="value">The object to serialize.</param>
+            <param name="settings">The <see cref="T:Newtonsoft.Json.JsonSerializerSettings"/> used to serialize the object.
+            If this is null, default serialization settings will be used.</param>
+            <returns>
+            A JSON string representation of the object.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.SerializeObject(System.Object,System.Type,Newtonsoft.Json.JsonSerializerSettings)">
+            <summary>
+            Serializes the specified object to a JSON string using a type, formatting and <see cref="T:Newtonsoft.Json.JsonSerializerSettings"/>.
+            </summary>
+            <param name="value">The object to serialize.</param>
+            <param name="settings">The <see cref="T:Newtonsoft.Json.JsonSerializerSettings"/> used to serialize the object.
+            If this is null, default serialization settings will be used.</param>
+            <param name="type">
+            The type of the value being serialized.
+            This parameter is used when <see cref="T:Newtonsoft.Json.TypeNameHandling"/> is Auto to write out the type name if the type of the value does not match.
+            Specifing the type is optional.
+            </param>
+            <returns>
+            A JSON string representation of the object.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.SerializeObject(System.Object,Newtonsoft.Json.Formatting,Newtonsoft.Json.JsonSerializerSettings)">
+            <summary>
+            Serializes the specified object to a JSON string using formatting and <see cref="T:Newtonsoft.Json.JsonSerializerSettings"/>.
+            </summary>
+            <param name="value">The object to serialize.</param>
+            <param name="formatting">Indicates how the output is formatted.</param>
+            <param name="settings">The <see cref="T:Newtonsoft.Json.JsonSerializerSettings"/> used to serialize the object.
+            If this is null, default serialization settings will be used.</param>
+            <returns>
+            A JSON string representation of the object.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.SerializeObject(System.Object,System.Type,Newtonsoft.Json.Formatting,Newtonsoft.Json.JsonSerializerSettings)">
+            <summary>
+            Serializes the specified object to a JSON string using a type, formatting and <see cref="T:Newtonsoft.Json.JsonSerializerSettings"/>.
+            </summary>
+            <param name="value">The object to serialize.</param>
+            <param name="formatting">Indicates how the output is formatted.</param>
+            <param name="settings">The <see cref="T:Newtonsoft.Json.JsonSerializerSettings"/> used to serialize the object.
+            If this is null, default serialization settings will be used.</param>
+            <param name="type">
+            The type of the value being serialized.
+            This parameter is used when <see cref="T:Newtonsoft.Json.TypeNameHandling"/> is Auto to write out the type name if the type of the value does not match.
+            Specifing the type is optional.
+            </param>
+            <returns>
+            A JSON string representation of the object.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.SerializeObjectAsync(System.Object)">
+            <summary>
+            Asynchronously serializes the specified object to a JSON string.
+            Serialization will happen on a new thread.
+            </summary>
+            <param name="value">The object to serialize.</param>
+            <returns>
+            A task that represents the asynchronous serialize operation. The value of the <c>TResult</c> parameter contains a JSON string representation of the object.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.SerializeObjectAsync(System.Object,Newtonsoft.Json.Formatting)">
+            <summary>
+            Asynchronously serializes the specified object to a JSON string using formatting.
+            Serialization will happen on a new thread.
+            </summary>
+            <param name="value">The object to serialize.</param>
+            <param name="formatting">Indicates how the output is formatted.</param>
+            <returns>
+            A task that represents the asynchronous serialize operation. The value of the <c>TResult</c> parameter contains a JSON string representation of the object.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.SerializeObjectAsync(System.Object,Newtonsoft.Json.Formatting,Newtonsoft.Json.JsonSerializerSettings)">
+            <summary>
+            Asynchronously serializes the specified object to a JSON string using formatting and a collection of <see cref="T:Newtonsoft.Json.JsonConverter"/>.
+            Serialization will happen on a new thread.
+            </summary>
+            <param name="value">The object to serialize.</param>
+            <param name="formatting">Indicates how the output is formatted.</param>
+            <param name="settings">The <see cref="T:Newtonsoft.Json.JsonSerializerSettings"/> used to serialize the object.
+            If this is null, default serialization settings will be used.</param>
+            <returns>
+            A task that represents the asynchronous serialize operation. The value of the <c>TResult</c> parameter contains a JSON string representation of the object.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.DeserializeObject(System.String)">
+            <summary>
+            Deserializes the JSON to a .NET object.
+            </summary>
+            <param name="value">The JSON to deserialize.</param>
+            <returns>The deserialized object from the JSON string.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.DeserializeObject(System.String,Newtonsoft.Json.JsonSerializerSettings)">
+            <summary>
+            Deserializes the JSON to a .NET object using <see cref="T:Newtonsoft.Json.JsonSerializerSettings"/>.
+            </summary>
+            <param name="value">The JSON to deserialize.</param>
+            <param name="settings">
+            The <see cref="T:Newtonsoft.Json.JsonSerializerSettings"/> used to deserialize the object.
+            If this is null, default serialization settings will be used.
+            </param>
+            <returns>The deserialized object from the JSON string.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.DeserializeObject(System.String,System.Type)">
+            <summary>
+            Deserializes the JSON to the specified .NET type.
+            </summary>
+            <param name="value">The JSON to deserialize.</param>
+            <param name="type">The <see cref="T:System.Type"/> of object being deserialized.</param>
+            <returns>The deserialized object from the JSON string.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.DeserializeObject``1(System.String)">
+            <summary>
+            Deserializes the JSON to the specified .NET type.
+            </summary>
+            <typeparam name="T">The type of the object to deserialize to.</typeparam>
+            <param name="value">The JSON to deserialize.</param>
+            <returns>The deserialized object from the JSON string.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.DeserializeAnonymousType``1(System.String,``0)">
+            <summary>
+            Deserializes the JSON to the given anonymous type.
+            </summary>
+            <typeparam name="T">
+            The anonymous type to deserialize to. This can't be specified
+            traditionally and must be infered from the anonymous type passed
+            as a parameter.
+            </typeparam>
+            <param name="value">The JSON to deserialize.</param>
+            <param name="anonymousTypeObject">The anonymous type object.</param>
+            <returns>The deserialized anonymous type from the JSON string.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.DeserializeAnonymousType``1(System.String,``0,Newtonsoft.Json.JsonSerializerSettings)">
+            <summary>
+            Deserializes the JSON to the given anonymous type using <see cref="T:Newtonsoft.Json.JsonSerializerSettings"/>.
+            </summary>
+            <typeparam name="T">
+            The anonymous type to deserialize to. This can't be specified
+            traditionally and must be infered from the anonymous type passed
+            as a parameter.
+            </typeparam>
+            <param name="value">The JSON to deserialize.</param>
+            <param name="anonymousTypeObject">The anonymous type object.</param>
+            <param name="settings">
+            The <see cref="T:Newtonsoft.Json.JsonSerializerSettings"/> used to deserialize the object.
+            If this is null, default serialization settings will be used.
+            </param>
+            <returns>The deserialized anonymous type from the JSON string.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.DeserializeObject``1(System.String,Newtonsoft.Json.JsonConverter[])">
+            <summary>
+            Deserializes the JSON to the specified .NET type using a collection of <see cref="T:Newtonsoft.Json.JsonConverter"/>.
+            </summary>
+            <typeparam name="T">The type of the object to deserialize to.</typeparam>
+            <param name="value">The JSON to deserialize.</param>
+            <param name="converters">Converters to use while deserializing.</param>
+            <returns>The deserialized object from the JSON string.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.DeserializeObject``1(System.String,Newtonsoft.Json.JsonSerializerSettings)">
+            <summary>
+            Deserializes the JSON to the specified .NET type using <see cref="T:Newtonsoft.Json.JsonSerializerSettings"/>.
+            </summary>
+            <typeparam name="T">The type of the object to deserialize to.</typeparam>
+            <param name="value">The object to deserialize.</param>
+            <param name="settings">
+            The <see cref="T:Newtonsoft.Json.JsonSerializerSettings"/> used to deserialize the object.
+            If this is null, default serialization settings will be used.
+            </param>
+            <returns>The deserialized object from the JSON string.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.DeserializeObject(System.String,System.Type,Newtonsoft.Json.JsonConverter[])">
+            <summary>
+            Deserializes the JSON to the specified .NET type using a collection of <see cref="T:Newtonsoft.Json.JsonConverter"/>.
+            </summary>
+            <param name="value">The JSON to deserialize.</param>
+            <param name="type">The type of the object to deserialize.</param>
+            <param name="converters">Converters to use while deserializing.</param>
+            <returns>The deserialized object from the JSON string.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.DeserializeObject(System.String,System.Type,Newtonsoft.Json.JsonSerializerSettings)">
+            <summary>
+            Deserializes the JSON to the specified .NET type using <see cref="T:Newtonsoft.Json.JsonSerializerSettings"/>.
+            </summary>
+            <param name="value">The JSON to deserialize.</param>
+            <param name="type">The type of the object to deserialize to.</param>
+            <param name="settings">
+            The <see cref="T:Newtonsoft.Json.JsonSerializerSettings"/> used to deserialize the object.
+            If this is null, default serialization settings will be used.
+            </param>
+            <returns>The deserialized object from the JSON string.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.DeserializeObjectAsync``1(System.String)">
+            <summary>
+            Asynchronously deserializes the JSON to the specified .NET type.
+            Deserialization will happen on a new thread.
+            </summary>
+            <typeparam name="T">The type of the object to deserialize to.</typeparam>
+            <param name="value">The JSON to deserialize.</param>
+            <returns>
+            A task that represents the asynchronous deserialize operation. The value of the <c>TResult</c> parameter contains the deserialized object from the JSON string.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.DeserializeObjectAsync``1(System.String,Newtonsoft.Json.JsonSerializerSettings)">
+            <summary>
+            Asynchronously deserializes the JSON to the specified .NET type using <see cref="T:Newtonsoft.Json.JsonSerializerSettings"/>.
+            Deserialization will happen on a new thread.
+            </summary>
+            <typeparam name="T">The type of the object to deserialize to.</typeparam>
+            <param name="value">The JSON to deserialize.</param>
+            <param name="settings">
+            The <see cref="T:Newtonsoft.Json.JsonSerializerSettings"/> used to deserialize the object.
+            If this is null, default serialization settings will be used.
+            </param>
+            <returns>
+            A task that represents the asynchronous deserialize operation. The value of the <c>TResult</c> parameter contains the deserialized object from the JSON string.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.DeserializeObjectAsync(System.String)">
+            <summary>
+            Asynchronously deserializes the JSON to the specified .NET type.
+            Deserialization will happen on a new thread.
+            </summary>
+            <param name="value">The JSON to deserialize.</param>
+            <returns>
+            A task that represents the asynchronous deserialize operation. The value of the <c>TResult</c> parameter contains the deserialized object from the JSON string.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.DeserializeObjectAsync(System.String,System.Type,Newtonsoft.Json.JsonSerializerSettings)">
+            <summary>
+            Asynchronously deserializes the JSON to the specified .NET type using <see cref="T:Newtonsoft.Json.JsonSerializerSettings"/>.
+            Deserialization will happen on a new thread.
+            </summary>
+            <param name="value">The JSON to deserialize.</param>
+            <param name="type">The type of the object to deserialize to.</param>
+            <param name="settings">
+            The <see cref="T:Newtonsoft.Json.JsonSerializerSettings"/> used to deserialize the object.
+            If this is null, default serialization settings will be used.
+            </param>
+            <returns>
+            A task that represents the asynchronous deserialize operation. The value of the <c>TResult</c> parameter contains the deserialized object from the JSON string.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.PopulateObject(System.String,System.Object)">
+            <summary>
+            Populates the object with values from the JSON string.
+            </summary>
+            <param name="value">The JSON to populate values from.</param>
+            <param name="target">The target object to populate values onto.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.PopulateObject(System.String,System.Object,Newtonsoft.Json.JsonSerializerSettings)">
+            <summary>
+            Populates the object with values from the JSON string using <see cref="T:Newtonsoft.Json.JsonSerializerSettings"/>.
+            </summary>
+            <param name="value">The JSON to populate values from.</param>
+            <param name="target">The target object to populate values onto.</param>
+            <param name="settings">
+            The <see cref="T:Newtonsoft.Json.JsonSerializerSettings"/> used to deserialize the object.
+            If this is null, default serialization settings will be used.
+            </param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.PopulateObjectAsync(System.String,System.Object,Newtonsoft.Json.JsonSerializerSettings)">
+            <summary>
+            Asynchronously populates the object with values from the JSON string using <see cref="T:Newtonsoft.Json.JsonSerializerSettings"/>.
+            </summary>
+            <param name="value">The JSON to populate values from.</param>
+            <param name="target">The target object to populate values onto.</param>
+            <param name="settings">
+            The <see cref="T:Newtonsoft.Json.JsonSerializerSettings"/> used to deserialize the object.
+            If this is null, default serialization settings will be used.
+            </param>
+            <returns>
+            A task that represents the asynchronous populate operation.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.SerializeXNode(System.Xml.Linq.XObject)">
+            <summary>
+            Serializes the <see cref="T:System.Xml.Linq.XNode"/> to a JSON string.
+            </summary>
+            <param name="node">The node to convert to JSON.</param>
+            <returns>A JSON string of the XNode.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.SerializeXNode(System.Xml.Linq.XObject,Newtonsoft.Json.Formatting)">
+            <summary>
+            Serializes the <see cref="T:System.Xml.Linq.XNode"/> to a JSON string using formatting.
+            </summary>
+            <param name="node">The node to convert to JSON.</param>
+            <param name="formatting">Indicates how the output is formatted.</param>
+            <returns>A JSON string of the XNode.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.SerializeXNode(System.Xml.Linq.XObject,Newtonsoft.Json.Formatting,System.Boolean)">
+            <summary>
+            Serializes the <see cref="T:System.Xml.Linq.XNode"/> to a JSON string using formatting and omits the root object if <paramref name="omitRootObject"/> is <c>true</c>.
+            </summary>
+            <param name="node">The node to serialize.</param>
+            <param name="formatting">Indicates how the output is formatted.</param>
+            <param name="omitRootObject">Omits writing the root object.</param>
+            <returns>A JSON string of the XNode.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.DeserializeXNode(System.String)">
+            <summary>
+            Deserializes the <see cref="T:System.Xml.Linq.XNode"/> from a JSON string.
+            </summary>
+            <param name="value">The JSON string.</param>
+            <returns>The deserialized XNode</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.DeserializeXNode(System.String,System.String)">
+            <summary>
+            Deserializes the <see cref="T:System.Xml.Linq.XNode"/> from a JSON string nested in a root elment specified by <paramref name="deserializeRootElementName"/>.
+            </summary>
+            <param name="value">The JSON string.</param>
+            <param name="deserializeRootElementName">The name of the root element to append when deserializing.</param>
+            <returns>The deserialized XNode</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConvert.DeserializeXNode(System.String,System.String,System.Boolean)">
+            <summary>
+            Deserializes the <see cref="T:System.Xml.Linq.XNode"/> from a JSON string nested in a root elment specified by <paramref name="deserializeRootElementName"/>
+            and writes a .NET array attribute for collections.
+            </summary>
+            <param name="value">The JSON string.</param>
+            <param name="deserializeRootElementName">The name of the root element to append when deserializing.</param>
+            <param name="writeArrayAttribute">
+            A flag to indicate whether to write the Json.NET array attribute.
+            This attribute helps preserve arrays when converting the written XML back to JSON.
+            </param>
+            <returns>The deserialized XNode</returns>
+        </member>
+        <member name="T:Newtonsoft.Json.JsonConverter">
+            <summary>
+            Converts an object to and from JSON.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConverter.WriteJson(Newtonsoft.Json.JsonWriter,System.Object,Newtonsoft.Json.JsonSerializer)">
+            <summary>
+            Writes the JSON representation of the object.
+            </summary>
+            <param name="writer">The <see cref="T:Newtonsoft.Json.JsonWriter"/> to write to.</param>
+            <param name="value">The value.</param>
+            <param name="serializer">The calling serializer.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConverter.ReadJson(Newtonsoft.Json.JsonReader,System.Type,System.Object,Newtonsoft.Json.JsonSerializer)">
+            <summary>
+            Reads the JSON representation of the object.
+            </summary>
+            <param name="reader">The <see cref="T:Newtonsoft.Json.JsonReader"/> to read from.</param>
+            <param name="objectType">Type of the object.</param>
+            <param name="existingValue">The existing value of object being read.</param>
+            <param name="serializer">The calling serializer.</param>
+            <returns>The object value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConverter.CanConvert(System.Type)">
+            <summary>
+            Determines whether this instance can convert the specified object type.
+            </summary>
+            <param name="objectType">Type of the object.</param>
+            <returns>
+            	<c>true</c> if this instance can convert the specified object type; otherwise, <c>false</c>.
+            </returns>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonConverter.CanRead">
+            <summary>
+            Gets a value indicating whether this <see cref="T:Newtonsoft.Json.JsonConverter"/> can read JSON.
+            </summary>
+            <value><c>true</c> if this <see cref="T:Newtonsoft.Json.JsonConverter"/> can read JSON; otherwise, <c>false</c>.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonConverter.CanWrite">
+            <summary>
+            Gets a value indicating whether this <see cref="T:Newtonsoft.Json.JsonConverter"/> can write JSON.
+            </summary>
+            <value><c>true</c> if this <see cref="T:Newtonsoft.Json.JsonConverter"/> can write JSON; otherwise, <c>false</c>.</value>
+        </member>
+        <member name="T:Newtonsoft.Json.JsonConverterAttribute">
+            <summary>
+            Instructs the <see cref="T:Newtonsoft.Json.JsonSerializer"/> to use the specified <see cref="T:Newtonsoft.Json.JsonConverter"/> when serializing the member or class.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonConverterAttribute.ConverterType">
+            <summary>
+            Gets the <see cref="T:System.Type"/> of the converter.
+            </summary>
+            <value>The <see cref="T:System.Type"/> of the converter.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonConverterAttribute.ConverterParameters">
+            <summary>
+            The parameter list to use when constructing the JsonConverter described by ConverterType.  
+            If null, the default constructor is used.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConverterAttribute.#ctor(System.Type)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonConverterAttribute"/> class.
+            </summary>
+            <param name="converterType">Type of the converter.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonConverterAttribute.#ctor(System.Type,System.Object[])">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonConverterAttribute"/> class.
+            </summary>
+            <param name="converterType">Type of the converter.</param>
+            <param name="converterParameters">Parameter list to use when constructing the JsonConverter. Can be null.</param>
+        </member>
+        <member name="T:Newtonsoft.Json.JsonConverterCollection">
+            <summary>
+            Represents a collection of <see cref="T:Newtonsoft.Json.JsonConverter"/>.
+            </summary>
+        </member>
+        <member name="T:Newtonsoft.Json.JsonDictionaryAttribute">
+            <summary>
+            Instructs the <see cref="T:Newtonsoft.Json.JsonSerializer"/> how to serialize the collection.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonDictionaryAttribute.#ctor">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonDictionaryAttribute"/> class.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonDictionaryAttribute.#ctor(System.String)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonDictionaryAttribute"/> class with the specified container Id.
+            </summary>
+            <param name="id">The container Id.</param>
+        </member>
+        <member name="T:Newtonsoft.Json.JsonException">
+            <summary>
+            The exception thrown when an error occurs during JSON serialization or deserialization.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonException.#ctor">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonException"/> class.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonException.#ctor(System.String)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonException"/> class
+            with a specified error message.
+            </summary>
+            <param name="message">The error message that explains the reason for the exception.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonException.#ctor(System.String,System.Exception)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonException"/> class
+            with a specified error message and a reference to the inner exception that is the cause of this exception.
+            </summary>
+            <param name="message">The error message that explains the reason for the exception.</param>
+            <param name="innerException">The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified.</param>
+        </member>
+        <member name="T:Newtonsoft.Json.JsonExtensionDataAttribute">
+            <summary>
+            Instructs the <see cref="T:Newtonsoft.Json.JsonSerializer"/> to deserialize properties with no matching class member into the specified collection
+            and write values during serialization.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonExtensionDataAttribute.WriteData">
+            <summary>
+            Gets or sets a value that indicates whether to write extension data when serializing the object.
+            </summary>
+            <value>
+            	<c>true</c> to write extension data when serializing the object; otherwise, <c>false</c>. The default is <c>true</c>.
+            </value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonExtensionDataAttribute.ReadData">
+            <summary>
+            Gets or sets a value that indicates whether to read extension data when deserializing the object.
+            </summary>
+            <value>
+            	<c>true</c> to read extension data when deserializing the object; otherwise, <c>false</c>. The default is <c>true</c>.
+            </value>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonExtensionDataAttribute.#ctor">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonExtensionDataAttribute"/> class.
+            </summary>
+        </member>
+        <member name="T:Newtonsoft.Json.JsonIgnoreAttribute">
+            <summary>
+            Instructs the <see cref="T:Newtonsoft.Json.JsonSerializer"/> not to serialize the public field or public read/write property value.
+            </summary>
+        </member>
+        <member name="T:Newtonsoft.Json.JsonObjectAttribute">
+            <summary>
+            Instructs the <see cref="T:Newtonsoft.Json.JsonSerializer"/> how to serialize the object.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonObjectAttribute.MemberSerialization">
+            <summary>
+            Gets or sets the member serialization.
+            </summary>
+            <value>The member serialization.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonObjectAttribute.ItemRequired">
+            <summary>
+            Gets or sets a value that indicates whether the object's properties are required.
+            </summary>
+            <value>
+            	A value indicating whether the object's properties are required.
+            </value>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonObjectAttribute.#ctor">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonObjectAttribute"/> class.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonObjectAttribute.#ctor(Newtonsoft.Json.MemberSerialization)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonObjectAttribute"/> class with the specified member serialization.
+            </summary>
+            <param name="memberSerialization">The member serialization.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonObjectAttribute.#ctor(System.String)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonObjectAttribute"/> class with the specified container Id.
+            </summary>
+            <param name="id">The container Id.</param>
+        </member>
+        <member name="T:Newtonsoft.Json.JsonPropertyAttribute">
+            <summary>
+            Instructs the <see cref="T:Newtonsoft.Json.JsonSerializer"/> to always serialize the member with the specified name.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonPropertyAttribute.ItemConverterType">
+            <summary>
+            Gets or sets the converter used when serializing the property's collection items.
+            </summary>
+            <value>The collection's items converter.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonPropertyAttribute.ItemConverterParameters">
+            <summary>
+            The parameter list to use when constructing the JsonConverter described by ItemConverterType.
+            If null, the default constructor is used.
+            When non-null, there must be a constructor defined in the JsonConverter that exactly matches the number,
+            order, and type of these parameters.
+            </summary>
+            <example>
+            [JsonProperty(ItemConverterType = typeof(MyContainerConverter), ItemConverterParameters = new object[] { 123, "Four" })]
+            </example>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonPropertyAttribute.NullValueHandling">
+            <summary>
+            Gets or sets the null value handling used when serializing this property.
+            </summary>
+            <value>The null value handling.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonPropertyAttribute.DefaultValueHandling">
+            <summary>
+            Gets or sets the default value handling used when serializing this property.
+            </summary>
+            <value>The default value handling.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonPropertyAttribute.ReferenceLoopHandling">
+            <summary>
+            Gets or sets the reference loop handling used when serializing this property.
+            </summary>
+            <value>The reference loop handling.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonPropertyAttribute.ObjectCreationHandling">
+            <summary>
+            Gets or sets the object creation handling used when deserializing this property.
+            </summary>
+            <value>The object creation handling.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonPropertyAttribute.TypeNameHandling">
+            <summary>
+            Gets or sets the type name handling used when serializing this property.
+            </summary>
+            <value>The type name handling.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonPropertyAttribute.IsReference">
+            <summary>
+            Gets or sets whether this property's value is serialized as a reference.
+            </summary>
+            <value>Whether this property's value is serialized as a reference.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonPropertyAttribute.Order">
+            <summary>
+            Gets or sets the order of serialization of a member.
+            </summary>
+            <value>The numeric order of serialization.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonPropertyAttribute.Required">
+            <summary>
+            Gets or sets a value indicating whether this property is required.
+            </summary>
+            <value>
+            	A value indicating whether this property is required.
+            </value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonPropertyAttribute.PropertyName">
+            <summary>
+            Gets or sets the name of the property.
+            </summary>
+            <value>The name of the property.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonPropertyAttribute.ItemReferenceLoopHandling">
+            <summary>
+            Gets or sets the the reference loop handling used when serializing the property's collection items.
+            </summary>
+            <value>The collection's items reference loop handling.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonPropertyAttribute.ItemTypeNameHandling">
+            <summary>
+            Gets or sets the the type name handling used when serializing the property's collection items.
+            </summary>
+            <value>The collection's items type name handling.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonPropertyAttribute.ItemIsReference">
+            <summary>
+            Gets or sets whether this property's collection items are serialized as a reference.
+            </summary>
+            <value>Whether this property's collection items are serialized as a reference.</value>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonPropertyAttribute.#ctor">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonPropertyAttribute"/> class.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonPropertyAttribute.#ctor(System.String)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonPropertyAttribute"/> class with the specified name.
+            </summary>
+            <param name="propertyName">Name of the property.</param>
+        </member>
+        <member name="T:Newtonsoft.Json.JsonReader">
+            <summary>
+            Represents a reader that provides fast, non-cached, forward-only access to serialized JSON data.
+            </summary>
+        </member>
+        <member name="T:Newtonsoft.Json.JsonReader.State">
+            <summary>
+            Specifies the state of the reader.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.JsonReader.State.Start">
+            <summary>
+            The Read method has not been called.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.JsonReader.State.Complete">
+            <summary>
+            The end of the file has been reached successfully.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.JsonReader.State.Property">
+            <summary>
+            Reader is at a property.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.JsonReader.State.ObjectStart">
+            <summary>
+            Reader is at the start of an object.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.JsonReader.State.Object">
+            <summary>
+            Reader is in an object.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.JsonReader.State.ArrayStart">
+            <summary>
+            Reader is at the start of an array.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.JsonReader.State.Array">
+            <summary>
+            Reader is in an array.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.JsonReader.State.Closed">
+            <summary>
+            The Close method has been called.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.JsonReader.State.PostValue">
+            <summary>
+            Reader has just read a value.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.JsonReader.State.ConstructorStart">
+            <summary>
+            Reader is at the start of a constructor.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.JsonReader.State.Constructor">
+            <summary>
+            Reader in a constructor.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.JsonReader.State.Error">
+            <summary>
+            An error occurred that prevents the read operation from continuing.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.JsonReader.State.Finished">
+            <summary>
+            The end of the file has been reached successfully.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonReader.CurrentState">
+            <summary>
+            Gets the current reader state.
+            </summary>
+            <value>The current reader state.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonReader.CloseInput">
+            <summary>
+            Gets or sets a value indicating whether the underlying stream or
+            <see cref="T:System.IO.TextReader"/> should be closed when the reader is closed.
+            </summary>
+            <value>
+            true to close the underlying stream or <see cref="T:System.IO.TextReader"/> when
+            the reader is closed; otherwise false. The default is true.
+            </value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonReader.SupportMultipleContent">
+            <summary>
+            Gets or sets a value indicating whether multiple pieces of JSON content can
+            be read from a continuous stream without erroring.
+            </summary>
+            <value>
+            true to support reading multiple pieces of JSON content; otherwise false. The default is false.
+            </value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonReader.QuoteChar">
+            <summary>
+            Gets the quotation mark character used to enclose the value of a string.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonReader.DateTimeZoneHandling">
+            <summary>
+            Get or set how <see cref="T:System.DateTime"/> time zones are handling when reading JSON.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonReader.DateParseHandling">
+            <summary>
+            Get or set how date formatted strings, e.g. "\/Date(1198908717056)\/" and "2012-03-21T05:40Z", are parsed when reading JSON.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonReader.FloatParseHandling">
+            <summary>
+            Get or set how floating point numbers, e.g. 1.0 and 9.9, are parsed when reading JSON text.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonReader.DateFormatString">
+            <summary>
+            Get or set how custom date formatted strings are parsed when reading JSON.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonReader.MaxDepth">
+            <summary>
+            Gets or sets the maximum depth allowed when reading JSON. Reading past this depth will throw a <see cref="T:Newtonsoft.Json.JsonReaderException"/>.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonReader.TokenType">
+            <summary>
+            Gets the type of the current JSON token. 
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonReader.Value">
+            <summary>
+            Gets the text value of the current JSON token.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonReader.ValueType">
+            <summary>
+            Gets The Common Language Runtime (CLR) type for the current JSON token.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonReader.Depth">
+            <summary>
+            Gets the depth of the current token in the JSON document.
+            </summary>
+            <value>The depth of the current token in the JSON document.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonReader.Path">
+            <summary>
+            Gets the path of the current JSON token. 
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonReader.Culture">
+            <summary>
+            Gets or sets the culture used when reading JSON. Defaults to <see cref="P:System.Globalization.CultureInfo.InvariantCulture"/>.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonReader.#ctor">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonReader"/> class with the specified <see cref="T:System.IO.TextReader"/>.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonReader.Read">
+            <summary>
+            Reads the next JSON token from the stream.
+            </summary>
+            <returns>true if the next token was read successfully; false if there are no more tokens to read.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonReader.ReadAsInt32">
+            <summary>
+            Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
+            </summary>
+            <returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonReader.ReadAsString">
+            <summary>
+            Reads the next JSON token from the stream as a <see cref="T:System.String"/>.
+            </summary>
+            <returns>A <see cref="T:System.String"/>. This method will return <c>null</c> at the end of an array.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonReader.ReadAsBytes">
+            <summary>
+            Reads the next JSON token from the stream as a <see cref="T:System.Byte"/>[].
+            </summary>
+            <returns>A <see cref="T:System.Byte"/>[] or a null reference if the next JSON token is null. This method will return <c>null</c> at the end of an array.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonReader.ReadAsDouble">
+            <summary>
+            Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
+            </summary>
+            <returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonReader.ReadAsBoolean">
+            <summary>
+            Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
+            </summary>
+            <returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonReader.ReadAsDecimal">
+            <summary>
+            Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
+            </summary>
+            <returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonReader.ReadAsDateTime">
+            <summary>
+            Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
+            </summary>
+            <returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonReader.ReadAsDateTimeOffset">
+            <summary>
+            Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
+            </summary>
+            <returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonReader.Skip">
+            <summary>
+            Skips the children of the current token.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonReader.SetToken(Newtonsoft.Json.JsonToken)">
+            <summary>
+            Sets the current token.
+            </summary>
+            <param name="newToken">The new token.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonReader.SetToken(Newtonsoft.Json.JsonToken,System.Object)">
+            <summary>
+            Sets the current token and value.
+            </summary>
+            <param name="newToken">The new token.</param>
+            <param name="value">The value.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonReader.SetStateBasedOnCurrent">
+            <summary>
+            Sets the state based on current token type.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonReader.System#IDisposable#Dispose">
+            <summary>
+            Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonReader.Dispose(System.Boolean)">
+            <summary>
+            Releases unmanaged and - optionally - managed resources
+            </summary>
+            <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonReader.Close">
+            <summary>
+            Changes the <see cref="T:Newtonsoft.Json.JsonReader.State"/> to Closed. 
+            </summary>
+        </member>
+        <member name="T:Newtonsoft.Json.JsonReaderException">
+            <summary>
+            The exception thrown when an error occurs while reading JSON text.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonReaderException.LineNumber">
+            <summary>
+            Gets the line number indicating where the error occurred.
+            </summary>
+            <value>The line number indicating where the error occurred.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonReaderException.LinePosition">
+            <summary>
+            Gets the line position indicating where the error occurred.
+            </summary>
+            <value>The line position indicating where the error occurred.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonReaderException.Path">
+            <summary>
+            Gets the path to the JSON where the error occurred.
+            </summary>
+            <value>The path to the JSON where the error occurred.</value>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonReaderException.#ctor">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonReaderException"/> class.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonReaderException.#ctor(System.String)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonReaderException"/> class
+            with a specified error message.
+            </summary>
+            <param name="message">The error message that explains the reason for the exception.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonReaderException.#ctor(System.String,System.Exception)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonReaderException"/> class
+            with a specified error message and a reference to the inner exception that is the cause of this exception.
+            </summary>
+            <param name="message">The error message that explains the reason for the exception.</param>
+            <param name="innerException">The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified.</param>
+        </member>
+        <member name="T:Newtonsoft.Json.JsonRequiredAttribute">
+            <summary>
+            Instructs the <see cref="T:Newtonsoft.Json.JsonSerializer"/> to always serialize the member, and require the member has a value.
+            </summary>
+        </member>
+        <member name="T:Newtonsoft.Json.JsonSerializationException">
+            <summary>
+            The exception thrown when an error occurs during JSON serialization or deserialization.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonSerializationException.#ctor">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonSerializationException"/> class.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonSerializationException.#ctor(System.String)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonSerializationException"/> class
+            with a specified error message.
+            </summary>
+            <param name="message">The error message that explains the reason for the exception.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonSerializationException.#ctor(System.String,System.Exception)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonSerializationException"/> class
+            with a specified error message and a reference to the inner exception that is the cause of this exception.
+            </summary>
+            <param name="message">The error message that explains the reason for the exception.</param>
+            <param name="innerException">The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified.</param>
+        </member>
+        <member name="T:Newtonsoft.Json.JsonSerializer">
+            <summary>
+            Serializes and deserializes objects into and from the JSON format.
+            The <see cref="T:Newtonsoft.Json.JsonSerializer"/> enables you to control how objects are encoded into JSON.
+            </summary>
+        </member>
+        <member name="E:Newtonsoft.Json.JsonSerializer.Error">
+            <summary>
+            Occurs when the <see cref="T:Newtonsoft.Json.JsonSerializer"/> errors during serialization and deserialization.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializer.ReferenceResolver">
+            <summary>
+            Gets or sets the <see cref="T:Newtonsoft.Json.Serialization.IReferenceResolver"/> used by the serializer when resolving references.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializer.Binder">
+            <summary>
+            Gets or sets the <see cref="T:Newtonsoft.Json.SerializationBinder"/> used by the serializer when resolving type names.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializer.TraceWriter">
+            <summary>
+            Gets or sets the <see cref="T:Newtonsoft.Json.Serialization.ITraceWriter"/> used by the serializer when writing trace messages.
+            </summary>
+            <value>The trace writer.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializer.EqualityComparer">
+            <summary>
+            Gets or sets the equality comparer used by the serializer when comparing references.
+            </summary>
+            <value>The equality comparer.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializer.TypeNameHandling">
+            <summary>
+            Gets or sets how type name writing and reading is handled by the serializer.
+            </summary>
+            <remarks>
+            <see cref="P:Newtonsoft.Json.JsonSerializer.TypeNameHandling"/> should be used with caution when your application deserializes JSON from an external source.
+            Incoming types should be validated with a custom <see cref="T:System.Runtime.Serialization.SerializationBinder"/>
+            when deserializing with a value other than <c>TypeNameHandling.None</c>.
+            </remarks>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializer.TypeNameAssemblyFormat">
+            <summary>
+            Gets or sets how a type name assembly is written and resolved by the serializer.
+            </summary>
+            <value>The type name assembly format.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializer.PreserveReferencesHandling">
+            <summary>
+            Gets or sets how object references are preserved by the serializer.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializer.ReferenceLoopHandling">
+            <summary>
+            Get or set how reference loops (e.g. a class referencing itself) is handled.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializer.MissingMemberHandling">
+            <summary>
+            Get or set how missing members (e.g. JSON contains a property that isn't a member on the object) are handled during deserialization.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializer.NullValueHandling">
+            <summary>
+            Get or set how null values are handled during serialization and deserialization.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializer.DefaultValueHandling">
+            <summary>
+            Get or set how null default are handled during serialization and deserialization.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializer.ObjectCreationHandling">
+            <summary>
+            Gets or sets how objects are created during deserialization.
+            </summary>
+            <value>The object creation handling.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializer.ConstructorHandling">
+            <summary>
+            Gets or sets how constructors are used during deserialization.
+            </summary>
+            <value>The constructor handling.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializer.MetadataPropertyHandling">
+            <summary>
+            Gets or sets how metadata properties are used during deserialization.
+            </summary>
+            <value>The metadata properties handling.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializer.Converters">
+            <summary>
+            Gets a collection <see cref="T:Newtonsoft.Json.JsonConverter"/> that will be used during serialization.
+            </summary>
+            <value>Collection <see cref="T:Newtonsoft.Json.JsonConverter"/> that will be used during serialization.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializer.ContractResolver">
+            <summary>
+            Gets or sets the contract resolver used by the serializer when
+            serializing .NET objects to JSON and vice versa.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializer.Context">
+            <summary>
+            Gets or sets the <see cref="T:System.Runtime.Serialization.StreamingContext"/> used by the serializer when invoking serialization callback methods.
+            </summary>
+            <value>The context.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializer.Formatting">
+            <summary>
+            Indicates how JSON text output is formatted.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializer.DateFormatHandling">
+            <summary>
+            Get or set how dates are written to JSON text.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializer.DateTimeZoneHandling">
+            <summary>
+            Get or set how <see cref="T:System.DateTime"/> time zones are handling during serialization and deserialization.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializer.DateParseHandling">
+            <summary>
+            Get or set how date formatted strings, e.g. "\/Date(1198908717056)\/" and "2012-03-21T05:40Z", are parsed when reading JSON.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializer.FloatParseHandling">
+            <summary>
+            Get or set how floating point numbers, e.g. 1.0 and 9.9, are parsed when reading JSON text.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializer.FloatFormatHandling">
+            <summary>
+            Get or set how special floating point numbers, e.g. <see cref="F:System.Double.NaN"/>,
+            <see cref="F:System.Double.PositiveInfinity"/> and <see cref="F:System.Double.NegativeInfinity"/>,
+            are written as JSON text.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializer.StringEscapeHandling">
+            <summary>
+            Get or set how strings are escaped when writing JSON text.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializer.DateFormatString">
+            <summary>
+            Get or set how <see cref="T:System.DateTime"/> and <see cref="T:System.DateTimeOffset"/> values are formatted when writing JSON text, and the expected date format when reading JSON text.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializer.Culture">
+            <summary>
+            Gets or sets the culture used when reading JSON. Defaults to <see cref="P:System.Globalization.CultureInfo.InvariantCulture"/>.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializer.MaxDepth">
+            <summary>
+            Gets or sets the maximum depth allowed when reading JSON. Reading past this depth will throw a <see cref="T:Newtonsoft.Json.JsonReaderException"/>.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializer.CheckAdditionalContent">
+            <summary>
+            Gets a value indicating whether there will be a check for additional JSON content after deserializing an object.
+            </summary>
+            <value>
+            	<c>true</c> if there will be a check for additional JSON content after deserializing an object; otherwise, <c>false</c>.
+            </value>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonSerializer.#ctor">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonSerializer"/> class.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonSerializer.Create">
+            <summary>
+            Creates a new <see cref="T:Newtonsoft.Json.JsonSerializer"/> instance.
+            The <see cref="T:Newtonsoft.Json.JsonSerializer"/> will not use default settings 
+            from <see cref="P:Newtonsoft.Json.JsonConvert.DefaultSettings"/>.
+            </summary>
+            <returns>
+            A new <see cref="T:Newtonsoft.Json.JsonSerializer"/> instance.
+            The <see cref="T:Newtonsoft.Json.JsonSerializer"/> will not use default settings 
+            from <see cref="P:Newtonsoft.Json.JsonConvert.DefaultSettings"/>.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonSerializer.Create(Newtonsoft.Json.JsonSerializerSettings)">
+            <summary>
+            Creates a new <see cref="T:Newtonsoft.Json.JsonSerializer"/> instance using the specified <see cref="T:Newtonsoft.Json.JsonSerializerSettings"/>.
+            The <see cref="T:Newtonsoft.Json.JsonSerializer"/> will not use default settings 
+            from <see cref="P:Newtonsoft.Json.JsonConvert.DefaultSettings"/>.
+            </summary>
+            <param name="settings">The settings to be applied to the <see cref="T:Newtonsoft.Json.JsonSerializer"/>.</param>
+            <returns>
+            A new <see cref="T:Newtonsoft.Json.JsonSerializer"/> instance using the specified <see cref="T:Newtonsoft.Json.JsonSerializerSettings"/>.
+            The <see cref="T:Newtonsoft.Json.JsonSerializer"/> will not use default settings 
+            from <see cref="P:Newtonsoft.Json.JsonConvert.DefaultSettings"/>.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonSerializer.CreateDefault">
+            <summary>
+            Creates a new <see cref="T:Newtonsoft.Json.JsonSerializer"/> instance.
+            The <see cref="T:Newtonsoft.Json.JsonSerializer"/> will use default settings 
+            from <see cref="P:Newtonsoft.Json.JsonConvert.DefaultSettings"/>.
+            </summary>
+            <returns>
+            A new <see cref="T:Newtonsoft.Json.JsonSerializer"/> instance.
+            The <see cref="T:Newtonsoft.Json.JsonSerializer"/> will use default settings 
+            from <see cref="P:Newtonsoft.Json.JsonConvert.DefaultSettings"/>.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonSerializer.CreateDefault(Newtonsoft.Json.JsonSerializerSettings)">
+            <summary>
+            Creates a new <see cref="T:Newtonsoft.Json.JsonSerializer"/> instance using the specified <see cref="T:Newtonsoft.Json.JsonSerializerSettings"/>.
+            The <see cref="T:Newtonsoft.Json.JsonSerializer"/> will use default settings 
+            from <see cref="P:Newtonsoft.Json.JsonConvert.DefaultSettings"/> as well as the specified <see cref="T:Newtonsoft.Json.JsonSerializerSettings"/>.
+            </summary>
+            <param name="settings">The settings to be applied to the <see cref="T:Newtonsoft.Json.JsonSerializer"/>.</param>
+            <returns>
+            A new <see cref="T:Newtonsoft.Json.JsonSerializer"/> instance using the specified <see cref="T:Newtonsoft.Json.JsonSerializerSettings"/>.
+            The <see cref="T:Newtonsoft.Json.JsonSerializer"/> will use default settings 
+            from <see cref="P:Newtonsoft.Json.JsonConvert.DefaultSettings"/> as well as the specified <see cref="T:Newtonsoft.Json.JsonSerializerSettings"/>.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonSerializer.Populate(System.IO.TextReader,System.Object)">
+            <summary>
+            Populates the JSON values onto the target object.
+            </summary>
+            <param name="reader">The <see cref="T:System.IO.TextReader"/> that contains the JSON structure to reader values from.</param>
+            <param name="target">The target object to populate values onto.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonSerializer.Populate(Newtonsoft.Json.JsonReader,System.Object)">
+            <summary>
+            Populates the JSON values onto the target object.
+            </summary>
+            <param name="reader">The <see cref="T:Newtonsoft.Json.JsonReader"/> that contains the JSON structure to reader values from.</param>
+            <param name="target">The target object to populate values onto.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonSerializer.Deserialize(Newtonsoft.Json.JsonReader)">
+            <summary>
+            Deserializes the JSON structure contained by the specified <see cref="T:Newtonsoft.Json.JsonReader"/>.
+            </summary>
+            <param name="reader">The <see cref="T:Newtonsoft.Json.JsonReader"/> that contains the JSON structure to deserialize.</param>
+            <returns>The <see cref="T:System.Object"/> being deserialized.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonSerializer.Deserialize(System.IO.TextReader,System.Type)">
+            <summary>
+            Deserializes the JSON structure contained by the specified <see cref="T:System.IO.StringReader"/>
+            into an instance of the specified type.
+            </summary>
+            <param name="reader">The <see cref="T:System.IO.TextReader"/> containing the object.</param>
+            <param name="objectType">The <see cref="T:System.Type"/> of object being deserialized.</param>
+            <returns>The instance of <paramref name="objectType"/> being deserialized.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonSerializer.Deserialize``1(Newtonsoft.Json.JsonReader)">
+            <summary>
+            Deserializes the JSON structure contained by the specified <see cref="T:Newtonsoft.Json.JsonReader"/>
+            into an instance of the specified type.
+            </summary>
+            <param name="reader">The <see cref="T:Newtonsoft.Json.JsonReader"/> containing the object.</param>
+            <typeparam name="T">The type of the object to deserialize.</typeparam>
+            <returns>The instance of <typeparamref name="T"/> being deserialized.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonSerializer.Deserialize(Newtonsoft.Json.JsonReader,System.Type)">
+            <summary>
+            Deserializes the JSON structure contained by the specified <see cref="T:Newtonsoft.Json.JsonReader"/>
+            into an instance of the specified type.
+            </summary>
+            <param name="reader">The <see cref="T:Newtonsoft.Json.JsonReader"/> containing the object.</param>
+            <param name="objectType">The <see cref="T:System.Type"/> of object being deserialized.</param>
+            <returns>The instance of <paramref name="objectType"/> being deserialized.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonSerializer.Serialize(System.IO.TextWriter,System.Object)">
+            <summary>
+            Serializes the specified <see cref="T:System.Object"/> and writes the JSON structure
+            to a <c>Stream</c> using the specified <see cref="T:System.IO.TextWriter"/>. 
+            </summary>
+            <param name="textWriter">The <see cref="T:System.IO.TextWriter"/> used to write the JSON structure.</param>
+            <param name="value">The <see cref="T:System.Object"/> to serialize.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonSerializer.Serialize(Newtonsoft.Json.JsonWriter,System.Object,System.Type)">
+            <summary>
+            Serializes the specified <see cref="T:System.Object"/> and writes the JSON structure
+            to a <c>Stream</c> using the specified <see cref="T:System.IO.TextWriter"/>. 
+            </summary>
+            <param name="jsonWriter">The <see cref="T:Newtonsoft.Json.JsonWriter"/> used to write the JSON structure.</param>
+            <param name="value">The <see cref="T:System.Object"/> to serialize.</param>
+            <param name="objectType">
+            The type of the value being serialized.
+            This parameter is used when <see cref="P:Newtonsoft.Json.JsonSerializer.TypeNameHandling"/> is Auto to write out the type name if the type of the value does not match.
+            Specifing the type is optional.
+            </param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonSerializer.Serialize(System.IO.TextWriter,System.Object,System.Type)">
+            <summary>
+            Serializes the specified <see cref="T:System.Object"/> and writes the JSON structure
+            to a <c>Stream</c> using the specified <see cref="T:System.IO.TextWriter"/>. 
+            </summary>
+            <param name="textWriter">The <see cref="T:System.IO.TextWriter"/> used to write the JSON structure.</param>
+            <param name="value">The <see cref="T:System.Object"/> to serialize.</param>
+            <param name="objectType">
+            The type of the value being serialized.
+            This parameter is used when <see cref="P:Newtonsoft.Json.JsonSerializer.TypeNameHandling"/> is Auto to write out the type name if the type of the value does not match.
+            Specifing the type is optional.
+            </param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonSerializer.Serialize(Newtonsoft.Json.JsonWriter,System.Object)">
+            <summary>
+            Serializes the specified <see cref="T:System.Object"/> and writes the JSON structure
+            to a <c>Stream</c> using the specified <see cref="T:Newtonsoft.Json.JsonWriter"/>. 
+            </summary>
+            <param name="jsonWriter">The <see cref="T:Newtonsoft.Json.JsonWriter"/> used to write the JSON structure.</param>
+            <param name="value">The <see cref="T:System.Object"/> to serialize.</param>
+        </member>
+        <member name="T:Newtonsoft.Json.JsonSerializerSettings">
+            <summary>
+            Specifies the settings on a <see cref="T:Newtonsoft.Json.JsonSerializer"/> object.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializerSettings.ReferenceLoopHandling">
+            <summary>
+            Gets or sets how reference loops (e.g. a class referencing itself) is handled.
+            </summary>
+            <value>Reference loop handling.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializerSettings.MissingMemberHandling">
+            <summary>
+            Gets or sets how missing members (e.g. JSON contains a property that isn't a member on the object) are handled during deserialization.
+            </summary>
+            <value>Missing member handling.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializerSettings.ObjectCreationHandling">
+            <summary>
+            Gets or sets how objects are created during deserialization.
+            </summary>
+            <value>The object creation handling.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializerSettings.NullValueHandling">
+            <summary>
+            Gets or sets how null values are handled during serialization and deserialization.
+            </summary>
+            <value>Null value handling.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializerSettings.DefaultValueHandling">
+            <summary>
+            Gets or sets how null default are handled during serialization and deserialization.
+            </summary>
+            <value>The default value handling.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializerSettings.Converters">
+            <summary>
+            Gets or sets a <see cref="T:Newtonsoft.Json.JsonConverter"/> collection that will be used during serialization.
+            </summary>
+            <value>The converters.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializerSettings.PreserveReferencesHandling">
+            <summary>
+            Gets or sets how object references are preserved by the serializer.
+            </summary>
+            <value>The preserve references handling.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializerSettings.TypeNameHandling">
+            <summary>
+            Gets or sets how type name writing and reading is handled by the serializer.
+            </summary>
+            <remarks>
+            <see cref="P:Newtonsoft.Json.JsonSerializerSettings.TypeNameHandling"/> should be used with caution when your application deserializes JSON from an external source.
+            Incoming types should be validated with a custom <see cref="T:System.Runtime.Serialization.SerializationBinder"/>
+            when deserializing with a value other than <c>TypeNameHandling.None</c>.
+            </remarks>
+            <value>The type name handling.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializerSettings.MetadataPropertyHandling">
+            <summary>
+            Gets or sets how metadata properties are used during deserialization.
+            </summary>
+            <value>The metadata properties handling.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializerSettings.TypeNameAssemblyFormat">
+            <summary>
+            Gets or sets how a type name assembly is written and resolved by the serializer.
+            </summary>
+            <value>The type name assembly format.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializerSettings.ConstructorHandling">
+            <summary>
+            Gets or sets how constructors are used during deserialization.
+            </summary>
+            <value>The constructor handling.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializerSettings.ContractResolver">
+            <summary>
+            Gets or sets the contract resolver used by the serializer when
+            serializing .NET objects to JSON and vice versa.
+            </summary>
+            <value>The contract resolver.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializerSettings.EqualityComparer">
+            <summary>
+            Gets or sets the equality comparer used by the serializer when comparing references.
+            </summary>
+            <value>The equality comparer.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializerSettings.ReferenceResolver">
+            <summary>
+            Gets or sets the <see cref="T:Newtonsoft.Json.Serialization.IReferenceResolver"/> used by the serializer when resolving references.
+            </summary>
+            <value>The reference resolver.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializerSettings.ReferenceResolverProvider">
+            <summary>
+            Gets or sets a function that creates the <see cref="T:Newtonsoft.Json.Serialization.IReferenceResolver"/> used by the serializer when resolving references.
+            </summary>
+            <value>A function that creates the <see cref="T:Newtonsoft.Json.Serialization.IReferenceResolver"/> used by the serializer when resolving references.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializerSettings.TraceWriter">
+            <summary>
+            Gets or sets the <see cref="T:Newtonsoft.Json.Serialization.ITraceWriter"/> used by the serializer when writing trace messages.
+            </summary>
+            <value>The trace writer.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializerSettings.Binder">
+            <summary>
+            Gets or sets the <see cref="T:Newtonsoft.Json.SerializationBinder"/> used by the serializer when resolving type names.
+            </summary>
+            <value>The binder.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializerSettings.Error">
+            <summary>
+            Gets or sets the error handler called during serialization and deserialization.
+            </summary>
+            <value>The error handler called during serialization and deserialization.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializerSettings.Context">
+            <summary>
+            Gets or sets the <see cref="T:System.Runtime.Serialization.StreamingContext"/> used by the serializer when invoking serialization callback methods.
+            </summary>
+            <value>The context.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializerSettings.DateFormatString">
+            <summary>
+            Get or set how <see cref="T:System.DateTime"/> and <see cref="T:System.DateTimeOffset"/> values are formatted when writing JSON text, and the expected date format when reading JSON text.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializerSettings.MaxDepth">
+            <summary>
+            Gets or sets the maximum depth allowed when reading JSON. Reading past this depth will throw a <see cref="T:Newtonsoft.Json.JsonReaderException"/>.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializerSettings.Formatting">
+            <summary>
+            Indicates how JSON text output is formatted.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializerSettings.DateFormatHandling">
+            <summary>
+            Get or set how dates are written to JSON text.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializerSettings.DateTimeZoneHandling">
+            <summary>
+            Get or set how <see cref="T:System.DateTime"/> time zones are handling during serialization and deserialization.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializerSettings.DateParseHandling">
+            <summary>
+            Get or set how date formatted strings, e.g. "\/Date(1198908717056)\/" and "2012-03-21T05:40Z", are parsed when reading JSON.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializerSettings.FloatFormatHandling">
+            <summary>
+            Get or set how special floating point numbers, e.g. <see cref="F:System.Double.NaN"/>,
+            <see cref="F:System.Double.PositiveInfinity"/> and <see cref="F:System.Double.NegativeInfinity"/>,
+            are written as JSON.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializerSettings.FloatParseHandling">
+            <summary>
+            Get or set how floating point numbers, e.g. 1.0 and 9.9, are parsed when reading JSON text.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializerSettings.StringEscapeHandling">
+            <summary>
+            Get or set how strings are escaped when writing JSON text.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializerSettings.Culture">
+            <summary>
+            Gets or sets the culture used when reading JSON. Defaults to <see cref="P:System.Globalization.CultureInfo.InvariantCulture"/>.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonSerializerSettings.CheckAdditionalContent">
+            <summary>
+            Gets a value indicating whether there will be a check for additional content after deserializing an object.
+            </summary>
+            <value>
+            	<c>true</c> if there will be a check for additional content after deserializing an object; otherwise, <c>false</c>.
+            </value>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonSerializerSettings.#ctor">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonSerializerSettings"/> class.
+            </summary>
+        </member>
+        <member name="T:Newtonsoft.Json.JsonTextReader">
+            <summary>
+            Represents a reader that provides fast, non-cached, forward-only access to JSON text data.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextReader.#ctor(System.IO.TextReader)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonReader"/> class with the specified <see cref="T:System.IO.TextReader"/>.
+            </summary>
+            <param name="reader">The <c>TextReader</c> containing the XML data to read.</param>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonTextReader.ArrayPool">
+            <summary>
+            Gets or sets the reader's character buffer pool.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextReader.Read">
+            <summary>
+            Reads the next JSON token from the stream.
+            </summary>
+            <returns>
+            true if the next token was read successfully; false if there are no more tokens to read.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextReader.ReadAsInt32">
+            <summary>
+            Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
+            </summary>
+            <returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextReader.ReadAsDateTime">
+            <summary>
+            Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
+            </summary>
+            <returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextReader.ReadAsString">
+            <summary>
+            Reads the next JSON token from the stream as a <see cref="T:System.String"/>.
+            </summary>
+            <returns>A <see cref="T:System.String"/>. This method will return <c>null</c> at the end of an array.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextReader.ReadAsBytes">
+            <summary>
+            Reads the next JSON token from the stream as a <see cref="T:System.Byte"/>[].
+            </summary>
+            <returns>A <see cref="T:System.Byte"/>[] or a null reference if the next JSON token is null. This method will return <c>null</c> at the end of an array.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextReader.ReadAsBoolean">
+            <summary>
+            Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
+            </summary>
+            <returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextReader.ReadAsDateTimeOffset">
+            <summary>
+            Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
+            </summary>
+            <returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextReader.ReadAsDecimal">
+            <summary>
+            Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
+            </summary>
+            <returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextReader.ReadAsDouble">
+            <summary>
+            Reads the next JSON token from the stream as a <see cref="T:System.Nullable`1"/>.
+            </summary>
+            <returns>A <see cref="T:System.Nullable`1"/>. This method will return <c>null</c> at the end of an array.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextReader.Close">
+            <summary>
+            Changes the state to closed. 
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextReader.HasLineInfo">
+            <summary>
+            Gets a value indicating whether the class can return line information.
+            </summary>
+            <returns>
+            	<c>true</c> if LineNumber and LinePosition can be provided; otherwise, <c>false</c>.
+            </returns>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonTextReader.LineNumber">
+            <summary>
+            Gets the current line number.
+            </summary>
+            <value>
+            The current line number or 0 if no line information is available (for example, HasLineInfo returns false).
+            </value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonTextReader.LinePosition">
+            <summary>
+            Gets the current line position.
+            </summary>
+            <value>
+            The current line position or 0 if no line information is available (for example, HasLineInfo returns false).
+            </value>
+        </member>
+        <member name="T:Newtonsoft.Json.JsonTextWriter">
+            <summary>
+            Represents a writer that provides a fast, non-cached, forward-only way of generating JSON data.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonTextWriter.ArrayPool">
+            <summary>
+            Gets or sets the writer's character array pool.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonTextWriter.Indentation">
+            <summary>
+            Gets or sets how many IndentChars to write for each level in the hierarchy when <see cref="T:Newtonsoft.Json.Formatting"/> is set to <c>Formatting.Indented</c>.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonTextWriter.QuoteChar">
+            <summary>
+            Gets or sets which character to use to quote attribute values.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonTextWriter.IndentChar">
+            <summary>
+            Gets or sets which character to use for indenting when <see cref="T:Newtonsoft.Json.Formatting"/> is set to <c>Formatting.Indented</c>.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonTextWriter.QuoteName">
+            <summary>
+            Gets or sets a value indicating whether object names will be surrounded with quotes.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextWriter.#ctor(System.IO.TextWriter)">
+            <summary>
+            Creates an instance of the <c>JsonWriter</c> class using the specified <see cref="T:System.IO.TextWriter"/>. 
+            </summary>
+            <param name="textWriter">The <c>TextWriter</c> to write to.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextWriter.Flush">
+            <summary>
+            Flushes whatever is in the buffer to the underlying streams and also flushes the underlying stream.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextWriter.Close">
+            <summary>
+            Closes this stream and the underlying stream.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextWriter.WriteStartObject">
+            <summary>
+            Writes the beginning of a JSON object.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextWriter.WriteStartArray">
+            <summary>
+            Writes the beginning of a JSON array.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextWriter.WriteStartConstructor(System.String)">
+            <summary>
+            Writes the start of a constructor with the given name.
+            </summary>
+            <param name="name">The name of the constructor.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextWriter.WriteEnd(Newtonsoft.Json.JsonToken)">
+            <summary>
+            Writes the specified end token.
+            </summary>
+            <param name="token">The end token to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextWriter.WritePropertyName(System.String)">
+            <summary>
+            Writes the property name of a name/value pair on a JSON object.
+            </summary>
+            <param name="name">The name of the property.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextWriter.WritePropertyName(System.String,System.Boolean)">
+            <summary>
+            Writes the property name of a name/value pair on a JSON object.
+            </summary>
+            <param name="name">The name of the property.</param>
+            <param name="escape">A flag to indicate whether the text should be escaped when it is written as a JSON property name.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextWriter.WriteIndent">
+            <summary>
+            Writes indent characters.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextWriter.WriteValueDelimiter">
+            <summary>
+            Writes the JSON value delimiter.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextWriter.WriteIndentSpace">
+            <summary>
+            Writes an indent space.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextWriter.WriteValue(System.Object)">
+            <summary>
+            Writes a <see cref="T:System.Object"/> value.
+            An error will raised if the value cannot be written as a single JSON token.
+            </summary>
+            <param name="value">The <see cref="T:System.Object"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextWriter.WriteNull">
+            <summary>
+            Writes a null value.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextWriter.WriteUndefined">
+            <summary>
+            Writes an undefined value.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextWriter.WriteRaw(System.String)">
+            <summary>
+            Writes raw JSON.
+            </summary>
+            <param name="json">The raw JSON to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextWriter.WriteValue(System.String)">
+            <summary>
+            Writes a <see cref="T:System.String"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.String"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextWriter.WriteValue(System.Int32)">
+            <summary>
+            Writes a <see cref="T:System.Int32"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Int32"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextWriter.WriteValue(System.UInt32)">
+            <summary>
+            Writes a <see cref="T:System.UInt32"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.UInt32"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextWriter.WriteValue(System.Int64)">
+            <summary>
+            Writes a <see cref="T:System.Int64"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Int64"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextWriter.WriteValue(System.UInt64)">
+            <summary>
+            Writes a <see cref="T:System.UInt64"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.UInt64"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextWriter.WriteValue(System.Single)">
+            <summary>
+            Writes a <see cref="T:System.Single"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Single"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextWriter.WriteValue(System.Nullable{System.Single})">
+            <summary>
+            Writes a <see cref="T:System.Nullable`1"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Nullable`1"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextWriter.WriteValue(System.Double)">
+            <summary>
+            Writes a <see cref="T:System.Double"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Double"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextWriter.WriteValue(System.Nullable{System.Double})">
+            <summary>
+            Writes a <see cref="T:System.Nullable`1"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Nullable`1"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextWriter.WriteValue(System.Boolean)">
+            <summary>
+            Writes a <see cref="T:System.Boolean"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Boolean"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextWriter.WriteValue(System.Int16)">
+            <summary>
+            Writes a <see cref="T:System.Int16"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Int16"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextWriter.WriteValue(System.UInt16)">
+            <summary>
+            Writes a <see cref="T:System.UInt16"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.UInt16"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextWriter.WriteValue(System.Char)">
+            <summary>
+            Writes a <see cref="T:System.Char"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Char"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextWriter.WriteValue(System.Byte)">
+            <summary>
+            Writes a <see cref="T:System.Byte"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Byte"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextWriter.WriteValue(System.SByte)">
+            <summary>
+            Writes a <see cref="T:System.SByte"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.SByte"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextWriter.WriteValue(System.Decimal)">
+            <summary>
+            Writes a <see cref="T:System.Decimal"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Decimal"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextWriter.WriteValue(System.DateTime)">
+            <summary>
+            Writes a <see cref="T:System.DateTime"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.DateTime"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextWriter.WriteValue(System.Byte[])">
+            <summary>
+            Writes a <see cref="T:System.Byte"/>[] value.
+            </summary>
+            <param name="value">The <see cref="T:System.Byte"/>[] value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextWriter.WriteValue(System.DateTimeOffset)">
+            <summary>
+            Writes a <see cref="T:System.DateTimeOffset"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.DateTimeOffset"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextWriter.WriteValue(System.Guid)">
+            <summary>
+            Writes a <see cref="T:System.Guid"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Guid"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextWriter.WriteValue(System.TimeSpan)">
+            <summary>
+            Writes a <see cref="T:System.TimeSpan"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.TimeSpan"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextWriter.WriteValue(System.Uri)">
+            <summary>
+            Writes a <see cref="T:System.Uri"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Uri"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextWriter.WriteComment(System.String)">
+            <summary>
+            Writes out a comment <code>/*...*/</code> containing the specified text. 
+            </summary>
+            <param name="text">Text to place inside the comment.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonTextWriter.WriteWhitespace(System.String)">
+            <summary>
+            Writes out the given white space.
+            </summary>
+            <param name="ws">The string of white space characters.</param>
+        </member>
+        <member name="T:Newtonsoft.Json.JsonToken">
+            <summary>
+            Specifies the type of JSON token.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.JsonToken.None">
+            <summary>
+            This is returned by the <see cref="T:Newtonsoft.Json.JsonReader"/> if a <see cref="M:Newtonsoft.Json.JsonReader.Read"/> method has not been called. 
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.JsonToken.StartObject">
+            <summary>
+            An object start token.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.JsonToken.StartArray">
+            <summary>
+            An array start token.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.JsonToken.StartConstructor">
+            <summary>
+            A constructor start token.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.JsonToken.PropertyName">
+            <summary>
+            An object property name.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.JsonToken.Comment">
+            <summary>
+            A comment.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.JsonToken.Raw">
+            <summary>
+            Raw JSON.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.JsonToken.Integer">
+            <summary>
+            An integer.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.JsonToken.Float">
+            <summary>
+            A float.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.JsonToken.String">
+            <summary>
+            A string.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.JsonToken.Boolean">
+            <summary>
+            A boolean.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.JsonToken.Null">
+            <summary>
+            A null token.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.JsonToken.Undefined">
+            <summary>
+            An undefined token.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.JsonToken.EndObject">
+            <summary>
+            An object end token.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.JsonToken.EndArray">
+            <summary>
+            An array end token.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.JsonToken.EndConstructor">
+            <summary>
+            A constructor end token.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.JsonToken.Date">
+            <summary>
+            A Date.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.JsonToken.Bytes">
+            <summary>
+            Byte data.
+            </summary>
+        </member>
+        <member name="T:Newtonsoft.Json.JsonWriter">
+            <summary>
+            Represents a writer that provides a fast, non-cached, forward-only way of generating JSON data.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonWriter.CloseOutput">
+            <summary>
+            Gets or sets a value indicating whether the underlying stream or
+            <see cref="T:System.IO.TextReader"/> should be closed when the writer is closed.
+            </summary>
+            <value>
+            true to close the underlying stream or <see cref="T:System.IO.TextReader"/> when
+            the writer is closed; otherwise false. The default is true.
+            </value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonWriter.Top">
+            <summary>
+            Gets the top.
+            </summary>
+            <value>The top.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonWriter.WriteState">
+            <summary>
+            Gets the state of the writer.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonWriter.Path">
+            <summary>
+            Gets the path of the writer. 
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonWriter.Formatting">
+            <summary>
+            Indicates how JSON text output is formatted.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonWriter.DateFormatHandling">
+            <summary>
+            Get or set how dates are written to JSON text.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonWriter.DateTimeZoneHandling">
+            <summary>
+            Get or set how <see cref="T:System.DateTime"/> time zones are handling when writing JSON text.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonWriter.StringEscapeHandling">
+            <summary>
+            Get or set how strings are escaped when writing JSON text.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonWriter.FloatFormatHandling">
+            <summary>
+            Get or set how special floating point numbers, e.g. <see cref="F:System.Double.NaN"/>,
+            <see cref="F:System.Double.PositiveInfinity"/> and <see cref="F:System.Double.NegativeInfinity"/>,
+            are written to JSON text.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonWriter.DateFormatString">
+            <summary>
+            Get or set how <see cref="T:System.DateTime"/> and <see cref="T:System.DateTimeOffset"/> values are formatting when writing JSON text.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonWriter.Culture">
+            <summary>
+            Gets or sets the culture used when writing JSON. Defaults to <see cref="P:System.Globalization.CultureInfo.InvariantCulture"/>.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.#ctor">
+            <summary>
+            Creates an instance of the <c>JsonWriter</c> class. 
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.Flush">
+            <summary>
+            Flushes whatever is in the buffer to the underlying streams and also flushes the underlying stream.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.Close">
+            <summary>
+            Closes this stream and the underlying stream.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteStartObject">
+            <summary>
+            Writes the beginning of a JSON object.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteEndObject">
+            <summary>
+            Writes the end of a JSON object.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteStartArray">
+            <summary>
+            Writes the beginning of a JSON array.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteEndArray">
+            <summary>
+            Writes the end of an array.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteStartConstructor(System.String)">
+            <summary>
+            Writes the start of a constructor with the given name.
+            </summary>
+            <param name="name">The name of the constructor.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteEndConstructor">
+            <summary>
+            Writes the end constructor.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WritePropertyName(System.String)">
+            <summary>
+            Writes the property name of a name/value pair on a JSON object.
+            </summary>
+            <param name="name">The name of the property.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WritePropertyName(System.String,System.Boolean)">
+            <summary>
+            Writes the property name of a name/value pair on a JSON object.
+            </summary>
+            <param name="name">The name of the property.</param>
+            <param name="escape">A flag to indicate whether the text should be escaped when it is written as a JSON property name.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteEnd">
+            <summary>
+            Writes the end of the current JSON object or array.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteToken(Newtonsoft.Json.JsonReader)">
+            <summary>
+            Writes the current <see cref="T:Newtonsoft.Json.JsonReader"/> token and its children.
+            </summary>
+            <param name="reader">The <see cref="T:Newtonsoft.Json.JsonReader"/> to read the token from.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteToken(Newtonsoft.Json.JsonReader,System.Boolean)">
+            <summary>
+            Writes the current <see cref="T:Newtonsoft.Json.JsonReader"/> token.
+            </summary>
+            <param name="reader">The <see cref="T:Newtonsoft.Json.JsonReader"/> to read the token from.</param>
+            <param name="writeChildren">A flag indicating whether the current token's children should be written.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteToken(Newtonsoft.Json.JsonToken,System.Object)">
+            <summary>
+            Writes the <see cref="T:Newtonsoft.Json.JsonToken"/> token and its value.
+            </summary>
+            <param name="token">The <see cref="T:Newtonsoft.Json.JsonToken"/> to write.</param>
+            <param name="value">
+            The value to write.
+            A value is only required for tokens that have an associated value, e.g. the <see cref="T:System.String"/> property name for <see cref="F:Newtonsoft.Json.JsonToken.PropertyName"/>.
+            A null value can be passed to the method for token's that don't have a value, e.g. <see cref="F:Newtonsoft.Json.JsonToken.StartObject"/>.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteToken(Newtonsoft.Json.JsonToken)">
+            <summary>
+            Writes the <see cref="T:Newtonsoft.Json.JsonToken"/> token.
+            </summary>
+            <param name="token">The <see cref="T:Newtonsoft.Json.JsonToken"/> to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteEnd(Newtonsoft.Json.JsonToken)">
+            <summary>
+            Writes the specified end token.
+            </summary>
+            <param name="token">The end token to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteIndent">
+            <summary>
+            Writes indent characters.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteValueDelimiter">
+            <summary>
+            Writes the JSON value delimiter.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteIndentSpace">
+            <summary>
+            Writes an indent space.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteNull">
+            <summary>
+            Writes a null value.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteUndefined">
+            <summary>
+            Writes an undefined value.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteRaw(System.String)">
+            <summary>
+            Writes raw JSON without changing the writer's state.
+            </summary>
+            <param name="json">The raw JSON to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteRawValue(System.String)">
+            <summary>
+            Writes raw JSON where a value is expected and updates the writer's state.
+            </summary>
+            <param name="json">The raw JSON to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteValue(System.String)">
+            <summary>
+            Writes a <see cref="T:System.String"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.String"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteValue(System.Int32)">
+            <summary>
+            Writes a <see cref="T:System.Int32"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Int32"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteValue(System.UInt32)">
+            <summary>
+            Writes a <see cref="T:System.UInt32"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.UInt32"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteValue(System.Int64)">
+            <summary>
+            Writes a <see cref="T:System.Int64"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Int64"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteValue(System.UInt64)">
+            <summary>
+            Writes a <see cref="T:System.UInt64"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.UInt64"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteValue(System.Single)">
+            <summary>
+            Writes a <see cref="T:System.Single"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Single"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteValue(System.Double)">
+            <summary>
+            Writes a <see cref="T:System.Double"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Double"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteValue(System.Boolean)">
+            <summary>
+            Writes a <see cref="T:System.Boolean"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Boolean"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteValue(System.Int16)">
+            <summary>
+            Writes a <see cref="T:System.Int16"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Int16"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteValue(System.UInt16)">
+            <summary>
+            Writes a <see cref="T:System.UInt16"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.UInt16"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteValue(System.Char)">
+            <summary>
+            Writes a <see cref="T:System.Char"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Char"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteValue(System.Byte)">
+            <summary>
+            Writes a <see cref="T:System.Byte"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Byte"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteValue(System.SByte)">
+            <summary>
+            Writes a <see cref="T:System.SByte"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.SByte"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteValue(System.Decimal)">
+            <summary>
+            Writes a <see cref="T:System.Decimal"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Decimal"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteValue(System.DateTime)">
+            <summary>
+            Writes a <see cref="T:System.DateTime"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.DateTime"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteValue(System.DateTimeOffset)">
+            <summary>
+            Writes a <see cref="T:System.DateTimeOffset"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.DateTimeOffset"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteValue(System.Guid)">
+            <summary>
+            Writes a <see cref="T:System.Guid"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Guid"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteValue(System.TimeSpan)">
+            <summary>
+            Writes a <see cref="T:System.TimeSpan"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.TimeSpan"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteValue(System.Nullable{System.Int32})">
+            <summary>
+            Writes a <see cref="T:System.Nullable`1"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Nullable`1"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteValue(System.Nullable{System.UInt32})">
+            <summary>
+            Writes a <see cref="T:System.Nullable`1"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Nullable`1"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteValue(System.Nullable{System.Int64})">
+            <summary>
+            Writes a <see cref="T:System.Nullable`1"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Nullable`1"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteValue(System.Nullable{System.UInt64})">
+            <summary>
+            Writes a <see cref="T:System.Nullable`1"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Nullable`1"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteValue(System.Nullable{System.Single})">
+            <summary>
+            Writes a <see cref="T:System.Nullable`1"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Nullable`1"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteValue(System.Nullable{System.Double})">
+            <summary>
+            Writes a <see cref="T:System.Nullable`1"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Nullable`1"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteValue(System.Nullable{System.Boolean})">
+            <summary>
+            Writes a <see cref="T:System.Nullable`1"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Nullable`1"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteValue(System.Nullable{System.Int16})">
+            <summary>
+            Writes a <see cref="T:System.Nullable`1"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Nullable`1"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteValue(System.Nullable{System.UInt16})">
+            <summary>
+            Writes a <see cref="T:System.Nullable`1"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Nullable`1"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteValue(System.Nullable{System.Char})">
+            <summary>
+            Writes a <see cref="T:System.Nullable`1"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Nullable`1"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteValue(System.Nullable{System.Byte})">
+            <summary>
+            Writes a <see cref="T:System.Nullable`1"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Nullable`1"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteValue(System.Nullable{System.SByte})">
+            <summary>
+            Writes a <see cref="T:System.Nullable`1"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Nullable`1"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteValue(System.Nullable{System.Decimal})">
+            <summary>
+            Writes a <see cref="T:System.Nullable`1"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Nullable`1"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteValue(System.Nullable{System.DateTime})">
+            <summary>
+            Writes a <see cref="T:System.Nullable`1"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Nullable`1"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteValue(System.Nullable{System.DateTimeOffset})">
+            <summary>
+            Writes a <see cref="T:System.Nullable`1"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Nullable`1"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteValue(System.Nullable{System.Guid})">
+            <summary>
+            Writes a <see cref="T:System.Nullable`1"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Nullable`1"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteValue(System.Nullable{System.TimeSpan})">
+            <summary>
+            Writes a <see cref="T:System.Nullable`1"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Nullable`1"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteValue(System.Byte[])">
+            <summary>
+            Writes a <see cref="T:System.Byte"/>[] value.
+            </summary>
+            <param name="value">The <see cref="T:System.Byte"/>[] value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteValue(System.Uri)">
+            <summary>
+            Writes a <see cref="T:System.Uri"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Uri"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteValue(System.Object)">
+            <summary>
+            Writes a <see cref="T:System.Object"/> value.
+            An error will raised if the value cannot be written as a single JSON token.
+            </summary>
+            <param name="value">The <see cref="T:System.Object"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteComment(System.String)">
+            <summary>
+            Writes out a comment <code>/*...*/</code> containing the specified text. 
+            </summary>
+            <param name="text">Text to place inside the comment.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.WriteWhitespace(System.String)">
+            <summary>
+            Writes out the given white space.
+            </summary>
+            <param name="ws">The string of white space characters.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.Dispose(System.Boolean)">
+            <summary>
+            Releases unmanaged and - optionally - managed resources
+            </summary>
+            <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriter.SetWriteState(Newtonsoft.Json.JsonToken,System.Object)">
+            <summary>
+            Sets the state of the JsonWriter,
+            </summary>
+            <param name="token">The JsonToken being written.</param>
+            <param name="value">The value being written.</param>
+        </member>
+        <member name="T:Newtonsoft.Json.JsonWriterException">
+            <summary>
+            The exception thrown when an error occurs while reading JSON text.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.JsonWriterException.Path">
+            <summary>
+            Gets the path to the JSON where the error occurred.
+            </summary>
+            <value>The path to the JSON where the error occurred.</value>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriterException.#ctor">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonWriterException"/> class.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriterException.#ctor(System.String)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonWriterException"/> class
+            with a specified error message.
+            </summary>
+            <param name="message">The error message that explains the reason for the exception.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.JsonWriterException.#ctor(System.String,System.Exception)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.JsonWriterException"/> class
+            with a specified error message and a reference to the inner exception that is the cause of this exception.
+            </summary>
+            <param name="message">The error message that explains the reason for the exception.</param>
+            <param name="innerException">The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified.</param>
+        </member>
+        <member name="T:Newtonsoft.Json.Linq.CommentHandling">
+            <summary>
+            Specifies how JSON comments are handled when loading JSON.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.Linq.CommentHandling.Ignore">
+            <summary>
+            Ignore comments.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.Linq.CommentHandling.Load">
+            <summary>
+            Load comments as a <see cref="T:Newtonsoft.Json.Linq.JValue"/> with type <see cref="F:Newtonsoft.Json.Linq.JTokenType.Comment"/>.
+            </summary>
+        </member>
+        <member name="T:Newtonsoft.Json.Linq.LineInfoHandling">
+            <summary>
+            Specifies how line information is handled when loading JSON.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.Linq.LineInfoHandling.Ignore">
+            <summary>
+            Ignore line information.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.Linq.LineInfoHandling.Load">
+            <summary>
+            Load line information.
+            </summary>
+        </member>
+        <member name="T:Newtonsoft.Json.Linq.Extensions">
+            <summary>
+            Contains the LINQ to JSON extension methods.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.Extensions.Ancestors``1(System.Collections.Generic.IEnumerable{``0})">
+            <summary>
+            Returns a collection of tokens that contains the ancestors of every token in the source collection.
+            </summary>
+            <typeparam name="T">The type of the objects in source, constrained to <see cref="T:Newtonsoft.Json.Linq.JToken"/>.</typeparam>
+            <param name="source">An <see cref="T:System.Collections.Generic.IEnumerable`1"/> of <see cref="T:Newtonsoft.Json.Linq.JToken"/> that contains the source collection.</param>
+            <returns>An <see cref="T:System.Collections.Generic.IEnumerable`1"/> of <see cref="T:Newtonsoft.Json.Linq.JToken"/> that contains the ancestors of every token in the source collection.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.Extensions.AncestorsAndSelf``1(System.Collections.Generic.IEnumerable{``0})">
+            <summary>
+            Returns a collection of tokens that contains every token in the source collection, and the ancestors of every token in the source collection.
+            </summary>
+            <typeparam name="T">The type of the objects in source, constrained to <see cref="T:Newtonsoft.Json.Linq.JToken"/>.</typeparam>
+            <param name="source">An <see cref="T:System.Collections.Generic.IEnumerable`1"/> of <see cref="T:Newtonsoft.Json.Linq.JToken"/> that contains the source collection.</param>
+            <returns>An <see cref="T:System.Collections.Generic.IEnumerable`1"/> of <see cref="T:Newtonsoft.Json.Linq.JToken"/> that contains every token in the source collection, the ancestors of every token in the source collection.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.Extensions.Descendants``1(System.Collections.Generic.IEnumerable{``0})">
+            <summary>
+            Returns a collection of tokens that contains the descendants of every token in the source collection.
+            </summary>
+            <typeparam name="T">The type of the objects in source, constrained to <see cref="T:Newtonsoft.Json.Linq.JContainer"/>.</typeparam>
+            <param name="source">An <see cref="T:System.Collections.Generic.IEnumerable`1"/> of <see cref="T:Newtonsoft.Json.Linq.JToken"/> that contains the source collection.</param>
+            <returns>An <see cref="T:System.Collections.Generic.IEnumerable`1"/> of <see cref="T:Newtonsoft.Json.Linq.JToken"/> that contains the descendants of every token in the source collection.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.Extensions.DescendantsAndSelf``1(System.Collections.Generic.IEnumerable{``0})">
+            <summary>
+            Returns a collection of tokens that contains every token in the source collection, and the descendants of every token in the source collection.
+            </summary>
+            <typeparam name="T">The type of the objects in source, constrained to <see cref="T:Newtonsoft.Json.Linq.JContainer"/>.</typeparam>
+            <param name="source">An <see cref="T:System.Collections.Generic.IEnumerable`1"/> of <see cref="T:Newtonsoft.Json.Linq.JToken"/> that contains the source collection.</param>
+            <returns>An <see cref="T:System.Collections.Generic.IEnumerable`1"/> of <see cref="T:Newtonsoft.Json.Linq.JToken"/> that contains every token in the source collection, and the descendants of every token in the source collection.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.Extensions.Properties(System.Collections.Generic.IEnumerable{Newtonsoft.Json.Linq.JObject})">
+            <summary>
+            Returns a collection of child properties of every object in the source collection.
+            </summary>
+            <param name="source">An <see cref="T:System.Collections.Generic.IEnumerable`1"/> of <see cref="T:Newtonsoft.Json.Linq.JObject"/> that contains the source collection.</param>
+            <returns>An <see cref="T:System.Collections.Generic.IEnumerable`1"/> of <see cref="T:Newtonsoft.Json.Linq.JProperty"/> that contains the properties of every object in the source collection.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.Extensions.Values(System.Collections.Generic.IEnumerable{Newtonsoft.Json.Linq.JToken},System.Object)">
+            <summary>
+            Returns a collection of child values of every object in the source collection with the given key.
+            </summary>
+            <param name="source">An <see cref="T:System.Collections.Generic.IEnumerable`1"/> of <see cref="T:Newtonsoft.Json.Linq.JToken"/> that contains the source collection.</param>
+            <param name="key">The token key.</param>
+            <returns>An <see cref="T:System.Collections.Generic.IEnumerable`1"/> of <see cref="T:Newtonsoft.Json.Linq.JToken"/> that contains the values of every token in the source collection with the given key.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.Extensions.Values(System.Collections.Generic.IEnumerable{Newtonsoft.Json.Linq.JToken})">
+            <summary>
+            Returns a collection of child values of every object in the source collection.
+            </summary>
+            <param name="source">An <see cref="T:System.Collections.Generic.IEnumerable`1"/> of <see cref="T:Newtonsoft.Json.Linq.JToken"/> that contains the source collection.</param>
+            <returns>An <see cref="T:System.Collections.Generic.IEnumerable`1"/> of <see cref="T:Newtonsoft.Json.Linq.JToken"/> that contains the values of every token in the source collection.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.Extensions.Values``1(System.Collections.Generic.IEnumerable{Newtonsoft.Json.Linq.JToken},System.Object)">
+            <summary>
+            Returns a collection of converted child values of every object in the source collection with the given key.
+            </summary>
+            <typeparam name="U">The type to convert the values to.</typeparam>
+            <param name="source">An <see cref="T:System.Collections.Generic.IEnumerable`1"/> of <see cref="T:Newtonsoft.Json.Linq.JToken"/> that contains the source collection.</param>
+            <param name="key">The token key.</param>
+            <returns>An <see cref="T:System.Collections.Generic.IEnumerable`1"/> that contains the converted values of every token in the source collection with the given key.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.Extensions.Values``1(System.Collections.Generic.IEnumerable{Newtonsoft.Json.Linq.JToken})">
+            <summary>
+            Returns a collection of converted child values of every object in the source collection.
+            </summary>
+            <typeparam name="U">The type to convert the values to.</typeparam>
+            <param name="source">An <see cref="T:System.Collections.Generic.IEnumerable`1"/> of <see cref="T:Newtonsoft.Json.Linq.JToken"/> that contains the source collection.</param>
+            <returns>An <see cref="T:System.Collections.Generic.IEnumerable`1"/> that contains the converted values of every token in the source collection.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.Extensions.Value``1(System.Collections.Generic.IEnumerable{Newtonsoft.Json.Linq.JToken})">
+            <summary>
+            Converts the value.
+            </summary>
+            <typeparam name="U">The type to convert the value to.</typeparam>
+            <param name="value">A <see cref="T:Newtonsoft.Json.Linq.JToken"/> cast as a <see cref="T:System.Collections.Generic.IEnumerable`1"/> of <see cref="T:Newtonsoft.Json.Linq.JToken"/>.</param>
+            <returns>A converted value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.Extensions.Value``2(System.Collections.Generic.IEnumerable{``0})">
+            <summary>
+            Converts the value.
+            </summary>
+            <typeparam name="T">The source collection type.</typeparam>
+            <typeparam name="U">The type to convert the value to.</typeparam>
+            <param name="value">A <see cref="T:Newtonsoft.Json.Linq.JToken"/> cast as a <see cref="T:System.Collections.Generic.IEnumerable`1"/> of <see cref="T:Newtonsoft.Json.Linq.JToken"/>.</param>
+            <returns>A converted value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.Extensions.Children``1(System.Collections.Generic.IEnumerable{``0})">
+            <summary>
+            Returns a collection of child tokens of every array in the source collection.
+            </summary>
+            <typeparam name="T">The source collection type.</typeparam>
+            <param name="source">An <see cref="T:System.Collections.Generic.IEnumerable`1"/> of <see cref="T:Newtonsoft.Json.Linq.JToken"/> that contains the source collection.</param>
+            <returns>An <see cref="T:System.Collections.Generic.IEnumerable`1"/> of <see cref="T:Newtonsoft.Json.Linq.JToken"/> that contains the values of every token in the source collection.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.Extensions.Children``2(System.Collections.Generic.IEnumerable{``0})">
+            <summary>
+            Returns a collection of converted child tokens of every array in the source collection.
+            </summary>
+            <param name="source">An <see cref="T:System.Collections.Generic.IEnumerable`1"/> of <see cref="T:Newtonsoft.Json.Linq.JToken"/> that contains the source collection.</param>
+            <typeparam name="U">The type to convert the values to.</typeparam>
+            <typeparam name="T">The source collection type.</typeparam>
+            <returns>An <see cref="T:System.Collections.Generic.IEnumerable`1"/> that contains the converted values of every token in the source collection.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.Extensions.AsJEnumerable(System.Collections.Generic.IEnumerable{Newtonsoft.Json.Linq.JToken})">
+            <summary>
+            Returns the input typed as <see cref="T:Newtonsoft.Json.Linq.IJEnumerable`1"/>.
+            </summary>
+            <param name="source">An <see cref="T:System.Collections.Generic.IEnumerable`1"/> of <see cref="T:Newtonsoft.Json.Linq.JToken"/> that contains the source collection.</param>
+            <returns>The input typed as <see cref="T:Newtonsoft.Json.Linq.IJEnumerable`1"/>.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.Extensions.AsJEnumerable``1(System.Collections.Generic.IEnumerable{``0})">
+            <summary>
+            Returns the input typed as <see cref="T:Newtonsoft.Json.Linq.IJEnumerable`1"/>.
+            </summary>
+            <typeparam name="T">The source collection type.</typeparam>
+            <param name="source">An <see cref="T:System.Collections.Generic.IEnumerable`1"/> of <see cref="T:Newtonsoft.Json.Linq.JToken"/> that contains the source collection.</param>
+            <returns>The input typed as <see cref="T:Newtonsoft.Json.Linq.IJEnumerable`1"/>.</returns>
+        </member>
+        <member name="T:Newtonsoft.Json.Linq.IJEnumerable`1">
+            <summary>
+            Represents a collection of <see cref="T:Newtonsoft.Json.Linq.JToken"/> objects.
+            </summary>
+            <typeparam name="T">The type of token</typeparam>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.IJEnumerable`1.Item(System.Object)">
+            <summary>
+            Gets the <see cref="T:Newtonsoft.Json.Linq.IJEnumerable`1"/> with the specified key.
+            </summary>
+            <value></value>
+        </member>
+        <member name="T:Newtonsoft.Json.Linq.JArray">
+            <summary>
+            Represents a JSON array.
+            </summary>
+            <example>
+              <code lang="cs" source="..\Src\Newtonsoft.Json.Tests\Documentation\LinqToJsonTests.cs" region="LinqToJsonCreateParseArray" title="Parsing a JSON Array from Text" />
+            </example>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JArray.ChildrenTokens">
+            <summary>
+            Gets the container's children tokens.
+            </summary>
+            <value>The container's children tokens.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JArray.Type">
+            <summary>
+            Gets the node type for this <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <value>The type.</value>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JArray.#ctor">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Linq.JArray"/> class.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JArray.#ctor(Newtonsoft.Json.Linq.JArray)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Linq.JArray"/> class from another <see cref="T:Newtonsoft.Json.Linq.JArray"/> object.
+            </summary>
+            <param name="other">A <see cref="T:Newtonsoft.Json.Linq.JArray"/> object to copy from.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JArray.#ctor(System.Object[])">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Linq.JArray"/> class with the specified content.
+            </summary>
+            <param name="content">The contents of the array.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JArray.#ctor(System.Object)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Linq.JArray"/> class with the specified content.
+            </summary>
+            <param name="content">The contents of the array.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JArray.Load(Newtonsoft.Json.JsonReader)">
+            <summary>
+            Loads an <see cref="T:Newtonsoft.Json.Linq.JArray"/> from a <see cref="T:Newtonsoft.Json.JsonReader"/>. 
+            </summary>
+            <param name="reader">A <see cref="T:Newtonsoft.Json.JsonReader"/> that will be read for the content of the <see cref="T:Newtonsoft.Json.Linq.JArray"/>.</param>
+            <returns>A <see cref="T:Newtonsoft.Json.Linq.JArray"/> that contains the JSON that was read from the specified <see cref="T:Newtonsoft.Json.JsonReader"/>.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JArray.Load(Newtonsoft.Json.JsonReader,Newtonsoft.Json.Linq.JsonLoadSettings)">
+            <summary>
+            Loads an <see cref="T:Newtonsoft.Json.Linq.JArray"/> from a <see cref="T:Newtonsoft.Json.JsonReader"/>. 
+            </summary>
+            <param name="reader">A <see cref="T:Newtonsoft.Json.JsonReader"/> that will be read for the content of the <see cref="T:Newtonsoft.Json.Linq.JArray"/>.</param>
+            <param name="settings">The <see cref="T:Newtonsoft.Json.Linq.JsonLoadSettings"/> used to load the JSON.
+            If this is null, default load settings will be used.</param>
+            <returns>A <see cref="T:Newtonsoft.Json.Linq.JArray"/> that contains the JSON that was read from the specified <see cref="T:Newtonsoft.Json.JsonReader"/>.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JArray.Parse(System.String)">
+            <summary>
+            Load a <see cref="T:Newtonsoft.Json.Linq.JArray"/> from a string that contains JSON.
+            </summary>
+            <param name="json">A <see cref="T:System.String"/> that contains JSON.</param>
+            <returns>A <see cref="T:Newtonsoft.Json.Linq.JArray"/> populated from the string that contains JSON.</returns>
+            <example>
+              <code lang="cs" source="..\Src\Newtonsoft.Json.Tests\Documentation\LinqToJsonTests.cs" region="LinqToJsonCreateParseArray" title="Parsing a JSON Array from Text" />
+            </example>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JArray.Parse(System.String,Newtonsoft.Json.Linq.JsonLoadSettings)">
+            <summary>
+            Load a <see cref="T:Newtonsoft.Json.Linq.JArray"/> from a string that contains JSON.
+            </summary>
+            <param name="json">A <see cref="T:System.String"/> that contains JSON.</param>
+            <param name="settings">The <see cref="T:Newtonsoft.Json.Linq.JsonLoadSettings"/> used to load the JSON.
+            If this is null, default load settings will be used.</param>
+            <returns>A <see cref="T:Newtonsoft.Json.Linq.JArray"/> populated from the string that contains JSON.</returns>
+            <example>
+              <code lang="cs" source="..\Src\Newtonsoft.Json.Tests\Documentation\LinqToJsonTests.cs" region="LinqToJsonCreateParseArray" title="Parsing a JSON Array from Text" />
+            </example>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JArray.FromObject(System.Object)">
+            <summary>
+            Creates a <see cref="T:Newtonsoft.Json.Linq.JArray"/> from an object.
+            </summary>
+            <param name="o">The object that will be used to create <see cref="T:Newtonsoft.Json.Linq.JArray"/>.</param>
+            <returns>A <see cref="T:Newtonsoft.Json.Linq.JArray"/> with the values of the specified object</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JArray.FromObject(System.Object,Newtonsoft.Json.JsonSerializer)">
+            <summary>
+            Creates a <see cref="T:Newtonsoft.Json.Linq.JArray"/> from an object.
+            </summary>
+            <param name="o">The object that will be used to create <see cref="T:Newtonsoft.Json.Linq.JArray"/>.</param>
+            <param name="jsonSerializer">The <see cref="T:Newtonsoft.Json.JsonSerializer"/> that will be used to read the object.</param>
+            <returns>A <see cref="T:Newtonsoft.Json.Linq.JArray"/> with the values of the specified object</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JArray.WriteTo(Newtonsoft.Json.JsonWriter,Newtonsoft.Json.JsonConverter[])">
+            <summary>
+            Writes this token to a <see cref="T:Newtonsoft.Json.JsonWriter"/>.
+            </summary>
+            <param name="writer">A <see cref="T:Newtonsoft.Json.JsonWriter"/> into which this method will write.</param>
+            <param name="converters">A collection of <see cref="T:Newtonsoft.Json.JsonConverter"/> which will be used when writing the token.</param>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JArray.Item(System.Object)">
+            <summary>
+            Gets the <see cref="T:Newtonsoft.Json.Linq.JToken"/> with the specified key.
+            </summary>
+            <value>The <see cref="T:Newtonsoft.Json.Linq.JToken"/> with the specified key.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JArray.Item(System.Int32)">
+            <summary>
+            Gets or sets the <see cref="T:Newtonsoft.Json.Linq.JToken"/> at the specified index.
+            </summary>
+            <value></value>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JArray.IndexOf(Newtonsoft.Json.Linq.JToken)">
+            <summary>
+            Determines the index of a specific item in the <see cref="T:System.Collections.Generic.IList`1"/>.
+            </summary>
+            <param name="item">The object to locate in the <see cref="T:System.Collections.Generic.IList`1"/>.</param>
+            <returns>
+            The index of <paramref name="item"/> if found in the list; otherwise, -1.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JArray.Insert(System.Int32,Newtonsoft.Json.Linq.JToken)">
+            <summary>
+            Inserts an item to the <see cref="T:System.Collections.Generic.IList`1"/> at the specified index.
+            </summary>
+            <param name="index">The zero-based index at which <paramref name="item"/> should be inserted.</param>
+            <param name="item">The object to insert into the <see cref="T:System.Collections.Generic.IList`1"/>.</param>
+            <exception cref="T:System.ArgumentOutOfRangeException">
+            	<paramref name="index"/> is not a valid index in the <see cref="T:System.Collections.Generic.IList`1"/>.</exception>
+            <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.IList`1"/> is read-only.</exception>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JArray.RemoveAt(System.Int32)">
+            <summary>
+            Removes the <see cref="T:System.Collections.Generic.IList`1"/> item at the specified index.
+            </summary>
+            <param name="index">The zero-based index of the item to remove.</param>
+            <exception cref="T:System.ArgumentOutOfRangeException">
+            	<paramref name="index"/> is not a valid index in the <see cref="T:System.Collections.Generic.IList`1"/>.</exception>
+            <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.IList`1"/> is read-only.</exception>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JArray.GetEnumerator">
+            <summary>
+            Returns an enumerator that iterates through the collection.
+            </summary>
+            <returns>
+            A <see cref="T:System.Collections.Generic.IEnumerator`1" /> that can be used to iterate through the collection.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JArray.Add(Newtonsoft.Json.Linq.JToken)">
+            <summary>
+            Adds an item to the <see cref="T:System.Collections.Generic.ICollection`1"/>.
+            </summary>
+            <param name="item">The object to add to the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param>
+            <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.</exception>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JArray.Clear">
+            <summary>
+            Removes all items from the <see cref="T:System.Collections.Generic.ICollection`1"/>.
+            </summary>
+            <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only. </exception>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JArray.Contains(Newtonsoft.Json.Linq.JToken)">
+            <summary>
+            Determines whether the <see cref="T:System.Collections.Generic.ICollection`1"/> contains a specific value.
+            </summary>
+            <param name="item">The object to locate in the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param>
+            <returns>
+            true if <paramref name="item"/> is found in the <see cref="T:System.Collections.Generic.ICollection`1"/>; otherwise, false.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JArray.CopyTo(Newtonsoft.Json.Linq.JToken[],System.Int32)">
+            <summary>
+            Copies to.
+            </summary>
+            <param name="array">The array.</param>
+            <param name="arrayIndex">Index of the array.</param>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JArray.IsReadOnly">
+            <summary>
+            Gets a value indicating whether the <see cref="T:System.Collections.Generic.ICollection`1" /> is read-only.
+            </summary>
+            <returns>true if the <see cref="T:System.Collections.Generic.ICollection`1" /> is read-only; otherwise, false.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JArray.Remove(Newtonsoft.Json.Linq.JToken)">
+            <summary>
+            Removes the first occurrence of a specific object from the <see cref="T:System.Collections.Generic.ICollection`1"/>.
+            </summary>
+            <param name="item">The object to remove from the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param>
+            <returns>
+            true if <paramref name="item"/> was successfully removed from the <see cref="T:System.Collections.Generic.ICollection`1"/>; otherwise, false. This method also returns false if <paramref name="item"/> is not found in the original <see cref="T:System.Collections.Generic.ICollection`1"/>.
+            </returns>
+            <exception cref="T:System.NotSupportedException">The <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.</exception>
+        </member>
+        <member name="T:Newtonsoft.Json.Linq.JConstructor">
+            <summary>
+            Represents a JSON constructor.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JConstructor.ChildrenTokens">
+            <summary>
+            Gets the container's children tokens.
+            </summary>
+            <value>The container's children tokens.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JConstructor.Name">
+            <summary>
+            Gets or sets the name of this constructor.
+            </summary>
+            <value>The constructor name.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JConstructor.Type">
+            <summary>
+            Gets the node type for this <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <value>The type.</value>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JConstructor.#ctor">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Linq.JConstructor"/> class.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JConstructor.#ctor(Newtonsoft.Json.Linq.JConstructor)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Linq.JConstructor"/> class from another <see cref="T:Newtonsoft.Json.Linq.JConstructor"/> object.
+            </summary>
+            <param name="other">A <see cref="T:Newtonsoft.Json.Linq.JConstructor"/> object to copy from.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JConstructor.#ctor(System.String,System.Object[])">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Linq.JConstructor"/> class with the specified name and content.
+            </summary>
+            <param name="name">The constructor name.</param>
+            <param name="content">The contents of the constructor.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JConstructor.#ctor(System.String,System.Object)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Linq.JConstructor"/> class with the specified name and content.
+            </summary>
+            <param name="name">The constructor name.</param>
+            <param name="content">The contents of the constructor.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JConstructor.#ctor(System.String)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Linq.JConstructor"/> class with the specified name.
+            </summary>
+            <param name="name">The constructor name.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JConstructor.WriteTo(Newtonsoft.Json.JsonWriter,Newtonsoft.Json.JsonConverter[])">
+            <summary>
+            Writes this token to a <see cref="T:Newtonsoft.Json.JsonWriter"/>.
+            </summary>
+            <param name="writer">A <see cref="T:Newtonsoft.Json.JsonWriter"/> into which this method will write.</param>
+            <param name="converters">A collection of <see cref="T:Newtonsoft.Json.JsonConverter"/> which will be used when writing the token.</param>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JConstructor.Item(System.Object)">
+            <summary>
+            Gets the <see cref="T:Newtonsoft.Json.Linq.JToken"/> with the specified key.
+            </summary>
+            <value>The <see cref="T:Newtonsoft.Json.Linq.JToken"/> with the specified key.</value>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JConstructor.Load(Newtonsoft.Json.JsonReader)">
+            <summary>
+            Loads an <see cref="T:Newtonsoft.Json.Linq.JConstructor"/> from a <see cref="T:Newtonsoft.Json.JsonReader"/>. 
+            </summary>
+            <param name="reader">A <see cref="T:Newtonsoft.Json.JsonReader"/> that will be read for the content of the <see cref="T:Newtonsoft.Json.Linq.JConstructor"/>.</param>
+            <returns>A <see cref="T:Newtonsoft.Json.Linq.JConstructor"/> that contains the JSON that was read from the specified <see cref="T:Newtonsoft.Json.JsonReader"/>.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JConstructor.Load(Newtonsoft.Json.JsonReader,Newtonsoft.Json.Linq.JsonLoadSettings)">
+            <summary>
+            Loads an <see cref="T:Newtonsoft.Json.Linq.JConstructor"/> from a <see cref="T:Newtonsoft.Json.JsonReader"/>. 
+            </summary>
+            <param name="reader">A <see cref="T:Newtonsoft.Json.JsonReader"/> that will be read for the content of the <see cref="T:Newtonsoft.Json.Linq.JConstructor"/>.</param>
+            <param name="settings">The <see cref="T:Newtonsoft.Json.Linq.JsonLoadSettings"/> used to load the JSON.
+            If this is null, default load settings will be used.</param>
+            <returns>A <see cref="T:Newtonsoft.Json.Linq.JConstructor"/> that contains the JSON that was read from the specified <see cref="T:Newtonsoft.Json.JsonReader"/>.</returns>
+        </member>
+        <member name="T:Newtonsoft.Json.Linq.JContainer">
+            <summary>
+            Represents a token that can contain other tokens.
+            </summary>
+        </member>
+        <member name="E:Newtonsoft.Json.Linq.JContainer.CollectionChanged">
+            <summary>
+            Occurs when the items list of the collection has changed, or the collection is reset.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JContainer.ChildrenTokens">
+            <summary>
+            Gets the container's children tokens.
+            </summary>
+            <value>The container's children tokens.</value>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JContainer.OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs)">
+            <summary>
+            Raises the <see cref="E:Newtonsoft.Json.Linq.JContainer.CollectionChanged"/> event.
+            </summary>
+            <param name="e">The <see cref="T:System.Collections.Specialized.NotifyCollectionChangedEventArgs"/> instance containing the event data.</param>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JContainer.HasValues">
+            <summary>
+            Gets a value indicating whether this token has child tokens.
+            </summary>
+            <value>
+            	<c>true</c> if this token has child values; otherwise, <c>false</c>.
+            </value>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JContainer.First">
+            <summary>
+            Get the first child token of this token.
+            </summary>
+            <value>
+            A <see cref="T:Newtonsoft.Json.Linq.JToken"/> containing the first child token of the <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </value>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JContainer.Last">
+            <summary>
+            Get the last child token of this token.
+            </summary>
+            <value>
+            A <see cref="T:Newtonsoft.Json.Linq.JToken"/> containing the last child token of the <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </value>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JContainer.Children">
+            <summary>
+            Returns a collection of the child tokens of this token, in document order.
+            </summary>
+            <returns>
+            An <see cref="T:System.Collections.Generic.IEnumerable`1"/> of <see cref="T:Newtonsoft.Json.Linq.JToken"/> containing the child tokens of this <see cref="T:Newtonsoft.Json.Linq.JToken"/>, in document order.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JContainer.Values``1">
+            <summary>
+            Returns a collection of the child values of this token, in document order.
+            </summary>
+            <typeparam name="T">The type to convert the values to.</typeparam>
+            <returns>
+            A <see cref="T:System.Collections.Generic.IEnumerable`1"/> containing the child values of this <see cref="T:Newtonsoft.Json.Linq.JToken"/>, in document order.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JContainer.Descendants">
+            <summary>
+            Returns a collection of the descendant tokens for this token in document order.
+            </summary>
+            <returns>An <see cref="T:System.Collections.Generic.IEnumerable`1"/> containing the descendant tokens of the <see cref="T:Newtonsoft.Json.Linq.JToken"/>.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JContainer.DescendantsAndSelf">
+            <summary>
+            Returns a collection of the tokens that contain this token, and all descendant tokens of this token, in document order.
+            </summary>
+            <returns>An <see cref="T:System.Collections.Generic.IEnumerable`1"/> containing this token, and all the descendant tokens of the <see cref="T:Newtonsoft.Json.Linq.JToken"/>.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JContainer.Add(System.Object)">
+            <summary>
+            Adds the specified content as children of this <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <param name="content">The content to be added.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JContainer.AddFirst(System.Object)">
+            <summary>
+            Adds the specified content as the first children of this <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <param name="content">The content to be added.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JContainer.CreateWriter">
+            <summary>
+            Creates an <see cref="T:Newtonsoft.Json.JsonWriter"/> that can be used to add tokens to the <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <returns>An <see cref="T:Newtonsoft.Json.JsonWriter"/> that is ready to have content written to it.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JContainer.ReplaceAll(System.Object)">
+            <summary>
+            Replaces the children nodes of this token with the specified content.
+            </summary>
+            <param name="content">The content.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JContainer.RemoveAll">
+            <summary>
+            Removes the child nodes from this token.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JContainer.Merge(System.Object)">
+            <summary>
+            Merge the specified content into this <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <param name="content">The content to be merged.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JContainer.Merge(System.Object,Newtonsoft.Json.Linq.JsonMergeSettings)">
+            <summary>
+            Merge the specified content into this <see cref="T:Newtonsoft.Json.Linq.JToken"/> using <see cref="T:Newtonsoft.Json.Linq.JsonMergeSettings"/>.
+            </summary>
+            <param name="content">The content to be merged.</param>
+            <param name="settings">The <see cref="T:Newtonsoft.Json.Linq.JsonMergeSettings"/> used to merge the content.</param>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JContainer.Count">
+            <summary>
+            Gets the count of child JSON tokens.
+            </summary>
+            <value>The count of child JSON tokens</value>
+        </member>
+        <member name="T:Newtonsoft.Json.Linq.JEnumerable`1">
+            <summary>
+            Represents a collection of <see cref="T:Newtonsoft.Json.Linq.JToken"/> objects.
+            </summary>
+            <typeparam name="T">The type of token</typeparam>
+        </member>
+        <member name="F:Newtonsoft.Json.Linq.JEnumerable`1.Empty">
+            <summary>
+            An empty collection of <see cref="T:Newtonsoft.Json.Linq.JToken"/> objects.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JEnumerable`1.#ctor(System.Collections.Generic.IEnumerable{`0})">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Linq.JEnumerable`1"/> struct.
+            </summary>
+            <param name="enumerable">The enumerable.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JEnumerable`1.GetEnumerator">
+            <summary>
+            Returns an enumerator that iterates through the collection.
+            </summary>
+            <returns>
+            A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JEnumerable`1.System#Collections#IEnumerable#GetEnumerator">
+            <summary>
+            Returns an enumerator that iterates through a collection.
+            </summary>
+            <returns>
+            An <see cref="T:System.Collections.IEnumerator"/> object that can be used to iterate through the collection.
+            </returns>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JEnumerable`1.Item(System.Object)">
+            <summary>
+            Gets the <see cref="T:Newtonsoft.Json.Linq.IJEnumerable`1"/> with the specified key.
+            </summary>
+            <value></value>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JEnumerable`1.Equals(Newtonsoft.Json.Linq.JEnumerable{`0})">
+            <summary>
+            Determines whether the specified <see cref="T:Newtonsoft.Json.Linq.JEnumerable`1"/> is equal to this instance.
+            </summary>
+            <param name="other">The <see cref="T:Newtonsoft.Json.Linq.JEnumerable`1"/> to compare with this instance.</param>
+            <returns>
+            	<c>true</c> if the specified <see cref="T:Newtonsoft.Json.Linq.JEnumerable`1"/> is equal to this instance; otherwise, <c>false</c>.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JEnumerable`1.Equals(System.Object)">
+            <summary>
+            Determines whether the specified <see cref="T:System.Object"/> is equal to this instance.
+            </summary>
+            <param name="obj">The <see cref="T:System.Object"/> to compare with this instance.</param>
+            <returns>
+            	<c>true</c> if the specified <see cref="T:System.Object"/> is equal to this instance; otherwise, <c>false</c>.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JEnumerable`1.GetHashCode">
+            <summary>
+            Returns a hash code for this instance.
+            </summary>
+            <returns>
+            A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. 
+            </returns>
+        </member>
+        <member name="T:Newtonsoft.Json.Linq.JObject">
+            <summary>
+            Represents a JSON object.
+            </summary>
+            <example>
+              <code lang="cs" source="..\Src\Newtonsoft.Json.Tests\Documentation\LinqToJsonTests.cs" region="LinqToJsonCreateParse" title="Parsing a JSON Object from Text" />
+            </example>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JObject.ChildrenTokens">
+            <summary>
+            Gets the container's children tokens.
+            </summary>
+            <value>The container's children tokens.</value>
+        </member>
+        <member name="E:Newtonsoft.Json.Linq.JObject.PropertyChanged">
+            <summary>
+            Occurs when a property value changes.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JObject.#ctor">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Linq.JObject"/> class.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JObject.#ctor(Newtonsoft.Json.Linq.JObject)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Linq.JObject"/> class from another <see cref="T:Newtonsoft.Json.Linq.JObject"/> object.
+            </summary>
+            <param name="other">A <see cref="T:Newtonsoft.Json.Linq.JObject"/> object to copy from.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JObject.#ctor(System.Object[])">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Linq.JObject"/> class with the specified content.
+            </summary>
+            <param name="content">The contents of the object.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JObject.#ctor(System.Object)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Linq.JObject"/> class with the specified content.
+            </summary>
+            <param name="content">The contents of the object.</param>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JObject.Type">
+            <summary>
+            Gets the node type for this <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <value>The type.</value>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JObject.Properties">
+            <summary>
+            Gets an <see cref="T:System.Collections.Generic.IEnumerable`1"/> of this object's properties.
+            </summary>
+            <returns>An <see cref="T:System.Collections.Generic.IEnumerable`1"/> of this object's properties.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JObject.Property(System.String)">
+            <summary>
+            Gets a <see cref="T:Newtonsoft.Json.Linq.JProperty"/> the specified name.
+            </summary>
+            <param name="name">The property name.</param>
+            <returns>A <see cref="T:Newtonsoft.Json.Linq.JProperty"/> with the specified name or null.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JObject.PropertyValues">
+            <summary>
+            Gets an <see cref="T:Newtonsoft.Json.Linq.JEnumerable`1"/> of this object's property values.
+            </summary>
+            <returns>An <see cref="T:Newtonsoft.Json.Linq.JEnumerable`1"/> of this object's property values.</returns>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JObject.Item(System.Object)">
+            <summary>
+            Gets the <see cref="T:Newtonsoft.Json.Linq.JToken"/> with the specified key.
+            </summary>
+            <value>The <see cref="T:Newtonsoft.Json.Linq.JToken"/> with the specified key.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JObject.Item(System.String)">
+            <summary>
+            Gets or sets the <see cref="T:Newtonsoft.Json.Linq.JToken"/> with the specified property name.
+            </summary>
+            <value></value>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JObject.Load(Newtonsoft.Json.JsonReader)">
+            <summary>
+            Loads an <see cref="T:Newtonsoft.Json.Linq.JObject"/> from a <see cref="T:Newtonsoft.Json.JsonReader"/>. 
+            </summary>
+            <param name="reader">A <see cref="T:Newtonsoft.Json.JsonReader"/> that will be read for the content of the <see cref="T:Newtonsoft.Json.Linq.JObject"/>.</param>
+            <returns>A <see cref="T:Newtonsoft.Json.Linq.JObject"/> that contains the JSON that was read from the specified <see cref="T:Newtonsoft.Json.JsonReader"/>.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JObject.Load(Newtonsoft.Json.JsonReader,Newtonsoft.Json.Linq.JsonLoadSettings)">
+            <summary>
+            Loads an <see cref="T:Newtonsoft.Json.Linq.JObject"/> from a <see cref="T:Newtonsoft.Json.JsonReader"/>. 
+            </summary>
+            <param name="reader">A <see cref="T:Newtonsoft.Json.JsonReader"/> that will be read for the content of the <see cref="T:Newtonsoft.Json.Linq.JObject"/>.</param>
+            <param name="settings">The <see cref="T:Newtonsoft.Json.Linq.JsonLoadSettings"/> used to load the JSON.
+            If this is null, default load settings will be used.</param>
+            <returns>A <see cref="T:Newtonsoft.Json.Linq.JObject"/> that contains the JSON that was read from the specified <see cref="T:Newtonsoft.Json.JsonReader"/>.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JObject.Parse(System.String)">
+            <summary>
+            Load a <see cref="T:Newtonsoft.Json.Linq.JObject"/> from a string that contains JSON.
+            </summary>
+            <param name="json">A <see cref="T:System.String"/> that contains JSON.</param>
+            <returns>A <see cref="T:Newtonsoft.Json.Linq.JObject"/> populated from the string that contains JSON.</returns>
+            <example>
+              <code lang="cs" source="..\Src\Newtonsoft.Json.Tests\Documentation\LinqToJsonTests.cs" region="LinqToJsonCreateParse" title="Parsing a JSON Object from Text" />
+            </example>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JObject.Parse(System.String,Newtonsoft.Json.Linq.JsonLoadSettings)">
+            <summary>
+            Load a <see cref="T:Newtonsoft.Json.Linq.JObject"/> from a string that contains JSON.
+            </summary>
+            <param name="json">A <see cref="T:System.String"/> that contains JSON.</param>
+            <param name="settings">The <see cref="T:Newtonsoft.Json.Linq.JsonLoadSettings"/> used to load the JSON.
+            If this is null, default load settings will be used.</param>
+            <returns>A <see cref="T:Newtonsoft.Json.Linq.JObject"/> populated from the string that contains JSON.</returns>
+            <example>
+              <code lang="cs" source="..\Src\Newtonsoft.Json.Tests\Documentation\LinqToJsonTests.cs" region="LinqToJsonCreateParse" title="Parsing a JSON Object from Text" />
+            </example>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JObject.FromObject(System.Object)">
+            <summary>
+            Creates a <see cref="T:Newtonsoft.Json.Linq.JObject"/> from an object.
+            </summary>
+            <param name="o">The object that will be used to create <see cref="T:Newtonsoft.Json.Linq.JObject"/>.</param>
+            <returns>A <see cref="T:Newtonsoft.Json.Linq.JObject"/> with the values of the specified object</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JObject.FromObject(System.Object,Newtonsoft.Json.JsonSerializer)">
+            <summary>
+            Creates a <see cref="T:Newtonsoft.Json.Linq.JObject"/> from an object.
+            </summary>
+            <param name="o">The object that will be used to create <see cref="T:Newtonsoft.Json.Linq.JObject"/>.</param>
+            <param name="jsonSerializer">The <see cref="T:Newtonsoft.Json.JsonSerializer"/> that will be used to read the object.</param>
+            <returns>A <see cref="T:Newtonsoft.Json.Linq.JObject"/> with the values of the specified object</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JObject.WriteTo(Newtonsoft.Json.JsonWriter,Newtonsoft.Json.JsonConverter[])">
+            <summary>
+            Writes this token to a <see cref="T:Newtonsoft.Json.JsonWriter"/>.
+            </summary>
+            <param name="writer">A <see cref="T:Newtonsoft.Json.JsonWriter"/> into which this method will write.</param>
+            <param name="converters">A collection of <see cref="T:Newtonsoft.Json.JsonConverter"/> which will be used when writing the token.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JObject.GetValue(System.String)">
+            <summary>
+            Gets the <see cref="T:Newtonsoft.Json.Linq.JToken"/> with the specified property name.
+            </summary>
+            <param name="propertyName">Name of the property.</param>
+            <returns>The <see cref="T:Newtonsoft.Json.Linq.JToken"/> with the specified property name.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JObject.GetValue(System.String,System.StringComparison)">
+            <summary>
+            Gets the <see cref="T:Newtonsoft.Json.Linq.JToken"/> with the specified property name.
+            The exact property name will be searched for first and if no matching property is found then
+            the <see cref="T:System.StringComparison"/> will be used to match a property.
+            </summary>
+            <param name="propertyName">Name of the property.</param>
+            <param name="comparison">One of the enumeration values that specifies how the strings will be compared.</param>
+            <returns>The <see cref="T:Newtonsoft.Json.Linq.JToken"/> with the specified property name.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JObject.TryGetValue(System.String,System.StringComparison,Newtonsoft.Json.Linq.JToken@)">
+            <summary>
+            Tries to get the <see cref="T:Newtonsoft.Json.Linq.JToken"/> with the specified property name.
+            The exact property name will be searched for first and if no matching property is found then
+            the <see cref="T:System.StringComparison"/> will be used to match a property.
+            </summary>
+            <param name="propertyName">Name of the property.</param>
+            <param name="value">The value.</param>
+            <param name="comparison">One of the enumeration values that specifies how the strings will be compared.</param>
+            <returns>true if a value was successfully retrieved; otherwise, false.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JObject.Add(System.String,Newtonsoft.Json.Linq.JToken)">
+            <summary>
+            Adds the specified property name.
+            </summary>
+            <param name="propertyName">Name of the property.</param>
+            <param name="value">The value.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JObject.Remove(System.String)">
+            <summary>
+            Removes the property with the specified name.
+            </summary>
+            <param name="propertyName">Name of the property.</param>
+            <returns>true if item was successfully removed; otherwise, false.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JObject.TryGetValue(System.String,Newtonsoft.Json.Linq.JToken@)">
+            <summary>
+            Tries the get value.
+            </summary>
+            <param name="propertyName">Name of the property.</param>
+            <param name="value">The value.</param>
+            <returns>true if a value was successfully retrieved; otherwise, false.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JObject.GetEnumerator">
+            <summary>
+            Returns an enumerator that iterates through the collection.
+            </summary>
+            <returns>
+            A <see cref="T:System.Collections.Generic.IEnumerator`1"/> that can be used to iterate through the collection.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JObject.OnPropertyChanged(System.String)">
+            <summary>
+            Raises the <see cref="E:Newtonsoft.Json.Linq.JObject.PropertyChanged"/> event with the provided arguments.
+            </summary>
+            <param name="propertyName">Name of the property.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JObject.GetMetaObject(System.Linq.Expressions.Expression)">
+            <summary>
+            Returns the <see cref="T:System.Dynamic.DynamicMetaObject"/> responsible for binding operations performed on this object.
+            </summary>
+            <param name="parameter">The expression tree representation of the runtime value.</param>
+            <returns>
+            The <see cref="T:System.Dynamic.DynamicMetaObject"/> to bind this object.
+            </returns>
+        </member>
+        <member name="T:Newtonsoft.Json.Linq.JProperty">
+            <summary>
+            Represents a JSON property.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JProperty.ChildrenTokens">
+            <summary>
+            Gets the container's children tokens.
+            </summary>
+            <value>The container's children tokens.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JProperty.Name">
+            <summary>
+            Gets the property name.
+            </summary>
+            <value>The property name.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JProperty.Value">
+            <summary>
+            Gets or sets the property value.
+            </summary>
+            <value>The property value.</value>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JProperty.#ctor(Newtonsoft.Json.Linq.JProperty)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Linq.JProperty"/> class from another <see cref="T:Newtonsoft.Json.Linq.JProperty"/> object.
+            </summary>
+            <param name="other">A <see cref="T:Newtonsoft.Json.Linq.JProperty"/> object to copy from.</param>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JProperty.Type">
+            <summary>
+            Gets the node type for this <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <value>The type.</value>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JProperty.#ctor(System.String,System.Object[])">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Linq.JProperty"/> class.
+            </summary>
+            <param name="name">The property name.</param>
+            <param name="content">The property content.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JProperty.#ctor(System.String,System.Object)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Linq.JProperty"/> class.
+            </summary>
+            <param name="name">The property name.</param>
+            <param name="content">The property content.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JProperty.WriteTo(Newtonsoft.Json.JsonWriter,Newtonsoft.Json.JsonConverter[])">
+            <summary>
+            Writes this token to a <see cref="T:Newtonsoft.Json.JsonWriter"/>.
+            </summary>
+            <param name="writer">A <see cref="T:Newtonsoft.Json.JsonWriter"/> into which this method will write.</param>
+            <param name="converters">A collection of <see cref="T:Newtonsoft.Json.JsonConverter"/> which will be used when writing the token.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JProperty.Load(Newtonsoft.Json.JsonReader)">
+            <summary>
+            Loads an <see cref="T:Newtonsoft.Json.Linq.JProperty"/> from a <see cref="T:Newtonsoft.Json.JsonReader"/>. 
+            </summary>
+            <param name="reader">A <see cref="T:Newtonsoft.Json.JsonReader"/> that will be read for the content of the <see cref="T:Newtonsoft.Json.Linq.JProperty"/>.</param>
+            <returns>A <see cref="T:Newtonsoft.Json.Linq.JProperty"/> that contains the JSON that was read from the specified <see cref="T:Newtonsoft.Json.JsonReader"/>.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JProperty.Load(Newtonsoft.Json.JsonReader,Newtonsoft.Json.Linq.JsonLoadSettings)">
+            <summary>
+            Loads an <see cref="T:Newtonsoft.Json.Linq.JProperty"/> from a <see cref="T:Newtonsoft.Json.JsonReader"/>. 
+            </summary>
+            <param name="reader">A <see cref="T:Newtonsoft.Json.JsonReader"/> that will be read for the content of the <see cref="T:Newtonsoft.Json.Linq.JProperty"/>.</param>
+            <param name="settings">The <see cref="T:Newtonsoft.Json.Linq.JsonLoadSettings"/> used to load the JSON.
+            If this is null, default load settings will be used.</param>
+            <returns>A <see cref="T:Newtonsoft.Json.Linq.JProperty"/> that contains the JSON that was read from the specified <see cref="T:Newtonsoft.Json.JsonReader"/>.</returns>
+        </member>
+        <member name="T:Newtonsoft.Json.Linq.JRaw">
+            <summary>
+            Represents a raw JSON string.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JRaw.#ctor(Newtonsoft.Json.Linq.JRaw)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Linq.JRaw"/> class from another <see cref="T:Newtonsoft.Json.Linq.JRaw"/> object.
+            </summary>
+            <param name="other">A <see cref="T:Newtonsoft.Json.Linq.JRaw"/> object to copy from.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JRaw.#ctor(System.Object)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Linq.JRaw"/> class.
+            </summary>
+            <param name="rawJson">The raw json.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JRaw.Create(Newtonsoft.Json.JsonReader)">
+            <summary>
+            Creates an instance of <see cref="T:Newtonsoft.Json.Linq.JRaw"/> with the content of the reader's current token.
+            </summary>
+            <param name="reader">The reader.</param>
+            <returns>An instance of <see cref="T:Newtonsoft.Json.Linq.JRaw"/> with the content of the reader's current token.</returns>
+        </member>
+        <member name="T:Newtonsoft.Json.Linq.JsonLoadSettings">
+            <summary>
+            Specifies the settings used when loading JSON.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JsonLoadSettings.CommentHandling">
+            <summary>
+            Gets or sets how JSON comments are handled when loading JSON.
+            </summary>
+            <value>The JSON comment handling.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JsonLoadSettings.LineInfoHandling">
+            <summary>
+            Gets or sets how JSON line info is handled when loading JSON.
+            </summary>
+            <value>The JSON line info handling.</value>
+        </member>
+        <member name="T:Newtonsoft.Json.Linq.JsonMergeSettings">
+            <summary>
+            Specifies the settings used when merging JSON.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JsonMergeSettings.MergeArrayHandling">
+            <summary>
+            Gets or sets the method used when merging JSON arrays.
+            </summary>
+            <value>The method used when merging JSON arrays.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JsonMergeSettings.MergeNullValueHandling">
+            <summary>
+            Gets or sets how how null value properties are merged.
+            </summary>
+            <value>How null value properties are merged.</value>
+        </member>
+        <member name="T:Newtonsoft.Json.Linq.JToken">
+            <summary>
+            Represents an abstract JSON token.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JToken.EqualityComparer">
+            <summary>
+            Gets a comparer that can compare two tokens for value equality.
+            </summary>
+            <value>A <see cref="T:Newtonsoft.Json.Linq.JTokenEqualityComparer"/> that can compare two nodes for value equality.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JToken.Parent">
+            <summary>
+            Gets or sets the parent.
+            </summary>
+            <value>The parent.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JToken.Root">
+            <summary>
+            Gets the root <see cref="T:Newtonsoft.Json.Linq.JToken"/> of this <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <value>The root <see cref="T:Newtonsoft.Json.Linq.JToken"/> of this <see cref="T:Newtonsoft.Json.Linq.JToken"/>.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JToken.Type">
+            <summary>
+            Gets the node type for this <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <value>The type.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JToken.HasValues">
+            <summary>
+            Gets a value indicating whether this token has child tokens.
+            </summary>
+            <value>
+            	<c>true</c> if this token has child values; otherwise, <c>false</c>.
+            </value>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.DeepEquals(Newtonsoft.Json.Linq.JToken,Newtonsoft.Json.Linq.JToken)">
+            <summary>
+            Compares the values of two tokens, including the values of all descendant tokens.
+            </summary>
+            <param name="t1">The first <see cref="T:Newtonsoft.Json.Linq.JToken"/> to compare.</param>
+            <param name="t2">The second <see cref="T:Newtonsoft.Json.Linq.JToken"/> to compare.</param>
+            <returns>true if the tokens are equal; otherwise false.</returns>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JToken.Next">
+            <summary>
+            Gets the next sibling token of this node.
+            </summary>
+            <value>The <see cref="T:Newtonsoft.Json.Linq.JToken"/> that contains the next sibling token.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JToken.Previous">
+            <summary>
+            Gets the previous sibling token of this node.
+            </summary>
+            <value>The <see cref="T:Newtonsoft.Json.Linq.JToken"/> that contains the previous sibling token.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JToken.Path">
+            <summary>
+            Gets the path of the JSON token. 
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.AddAfterSelf(System.Object)">
+            <summary>
+            Adds the specified content immediately after this token.
+            </summary>
+            <param name="content">A content object that contains simple content or a collection of content objects to be added after this token.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.AddBeforeSelf(System.Object)">
+            <summary>
+            Adds the specified content immediately before this token.
+            </summary>
+            <param name="content">A content object that contains simple content or a collection of content objects to be added before this token.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.Ancestors">
+            <summary>
+            Returns a collection of the ancestor tokens of this token.
+            </summary>
+            <returns>A collection of the ancestor tokens of this token.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.AncestorsAndSelf">
+            <summary>
+            Returns a collection of tokens that contain this token, and the ancestors of this token.
+            </summary>
+            <returns>A collection of tokens that contain this token, and the ancestors of this token.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.AfterSelf">
+            <summary>
+            Returns a collection of the sibling tokens after this token, in document order.
+            </summary>
+            <returns>A collection of the sibling tokens after this tokens, in document order.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.BeforeSelf">
+            <summary>
+            Returns a collection of the sibling tokens before this token, in document order.
+            </summary>
+            <returns>A collection of the sibling tokens before this token, in document order.</returns>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JToken.Item(System.Object)">
+            <summary>
+            Gets the <see cref="T:Newtonsoft.Json.Linq.JToken"/> with the specified key.
+            </summary>
+            <value>The <see cref="T:Newtonsoft.Json.Linq.JToken"/> with the specified key.</value>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.Value``1(System.Object)">
+            <summary>
+            Gets the <see cref="T:Newtonsoft.Json.Linq.JToken"/> with the specified key converted to the specified type.
+            </summary>
+            <typeparam name="T">The type to convert the token to.</typeparam>
+            <param name="key">The token key.</param>
+            <returns>The converted token value.</returns>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JToken.First">
+            <summary>
+            Get the first child token of this token.
+            </summary>
+            <value>A <see cref="T:Newtonsoft.Json.Linq.JToken"/> containing the first child token of the <see cref="T:Newtonsoft.Json.Linq.JToken"/>.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JToken.Last">
+            <summary>
+            Get the last child token of this token.
+            </summary>
+            <value>A <see cref="T:Newtonsoft.Json.Linq.JToken"/> containing the last child token of the <see cref="T:Newtonsoft.Json.Linq.JToken"/>.</value>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.Children">
+            <summary>
+            Returns a collection of the child tokens of this token, in document order.
+            </summary>
+            <returns>An <see cref="T:System.Collections.Generic.IEnumerable`1"/> of <see cref="T:Newtonsoft.Json.Linq.JToken"/> containing the child tokens of this <see cref="T:Newtonsoft.Json.Linq.JToken"/>, in document order.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.Children``1">
+            <summary>
+            Returns a collection of the child tokens of this token, in document order, filtered by the specified type.
+            </summary>
+            <typeparam name="T">The type to filter the child tokens on.</typeparam>
+            <returns>A <see cref="T:Newtonsoft.Json.Linq.JEnumerable`1"/> containing the child tokens of this <see cref="T:Newtonsoft.Json.Linq.JToken"/>, in document order.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.Values``1">
+            <summary>
+            Returns a collection of the child values of this token, in document order.
+            </summary>
+            <typeparam name="T">The type to convert the values to.</typeparam>
+            <returns>A <see cref="T:System.Collections.Generic.IEnumerable`1"/> containing the child values of this <see cref="T:Newtonsoft.Json.Linq.JToken"/>, in document order.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.Remove">
+            <summary>
+            Removes this token from its parent.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.Replace(Newtonsoft.Json.Linq.JToken)">
+            <summary>
+            Replaces this token with the specified token.
+            </summary>
+            <param name="value">The value.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.WriteTo(Newtonsoft.Json.JsonWriter,Newtonsoft.Json.JsonConverter[])">
+            <summary>
+            Writes this token to a <see cref="T:Newtonsoft.Json.JsonWriter"/>.
+            </summary>
+            <param name="writer">A <see cref="T:Newtonsoft.Json.JsonWriter"/> into which this method will write.</param>
+            <param name="converters">A collection of <see cref="T:Newtonsoft.Json.JsonConverter"/> which will be used when writing the token.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.ToString">
+            <summary>
+            Returns the indented JSON for this token.
+            </summary>
+            <returns>
+            The indented JSON for this token.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.ToString(Newtonsoft.Json.Formatting,Newtonsoft.Json.JsonConverter[])">
+            <summary>
+            Returns the JSON for this token using the given formatting and converters.
+            </summary>
+            <param name="formatting">Indicates how the output is formatted.</param>
+            <param name="converters">A collection of <see cref="T:Newtonsoft.Json.JsonConverter"/> which will be used when writing the token.</param>
+            <returns>The JSON for this token using the given formatting and converters.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Explicit(Newtonsoft.Json.Linq.JToken)~System.Boolean">
+            <summary>
+            Performs an explicit conversion from <see cref="T:Newtonsoft.Json.Linq.JToken"/> to <see cref="T:System.Boolean"/>.
+            </summary>
+            <param name="value">The value.</param>
+            <returns>The result of the conversion.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Explicit(Newtonsoft.Json.Linq.JToken)~System.DateTimeOffset">
+            <summary>
+            Performs an explicit conversion from <see cref="T:Newtonsoft.Json.Linq.JToken"/> to <see cref="T:System.DateTimeOffset"/>.
+            </summary>
+            <param name="value">The value.</param>
+            <returns>The result of the conversion.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Explicit(Newtonsoft.Json.Linq.JToken)~System.Nullable{System.Boolean}">
+            <summary>
+            Performs an explicit conversion from <see cref="T:Newtonsoft.Json.Linq.JToken"/> to <see cref="T:System.Nullable`1"/>.
+            </summary>
+            <param name="value">The value.</param>
+            <returns>The result of the conversion.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Explicit(Newtonsoft.Json.Linq.JToken)~System.Int64">
+            <summary>
+            Performs an explicit conversion from <see cref="T:Newtonsoft.Json.Linq.JToken"/> to <see cref="T:System.Int64"/>.
+            </summary>
+            <param name="value">The value.</param>
+            <returns>The result of the conversion.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Explicit(Newtonsoft.Json.Linq.JToken)~System.Nullable{System.DateTime}">
+            <summary>
+            Performs an explicit conversion from <see cref="T:Newtonsoft.Json.Linq.JToken"/> to <see cref="T:System.Nullable`1"/>.
+            </summary>
+            <param name="value">The value.</param>
+            <returns>The result of the conversion.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Explicit(Newtonsoft.Json.Linq.JToken)~System.Nullable{System.DateTimeOffset}">
+            <summary>
+            Performs an explicit conversion from <see cref="T:Newtonsoft.Json.Linq.JToken"/> to <see cref="T:System.Nullable`1"/>.
+            </summary>
+            <param name="value">The value.</param>
+            <returns>The result of the conversion.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Explicit(Newtonsoft.Json.Linq.JToken)~System.Nullable{System.Decimal}">
+            <summary>
+            Performs an explicit conversion from <see cref="T:Newtonsoft.Json.Linq.JToken"/> to <see cref="T:System.Nullable`1"/>.
+            </summary>
+            <param name="value">The value.</param>
+            <returns>The result of the conversion.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Explicit(Newtonsoft.Json.Linq.JToken)~System.Nullable{System.Double}">
+            <summary>
+            Performs an explicit conversion from <see cref="T:Newtonsoft.Json.Linq.JToken"/> to <see cref="T:System.Nullable`1"/>.
+            </summary>
+            <param name="value">The value.</param>
+            <returns>The result of the conversion.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Explicit(Newtonsoft.Json.Linq.JToken)~System.Nullable{System.Char}">
+            <summary>
+            Performs an explicit conversion from <see cref="T:Newtonsoft.Json.Linq.JToken"/> to <see cref="T:System.Nullable`1"/>.
+            </summary>
+            <param name="value">The value.</param>
+            <returns>The result of the conversion.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Explicit(Newtonsoft.Json.Linq.JToken)~System.Int32">
+            <summary>
+            Performs an explicit conversion from <see cref="T:Newtonsoft.Json.Linq.JToken"/> to <see cref="T:System.Int32"/>.
+            </summary>
+            <param name="value">The value.</param>
+            <returns>The result of the conversion.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Explicit(Newtonsoft.Json.Linq.JToken)~System.Int16">
+            <summary>
+            Performs an explicit conversion from <see cref="T:Newtonsoft.Json.Linq.JToken"/> to <see cref="T:System.Int16"/>.
+            </summary>
+            <param name="value">The value.</param>
+            <returns>The result of the conversion.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Explicit(Newtonsoft.Json.Linq.JToken)~System.UInt16">
+            <summary>
+            Performs an explicit conversion from <see cref="T:Newtonsoft.Json.Linq.JToken"/> to <see cref="T:System.UInt16"/>.
+            </summary>
+            <param name="value">The value.</param>
+            <returns>The result of the conversion.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Explicit(Newtonsoft.Json.Linq.JToken)~System.Char">
+            <summary>
+            Performs an explicit conversion from <see cref="T:Newtonsoft.Json.Linq.JToken"/> to <see cref="T:System.Char"/>.
+            </summary>
+            <param name="value">The value.</param>
+            <returns>The result of the conversion.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Explicit(Newtonsoft.Json.Linq.JToken)~System.Byte">
+            <summary>
+            Performs an explicit conversion from <see cref="T:Newtonsoft.Json.Linq.JToken"/> to <see cref="T:System.Byte"/>.
+            </summary>
+            <param name="value">The value.</param>
+            <returns>The result of the conversion.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Explicit(Newtonsoft.Json.Linq.JToken)~System.SByte">
+            <summary>
+            Performs an explicit conversion from <see cref="T:Newtonsoft.Json.Linq.JToken"/> to <see cref="T:System.SByte"/>.
+            </summary>
+            <param name="value">The value.</param>
+            <returns>The result of the conversion.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Explicit(Newtonsoft.Json.Linq.JToken)~System.Nullable{System.Int32}">
+            <summary>
+            Performs an explicit conversion from <see cref="T:Newtonsoft.Json.Linq.JToken"/> to <see cref="T:System.Nullable`1"/>.
+            </summary>
+            <param name="value">The value.</param>
+            <returns>The result of the conversion.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Explicit(Newtonsoft.Json.Linq.JToken)~System.Nullable{System.Int16}">
+            <summary>
+            Performs an explicit conversion from <see cref="T:Newtonsoft.Json.Linq.JToken"/> to <see cref="T:System.Nullable`1"/>.
+            </summary>
+            <param name="value">The value.</param>
+            <returns>The result of the conversion.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Explicit(Newtonsoft.Json.Linq.JToken)~System.Nullable{System.UInt16}">
+            <summary>
+            Performs an explicit conversion from <see cref="T:Newtonsoft.Json.Linq.JToken"/> to <see cref="T:System.Nullable`1"/>.
+            </summary>
+            <param name="value">The value.</param>
+            <returns>The result of the conversion.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Explicit(Newtonsoft.Json.Linq.JToken)~System.Nullable{System.Byte}">
+            <summary>
+            Performs an explicit conversion from <see cref="T:Newtonsoft.Json.Linq.JToken"/> to <see cref="T:System.Nullable`1"/>.
+            </summary>
+            <param name="value">The value.</param>
+            <returns>The result of the conversion.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Explicit(Newtonsoft.Json.Linq.JToken)~System.Nullable{System.SByte}">
+            <summary>
+            Performs an explicit conversion from <see cref="T:Newtonsoft.Json.Linq.JToken"/> to <see cref="T:System.Nullable`1"/>.
+            </summary>
+            <param name="value">The value.</param>
+            <returns>The result of the conversion.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Explicit(Newtonsoft.Json.Linq.JToken)~System.DateTime">
+            <summary>
+            Performs an explicit conversion from <see cref="T:Newtonsoft.Json.Linq.JToken"/> to <see cref="T:System.DateTime"/>.
+            </summary>
+            <param name="value">The value.</param>
+            <returns>The result of the conversion.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Explicit(Newtonsoft.Json.Linq.JToken)~System.Nullable{System.Int64}">
+            <summary>
+            Performs an explicit conversion from <see cref="T:Newtonsoft.Json.Linq.JToken"/> to <see cref="T:System.Nullable`1"/>.
+            </summary>
+            <param name="value">The value.</param>
+            <returns>The result of the conversion.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Explicit(Newtonsoft.Json.Linq.JToken)~System.Nullable{System.Single}">
+            <summary>
+            Performs an explicit conversion from <see cref="T:Newtonsoft.Json.Linq.JToken"/> to <see cref="T:System.Nullable`1"/>.
+            </summary>
+            <param name="value">The value.</param>
+            <returns>The result of the conversion.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Explicit(Newtonsoft.Json.Linq.JToken)~System.Decimal">
+            <summary>
+            Performs an explicit conversion from <see cref="T:Newtonsoft.Json.Linq.JToken"/> to <see cref="T:System.Decimal"/>.
+            </summary>
+            <param name="value">The value.</param>
+            <returns>The result of the conversion.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Explicit(Newtonsoft.Json.Linq.JToken)~System.Nullable{System.UInt32}">
+            <summary>
+            Performs an explicit conversion from <see cref="T:Newtonsoft.Json.Linq.JToken"/> to <see cref="T:System.Nullable`1"/>.
+            </summary>
+            <param name="value">The value.</param>
+            <returns>The result of the conversion.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Explicit(Newtonsoft.Json.Linq.JToken)~System.Nullable{System.UInt64}">
+            <summary>
+            Performs an explicit conversion from <see cref="T:Newtonsoft.Json.Linq.JToken"/> to <see cref="T:System.Nullable`1"/>.
+            </summary>
+            <param name="value">The value.</param>
+            <returns>The result of the conversion.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Explicit(Newtonsoft.Json.Linq.JToken)~System.Double">
+            <summary>
+            Performs an explicit conversion from <see cref="T:Newtonsoft.Json.Linq.JToken"/> to <see cref="T:System.Double"/>.
+            </summary>
+            <param name="value">The value.</param>
+            <returns>The result of the conversion.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Explicit(Newtonsoft.Json.Linq.JToken)~System.Single">
+            <summary>
+            Performs an explicit conversion from <see cref="T:Newtonsoft.Json.Linq.JToken"/> to <see cref="T:System.Single"/>.
+            </summary>
+            <param name="value">The value.</param>
+            <returns>The result of the conversion.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Explicit(Newtonsoft.Json.Linq.JToken)~System.String">
+            <summary>
+            Performs an explicit conversion from <see cref="T:Newtonsoft.Json.Linq.JToken"/> to <see cref="T:System.String"/>.
+            </summary>
+            <param name="value">The value.</param>
+            <returns>The result of the conversion.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Explicit(Newtonsoft.Json.Linq.JToken)~System.UInt32">
+            <summary>
+            Performs an explicit conversion from <see cref="T:Newtonsoft.Json.Linq.JToken"/> to <see cref="T:System.UInt32"/>.
+            </summary>
+            <param name="value">The value.</param>
+            <returns>The result of the conversion.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Explicit(Newtonsoft.Json.Linq.JToken)~System.UInt64">
+            <summary>
+            Performs an explicit conversion from <see cref="T:Newtonsoft.Json.Linq.JToken"/> to <see cref="T:System.UInt64"/>.
+            </summary>
+            <param name="value">The value.</param>
+            <returns>The result of the conversion.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Explicit(Newtonsoft.Json.Linq.JToken)~System.Byte[]">
+            <summary>
+            Performs an explicit conversion from <see cref="T:Newtonsoft.Json.Linq.JToken"/> to <see cref="T:System.Byte"/>[].
+            </summary>
+            <param name="value">The value.</param>
+            <returns>The result of the conversion.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Explicit(Newtonsoft.Json.Linq.JToken)~System.Guid">
+            <summary>
+            Performs an explicit conversion from <see cref="T:Newtonsoft.Json.Linq.JToken"/> to <see cref="T:System.Guid"/>.
+            </summary>
+            <param name="value">The value.</param>
+            <returns>The result of the conversion.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Explicit(Newtonsoft.Json.Linq.JToken)~System.Nullable{System.Guid}">
+            <summary>
+            Performs an explicit conversion from <see cref="T:Newtonsoft.Json.Linq.JToken"/> to <see cref="T:System.Guid"/>.
+            </summary>
+            <param name="value">The value.</param>
+            <returns>The result of the conversion.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Explicit(Newtonsoft.Json.Linq.JToken)~System.TimeSpan">
+            <summary>
+            Performs an explicit conversion from <see cref="T:Newtonsoft.Json.Linq.JToken"/> to <see cref="T:System.TimeSpan"/>.
+            </summary>
+            <param name="value">The value.</param>
+            <returns>The result of the conversion.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Explicit(Newtonsoft.Json.Linq.JToken)~System.Nullable{System.TimeSpan}">
+            <summary>
+            Performs an explicit conversion from <see cref="T:Newtonsoft.Json.Linq.JToken"/> to <see cref="T:System.TimeSpan"/>.
+            </summary>
+            <param name="value">The value.</param>
+            <returns>The result of the conversion.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Explicit(Newtonsoft.Json.Linq.JToken)~System.Uri">
+            <summary>
+            Performs an explicit conversion from <see cref="T:Newtonsoft.Json.Linq.JToken"/> to <see cref="T:System.Uri"/>.
+            </summary>
+            <param name="value">The value.</param>
+            <returns>The result of the conversion.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Implicit(System.Boolean)~Newtonsoft.Json.Linq.JToken">
+            <summary>
+            Performs an implicit conversion from <see cref="T:System.Boolean"/> to <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <param name="value">The value to create a <see cref="T:Newtonsoft.Json.Linq.JValue"/> from.</param>
+            <returns>The <see cref="T:Newtonsoft.Json.Linq.JValue"/> initialized with the specified value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Implicit(System.DateTimeOffset)~Newtonsoft.Json.Linq.JToken">
+            <summary>
+            Performs an implicit conversion from <see cref="T:System.DateTimeOffset"/> to <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <param name="value">The value to create a <see cref="T:Newtonsoft.Json.Linq.JValue"/> from.</param>
+            <returns>The <see cref="T:Newtonsoft.Json.Linq.JValue"/> initialized with the specified value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Implicit(System.Byte)~Newtonsoft.Json.Linq.JToken">
+            <summary>
+            Performs an implicit conversion from <see cref="T:System.Byte"/> to <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <param name="value">The value to create a <see cref="T:Newtonsoft.Json.Linq.JValue"/> from.</param>
+            <returns>The <see cref="T:Newtonsoft.Json.Linq.JValue"/> initialized with the specified value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Implicit(System.Nullable{System.Byte})~Newtonsoft.Json.Linq.JToken">
+            <summary>
+            Performs an implicit conversion from <see cref="T:System.Nullable`1"/> to <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <param name="value">The value to create a <see cref="T:Newtonsoft.Json.Linq.JValue"/> from.</param>
+            <returns>The <see cref="T:Newtonsoft.Json.Linq.JValue"/> initialized with the specified value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Implicit(System.SByte)~Newtonsoft.Json.Linq.JToken">
+            <summary>
+            Performs an implicit conversion from <see cref="T:System.SByte"/> to <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <param name="value">The value to create a <see cref="T:Newtonsoft.Json.Linq.JValue"/> from.</param>
+            <returns>The <see cref="T:Newtonsoft.Json.Linq.JValue"/> initialized with the specified value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Implicit(System.Nullable{System.SByte})~Newtonsoft.Json.Linq.JToken">
+            <summary>
+            Performs an implicit conversion from <see cref="T:System.Nullable`1"/> to <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <param name="value">The value to create a <see cref="T:Newtonsoft.Json.Linq.JValue"/> from.</param>
+            <returns>The <see cref="T:Newtonsoft.Json.Linq.JValue"/> initialized with the specified value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Implicit(System.Nullable{System.Boolean})~Newtonsoft.Json.Linq.JToken">
+            <summary>
+            Performs an implicit conversion from <see cref="T:System.Nullable`1"/> to <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <param name="value">The value to create a <see cref="T:Newtonsoft.Json.Linq.JValue"/> from.</param>
+            <returns>The <see cref="T:Newtonsoft.Json.Linq.JValue"/> initialized with the specified value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Implicit(System.Int64)~Newtonsoft.Json.Linq.JToken">
+            <summary>
+            Performs an implicit conversion from <see cref="T:System.Nullable`1"/> to <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <param name="value">The value to create a <see cref="T:Newtonsoft.Json.Linq.JValue"/> from.</param>
+            <returns>The <see cref="T:Newtonsoft.Json.Linq.JValue"/> initialized with the specified value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Implicit(System.Nullable{System.DateTime})~Newtonsoft.Json.Linq.JToken">
+            <summary>
+            Performs an implicit conversion from <see cref="T:System.Nullable`1"/> to <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <param name="value">The value to create a <see cref="T:Newtonsoft.Json.Linq.JValue"/> from.</param>
+            <returns>The <see cref="T:Newtonsoft.Json.Linq.JValue"/> initialized with the specified value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Implicit(System.Nullable{System.DateTimeOffset})~Newtonsoft.Json.Linq.JToken">
+            <summary>
+            Performs an implicit conversion from <see cref="T:System.Nullable`1"/> to <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <param name="value">The value to create a <see cref="T:Newtonsoft.Json.Linq.JValue"/> from.</param>
+            <returns>The <see cref="T:Newtonsoft.Json.Linq.JValue"/> initialized with the specified value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Implicit(System.Nullable{System.Decimal})~Newtonsoft.Json.Linq.JToken">
+            <summary>
+            Performs an implicit conversion from <see cref="T:System.Nullable`1"/> to <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <param name="value">The value to create a <see cref="T:Newtonsoft.Json.Linq.JValue"/> from.</param>
+            <returns>The <see cref="T:Newtonsoft.Json.Linq.JValue"/> initialized with the specified value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Implicit(System.Nullable{System.Double})~Newtonsoft.Json.Linq.JToken">
+            <summary>
+            Performs an implicit conversion from <see cref="T:System.Nullable`1"/> to <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <param name="value">The value to create a <see cref="T:Newtonsoft.Json.Linq.JValue"/> from.</param>
+            <returns>The <see cref="T:Newtonsoft.Json.Linq.JValue"/> initialized with the specified value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Implicit(System.Int16)~Newtonsoft.Json.Linq.JToken">
+            <summary>
+            Performs an implicit conversion from <see cref="T:System.Int16"/> to <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <param name="value">The value to create a <see cref="T:Newtonsoft.Json.Linq.JValue"/> from.</param>
+            <returns>The <see cref="T:Newtonsoft.Json.Linq.JValue"/> initialized with the specified value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Implicit(System.UInt16)~Newtonsoft.Json.Linq.JToken">
+            <summary>
+            Performs an implicit conversion from <see cref="T:System.UInt16"/> to <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <param name="value">The value to create a <see cref="T:Newtonsoft.Json.Linq.JValue"/> from.</param>
+            <returns>The <see cref="T:Newtonsoft.Json.Linq.JValue"/> initialized with the specified value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Implicit(System.Int32)~Newtonsoft.Json.Linq.JToken">
+            <summary>
+            Performs an implicit conversion from <see cref="T:System.Int32"/> to <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <param name="value">The value to create a <see cref="T:Newtonsoft.Json.Linq.JValue"/> from.</param>
+            <returns>The <see cref="T:Newtonsoft.Json.Linq.JValue"/> initialized with the specified value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Implicit(System.Nullable{System.Int32})~Newtonsoft.Json.Linq.JToken">
+            <summary>
+            Performs an implicit conversion from <see cref="T:System.Nullable`1"/> to <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <param name="value">The value to create a <see cref="T:Newtonsoft.Json.Linq.JValue"/> from.</param>
+            <returns>The <see cref="T:Newtonsoft.Json.Linq.JValue"/> initialized with the specified value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Implicit(System.DateTime)~Newtonsoft.Json.Linq.JToken">
+            <summary>
+            Performs an implicit conversion from <see cref="T:System.DateTime"/> to <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <param name="value">The value to create a <see cref="T:Newtonsoft.Json.Linq.JValue"/> from.</param>
+            <returns>The <see cref="T:Newtonsoft.Json.Linq.JValue"/> initialized with the specified value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Implicit(System.Nullable{System.Int64})~Newtonsoft.Json.Linq.JToken">
+            <summary>
+            Performs an implicit conversion from <see cref="T:System.Nullable`1"/> to <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <param name="value">The value to create a <see cref="T:Newtonsoft.Json.Linq.JValue"/> from.</param>
+            <returns>The <see cref="T:Newtonsoft.Json.Linq.JValue"/> initialized with the specified value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Implicit(System.Nullable{System.Single})~Newtonsoft.Json.Linq.JToken">
+            <summary>
+            Performs an implicit conversion from <see cref="T:System.Nullable`1"/> to <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <param name="value">The value to create a <see cref="T:Newtonsoft.Json.Linq.JValue"/> from.</param>
+            <returns>The <see cref="T:Newtonsoft.Json.Linq.JValue"/> initialized with the specified value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Implicit(System.Decimal)~Newtonsoft.Json.Linq.JToken">
+            <summary>
+            Performs an implicit conversion from <see cref="T:System.Decimal"/> to <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <param name="value">The value to create a <see cref="T:Newtonsoft.Json.Linq.JValue"/> from.</param>
+            <returns>The <see cref="T:Newtonsoft.Json.Linq.JValue"/> initialized with the specified value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Implicit(System.Nullable{System.Int16})~Newtonsoft.Json.Linq.JToken">
+            <summary>
+            Performs an implicit conversion from <see cref="T:System.Nullable`1"/> to <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <param name="value">The value to create a <see cref="T:Newtonsoft.Json.Linq.JValue"/> from.</param>
+            <returns>The <see cref="T:Newtonsoft.Json.Linq.JValue"/> initialized with the specified value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Implicit(System.Nullable{System.UInt16})~Newtonsoft.Json.Linq.JToken">
+            <summary>
+            Performs an implicit conversion from <see cref="T:System.Nullable`1"/> to <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <param name="value">The value to create a <see cref="T:Newtonsoft.Json.Linq.JValue"/> from.</param>
+            <returns>The <see cref="T:Newtonsoft.Json.Linq.JValue"/> initialized with the specified value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Implicit(System.Nullable{System.UInt32})~Newtonsoft.Json.Linq.JToken">
+            <summary>
+            Performs an implicit conversion from <see cref="T:System.Nullable`1"/> to <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <param name="value">The value to create a <see cref="T:Newtonsoft.Json.Linq.JValue"/> from.</param>
+            <returns>The <see cref="T:Newtonsoft.Json.Linq.JValue"/> initialized with the specified value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Implicit(System.Nullable{System.UInt64})~Newtonsoft.Json.Linq.JToken">
+            <summary>
+            Performs an implicit conversion from <see cref="T:System.Nullable`1"/> to <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <param name="value">The value to create a <see cref="T:Newtonsoft.Json.Linq.JValue"/> from.</param>
+            <returns>The <see cref="T:Newtonsoft.Json.Linq.JValue"/> initialized with the specified value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Implicit(System.Double)~Newtonsoft.Json.Linq.JToken">
+            <summary>
+            Performs an implicit conversion from <see cref="T:System.Double"/> to <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <param name="value">The value to create a <see cref="T:Newtonsoft.Json.Linq.JValue"/> from.</param>
+            <returns>The <see cref="T:Newtonsoft.Json.Linq.JValue"/> initialized with the specified value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Implicit(System.Single)~Newtonsoft.Json.Linq.JToken">
+            <summary>
+            Performs an implicit conversion from <see cref="T:System.Single"/> to <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <param name="value">The value to create a <see cref="T:Newtonsoft.Json.Linq.JValue"/> from.</param>
+            <returns>The <see cref="T:Newtonsoft.Json.Linq.JValue"/> initialized with the specified value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Implicit(System.String)~Newtonsoft.Json.Linq.JToken">
+            <summary>
+            Performs an implicit conversion from <see cref="T:System.String"/> to <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <param name="value">The value to create a <see cref="T:Newtonsoft.Json.Linq.JValue"/> from.</param>
+            <returns>The <see cref="T:Newtonsoft.Json.Linq.JValue"/> initialized with the specified value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Implicit(System.UInt32)~Newtonsoft.Json.Linq.JToken">
+            <summary>
+            Performs an implicit conversion from <see cref="T:System.UInt32"/> to <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <param name="value">The value to create a <see cref="T:Newtonsoft.Json.Linq.JValue"/> from.</param>
+            <returns>The <see cref="T:Newtonsoft.Json.Linq.JValue"/> initialized with the specified value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Implicit(System.UInt64)~Newtonsoft.Json.Linq.JToken">
+            <summary>
+            Performs an implicit conversion from <see cref="T:System.UInt64"/> to <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <param name="value">The value to create a <see cref="T:Newtonsoft.Json.Linq.JValue"/> from.</param>
+            <returns>The <see cref="T:Newtonsoft.Json.Linq.JValue"/> initialized with the specified value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Implicit(System.Byte[])~Newtonsoft.Json.Linq.JToken">
+            <summary>
+            Performs an implicit conversion from <see cref="T:System.Byte"/>[] to <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <param name="value">The value to create a <see cref="T:Newtonsoft.Json.Linq.JValue"/> from.</param>
+            <returns>The <see cref="T:Newtonsoft.Json.Linq.JValue"/> initialized with the specified value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Implicit(System.Uri)~Newtonsoft.Json.Linq.JToken">
+            <summary>
+            Performs an implicit conversion from <see cref="T:System.Uri"/> to <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <param name="value">The value to create a <see cref="T:Newtonsoft.Json.Linq.JValue"/> from.</param>
+            <returns>The <see cref="T:Newtonsoft.Json.Linq.JValue"/> initialized with the specified value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Implicit(System.TimeSpan)~Newtonsoft.Json.Linq.JToken">
+            <summary>
+            Performs an implicit conversion from <see cref="T:System.TimeSpan"/> to <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <param name="value">The value to create a <see cref="T:Newtonsoft.Json.Linq.JValue"/> from.</param>
+            <returns>The <see cref="T:Newtonsoft.Json.Linq.JValue"/> initialized with the specified value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Implicit(System.Nullable{System.TimeSpan})~Newtonsoft.Json.Linq.JToken">
+            <summary>
+            Performs an implicit conversion from <see cref="T:System.Nullable`1"/> to <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <param name="value">The value to create a <see cref="T:Newtonsoft.Json.Linq.JValue"/> from.</param>
+            <returns>The <see cref="T:Newtonsoft.Json.Linq.JValue"/> initialized with the specified value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Implicit(System.Guid)~Newtonsoft.Json.Linq.JToken">
+            <summary>
+            Performs an implicit conversion from <see cref="T:System.Guid"/> to <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <param name="value">The value to create a <see cref="T:Newtonsoft.Json.Linq.JValue"/> from.</param>
+            <returns>The <see cref="T:Newtonsoft.Json.Linq.JValue"/> initialized with the specified value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.op_Implicit(System.Nullable{System.Guid})~Newtonsoft.Json.Linq.JToken">
+            <summary>
+            Performs an implicit conversion from <see cref="T:System.Nullable`1"/> to <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <param name="value">The value to create a <see cref="T:Newtonsoft.Json.Linq.JValue"/> from.</param>
+            <returns>The <see cref="T:Newtonsoft.Json.Linq.JValue"/> initialized with the specified value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.CreateReader">
+            <summary>
+            Creates an <see cref="T:Newtonsoft.Json.JsonReader"/> for this token.
+            </summary>
+            <returns>An <see cref="T:Newtonsoft.Json.JsonReader"/> that can be used to read this token and its descendants.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.FromObject(System.Object)">
+            <summary>
+            Creates a <see cref="T:Newtonsoft.Json.Linq.JToken"/> from an object.
+            </summary>
+            <param name="o">The object that will be used to create <see cref="T:Newtonsoft.Json.Linq.JToken"/>.</param>
+            <returns>A <see cref="T:Newtonsoft.Json.Linq.JToken"/> with the value of the specified object</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.FromObject(System.Object,Newtonsoft.Json.JsonSerializer)">
+            <summary>
+            Creates a <see cref="T:Newtonsoft.Json.Linq.JToken"/> from an object using the specified <see cref="T:Newtonsoft.Json.JsonSerializer"/>.
+            </summary>
+            <param name="o">The object that will be used to create <see cref="T:Newtonsoft.Json.Linq.JToken"/>.</param>
+            <param name="jsonSerializer">The <see cref="T:Newtonsoft.Json.JsonSerializer"/> that will be used when reading the object.</param>
+            <returns>A <see cref="T:Newtonsoft.Json.Linq.JToken"/> with the value of the specified object</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.ToObject``1">
+            <summary>
+            Creates the specified .NET type from the <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <typeparam name="T">The object type that the token will be deserialized to.</typeparam>
+            <returns>The new object created from the JSON value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.ToObject(System.Type)">
+            <summary>
+            Creates the specified .NET type from the <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <param name="objectType">The object type that the token will be deserialized to.</param>
+            <returns>The new object created from the JSON value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.ToObject``1(Newtonsoft.Json.JsonSerializer)">
+            <summary>
+            Creates the specified .NET type from the <see cref="T:Newtonsoft.Json.Linq.JToken"/> using the specified <see cref="T:Newtonsoft.Json.JsonSerializer"/>.
+            </summary>
+            <typeparam name="T">The object type that the token will be deserialized to.</typeparam>
+            <param name="jsonSerializer">The <see cref="T:Newtonsoft.Json.JsonSerializer"/> that will be used when creating the object.</param>
+            <returns>The new object created from the JSON value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.ToObject(System.Type,Newtonsoft.Json.JsonSerializer)">
+            <summary>
+            Creates the specified .NET type from the <see cref="T:Newtonsoft.Json.Linq.JToken"/> using the specified <see cref="T:Newtonsoft.Json.JsonSerializer"/>.
+            </summary>
+            <param name="objectType">The object type that the token will be deserialized to.</param>
+            <param name="jsonSerializer">The <see cref="T:Newtonsoft.Json.JsonSerializer"/> that will be used when creating the object.</param>
+            <returns>The new object created from the JSON value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.ReadFrom(Newtonsoft.Json.JsonReader)">
+            <summary>
+            Creates a <see cref="T:Newtonsoft.Json.Linq.JToken"/> from a <see cref="T:Newtonsoft.Json.JsonReader"/>.
+            </summary>
+            <param name="reader">An <see cref="T:Newtonsoft.Json.JsonReader"/> positioned at the token to read into this <see cref="T:Newtonsoft.Json.Linq.JToken"/>.</param>
+            <returns>
+            An <see cref="T:Newtonsoft.Json.Linq.JToken"/> that contains the token and its descendant tokens
+            that were read from the reader. The runtime type of the token is determined
+            by the token type of the first token encountered in the reader.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.ReadFrom(Newtonsoft.Json.JsonReader,Newtonsoft.Json.Linq.JsonLoadSettings)">
+            <summary>
+            Creates a <see cref="T:Newtonsoft.Json.Linq.JToken"/> from a <see cref="T:Newtonsoft.Json.JsonReader"/>.
+            </summary>
+            <param name="reader">An <see cref="T:Newtonsoft.Json.JsonReader"/> positioned at the token to read into this <see cref="T:Newtonsoft.Json.Linq.JToken"/>.</param>
+            <param name="settings">The <see cref="T:Newtonsoft.Json.Linq.JsonLoadSettings"/> used to load the JSON.
+            If this is null, default load settings will be used.</param>
+            <returns>
+            An <see cref="T:Newtonsoft.Json.Linq.JToken"/> that contains the token and its descendant tokens
+            that were read from the reader. The runtime type of the token is determined
+            by the token type of the first token encountered in the reader.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.Parse(System.String)">
+            <summary>
+            Load a <see cref="T:Newtonsoft.Json.Linq.JToken"/> from a string that contains JSON.
+            </summary>
+            <param name="json">A <see cref="T:System.String"/> that contains JSON.</param>
+            <returns>A <see cref="T:Newtonsoft.Json.Linq.JToken"/> populated from the string that contains JSON.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.Parse(System.String,Newtonsoft.Json.Linq.JsonLoadSettings)">
+            <summary>
+            Load a <see cref="T:Newtonsoft.Json.Linq.JToken"/> from a string that contains JSON.
+            </summary>
+            <param name="json">A <see cref="T:System.String"/> that contains JSON.</param>
+            <param name="settings">The <see cref="T:Newtonsoft.Json.Linq.JsonLoadSettings"/> used to load the JSON.
+            If this is null, default load settings will be used.</param>
+            <returns>A <see cref="T:Newtonsoft.Json.Linq.JToken"/> populated from the string that contains JSON.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.Load(Newtonsoft.Json.JsonReader,Newtonsoft.Json.Linq.JsonLoadSettings)">
+            <summary>
+            Creates a <see cref="T:Newtonsoft.Json.Linq.JToken"/> from a <see cref="T:Newtonsoft.Json.JsonReader"/>.
+            </summary>
+            <param name="reader">An <see cref="T:Newtonsoft.Json.JsonReader"/> positioned at the token to read into this <see cref="T:Newtonsoft.Json.Linq.JToken"/>.</param>
+            <param name="settings">The <see cref="T:Newtonsoft.Json.Linq.JsonLoadSettings"/> used to load the JSON.
+            If this is null, default load settings will be used.</param>
+            <returns>
+            An <see cref="T:Newtonsoft.Json.Linq.JToken"/> that contains the token and its descendant tokens
+            that were read from the reader. The runtime type of the token is determined
+            by the token type of the first token encountered in the reader.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.Load(Newtonsoft.Json.JsonReader)">
+            <summary>
+            Creates a <see cref="T:Newtonsoft.Json.Linq.JToken"/> from a <see cref="T:Newtonsoft.Json.JsonReader"/>.
+            </summary>
+            <param name="reader">An <see cref="T:Newtonsoft.Json.JsonReader"/> positioned at the token to read into this <see cref="T:Newtonsoft.Json.Linq.JToken"/>.</param>
+            <returns>
+            An <see cref="T:Newtonsoft.Json.Linq.JToken"/> that contains the token and its descendant tokens
+            that were read from the reader. The runtime type of the token is determined
+            by the token type of the first token encountered in the reader.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.SelectToken(System.String)">
+            <summary>
+            Selects a <see cref="T:Newtonsoft.Json.Linq.JToken"/> using a JPath expression. Selects the token that matches the object path.
+            </summary>
+            <param name="path">
+            A <see cref="T:System.String"/> that contains a JPath expression.
+            </param>
+            <returns>A <see cref="T:Newtonsoft.Json.Linq.JToken"/>, or null.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.SelectToken(System.String,System.Boolean)">
+            <summary>
+            Selects a <see cref="T:Newtonsoft.Json.Linq.JToken"/> using a JPath expression. Selects the token that matches the object path.
+            </summary>
+            <param name="path">
+            A <see cref="T:System.String"/> that contains a JPath expression.
+            </param>
+            <param name="errorWhenNoMatch">A flag to indicate whether an error should be thrown if no tokens are found when evaluating part of the expression.</param>
+            <returns>A <see cref="T:Newtonsoft.Json.Linq.JToken"/>.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.SelectTokens(System.String)">
+            <summary>
+            Selects a collection of elements using a JPath expression.
+            </summary>
+            <param name="path">
+            A <see cref="T:System.String"/> that contains a JPath expression.
+            </param>
+            <returns>An <see cref="T:System.Collections.Generic.IEnumerable`1"/> that contains the selected elements.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.SelectTokens(System.String,System.Boolean)">
+            <summary>
+            Selects a collection of elements using a JPath expression.
+            </summary>
+            <param name="path">
+            A <see cref="T:System.String"/> that contains a JPath expression.
+            </param>
+            <param name="errorWhenNoMatch">A flag to indicate whether an error should be thrown if no tokens are found when evaluating part of the expression.</param>
+            <returns>An <see cref="T:System.Collections.Generic.IEnumerable`1"/> that contains the selected elements.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.GetMetaObject(System.Linq.Expressions.Expression)">
+            <summary>
+            Returns the <see cref="T:System.Dynamic.DynamicMetaObject"/> responsible for binding operations performed on this object.
+            </summary>
+            <param name="parameter">The expression tree representation of the runtime value.</param>
+            <returns>
+            The <see cref="T:System.Dynamic.DynamicMetaObject"/> to bind this object.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.System#Dynamic#IDynamicMetaObjectProvider#GetMetaObject(System.Linq.Expressions.Expression)">
+            <summary>
+            Returns the <see cref="T:System.Dynamic.DynamicMetaObject"/> responsible for binding operations performed on this object.
+            </summary>
+            <param name="parameter">The expression tree representation of the runtime value.</param>
+            <returns>
+            The <see cref="T:System.Dynamic.DynamicMetaObject"/> to bind this object.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.DeepClone">
+            <summary>
+            Creates a new instance of the <see cref="T:Newtonsoft.Json.Linq.JToken"/>. All child tokens are recursively cloned.
+            </summary>
+            <returns>A new instance of the <see cref="T:Newtonsoft.Json.Linq.JToken"/>.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.AddAnnotation(System.Object)">
+            <summary>
+            Adds an object to the annotation list of this <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <param name="annotation">The annotation to add.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.Annotation``1">
+            <summary>
+            Get the first annotation object of the specified type from this <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <typeparam name="T">The type of the annotation to retrieve.</typeparam>
+            <returns>The first annotation object that matches the specified type, or <c>null</c> if no annotation is of the specified type.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.Annotation(System.Type)">
+            <summary>
+            Gets the first annotation object of the specified type from this <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <param name="type">The <see cref="P:Newtonsoft.Json.Linq.JToken.Type"/> of the annotation to retrieve.</param>
+            <returns>The first annotation object that matches the specified type, or <c>null</c> if no annotation is of the specified type.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.Annotations``1">
+            <summary>
+            Gets a collection of annotations of the specified type for this <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <typeparam name="T">The type of the annotations to retrieve.</typeparam>
+            <returns>An <see cref="T:System.Collections.Generic.IEnumerable`1"/>  that contains the annotations for this <see cref="T:Newtonsoft.Json.Linq.JToken"/>.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.Annotations(System.Type)">
+            <summary>
+            Gets a collection of annotations of the specified type for this <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <param name="type">The <see cref="P:Newtonsoft.Json.Linq.JToken.Type"/> of the annotations to retrieve.</param>
+            <returns>An <see cref="T:System.Collections.Generic.IEnumerable`1"/> of <see cref="T:System.Object"/> that contains the annotations that match the specified type for this <see cref="T:Newtonsoft.Json.Linq.JToken"/>.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.RemoveAnnotations``1">
+            <summary>
+            Removes the annotations of the specified type from this <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <typeparam name="T">The type of annotations to remove.</typeparam>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JToken.RemoveAnnotations(System.Type)">
+            <summary>
+            Removes the annotations of the specified type from this <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <param name="type">The <see cref="P:Newtonsoft.Json.Linq.JToken.Type"/> of annotations to remove.</param>
+        </member>
+        <member name="T:Newtonsoft.Json.Linq.JTokenEqualityComparer">
+            <summary>
+            Compares tokens to determine whether they are equal.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JTokenEqualityComparer.Equals(Newtonsoft.Json.Linq.JToken,Newtonsoft.Json.Linq.JToken)">
+            <summary>
+            Determines whether the specified objects are equal.
+            </summary>
+            <param name="x">The first object of type <see cref="T:Newtonsoft.Json.Linq.JToken"/> to compare.</param>
+            <param name="y">The second object of type <see cref="T:Newtonsoft.Json.Linq.JToken"/> to compare.</param>
+            <returns>
+            true if the specified objects are equal; otherwise, false.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JTokenEqualityComparer.GetHashCode(Newtonsoft.Json.Linq.JToken)">
+            <summary>
+            Returns a hash code for the specified object.
+            </summary>
+            <param name="obj">The <see cref="T:System.Object"/> for which a hash code is to be returned.</param>
+            <returns>A hash code for the specified object.</returns>
+            <exception cref="T:System.ArgumentNullException">The type of <paramref name="obj"/> is a reference type and <paramref name="obj"/> is null.</exception>
+        </member>
+        <member name="T:Newtonsoft.Json.Linq.JTokenReader">
+            <summary>
+            Represents a reader that provides fast, non-cached, forward-only access to serialized JSON data.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JTokenReader.CurrentToken">
+            <summary>
+            Gets the <see cref="T:Newtonsoft.Json.Linq.JToken"/> at the reader's current position.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JTokenReader.#ctor(Newtonsoft.Json.Linq.JToken)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Linq.JTokenReader"/> class.
+            </summary>
+            <param name="token">The token to read from.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JTokenReader.Read">
+            <summary>
+            Reads the next JSON token from the stream.
+            </summary>
+            <returns>
+            true if the next token was read successfully; false if there are no more tokens to read.
+            </returns>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JTokenReader.Path">
+            <summary>
+            Gets the path of the current JSON token. 
+            </summary>
+        </member>
+        <member name="T:Newtonsoft.Json.Linq.JTokenType">
+            <summary>
+            Specifies the type of token.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.Linq.JTokenType.None">
+            <summary>
+            No token type has been set.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.Linq.JTokenType.Object">
+            <summary>
+            A JSON object.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.Linq.JTokenType.Array">
+            <summary>
+            A JSON array.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.Linq.JTokenType.Constructor">
+            <summary>
+            A JSON constructor.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.Linq.JTokenType.Property">
+            <summary>
+            A JSON object property.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.Linq.JTokenType.Comment">
+            <summary>
+            A comment.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.Linq.JTokenType.Integer">
+            <summary>
+            An integer value.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.Linq.JTokenType.Float">
+            <summary>
+            A float value.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.Linq.JTokenType.String">
+            <summary>
+            A string value.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.Linq.JTokenType.Boolean">
+            <summary>
+            A boolean value.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.Linq.JTokenType.Null">
+            <summary>
+            A null value.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.Linq.JTokenType.Undefined">
+            <summary>
+            An undefined value.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.Linq.JTokenType.Date">
+            <summary>
+            A date value.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.Linq.JTokenType.Raw">
+            <summary>
+            A raw JSON value.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.Linq.JTokenType.Bytes">
+            <summary>
+            A collection of bytes value.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.Linq.JTokenType.Guid">
+            <summary>
+            A Guid value.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.Linq.JTokenType.Uri">
+            <summary>
+            A Uri value.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.Linq.JTokenType.TimeSpan">
+            <summary>
+            A TimeSpan value.
+            </summary>
+        </member>
+        <member name="T:Newtonsoft.Json.Linq.JTokenWriter">
+            <summary>
+            Represents a writer that provides a fast, non-cached, forward-only way of generating JSON data.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JTokenWriter.CurrentToken">
+            <summary>
+            Gets the <see cref="T:Newtonsoft.Json.Linq.JToken"/> at the writer's current position.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JTokenWriter.Token">
+            <summary>
+            Gets the token being writen.
+            </summary>
+            <value>The token being writen.</value>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JTokenWriter.#ctor(Newtonsoft.Json.Linq.JContainer)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Linq.JTokenWriter"/> class writing to the given <see cref="T:Newtonsoft.Json.Linq.JContainer"/>.
+            </summary>
+            <param name="container">The container being written to.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JTokenWriter.#ctor">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Linq.JTokenWriter"/> class.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JTokenWriter.Flush">
+            <summary>
+            Flushes whatever is in the buffer to the underlying streams and also flushes the underlying stream.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JTokenWriter.Close">
+            <summary>
+            Closes this stream and the underlying stream.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JTokenWriter.WriteStartObject">
+            <summary>
+            Writes the beginning of a JSON object.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JTokenWriter.WriteStartArray">
+            <summary>
+            Writes the beginning of a JSON array.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JTokenWriter.WriteStartConstructor(System.String)">
+            <summary>
+            Writes the start of a constructor with the given name.
+            </summary>
+            <param name="name">The name of the constructor.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JTokenWriter.WriteEnd(Newtonsoft.Json.JsonToken)">
+            <summary>
+            Writes the end.
+            </summary>
+            <param name="token">The token.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JTokenWriter.WritePropertyName(System.String)">
+            <summary>
+            Writes the property name of a name/value pair on a JSON object.
+            </summary>
+            <param name="name">The name of the property.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JTokenWriter.WriteValue(System.Object)">
+            <summary>
+            Writes a <see cref="T:System.Object"/> value.
+            An error will raised if the value cannot be written as a single JSON token.
+            </summary>
+            <param name="value">The <see cref="T:System.Object"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JTokenWriter.WriteNull">
+            <summary>
+            Writes a null value.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JTokenWriter.WriteUndefined">
+            <summary>
+            Writes an undefined value.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JTokenWriter.WriteRaw(System.String)">
+            <summary>
+            Writes raw JSON.
+            </summary>
+            <param name="json">The raw JSON to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JTokenWriter.WriteComment(System.String)">
+            <summary>
+            Writes out a comment <code>/*...*/</code> containing the specified text.
+            </summary>
+            <param name="text">Text to place inside the comment.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JTokenWriter.WriteValue(System.String)">
+            <summary>
+            Writes a <see cref="T:System.String"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.String"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JTokenWriter.WriteValue(System.Int32)">
+            <summary>
+            Writes a <see cref="T:System.Int32"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Int32"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JTokenWriter.WriteValue(System.UInt32)">
+            <summary>
+            Writes a <see cref="T:System.UInt32"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.UInt32"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JTokenWriter.WriteValue(System.Int64)">
+            <summary>
+            Writes a <see cref="T:System.Int64"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Int64"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JTokenWriter.WriteValue(System.UInt64)">
+            <summary>
+            Writes a <see cref="T:System.UInt64"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.UInt64"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JTokenWriter.WriteValue(System.Single)">
+            <summary>
+            Writes a <see cref="T:System.Single"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Single"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JTokenWriter.WriteValue(System.Double)">
+            <summary>
+            Writes a <see cref="T:System.Double"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Double"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JTokenWriter.WriteValue(System.Boolean)">
+            <summary>
+            Writes a <see cref="T:System.Boolean"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Boolean"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JTokenWriter.WriteValue(System.Int16)">
+            <summary>
+            Writes a <see cref="T:System.Int16"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Int16"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JTokenWriter.WriteValue(System.UInt16)">
+            <summary>
+            Writes a <see cref="T:System.UInt16"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.UInt16"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JTokenWriter.WriteValue(System.Char)">
+            <summary>
+            Writes a <see cref="T:System.Char"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Char"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JTokenWriter.WriteValue(System.Byte)">
+            <summary>
+            Writes a <see cref="T:System.Byte"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Byte"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JTokenWriter.WriteValue(System.SByte)">
+            <summary>
+            Writes a <see cref="T:System.SByte"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.SByte"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JTokenWriter.WriteValue(System.Decimal)">
+            <summary>
+            Writes a <see cref="T:System.Decimal"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Decimal"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JTokenWriter.WriteValue(System.DateTime)">
+            <summary>
+            Writes a <see cref="T:System.DateTime"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.DateTime"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JTokenWriter.WriteValue(System.DateTimeOffset)">
+            <summary>
+            Writes a <see cref="T:System.DateTimeOffset"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.DateTimeOffset"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JTokenWriter.WriteValue(System.Byte[])">
+            <summary>
+            Writes a <see cref="T:System.Byte"/>[] value.
+            </summary>
+            <param name="value">The <see cref="T:System.Byte"/>[] value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JTokenWriter.WriteValue(System.TimeSpan)">
+            <summary>
+            Writes a <see cref="T:System.TimeSpan"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.TimeSpan"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JTokenWriter.WriteValue(System.Guid)">
+            <summary>
+            Writes a <see cref="T:System.Guid"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Guid"/> value to write.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JTokenWriter.WriteValue(System.Uri)">
+            <summary>
+            Writes a <see cref="T:System.Uri"/> value.
+            </summary>
+            <param name="value">The <see cref="T:System.Uri"/> value to write.</param>
+        </member>
+        <member name="T:Newtonsoft.Json.Linq.JValue">
+            <summary>
+            Represents a value in JSON (string, integer, date, etc).
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JValue.#ctor(Newtonsoft.Json.Linq.JValue)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Linq.JValue"/> class from another <see cref="T:Newtonsoft.Json.Linq.JValue"/> object.
+            </summary>
+            <param name="other">A <see cref="T:Newtonsoft.Json.Linq.JValue"/> object to copy from.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JValue.#ctor(System.Int64)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Linq.JValue"/> class with the given value.
+            </summary>
+            <param name="value">The value.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JValue.#ctor(System.Decimal)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Linq.JValue"/> class with the given value.
+            </summary>
+            <param name="value">The value.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JValue.#ctor(System.Char)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Linq.JValue"/> class with the given value.
+            </summary>
+            <param name="value">The value.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JValue.#ctor(System.UInt64)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Linq.JValue"/> class with the given value.
+            </summary>
+            <param name="value">The value.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JValue.#ctor(System.Double)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Linq.JValue"/> class with the given value.
+            </summary>
+            <param name="value">The value.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JValue.#ctor(System.Single)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Linq.JValue"/> class with the given value.
+            </summary>
+            <param name="value">The value.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JValue.#ctor(System.DateTime)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Linq.JValue"/> class with the given value.
+            </summary>
+            <param name="value">The value.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JValue.#ctor(System.DateTimeOffset)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Linq.JValue"/> class with the given value.
+            </summary>
+            <param name="value">The value.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JValue.#ctor(System.Boolean)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Linq.JValue"/> class with the given value.
+            </summary>
+            <param name="value">The value.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JValue.#ctor(System.String)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Linq.JValue"/> class with the given value.
+            </summary>
+            <param name="value">The value.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JValue.#ctor(System.Guid)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Linq.JValue"/> class with the given value.
+            </summary>
+            <param name="value">The value.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JValue.#ctor(System.Uri)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Linq.JValue"/> class with the given value.
+            </summary>
+            <param name="value">The value.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JValue.#ctor(System.TimeSpan)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Linq.JValue"/> class with the given value.
+            </summary>
+            <param name="value">The value.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JValue.#ctor(System.Object)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Linq.JValue"/> class with the given value.
+            </summary>
+            <param name="value">The value.</param>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JValue.HasValues">
+            <summary>
+            Gets a value indicating whether this token has child tokens.
+            </summary>
+            <value>
+            	<c>true</c> if this token has child values; otherwise, <c>false</c>.
+            </value>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JValue.CreateComment(System.String)">
+            <summary>
+            Creates a <see cref="T:Newtonsoft.Json.Linq.JValue"/> comment with the given value.
+            </summary>
+            <param name="value">The value.</param>
+            <returns>A <see cref="T:Newtonsoft.Json.Linq.JValue"/> comment with the given value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JValue.CreateString(System.String)">
+            <summary>
+            Creates a <see cref="T:Newtonsoft.Json.Linq.JValue"/> string with the given value.
+            </summary>
+            <param name="value">The value.</param>
+            <returns>A <see cref="T:Newtonsoft.Json.Linq.JValue"/> string with the given value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JValue.CreateNull">
+            <summary>
+            Creates a <see cref="T:Newtonsoft.Json.Linq.JValue"/> null value.
+            </summary>
+            <returns>A <see cref="T:Newtonsoft.Json.Linq.JValue"/> null value.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JValue.CreateUndefined">
+            <summary>
+            Creates a <see cref="T:Newtonsoft.Json.Linq.JValue"/> undefined value.
+            </summary>
+            <returns>A <see cref="T:Newtonsoft.Json.Linq.JValue"/> undefined value.</returns>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JValue.Type">
+            <summary>
+            Gets the node type for this <see cref="T:Newtonsoft.Json.Linq.JToken"/>.
+            </summary>
+            <value>The type.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Linq.JValue.Value">
+            <summary>
+            Gets or sets the underlying token value.
+            </summary>
+            <value>The underlying token value.</value>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JValue.WriteTo(Newtonsoft.Json.JsonWriter,Newtonsoft.Json.JsonConverter[])">
+            <summary>
+            Writes this token to a <see cref="T:Newtonsoft.Json.JsonWriter"/>.
+            </summary>
+            <param name="writer">A <see cref="T:Newtonsoft.Json.JsonWriter"/> into which this method will write.</param>
+            <param name="converters">A collection of <see cref="T:Newtonsoft.Json.JsonConverter"/> which will be used when writing the token.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JValue.Equals(Newtonsoft.Json.Linq.JValue)">
+            <summary>
+            Indicates whether the current object is equal to another object of the same type.
+            </summary>
+            <returns>
+            true if the current object is equal to the <paramref name="other"/> parameter; otherwise, false.
+            </returns>
+            <param name="other">An object to compare with this object.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JValue.Equals(System.Object)">
+            <summary>
+            Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
+            </summary>
+            <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
+            <returns>
+            true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
+            </returns>
+            <exception cref="T:System.NullReferenceException">
+            The <paramref name="obj"/> parameter is null.
+            </exception>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JValue.GetHashCode">
+            <summary>
+            Serves as a hash function for a particular type.
+            </summary>
+            <returns>
+            A hash code for the current <see cref="T:System.Object"/>.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JValue.ToString">
+            <summary>
+            Returns a <see cref="T:System.String"/> that represents this instance.
+            </summary>
+            <returns>
+            A <see cref="T:System.String"/> that represents this instance.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JValue.ToString(System.String)">
+            <summary>
+            Returns a <see cref="T:System.String"/> that represents this instance.
+            </summary>
+            <param name="format">The format.</param>
+            <returns>
+            A <see cref="T:System.String"/> that represents this instance.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JValue.ToString(System.IFormatProvider)">
+            <summary>
+            Returns a <see cref="T:System.String"/> that represents this instance.
+            </summary>
+            <param name="formatProvider">The format provider.</param>
+            <returns>
+            A <see cref="T:System.String"/> that represents this instance.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JValue.ToString(System.String,System.IFormatProvider)">
+            <summary>
+            Returns a <see cref="T:System.String"/> that represents this instance.
+            </summary>
+            <param name="format">The format.</param>
+            <param name="formatProvider">The format provider.</param>
+            <returns>
+            A <see cref="T:System.String"/> that represents this instance.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JValue.GetMetaObject(System.Linq.Expressions.Expression)">
+            <summary>
+            Returns the <see cref="T:System.Dynamic.DynamicMetaObject"/> responsible for binding operations performed on this object.
+            </summary>
+            <param name="parameter">The expression tree representation of the runtime value.</param>
+            <returns>
+            The <see cref="T:System.Dynamic.DynamicMetaObject"/> to bind this object.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Linq.JValue.CompareTo(Newtonsoft.Json.Linq.JValue)">
+            <summary>
+            Compares the current instance with another object of the same type and returns an integer that indicates whether the current instance precedes, follows, or occurs in the same position in the sort order as the other object.
+            </summary>
+            <param name="obj">An object to compare with this instance.</param>
+            <returns>
+            A 32-bit signed integer that indicates the relative order of the objects being compared. The return value has these meanings:
+            Value
+            Meaning
+            Less than zero
+            This instance is less than <paramref name="obj"/>.
+            Zero
+            This instance is equal to <paramref name="obj"/>.
+            Greater than zero
+            This instance is greater than <paramref name="obj"/>.
+            </returns>
+            <exception cref="T:System.ArgumentException">
+            	<paramref name="obj"/> is not the same type as this instance.
+            </exception>
+        </member>
+        <member name="T:Newtonsoft.Json.Linq.MergeArrayHandling">
+            <summary>
+            Specifies how JSON arrays are merged together.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.Linq.MergeArrayHandling.Concat">
+            <summary>Concatenate arrays.</summary>
+        </member>
+        <member name="F:Newtonsoft.Json.Linq.MergeArrayHandling.Union">
+            <summary>Union arrays, skipping items that already exist.</summary>
+        </member>
+        <member name="F:Newtonsoft.Json.Linq.MergeArrayHandling.Replace">
+            <summary>Replace all array items.</summary>
+        </member>
+        <member name="F:Newtonsoft.Json.Linq.MergeArrayHandling.Merge">
+            <summary>Merge array items together, matched by index.</summary>
+        </member>
+        <member name="T:Newtonsoft.Json.Linq.MergeNullValueHandling">
+            <summary>
+            Specifies how null value properties are merged.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.Linq.MergeNullValueHandling.Ignore">
+            <summary>
+            The content's null value properties will be ignored during merging.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.Linq.MergeNullValueHandling.Merge">
+            <summary>
+            The content's null value properties will be merged.
+            </summary>
+        </member>
+        <member name="T:Newtonsoft.Json.MemberSerialization">
+            <summary>
+            Specifies the member serialization options for the <see cref="T:Newtonsoft.Json.JsonSerializer"/>.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.MemberSerialization.OptOut">
+            <summary>
+            All public members are serialized by default. Members can be excluded using <see cref="T:Newtonsoft.Json.JsonIgnoreAttribute"/> or <see cref="!:NonSerializedAttribute"/>.
+            This is the default member serialization mode.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.MemberSerialization.OptIn">
+            <summary>
+            Only members marked with <see cref="T:Newtonsoft.Json.JsonPropertyAttribute"/> or <see cref="T:System.Runtime.Serialization.DataMemberAttribute"/> are serialized.
+            This member serialization mode can also be set by marking the class with <see cref="T:System.Runtime.Serialization.DataContractAttribute"/>.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.MemberSerialization.Fields">
+            <summary>
+            All public and private fields are serialized. Members can be excluded using <see cref="T:Newtonsoft.Json.JsonIgnoreAttribute"/> or <see cref="!:NonSerializedAttribute"/>.
+            This member serialization mode can also be set by marking the class with <see cref="!:SerializableAttribute"/>
+            and setting IgnoreSerializableAttribute on <see cref="T:Newtonsoft.Json.Serialization.DefaultContractResolver"/> to false.
+            </summary>
+        </member>
+        <member name="T:Newtonsoft.Json.MetadataPropertyHandling">
+            <summary>
+            Specifies metadata property handling options for the <see cref="T:Newtonsoft.Json.JsonSerializer"/>.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.MetadataPropertyHandling.Default">
+            <summary>
+            Read metadata properties located at the start of a JSON object.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.MetadataPropertyHandling.ReadAhead">
+            <summary>
+            Read metadata properties located anywhere in a JSON object. Note that this setting will impact performance.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.MetadataPropertyHandling.Ignore">
+            <summary>
+            Do not try to read metadata properties.
+            </summary>
+        </member>
+        <member name="T:Newtonsoft.Json.MissingMemberHandling">
+            <summary>
+            Specifies missing member handling options for the <see cref="T:Newtonsoft.Json.JsonSerializer"/>.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.MissingMemberHandling.Ignore">
+            <summary>
+            Ignore a missing member and do not attempt to deserialize it.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.MissingMemberHandling.Error">
+            <summary>
+            Throw a <see cref="T:Newtonsoft.Json.JsonSerializationException"/> when a missing member is encountered during deserialization.
+            </summary>
+        </member>
+        <member name="T:Newtonsoft.Json.NullValueHandling">
+            <summary>
+            Specifies null value handling options for the <see cref="T:Newtonsoft.Json.JsonSerializer"/>.
+            </summary>
+            <example>
+              <code lang="cs" source="..\Src\Newtonsoft.Json.Tests\Documentation\SerializationTests.cs" region="ReducingSerializedJsonSizeNullValueHandlingObject" title="NullValueHandling Class" />
+              <code lang="cs" source="..\Src\Newtonsoft.Json.Tests\Documentation\SerializationTests.cs" region="ReducingSerializedJsonSizeNullValueHandlingExample" title="NullValueHandling Ignore Example" />
+            </example>
+        </member>
+        <member name="F:Newtonsoft.Json.NullValueHandling.Include">
+            <summary>
+            Include null values when serializing and deserializing objects.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.NullValueHandling.Ignore">
+            <summary>
+            Ignore null values when serializing and deserializing objects.
+            </summary>
+        </member>
+        <member name="T:Newtonsoft.Json.ObjectCreationHandling">
+            <summary>
+            Specifies how object creation is handled by the <see cref="T:Newtonsoft.Json.JsonSerializer"/>.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.ObjectCreationHandling.Auto">
+            <summary>
+            Reuse existing objects, create new objects when needed.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.ObjectCreationHandling.Reuse">
+            <summary>
+            Only reuse existing objects.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.ObjectCreationHandling.Replace">
+            <summary>
+            Always create new objects.
+            </summary>
+        </member>
+        <member name="T:Newtonsoft.Json.PreserveReferencesHandling">
+            <summary>
+            Specifies reference handling options for the <see cref="T:Newtonsoft.Json.JsonSerializer"/>.
+            Note that references cannot be preserved when a value is set via a non-default constructor such as types that implement ISerializable.
+            </summary>
+            <example>
+              <code lang="cs" source="..\Src\Newtonsoft.Json.Tests\Documentation\SerializationTests.cs" region="PreservingObjectReferencesOn" title="Preserve Object References" />       
+            </example>
+        </member>
+        <member name="F:Newtonsoft.Json.PreserveReferencesHandling.None">
+            <summary>
+            Do not preserve references when serializing types.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.PreserveReferencesHandling.Objects">
+            <summary>
+            Preserve references when serializing into a JSON object structure.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.PreserveReferencesHandling.Arrays">
+            <summary>
+            Preserve references when serializing into a JSON array structure.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.PreserveReferencesHandling.All">
+            <summary>
+            Preserve references when serializing.
+            </summary>
+        </member>
+        <member name="T:Newtonsoft.Json.ReferenceLoopHandling">
+            <summary>
+            Specifies reference loop handling options for the <see cref="T:Newtonsoft.Json.JsonSerializer"/>.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.ReferenceLoopHandling.Error">
+            <summary>
+            Throw a <see cref="T:Newtonsoft.Json.JsonSerializationException"/> when a loop is encountered.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.ReferenceLoopHandling.Ignore">
+            <summary>
+            Ignore loop references and do not serialize.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.ReferenceLoopHandling.Serialize">
+            <summary>
+            Serialize loop references.
+            </summary>
+        </member>
+        <member name="T:Newtonsoft.Json.Required">
+            <summary>
+            Indicating whether a property is required.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.Required.Default">
+            <summary>
+            The property is not required. The default state.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.Required.AllowNull">
+            <summary>
+            The property must be defined in JSON but can be a null value.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.Required.Always">
+            <summary>
+            The property must be defined in JSON and cannot be a null value.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.Required.DisallowNull">
+            <summary>
+            The property is not required but it cannot be a null value.
+            </summary>
+        </member>
+        <member name="T:Newtonsoft.Json.SerializationBinder">
+            <summary>
+            Allows users to control class loading and mandate what class to load.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.SerializationBinder.BindToType(System.String,System.String)">
+            <summary>
+            When overridden in a derived class, controls the binding of a serialized object to a type.
+            </summary>
+            <param name="assemblyName">Specifies the <see cref="T:System.Reflection.Assembly"/> name of the serialized object.</param>
+            <param name="typeName">Specifies the <see cref="T:System.Type"/> name of the serialized object</param>
+            <returns>The type of the object the formatter creates a new instance of.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.SerializationBinder.BindToName(System.Type,System.String@,System.String@)">
+            <summary>
+            When overridden in a derived class, controls the binding of a serialized object to a type.
+            </summary>
+            <param name="serializedType">The type of the object the formatter creates a new instance of.</param>
+            <param name="assemblyName">Specifies the <see cref="T:System.Reflection.Assembly"/> name of the serialized object.</param>
+            <param name="typeName">Specifies the <see cref="T:System.Type"/> name of the serialized object.</param>
+        </member>
+        <member name="T:Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver">
+            <summary>
+            Resolves member mappings for a type, camel casing property names.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver.#ctor">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver"/> class.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.CamelCasePropertyNamesContractResolver.ResolvePropertyName(System.String)">
+            <summary>
+            Resolves the name of the property.
+            </summary>
+            <param name="propertyName">Name of the property.</param>
+            <returns>The property name camel cased.</returns>
+        </member>
+        <member name="T:Newtonsoft.Json.Serialization.ExpressionValueProvider">
+            <summary>
+            Get and set values for a <see cref="T:System.Reflection.MemberInfo"/> using dynamic methods.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.ExpressionValueProvider.#ctor(System.Reflection.MemberInfo)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.ExpressionValueProvider"/> class.
+            </summary>
+            <param name="memberInfo">The member info.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.ExpressionValueProvider.SetValue(System.Object,System.Object)">
+            <summary>
+            Sets the value.
+            </summary>
+            <param name="target">The target to set the value on.</param>
+            <param name="value">The value to set on the target.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.ExpressionValueProvider.GetValue(System.Object)">
+            <summary>
+            Gets the value.
+            </summary>
+            <param name="target">The target to get the value from.</param>
+            <returns>The value.</returns>
+        </member>
+        <member name="T:Newtonsoft.Json.Serialization.DefaultContractResolver">
+            <summary>
+            Used by <see cref="T:Newtonsoft.Json.JsonSerializer"/> to resolves a <see cref="T:Newtonsoft.Json.Serialization.JsonContract"/> for a given <see cref="T:System.Type"/>.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.DefaultContractResolver.DynamicCodeGeneration">
+            <summary>
+            Gets a value indicating whether members are being get and set using dynamic code generation.
+            This value is determined by the runtime permissions available.
+            </summary>
+            <value>
+            	<c>true</c> if using dynamic code generation; otherwise, <c>false</c>.
+            </value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.DefaultContractResolver.SerializeCompilerGeneratedMembers">
+            <summary>
+            Gets or sets a value indicating whether compiler generated members should be serialized.
+            </summary>
+            <value>
+            	<c>true</c> if serialized compiler generated members; otherwise, <c>false</c>.
+            </value>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.DefaultContractResolver.#ctor">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.DefaultContractResolver"/> class.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.DefaultContractResolver.#ctor(System.Boolean)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.DefaultContractResolver"/> class.
+            </summary>
+            <param name="shareCache">
+            If set to <c>true</c> the <see cref="T:Newtonsoft.Json.Serialization.DefaultContractResolver"/> will use a cached shared with other resolvers of the same type.
+            Sharing the cache will significantly improve performance with multiple resolver instances because expensive reflection will only
+            happen once. This setting can cause unexpected behavior if different instances of the resolver are suppose to produce different
+            results. When set to false it is highly recommended to reuse <see cref="T:Newtonsoft.Json.Serialization.DefaultContractResolver"/> instances with the <see cref="T:Newtonsoft.Json.JsonSerializer"/>.
+            </param>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.DefaultContractResolver.ResolveContract(System.Type)">
+            <summary>
+            Resolves the contract for a given type.
+            </summary>
+            <param name="type">The type to resolve a contract for.</param>
+            <returns>The contract for a given type.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.DefaultContractResolver.GetSerializableMembers(System.Type)">
+            <summary>
+            Gets the serializable members for the type.
+            </summary>
+            <param name="objectType">The type to get serializable members for.</param>
+            <returns>The serializable members for the type.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.DefaultContractResolver.CreateObjectContract(System.Type)">
+            <summary>
+            Creates a <see cref="T:Newtonsoft.Json.Serialization.JsonObjectContract"/> for the given type.
+            </summary>
+            <param name="objectType">Type of the object.</param>
+            <returns>A <see cref="T:Newtonsoft.Json.Serialization.JsonObjectContract"/> for the given type.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.DefaultContractResolver.CreateConstructorParameters(System.Reflection.ConstructorInfo,Newtonsoft.Json.Serialization.JsonPropertyCollection)">
+            <summary>
+            Creates the constructor parameters.
+            </summary>
+            <param name="constructor">The constructor to create properties for.</param>
+            <param name="memberProperties">The type's member properties.</param>
+            <returns>Properties for the given <see cref="T:System.Reflection.ConstructorInfo"/>.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.DefaultContractResolver.CreatePropertyFromConstructorParameter(Newtonsoft.Json.Serialization.JsonProperty,System.Reflection.ParameterInfo)">
+            <summary>
+            Creates a <see cref="T:Newtonsoft.Json.Serialization.JsonProperty"/> for the given <see cref="T:System.Reflection.ParameterInfo"/>.
+            </summary>
+            <param name="matchingMemberProperty">The matching member property.</param>
+            <param name="parameterInfo">The constructor parameter.</param>
+            <returns>A created <see cref="T:Newtonsoft.Json.Serialization.JsonProperty"/> for the given <see cref="T:System.Reflection.ParameterInfo"/>.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.DefaultContractResolver.ResolveContractConverter(System.Type)">
+            <summary>
+            Resolves the default <see cref="T:Newtonsoft.Json.JsonConverter" /> for the contract.
+            </summary>
+            <param name="objectType">Type of the object.</param>
+            <returns>The contract's default <see cref="T:Newtonsoft.Json.JsonConverter" />.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.DefaultContractResolver.CreateDictionaryContract(System.Type)">
+            <summary>
+            Creates a <see cref="T:Newtonsoft.Json.Serialization.JsonDictionaryContract"/> for the given type.
+            </summary>
+            <param name="objectType">Type of the object.</param>
+            <returns>A <see cref="T:Newtonsoft.Json.Serialization.JsonDictionaryContract"/> for the given type.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.DefaultContractResolver.CreateArrayContract(System.Type)">
+            <summary>
+            Creates a <see cref="T:Newtonsoft.Json.Serialization.JsonArrayContract"/> for the given type.
+            </summary>
+            <param name="objectType">Type of the object.</param>
+            <returns>A <see cref="T:Newtonsoft.Json.Serialization.JsonArrayContract"/> for the given type.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.DefaultContractResolver.CreatePrimitiveContract(System.Type)">
+            <summary>
+            Creates a <see cref="T:Newtonsoft.Json.Serialization.JsonPrimitiveContract"/> for the given type.
+            </summary>
+            <param name="objectType">Type of the object.</param>
+            <returns>A <see cref="T:Newtonsoft.Json.Serialization.JsonPrimitiveContract"/> for the given type.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.DefaultContractResolver.CreateLinqContract(System.Type)">
+            <summary>
+            Creates a <see cref="T:Newtonsoft.Json.Serialization.JsonLinqContract"/> for the given type.
+            </summary>
+            <param name="objectType">Type of the object.</param>
+            <returns>A <see cref="T:Newtonsoft.Json.Serialization.JsonLinqContract"/> for the given type.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.DefaultContractResolver.CreateDynamicContract(System.Type)">
+            <summary>
+            Creates a <see cref="T:Newtonsoft.Json.Serialization.JsonDynamicContract"/> for the given type.
+            </summary>
+            <param name="objectType">Type of the object.</param>
+            <returns>A <see cref="T:Newtonsoft.Json.Serialization.JsonDynamicContract"/> for the given type.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.DefaultContractResolver.CreateStringContract(System.Type)">
+            <summary>
+            Creates a <see cref="T:Newtonsoft.Json.Serialization.JsonStringContract"/> for the given type.
+            </summary>
+            <param name="objectType">Type of the object.</param>
+            <returns>A <see cref="T:Newtonsoft.Json.Serialization.JsonStringContract"/> for the given type.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.DefaultContractResolver.CreateContract(System.Type)">
+            <summary>
+            Determines which contract type is created for the given type.
+            </summary>
+            <param name="objectType">Type of the object.</param>
+            <returns>A <see cref="T:Newtonsoft.Json.Serialization.JsonContract"/> for the given type.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.DefaultContractResolver.CreateProperties(System.Type,Newtonsoft.Json.MemberSerialization)">
+            <summary>
+            Creates properties for the given <see cref="T:Newtonsoft.Json.Serialization.JsonContract"/>.
+            </summary>
+            <param name="type">The type to create properties for.</param>
+            /// <param name="memberSerialization">The member serialization mode for the type.</param>
+            <returns>Properties for the given <see cref="T:Newtonsoft.Json.Serialization.JsonContract"/>.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.DefaultContractResolver.CreateMemberValueProvider(System.Reflection.MemberInfo)">
+            <summary>
+            Creates the <see cref="T:Newtonsoft.Json.Serialization.IValueProvider"/> used by the serializer to get and set values from a member.
+            </summary>
+            <param name="member">The member.</param>
+            <returns>The <see cref="T:Newtonsoft.Json.Serialization.IValueProvider"/> used by the serializer to get and set values from a member.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.DefaultContractResolver.CreateProperty(System.Reflection.MemberInfo,Newtonsoft.Json.MemberSerialization)">
+            <summary>
+            Creates a <see cref="T:Newtonsoft.Json.Serialization.JsonProperty"/> for the given <see cref="T:System.Reflection.MemberInfo"/>.
+            </summary>
+            <param name="memberSerialization">The member's parent <see cref="T:Newtonsoft.Json.MemberSerialization"/>.</param>
+            <param name="member">The member to create a <see cref="T:Newtonsoft.Json.Serialization.JsonProperty"/> for.</param>
+            <returns>A created <see cref="T:Newtonsoft.Json.Serialization.JsonProperty"/> for the given <see cref="T:System.Reflection.MemberInfo"/>.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.DefaultContractResolver.ResolvePropertyName(System.String)">
+            <summary>
+            Resolves the name of the property.
+            </summary>
+            <param name="propertyName">Name of the property.</param>
+            <returns>Resolved name of the property.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.DefaultContractResolver.ResolveDictionaryKey(System.String)">
+            <summary>
+            Resolves the key of the dictionary. By default <see cref="M:Newtonsoft.Json.Serialization.DefaultContractResolver.ResolvePropertyName(System.String)"/> is used to resolve dictionary keys.
+            </summary>
+            <param name="dictionaryKey">Key of the dictionary.</param>
+            <returns>Resolved key of the dictionary.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.DefaultContractResolver.GetResolvedPropertyName(System.String)">
+            <summary>
+            Gets the resolved name of the property.
+            </summary>
+            <param name="propertyName">Name of the property.</param>
+            <returns>Name of the property.</returns>
+        </member>
+        <member name="T:Newtonsoft.Json.Serialization.DefaultSerializationBinder">
+            <summary>
+            The default serialization binder used when resolving and loading classes from type names.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.DefaultSerializationBinder.BindToType(System.String,System.String)">
+            <summary>
+            When overridden in a derived class, controls the binding of a serialized object to a type.
+            </summary>
+            <param name="assemblyName">Specifies the <see cref="T:System.Reflection.Assembly"/> name of the serialized object.</param>
+            <param name="typeName">Specifies the <see cref="T:System.Type"/> name of the serialized object.</param>
+            <returns>
+            The type of the object the formatter creates a new instance of.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.DefaultSerializationBinder.BindToName(System.Type,System.String@,System.String@)">
+            <summary>
+            When overridden in a derived class, controls the binding of a serialized object to a type.
+            </summary>
+            <param name="serializedType">The type of the object the formatter creates a new instance of.</param>
+            <param name="assemblyName">Specifies the <see cref="T:System.Reflection.Assembly"/> name of the serialized object. </param>
+            <param name="typeName">Specifies the <see cref="T:System.Type"/> name of the serialized object. </param>
+        </member>
+        <member name="T:Newtonsoft.Json.Serialization.ErrorContext">
+            <summary>
+            Provides information surrounding an error.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.ErrorContext.Error">
+            <summary>
+            Gets the error.
+            </summary>
+            <value>The error.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.ErrorContext.OriginalObject">
+            <summary>
+            Gets the original object that caused the error.
+            </summary>
+            <value>The original object that caused the error.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.ErrorContext.Member">
+            <summary>
+            Gets the member that caused the error.
+            </summary>
+            <value>The member that caused the error.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.ErrorContext.Path">
+            <summary>
+            Gets the path of the JSON location where the error occurred.
+            </summary>
+            <value>The path of the JSON location where the error occurred.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.ErrorContext.Handled">
+            <summary>
+            Gets or sets a value indicating whether this <see cref="T:Newtonsoft.Json.Serialization.ErrorContext"/> is handled.
+            </summary>
+            <value><c>true</c> if handled; otherwise, <c>false</c>.</value>
+        </member>
+        <member name="T:Newtonsoft.Json.Serialization.ErrorEventArgs">
+            <summary>
+            Provides data for the Error event.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.ErrorEventArgs.CurrentObject">
+            <summary>
+            Gets the current object the error event is being raised against.
+            </summary>
+            <value>The current object the error event is being raised against.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.ErrorEventArgs.ErrorContext">
+            <summary>
+            Gets the error context.
+            </summary>
+            <value>The error context.</value>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.ErrorEventArgs.#ctor(System.Object,Newtonsoft.Json.Serialization.ErrorContext)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.ErrorEventArgs"/> class.
+            </summary>
+            <param name="currentObject">The current object.</param>
+            <param name="errorContext">The error context.</param>
+        </member>
+        <member name="T:Newtonsoft.Json.Serialization.IAttributeProvider">
+            <summary>
+            Provides methods to get attributes.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.IAttributeProvider.GetAttributes(System.Boolean)">
+            <summary>
+            Returns a collection of all of the attributes, or an empty collection if there are no attributes.
+            </summary>
+            <param name="inherit">When true, look up the hierarchy chain for the inherited custom attribute.</param>
+            <returns>A collection of <see cref="T:System.Attribute"/>s, or an empty collection.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.IAttributeProvider.GetAttributes(System.Type,System.Boolean)">
+            <summary>
+            Returns a collection of attributes, identified by type, or an empty collection if there are no attributes.
+            </summary>
+            <param name="attributeType">The type of the attributes.</param>
+            <param name="inherit">When true, look up the hierarchy chain for the inherited custom attribute.</param>
+            <returns>A collection of <see cref="T:System.Attribute"/>s, or an empty collection.</returns>
+        </member>
+        <member name="T:Newtonsoft.Json.Serialization.IContractResolver">
+            <summary>
+            Used by <see cref="T:Newtonsoft.Json.JsonSerializer"/> to resolves a <see cref="T:Newtonsoft.Json.Serialization.JsonContract"/> for a given <see cref="T:System.Type"/>.
+            </summary>
+            <example>
+              <code lang="cs" source="..\Src\Newtonsoft.Json.Tests\Documentation\SerializationTests.cs" region="ReducingSerializedJsonSizeContractResolverObject" title="IContractResolver Class" />
+              <code lang="cs" source="..\Src\Newtonsoft.Json.Tests\Documentation\SerializationTests.cs" region="ReducingSerializedJsonSizeContractResolverExample" title="IContractResolver Example" />
+            </example>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.IContractResolver.ResolveContract(System.Type)">
+            <summary>
+            Resolves the contract for a given type.
+            </summary>
+            <param name="type">The type to resolve a contract for.</param>
+            <returns>The contract for a given type.</returns>
+        </member>
+        <member name="T:Newtonsoft.Json.Serialization.IReferenceResolver">
+            <summary>
+            Used to resolve references when serializing and deserializing JSON by the <see cref="T:Newtonsoft.Json.JsonSerializer"/>.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.IReferenceResolver.ResolveReference(System.Object,System.String)">
+            <summary>
+            Resolves a reference to its object.
+            </summary>
+            <param name="context">The serialization context.</param>
+            <param name="reference">The reference to resolve.</param>
+            <returns>The object that</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.IReferenceResolver.GetReference(System.Object,System.Object)">
+            <summary>
+            Gets the reference for the sepecified object.
+            </summary>
+            <param name="context">The serialization context.</param>
+            <param name="value">The object to get a reference for.</param>
+            <returns>The reference to the object.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.IReferenceResolver.IsReferenced(System.Object,System.Object)">
+            <summary>
+            Determines whether the specified object is referenced.
+            </summary>
+            <param name="context">The serialization context.</param>
+            <param name="value">The object to test for a reference.</param>
+            <returns>
+            	<c>true</c> if the specified object is referenced; otherwise, <c>false</c>.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.IReferenceResolver.AddReference(System.Object,System.String,System.Object)">
+            <summary>
+            Adds a reference to the specified object.
+            </summary>
+            <param name="context">The serialization context.</param>
+            <param name="reference">The reference.</param>
+            <param name="value">The object to reference.</param>
+        </member>
+        <member name="T:Newtonsoft.Json.Serialization.ITraceWriter">
+            <summary>
+            Represents a trace writer.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.ITraceWriter.LevelFilter">
+            <summary>
+            Gets the <see cref="T:Newtonsoft.Json.TraceLevel"/> that will be used to filter the trace messages passed to the writer.
+            For example a filter level of <code>Info</code> will exclude <code>Verbose</code> messages and include <code>Info</code>,
+            <code>Warning</code> and <code>Error</code> messages.
+            </summary>
+            <value>The <see cref="T:Newtonsoft.Json.TraceLevel"/> that will be used to filter the trace messages passed to the writer.</value>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.ITraceWriter.Trace(Newtonsoft.Json.TraceLevel,System.String,System.Exception)">
+            <summary>
+            Writes the specified trace level, message and optional exception.
+            </summary>
+            <param name="level">The <see cref="T:Newtonsoft.Json.TraceLevel"/> at which to write this trace.</param>
+            <param name="message">The trace message.</param>
+            <param name="ex">The trace exception. This parameter is optional.</param>
+        </member>
+        <member name="T:Newtonsoft.Json.Serialization.IValueProvider">
+            <summary>
+            Provides methods to get and set values.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.IValueProvider.SetValue(System.Object,System.Object)">
+            <summary>
+            Sets the value.
+            </summary>
+            <param name="target">The target to set the value on.</param>
+            <param name="value">The value to set on the target.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.IValueProvider.GetValue(System.Object)">
+            <summary>
+            Gets the value.
+            </summary>
+            <param name="target">The target to get the value from.</param>
+            <returns>The value.</returns>
+        </member>
+        <member name="T:Newtonsoft.Json.Serialization.JsonArrayContract">
+            <summary>
+            Contract details for a <see cref="T:System.Type"/> used by the <see cref="T:Newtonsoft.Json.JsonSerializer"/>.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonArrayContract.CollectionItemType">
+            <summary>
+            Gets the <see cref="T:System.Type"/> of the collection items.
+            </summary>
+            <value>The <see cref="T:System.Type"/> of the collection items.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonArrayContract.IsMultidimensionalArray">
+            <summary>
+            Gets a value indicating whether the collection type is a multidimensional array.
+            </summary>
+            <value><c>true</c> if the collection type is a multidimensional array; otherwise, <c>false</c>.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonArrayContract.OverrideCreator">
+            <summary>
+            Gets or sets the function used to create the object. When set this function will override <see cref="P:Newtonsoft.Json.Serialization.JsonContract.DefaultCreator"/>.
+            </summary>
+            <value>The function used to create the object.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonArrayContract.HasParameterizedCreator">
+            <summary>
+            Gets a value indicating whether the creator has a parameter with the collection values.
+            </summary>
+            <value><c>true</c> if the creator has a parameter with the collection values; otherwise, <c>false</c>.</value>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.JsonArrayContract.#ctor(System.Type)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.JsonArrayContract"/> class.
+            </summary>
+            <param name="underlyingType">The underlying type for the contract.</param>
+        </member>
+        <member name="T:Newtonsoft.Json.Serialization.JsonContainerContract">
+            <summary>
+            Contract details for a <see cref="T:System.Type"/> used by the <see cref="T:Newtonsoft.Json.JsonSerializer"/>.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonContainerContract.ItemConverter">
+            <summary>
+            Gets or sets the default collection items <see cref="T:Newtonsoft.Json.JsonConverter" />.
+            </summary>
+            <value>The converter.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonContainerContract.ItemIsReference">
+            <summary>
+            Gets or sets a value indicating whether the collection items preserve object references.
+            </summary>
+            <value><c>true</c> if collection items preserve object references; otherwise, <c>false</c>.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonContainerContract.ItemReferenceLoopHandling">
+            <summary>
+            Gets or sets the collection item reference loop handling.
+            </summary>
+            <value>The reference loop handling.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonContainerContract.ItemTypeNameHandling">
+            <summary>
+            Gets or sets the collection item type name handling.
+            </summary>
+            <value>The type name handling.</value>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.JsonContainerContract.#ctor(System.Type)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.JsonContainerContract"/> class.
+            </summary>
+            <param name="underlyingType">The underlying type for the contract.</param>
+        </member>
+        <member name="T:Newtonsoft.Json.Serialization.SerializationCallback">
+            <summary>
+            Handles <see cref="T:Newtonsoft.Json.JsonSerializer"/> serialization callback events.
+            </summary>
+            <param name="o">The object that raised the callback event.</param>
+            <param name="context">The streaming context.</param>
+        </member>
+        <member name="T:Newtonsoft.Json.Serialization.SerializationErrorCallback">
+            <summary>
+            Handles <see cref="T:Newtonsoft.Json.JsonSerializer"/> serialization error callback events.
+            </summary>
+            <param name="o">The object that raised the callback event.</param>
+            <param name="context">The streaming context.</param>
+            <param name="errorContext">The error context.</param>
+        </member>
+        <member name="T:Newtonsoft.Json.Serialization.ExtensionDataSetter">
+            <summary>
+            Sets extension data for an object during deserialization.
+            </summary>
+            <param name="o">The object to set extension data on.</param>
+            <param name="key">The extension data key.</param>
+            <param name="value">The extension data value.</param>
+        </member>
+        <member name="T:Newtonsoft.Json.Serialization.ExtensionDataGetter">
+            <summary>
+            Gets extension data for an object during serialization.
+            </summary>
+            <param name="o">The object to set extension data on.</param>
+        </member>
+        <member name="T:Newtonsoft.Json.Serialization.JsonContract">
+            <summary>
+            Contract details for a <see cref="T:System.Type"/> used by the <see cref="T:Newtonsoft.Json.JsonSerializer"/>.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonContract.UnderlyingType">
+            <summary>
+            Gets the underlying type for the contract.
+            </summary>
+            <value>The underlying type for the contract.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonContract.CreatedType">
+            <summary>
+            Gets or sets the type created during deserialization.
+            </summary>
+            <value>The type created during deserialization.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonContract.IsReference">
+            <summary>
+            Gets or sets whether this type contract is serialized as a reference.
+            </summary>
+            <value>Whether this type contract is serialized as a reference.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonContract.Converter">
+            <summary>
+            Gets or sets the default <see cref="T:Newtonsoft.Json.JsonConverter" /> for this contract.
+            </summary>
+            <value>The converter.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonContract.OnDeserializedCallbacks">
+            <summary>
+            Gets or sets all methods called immediately after deserialization of the object.
+            </summary>
+            <value>The methods called immediately after deserialization of the object.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonContract.OnDeserializingCallbacks">
+            <summary>
+            Gets or sets all methods called during deserialization of the object.
+            </summary>
+            <value>The methods called during deserialization of the object.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonContract.OnSerializedCallbacks">
+            <summary>
+            Gets or sets all methods called after serialization of the object graph.
+            </summary>
+            <value>The methods called after serialization of the object graph.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonContract.OnSerializingCallbacks">
+            <summary>
+            Gets or sets all methods called before serialization of the object.
+            </summary>
+            <value>The methods called before serialization of the object.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonContract.OnErrorCallbacks">
+            <summary>
+            Gets or sets all method called when an error is thrown during the serialization of the object.
+            </summary>
+            <value>The methods called when an error is thrown during the serialization of the object.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonContract.OnDeserialized">
+            <summary>
+            Gets or sets the method called immediately after deserialization of the object.
+            </summary>
+            <value>The method called immediately after deserialization of the object.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonContract.OnDeserializing">
+            <summary>
+            Gets or sets the method called during deserialization of the object.
+            </summary>
+            <value>The method called during deserialization of the object.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonContract.OnSerialized">
+            <summary>
+            Gets or sets the method called after serialization of the object graph.
+            </summary>
+            <value>The method called after serialization of the object graph.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonContract.OnSerializing">
+            <summary>
+            Gets or sets the method called before serialization of the object.
+            </summary>
+            <value>The method called before serialization of the object.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonContract.OnError">
+            <summary>
+            Gets or sets the method called when an error is thrown during the serialization of the object.
+            </summary>
+            <value>The method called when an error is thrown during the serialization of the object.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonContract.DefaultCreator">
+            <summary>
+            Gets or sets the default creator method used to create the object.
+            </summary>
+            <value>The default creator method used to create the object.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonContract.DefaultCreatorNonPublic">
+            <summary>
+            Gets or sets a value indicating whether the default creator is non public.
+            </summary>
+            <value><c>true</c> if the default object creator is non-public; otherwise, <c>false</c>.</value>
+        </member>
+        <member name="T:Newtonsoft.Json.Serialization.JsonDictionaryContract">
+            <summary>
+            Contract details for a <see cref="T:System.Type"/> used by the <see cref="T:Newtonsoft.Json.JsonSerializer"/>.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonDictionaryContract.PropertyNameResolver">
+            <summary>
+            Gets or sets the property name resolver.
+            </summary>
+            <value>The property name resolver.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonDictionaryContract.DictionaryKeyResolver">
+            <summary>
+            Gets or sets the dictionary key resolver.
+            </summary>
+            <value>The dictionary key resolver.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonDictionaryContract.DictionaryKeyType">
+            <summary>
+            Gets the <see cref="T:System.Type"/> of the dictionary keys.
+            </summary>
+            <value>The <see cref="T:System.Type"/> of the dictionary keys.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonDictionaryContract.DictionaryValueType">
+            <summary>
+            Gets the <see cref="T:System.Type"/> of the dictionary values.
+            </summary>
+            <value>The <see cref="T:System.Type"/> of the dictionary values.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonDictionaryContract.OverrideCreator">
+            <summary>
+            Gets or sets the function used to create the object. When set this function will override <see cref="P:Newtonsoft.Json.Serialization.JsonContract.DefaultCreator"/>.
+            </summary>
+            <value>The function used to create the object.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonDictionaryContract.HasParameterizedCreator">
+            <summary>
+            Gets a value indicating whether the creator has a parameter with the dictionary values.
+            </summary>
+            <value><c>true</c> if the creator has a parameter with the dictionary values; otherwise, <c>false</c>.</value>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.JsonDictionaryContract.#ctor(System.Type)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.JsonDictionaryContract"/> class.
+            </summary>
+            <param name="underlyingType">The underlying type for the contract.</param>
+        </member>
+        <member name="T:Newtonsoft.Json.Serialization.JsonDynamicContract">
+            <summary>
+            Contract details for a <see cref="T:System.Type"/> used by the <see cref="T:Newtonsoft.Json.JsonSerializer"/>.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonDynamicContract.Properties">
+            <summary>
+            Gets the object's properties.
+            </summary>
+            <value>The object's properties.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonDynamicContract.PropertyNameResolver">
+            <summary>
+            Gets or sets the property name resolver.
+            </summary>
+            <value>The property name resolver.</value>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.JsonDynamicContract.#ctor(System.Type)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.JsonDynamicContract"/> class.
+            </summary>
+            <param name="underlyingType">The underlying type for the contract.</param>
+        </member>
+        <member name="T:Newtonsoft.Json.Serialization.JsonLinqContract">
+            <summary>
+            Contract details for a <see cref="T:System.Type"/> used by the <see cref="T:Newtonsoft.Json.JsonSerializer"/>.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.JsonLinqContract.#ctor(System.Type)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.JsonLinqContract"/> class.
+            </summary>
+            <param name="underlyingType">The underlying type for the contract.</param>
+        </member>
+        <member name="T:Newtonsoft.Json.Serialization.JsonObjectContract">
+            <summary>
+            Contract details for a <see cref="T:System.Type"/> used by the <see cref="T:Newtonsoft.Json.JsonSerializer"/>.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonObjectContract.MemberSerialization">
+            <summary>
+            Gets or sets the object member serialization.
+            </summary>
+            <value>The member object serialization.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonObjectContract.ItemRequired">
+            <summary>
+            Gets or sets a value that indicates whether the object's properties are required.
+            </summary>
+            <value>
+            	A value indicating whether the object's properties are required.
+            </value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonObjectContract.Properties">
+            <summary>
+            Gets the object's properties.
+            </summary>
+            <value>The object's properties.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonObjectContract.ConstructorParameters">
+            <summary>
+            Gets the constructor parameters required for any non-default constructor
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonObjectContract.CreatorParameters">
+            <summary>
+            Gets a collection of <see cref="T:Newtonsoft.Json.Serialization.JsonProperty"/> instances that define the parameters used with <see cref="P:Newtonsoft.Json.Serialization.JsonObjectContract.OverrideCreator"/>.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonObjectContract.OverrideConstructor">
+            <summary>
+            Gets or sets the override constructor used to create the object.
+            This is set when a constructor is marked up using the
+            JsonConstructor attribute.
+            </summary>
+            <value>The override constructor.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonObjectContract.ParametrizedConstructor">
+            <summary>
+            Gets or sets the parametrized constructor used to create the object.
+            </summary>
+            <value>The parametrized constructor.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonObjectContract.OverrideCreator">
+            <summary>
+            Gets or sets the function used to create the object. When set this function will override <see cref="P:Newtonsoft.Json.Serialization.JsonContract.DefaultCreator"/>.
+            This function is called with a collection of arguments which are defined by the <see cref="P:Newtonsoft.Json.Serialization.JsonObjectContract.CreatorParameters"/> collection.
+            </summary>
+            <value>The function used to create the object.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonObjectContract.ExtensionDataSetter">
+            <summary>
+            Gets or sets the extension data setter.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonObjectContract.ExtensionDataGetter">
+            <summary>
+            Gets or sets the extension data getter.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonObjectContract.ExtensionDataValueType">
+            <summary>
+            Gets or sets the extension data value type.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.JsonObjectContract.#ctor(System.Type)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.JsonObjectContract"/> class.
+            </summary>
+            <param name="underlyingType">The underlying type for the contract.</param>
+        </member>
+        <member name="T:Newtonsoft.Json.Serialization.JsonPrimitiveContract">
+            <summary>
+            Contract details for a <see cref="T:System.Type"/> used by the <see cref="T:Newtonsoft.Json.JsonSerializer"/>.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.JsonPrimitiveContract.#ctor(System.Type)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.JsonPrimitiveContract"/> class.
+            </summary>
+            <param name="underlyingType">The underlying type for the contract.</param>
+        </member>
+        <member name="T:Newtonsoft.Json.Serialization.JsonProperty">
+            <summary>
+            Maps a JSON property to a .NET member or constructor parameter.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonProperty.PropertyName">
+            <summary>
+            Gets or sets the name of the property.
+            </summary>
+            <value>The name of the property.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonProperty.DeclaringType">
+            <summary>
+            Gets or sets the type that declared this property.
+            </summary>
+            <value>The type that declared this property.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonProperty.Order">
+            <summary>
+            Gets or sets the order of serialization of a member.
+            </summary>
+            <value>The numeric order of serialization.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonProperty.UnderlyingName">
+            <summary>
+            Gets or sets the name of the underlying member or parameter.
+            </summary>
+            <value>The name of the underlying member or parameter.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonProperty.ValueProvider">
+            <summary>
+            Gets the <see cref="T:Newtonsoft.Json.Serialization.IValueProvider"/> that will get and set the <see cref="T:Newtonsoft.Json.Serialization.JsonProperty"/> during serialization.
+            </summary>
+            <value>The <see cref="T:Newtonsoft.Json.Serialization.IValueProvider"/> that will get and set the <see cref="T:Newtonsoft.Json.Serialization.JsonProperty"/> during serialization.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonProperty.AttributeProvider">
+            <summary>
+            Gets or sets the <see cref="T:Newtonsoft.Json.Serialization.IAttributeProvider"/> for this property.
+            </summary>
+            <value>The <see cref="T:Newtonsoft.Json.Serialization.IAttributeProvider"/> for this property.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonProperty.PropertyType">
+            <summary>
+            Gets or sets the type of the property.
+            </summary>
+            <value>The type of the property.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonProperty.Converter">
+            <summary>
+            Gets or sets the <see cref="T:Newtonsoft.Json.JsonConverter" /> for the property.
+            If set this converter takes presidence over the contract converter for the property type.
+            </summary>
+            <value>The converter.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonProperty.MemberConverter">
+            <summary>
+            Gets or sets the member converter.
+            </summary>
+            <value>The member converter.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonProperty.Ignored">
+            <summary>
+            Gets or sets a value indicating whether this <see cref="T:Newtonsoft.Json.Serialization.JsonProperty"/> is ignored.
+            </summary>
+            <value><c>true</c> if ignored; otherwise, <c>false</c>.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonProperty.Readable">
+            <summary>
+            Gets or sets a value indicating whether this <see cref="T:Newtonsoft.Json.Serialization.JsonProperty"/> is readable.
+            </summary>
+            <value><c>true</c> if readable; otherwise, <c>false</c>.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonProperty.Writable">
+            <summary>
+            Gets or sets a value indicating whether this <see cref="T:Newtonsoft.Json.Serialization.JsonProperty"/> is writable.
+            </summary>
+            <value><c>true</c> if writable; otherwise, <c>false</c>.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonProperty.HasMemberAttribute">
+            <summary>
+            Gets or sets a value indicating whether this <see cref="T:Newtonsoft.Json.Serialization.JsonProperty"/> has a member attribute.
+            </summary>
+            <value><c>true</c> if has a member attribute; otherwise, <c>false</c>.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonProperty.DefaultValue">
+            <summary>
+            Gets the default value.
+            </summary>
+            <value>The default value.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonProperty.Required">
+            <summary>
+            Gets or sets a value indicating whether this <see cref="T:Newtonsoft.Json.Serialization.JsonProperty"/> is required.
+            </summary>
+            <value>A value indicating whether this <see cref="T:Newtonsoft.Json.Serialization.JsonProperty"/> is required.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonProperty.IsReference">
+            <summary>
+            Gets or sets a value indicating whether this property preserves object references.
+            </summary>
+            <value>
+            	<c>true</c> if this instance is reference; otherwise, <c>false</c>.
+            </value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonProperty.NullValueHandling">
+            <summary>
+            Gets or sets the property null value handling.
+            </summary>
+            <value>The null value handling.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonProperty.DefaultValueHandling">
+            <summary>
+            Gets or sets the property default value handling.
+            </summary>
+            <value>The default value handling.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonProperty.ReferenceLoopHandling">
+            <summary>
+            Gets or sets the property reference loop handling.
+            </summary>
+            <value>The reference loop handling.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonProperty.ObjectCreationHandling">
+            <summary>
+            Gets or sets the property object creation handling.
+            </summary>
+            <value>The object creation handling.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonProperty.TypeNameHandling">
+            <summary>
+            Gets or sets or sets the type name handling.
+            </summary>
+            <value>The type name handling.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonProperty.ShouldSerialize">
+            <summary>
+            Gets or sets a predicate used to determine whether the property should be serialize.
+            </summary>
+            <value>A predicate used to determine whether the property should be serialize.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonProperty.ShouldDeserialize">
+            <summary>
+            Gets or sets a predicate used to determine whether the property should be deserialized.
+            </summary>
+            <value>A predicate used to determine whether the property should be deserialized.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonProperty.GetIsSpecified">
+            <summary>
+            Gets or sets a predicate used to determine whether the property should be serialized.
+            </summary>
+            <value>A predicate used to determine whether the property should be serialized.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonProperty.SetIsSpecified">
+            <summary>
+            Gets or sets an action used to set whether the property has been deserialized.
+            </summary>
+            <value>An action used to set whether the property has been deserialized.</value>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.JsonProperty.ToString">
+            <summary>
+            Returns a <see cref="T:System.String"/> that represents this instance.
+            </summary>
+            <returns>
+            A <see cref="T:System.String"/> that represents this instance.
+            </returns>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonProperty.ItemConverter">
+            <summary>
+            Gets or sets the converter used when serializing the property's collection items.
+            </summary>
+            <value>The collection's items converter.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonProperty.ItemIsReference">
+            <summary>
+            Gets or sets whether this property's collection items are serialized as a reference.
+            </summary>
+            <value>Whether this property's collection items are serialized as a reference.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonProperty.ItemTypeNameHandling">
+            <summary>
+            Gets or sets the the type name handling used when serializing the property's collection items.
+            </summary>
+            <value>The collection's items type name handling.</value>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.JsonProperty.ItemReferenceLoopHandling">
+            <summary>
+            Gets or sets the the reference loop handling used when serializing the property's collection items.
+            </summary>
+            <value>The collection's items reference loop handling.</value>
+        </member>
+        <member name="T:Newtonsoft.Json.Serialization.JsonPropertyCollection">
+            <summary>
+            A collection of <see cref="T:Newtonsoft.Json.Serialization.JsonProperty"/> objects.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.JsonPropertyCollection.#ctor(System.Type)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.JsonPropertyCollection"/> class.
+            </summary>
+            <param name="type">The type.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.JsonPropertyCollection.GetKeyForItem(Newtonsoft.Json.Serialization.JsonProperty)">
+            <summary>
+            When implemented in a derived class, extracts the key from the specified element.
+            </summary>
+            <param name="item">The element from which to extract the key.</param>
+            <returns>The key for the specified element.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.JsonPropertyCollection.AddProperty(Newtonsoft.Json.Serialization.JsonProperty)">
+            <summary>
+            Adds a <see cref="T:Newtonsoft.Json.Serialization.JsonProperty"/> object.
+            </summary>
+            <param name="property">The property to add to the collection.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.JsonPropertyCollection.GetClosestMatchProperty(System.String)">
+            <summary>
+            Gets the closest matching <see cref="T:Newtonsoft.Json.Serialization.JsonProperty"/> object.
+            First attempts to get an exact case match of propertyName and then
+            a case insensitive match.
+            </summary>
+            <param name="propertyName">Name of the property.</param>
+            <returns>A matching property if found.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.JsonPropertyCollection.GetProperty(System.String,System.StringComparison)">
+            <summary>
+            Gets a property by property name.
+            </summary>
+            <param name="propertyName">The name of the property to get.</param>
+            <param name="comparisonType">Type property name string comparison.</param>
+            <returns>A matching property if found.</returns>
+        </member>
+        <member name="T:Newtonsoft.Json.Serialization.JsonStringContract">
+            <summary>
+            Contract details for a <see cref="T:System.Type"/> used by the <see cref="T:Newtonsoft.Json.JsonSerializer"/>.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.JsonStringContract.#ctor(System.Type)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.JsonStringContract"/> class.
+            </summary>
+            <param name="underlyingType">The underlying type for the contract.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.JsonTypeReflector.CreateJsonConverterInstance(System.Type,System.Object[])">
+            <summary>
+            Lookup and create an instance of the JsonConverter type described by the argument.
+            </summary>
+            <param name="converterType">The JsonConverter type to create.</param>
+            <param name="converterArgs">Optional arguments to pass to an initializing constructor of the JsonConverter.
+            If null, the default constructor is used.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.JsonTypeReflector.GetJsonConverterCreator(System.Type)">
+            <summary>
+            Create a factory function that can be used to create instances of a JsonConverter described by the 
+            argument type.  The returned function can then be used to either invoke the converter's default ctor, or any 
+            parameterized constructors by way of an object array.
+            </summary>
+        </member>
+        <member name="T:Newtonsoft.Json.Serialization.MemoryTraceWriter">
+            <summary>
+            Represents a trace writer that writes to memory. When the trace message limit is
+            reached then old trace messages will be removed as new messages are added.
+            </summary>
+        </member>
+        <member name="P:Newtonsoft.Json.Serialization.MemoryTraceWriter.LevelFilter">
+            <summary>
+            Gets the <see cref="T:Newtonsoft.Json.TraceLevel"/> that will be used to filter the trace messages passed to the writer.
+            For example a filter level of <code>Info</code> will exclude <code>Verbose</code> messages and include <code>Info</code>,
+            <code>Warning</code> and <code>Error</code> messages.
+            </summary>
+            <value>
+            The <see cref="T:Newtonsoft.Json.TraceLevel"/> that will be used to filter the trace messages passed to the writer.
+            </value>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.MemoryTraceWriter.#ctor">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.MemoryTraceWriter"/> class.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.MemoryTraceWriter.Trace(Newtonsoft.Json.TraceLevel,System.String,System.Exception)">
+            <summary>
+            Writes the specified trace level, message and optional exception.
+            </summary>
+            <param name="level">The <see cref="T:Newtonsoft.Json.TraceLevel"/> at which to write this trace.</param>
+            <param name="message">The trace message.</param>
+            <param name="ex">The trace exception. This parameter is optional.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.MemoryTraceWriter.GetTraceMessages">
+            <summary>
+            Returns an enumeration of the most recent trace messages.
+            </summary>
+            <returns>An enumeration of the most recent trace messages.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.MemoryTraceWriter.ToString">
+            <summary>
+            Returns a <see cref="T:System.String"/> of the most recent trace messages.
+            </summary>
+            <returns>
+            A <see cref="T:System.String"/> of the most recent trace messages.
+            </returns>
+        </member>
+        <member name="T:Newtonsoft.Json.Serialization.ObjectConstructor`1">
+            <summary>
+            Represents a method that constructs an object.
+            </summary>
+            <typeparam name="T">The object type to create.</typeparam>
+        </member>
+        <member name="T:Newtonsoft.Json.Serialization.OnErrorAttribute">
+            <summary>
+            When applied to a method, specifies that the method is called when an error occurs serializing an object.
+            </summary>
+        </member>
+        <member name="T:Newtonsoft.Json.Serialization.ReflectionAttributeProvider">
+            <summary>
+            Provides methods to get attributes from a <see cref="T:System.Type"/>, <see cref="T:System.Reflection.MemberInfo"/>, <see cref="T:System.Reflection.ParameterInfo"/> or <see cref="T:System.Reflection.Assembly"/>.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.ReflectionAttributeProvider.#ctor(System.Object)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.ReflectionAttributeProvider"/> class.
+            </summary>
+            <param name="attributeProvider">The instance to get attributes for. This parameter should be a <see cref="T:System.Type"/>, <see cref="T:System.Reflection.MemberInfo"/>, <see cref="T:System.Reflection.ParameterInfo"/> or <see cref="T:System.Reflection.Assembly"/>.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.ReflectionAttributeProvider.GetAttributes(System.Boolean)">
+            <summary>
+            Returns a collection of all of the attributes, or an empty collection if there are no attributes.
+            </summary>
+            <param name="inherit">When true, look up the hierarchy chain for the inherited custom attribute.</param>
+            <returns>A collection of <see cref="T:System.Attribute"/>s, or an empty collection.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.ReflectionAttributeProvider.GetAttributes(System.Type,System.Boolean)">
+            <summary>
+            Returns a collection of attributes, identified by type, or an empty collection if there are no attributes.
+            </summary>
+            <param name="attributeType">The type of the attributes.</param>
+            <param name="inherit">When true, look up the hierarchy chain for the inherited custom attribute.</param>
+            <returns>A collection of <see cref="T:System.Attribute"/>s, or an empty collection.</returns>
+        </member>
+        <member name="T:Newtonsoft.Json.Serialization.ReflectionValueProvider">
+            <summary>
+            Get and set values for a <see cref="T:System.Reflection.MemberInfo"/> using reflection.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.ReflectionValueProvider.#ctor(System.Reflection.MemberInfo)">
+            <summary>
+            Initializes a new instance of the <see cref="T:Newtonsoft.Json.Serialization.ReflectionValueProvider"/> class.
+            </summary>
+            <param name="memberInfo">The member info.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.ReflectionValueProvider.SetValue(System.Object,System.Object)">
+            <summary>
+            Sets the value.
+            </summary>
+            <param name="target">The target to set the value on.</param>
+            <param name="value">The value to set on the target.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Serialization.ReflectionValueProvider.GetValue(System.Object)">
+            <summary>
+            Gets the value.
+            </summary>
+            <param name="target">The target to get the value from.</param>
+            <returns>The value.</returns>
+        </member>
+        <member name="T:Newtonsoft.Json.StringEscapeHandling">
+            <summary>
+            Specifies how strings are escaped when writing JSON text.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.StringEscapeHandling.Default">
+            <summary>
+            Only control characters (e.g. newline) are escaped.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.StringEscapeHandling.EscapeNonAscii">
+            <summary>
+            All non-ASCII and control characters (e.g. newline) are escaped.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.StringEscapeHandling.EscapeHtml">
+            <summary>
+            HTML (&lt;, &gt;, &amp;, &apos;, &quot;) and control characters (e.g. newline) are escaped.
+            </summary>
+        </member>
+        <member name="T:Newtonsoft.Json.TraceLevel">
+            <summary>
+            Specifies what messages to output for the <see cref="T:Newtonsoft.Json.Serialization.ITraceWriter"/> class.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.TraceLevel.Off">
+            <summary>
+            Output no tracing and debugging messages.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.TraceLevel.Error">
+            <summary>
+            Output error-handling messages.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.TraceLevel.Warning">
+            <summary>
+            Output warnings and error-handling messages.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.TraceLevel.Info">
+            <summary>
+            Output informational messages, warnings, and error-handling messages.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.TraceLevel.Verbose">
+            <summary>
+            Output all debugging and tracing messages.
+            </summary>
+        </member>
+        <member name="T:Newtonsoft.Json.TypeNameHandling">
+            <summary>
+            Specifies type name handling options for the <see cref="T:Newtonsoft.Json.JsonSerializer"/>.
+            </summary>
+            <remarks>
+            <see cref="T:Newtonsoft.Json.TypeNameHandling"/> should be used with caution when your application deserializes JSON from an external source.
+            Incoming types should be validated with a custom <see cref="T:System.Runtime.Serialization.SerializationBinder"/>
+            when deserializing with a value other than <c>TypeNameHandling.None</c>.
+            </remarks>
+        </member>
+        <member name="F:Newtonsoft.Json.TypeNameHandling.None">
+            <summary>
+            Do not include the .NET type name when serializing types.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.TypeNameHandling.Objects">
+            <summary>
+            Include the .NET type name when serializing into a JSON object structure.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.TypeNameHandling.Arrays">
+            <summary>
+            Include the .NET type name when serializing into a JSON array structure.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.TypeNameHandling.All">
+            <summary>
+            Always include the .NET type name when serializing.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.TypeNameHandling.Auto">
+            <summary>
+            Include the .NET type name when the type of the object being serialized is not the same as its declared type.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Utilities.CollectionUtils.IsNullOrEmpty``1(System.Collections.Generic.ICollection{``0})">
+            <summary>
+            Determines whether the collection is null or empty.
+            </summary>
+            <param name="collection">The collection.</param>
+            <returns>
+            	<c>true</c> if the collection is null or empty; otherwise, <c>false</c>.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Utilities.CollectionUtils.AddRange``1(System.Collections.Generic.IList{``0},System.Collections.Generic.IEnumerable{``0})">
+            <summary>
+            Adds the elements of the specified collection to the specified generic IList.
+            </summary>
+            <param name="initial">The list to add to.</param>
+            <param name="collection">The collection of elements to add.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Utilities.ConvertUtils.ConvertOrCast(System.Object,System.Globalization.CultureInfo,System.Type)">
+            <summary>
+            Converts the value to the specified type. If the value is unable to be converted, the
+            value is checked whether it assignable to the specified type.
+            </summary>
+            <param name="initialValue">The value to convert.</param>
+            <param name="culture">The culture to use when converting.</param>
+            <param name="targetType">The type to convert or cast the value to.</param>
+            <returns>
+            The converted type. If conversion was unsuccessful, the initial value
+            is returned if assignable to the target type.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Utilities.DynamicProxyMetaObject`1.CallMethodWithResult(System.String,System.Dynamic.DynamicMetaObjectBinder,System.Linq.Expressions.Expression[],Newtonsoft.Json.Utilities.DynamicProxyMetaObject{`0}.Fallback,Newtonsoft.Json.Utilities.DynamicProxyMetaObject{`0}.Fallback)">
+            <summary>
+            Helper method for generating a MetaObject which calls a
+            specific method on Dynamic that returns a result
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Utilities.DynamicProxyMetaObject`1.CallMethodReturnLast(System.String,System.Dynamic.DynamicMetaObjectBinder,System.Linq.Expressions.Expression[],Newtonsoft.Json.Utilities.DynamicProxyMetaObject{`0}.Fallback)">
+            <summary>
+            Helper method for generating a MetaObject which calls a
+            specific method on Dynamic, but uses one of the arguments for
+            the result.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Utilities.DynamicProxyMetaObject`1.CallMethodNoResult(System.String,System.Dynamic.DynamicMetaObjectBinder,System.Linq.Expressions.Expression[],Newtonsoft.Json.Utilities.DynamicProxyMetaObject{`0}.Fallback)">
+            <summary>
+            Helper method for generating a MetaObject which calls a
+            specific method on Dynamic, but uses one of the arguments for
+            the result.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Utilities.DynamicProxyMetaObject`1.GetRestrictions">
+            <summary>
+            Returns a Restrictions object which includes our current restrictions merged
+            with a restriction limiting our type
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Utilities.EnumUtils.GetNamesAndValues``1">
+            <summary>
+            Gets a dictionary of the names and values of an Enum type.
+            </summary>
+            <returns></returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Utilities.EnumUtils.GetNamesAndValues``1(System.Type)">
+            <summary>
+            Gets a dictionary of the names and values of an Enum type.
+            </summary>
+            <param name="enumType">The enum type to get names and values for.</param>
+            <returns></returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Utilities.ReflectionUtils.GetCollectionItemType(System.Type)">
+            <summary>
+            Gets the type of the typed collection's items.
+            </summary>
+            <param name="type">The type.</param>
+            <returns>The type of the typed collection's items.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Utilities.ReflectionUtils.GetMemberUnderlyingType(System.Reflection.MemberInfo)">
+            <summary>
+            Gets the member's underlying type.
+            </summary>
+            <param name="member">The member.</param>
+            <returns>The underlying type of the member.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Utilities.ReflectionUtils.IsIndexedProperty(System.Reflection.MemberInfo)">
+            <summary>
+            Determines whether the member is an indexed property.
+            </summary>
+            <param name="member">The member.</param>
+            <returns>
+            	<c>true</c> if the member is an indexed property; otherwise, <c>false</c>.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Utilities.ReflectionUtils.IsIndexedProperty(System.Reflection.PropertyInfo)">
+            <summary>
+            Determines whether the property is an indexed property.
+            </summary>
+            <param name="property">The property.</param>
+            <returns>
+            	<c>true</c> if the property is an indexed property; otherwise, <c>false</c>.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Utilities.ReflectionUtils.GetMemberValue(System.Reflection.MemberInfo,System.Object)">
+            <summary>
+            Gets the member's value on the object.
+            </summary>
+            <param name="member">The member.</param>
+            <param name="target">The target object.</param>
+            <returns>The member's value on the object.</returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Utilities.ReflectionUtils.SetMemberValue(System.Reflection.MemberInfo,System.Object,System.Object)">
+            <summary>
+            Sets the member's value on the target object.
+            </summary>
+            <param name="member">The member.</param>
+            <param name="target">The target.</param>
+            <param name="value">The value.</param>
+        </member>
+        <member name="M:Newtonsoft.Json.Utilities.ReflectionUtils.CanReadMemberValue(System.Reflection.MemberInfo,System.Boolean)">
+            <summary>
+            Determines whether the specified MemberInfo can be read.
+            </summary>
+            <param name="member">The MemberInfo to determine whether can be read.</param>
+            /// <param name="nonPublic">if set to <c>true</c> then allow the member to be gotten non-publicly.</param>
+            <returns>
+            	<c>true</c> if the specified MemberInfo can be read; otherwise, <c>false</c>.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Utilities.ReflectionUtils.CanSetMemberValue(System.Reflection.MemberInfo,System.Boolean,System.Boolean)">
+            <summary>
+            Determines whether the specified MemberInfo can be set.
+            </summary>
+            <param name="member">The MemberInfo to determine whether can be set.</param>
+            <param name="nonPublic">if set to <c>true</c> then allow the member to be set non-publicly.</param>
+            <param name="canSetReadOnly">if set to <c>true</c> then allow the member to be set if read-only.</param>
+            <returns>
+            	<c>true</c> if the specified MemberInfo can be set; otherwise, <c>false</c>.
+            </returns>
+        </member>
+        <member name="T:Newtonsoft.Json.Utilities.StringBuffer">
+            <summary>
+            Builds a string. Unlike StringBuilder this class lets you reuse it's internal buffer.
+            </summary>
+        </member>
+        <member name="M:Newtonsoft.Json.Utilities.StringUtils.IsWhiteSpace(System.String)">
+            <summary>
+            Determines whether the string is all white space. Empty string will return false.
+            </summary>
+            <param name="s">The string to test whether it is all white space.</param>
+            <returns>
+            	<c>true</c> if the string is all white space; otherwise, <c>false</c>.
+            </returns>
+        </member>
+        <member name="M:Newtonsoft.Json.Utilities.StringUtils.NullEmptyString(System.String)">
+            <summary>
+            Nulls an empty string.
+            </summary>
+            <param name="s">The string.</param>
+            <returns>Null if the string was null, otherwise the string unchanged.</returns>
+        </member>
+        <member name="T:Newtonsoft.Json.WriteState">
+            <summary>
+            Specifies the state of the <see cref="T:Newtonsoft.Json.JsonWriter"/>.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.WriteState.Error">
+            <summary>
+            An exception has been thrown, which has left the <see cref="T:Newtonsoft.Json.JsonWriter"/> in an invalid state.
+            You may call the <see cref="M:Newtonsoft.Json.JsonWriter.Close"/> method to put the <see cref="T:Newtonsoft.Json.JsonWriter"/> in the <c>Closed</c> state.
+            Any other <see cref="T:Newtonsoft.Json.JsonWriter"/> method calls results in an <see cref="T:System.InvalidOperationException"/> being thrown. 
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.WriteState.Closed">
+            <summary>
+            The <see cref="M:Newtonsoft.Json.JsonWriter.Close"/> method has been called. 
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.WriteState.Object">
+            <summary>
+            An object is being written. 
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.WriteState.Array">
+            <summary>
+            A array is being written.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.WriteState.Constructor">
+            <summary>
+            A constructor is being written.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.WriteState.Property">
+            <summary>
+            A property is being written.
+            </summary>
+        </member>
+        <member name="F:Newtonsoft.Json.WriteState.Start">
+            <summary>
+            A write method has not been called.
+            </summary>
+        </member>
+        <member name="T:System.Runtime.Serialization.Formatters.FormatterAssemblyStyle">
+            <summary>
+            Indicates the method that will be used during deserialization for locating and loading assemblies.
+            </summary>
+        </member>
+        <member name="F:System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Simple">
+            <summary>
+            In simple mode, the assembly used during deserialization need not match exactly the assembly used during serialization. Specifically, the version numbers need not match as the LoadWithPartialName method is used to load the assembly.
+            </summary>
+        </member>
+        <member name="F:System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Full">
+            <summary>
+            In full mode, the assembly used during deserialization must match exactly the assembly used during serialization. The Load method of the Assembly class is used to load the assembly.
+            </summary>
+        </member>
+    </members>
+</doc>
diff --git a/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink/Newtonsoft.Json.XML.meta b/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink/Newtonsoft.Json.XML.meta
new file mode 100644
index 0000000000000000000000000000000000000000..c7989d433a09161dae9cf71818e4212836ced859
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink/Newtonsoft.Json.XML.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: bf169e977b9c9fb43a1322b2a6274587
+TextScriptImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink/OafIpc.dll b/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink/OafIpc.dll
new file mode 100644
index 0000000000000000000000000000000000000000..04577047b23a7ba10dcca0cd25245e2e410617bc
Binary files /dev/null and b/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink/OafIpc.dll differ
diff --git a/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink/OafIpc.dll.meta b/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink/OafIpc.dll.meta
new file mode 100644
index 0000000000000000000000000000000000000000..657492f5091a0bcde134258cd916d9999c8997a0
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink/OafIpc.dll.meta
@@ -0,0 +1,95 @@
+fileFormatVersion: 2
+guid: c096e0eeee7560642813dfd775734519
+PluginImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  iconMap: {}
+  executionOrder: {}
+  defineConstraints: []
+  isPreloaded: 0
+  isOverridable: 0
+  isExplicitlyReferenced: 0
+  validateReferences: 1
+  platformData:
+  - first:
+      : Any
+    second:
+      enabled: 0
+      settings:
+        Exclude Android: 1
+        Exclude Editor: 0
+        Exclude Linux: 0
+        Exclude Linux64: 0
+        Exclude LinuxUniversal: 0
+        Exclude OSXUniversal: 0
+        Exclude Win: 0
+        Exclude Win64: 0
+  - first:
+      Android: Android
+    second:
+      enabled: 0
+      settings:
+        CPU: ARMv7
+  - first:
+      Any: 
+    second:
+      enabled: 1
+      settings: {}
+  - first:
+      Editor: Editor
+    second:
+      enabled: 1
+      settings:
+        CPU: AnyCPU
+        DefaultValueInitialized: true
+        OS: AnyOS
+  - first:
+      Facebook: Win
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  - first:
+      Facebook: Win64
+    second:
+      enabled: 0
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: Linux
+    second:
+      enabled: 1
+      settings:
+        CPU: x86
+  - first:
+      Standalone: Linux64
+    second:
+      enabled: 1
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: LinuxUniversal
+    second:
+      enabled: 1
+      settings: {}
+  - first:
+      Standalone: OSXUniversal
+    second:
+      enabled: 1
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: Win
+    second:
+      enabled: 1
+      settings:
+        CPU: AnyCPU
+  - first:
+      Standalone: Win64
+    second:
+      enabled: 1
+      settings:
+        CPU: AnyCPU
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink/README.txt b/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink/README.txt
new file mode 100644
index 0000000000000000000000000000000000000000..4b3e76ccb506fca818b4ccbf6641d625d78b573f
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink/README.txt
@@ -0,0 +1,4 @@
+The DLLs in this folder are only used for Avatar Editor Deeplinking. If you do not plan to open the avatar editor from your app, follow these steps to remove the dlls from your builds:
+1) Add DISABLE_IPC_CONNECTOR to your Scripting Define Symbols
+2) Remove all calls to AvatarEditorDeeplink.LaunchAvatarEditor()
+3) Delete the AvatarEditorDeeplink folder and its contents
\ No newline at end of file
diff --git a/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink/README.txt.meta b/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink/README.txt.meta
new file mode 100644
index 0000000000000000000000000000000000000000..fa648998bffc96484c6d23e0b6a5a7864a305adb
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/AvatarEditorDeeplink/README.txt.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: f2d0cc772f747ad4482b5649ff71466c
+TextScriptImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/CAPI.meta b/Assets/Oculus/Avatar2/Scripts/CAPI.meta
new file mode 100644
index 0000000000000000000000000000000000000000..a67319d9dd11a1188cc7df37a908521cb7ea32f8
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/CAPI.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: e765eaafd0cc9a64ebcb1f1c0a03cbeb
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Asset.cs b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Asset.cs
new file mode 100644
index 0000000000000000000000000000000000000000..954a11a6825e1492c36d925f6774dd71ebe51aa9
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Asset.cs
@@ -0,0 +1,650 @@
+using System;
+using System.Runtime.InteropServices;
+
+using Unity.Collections;
+
+namespace Oculus.Avatar2
+{
+    public partial class CAPI
+    {
+        //-----------------------------------------------------------------
+        // For the following ovrAvatar2Primitive_GetVertex* and ovrAvatar2MorphTarget_GetVertex* functions,
+        // the buffer, bytes, and stride can be set to 0 to check the existence of that attribute.
+        // If it is not available, the result will be ovrAvatar2Result_DataNotAvailable
+        //
+
+        //-----------------------------------------------------------------
+        //
+        // Diagnostic
+        //
+        //
+
+        private const string assetLogScope = "OvrAvatarAPI_Asset";
+
+        // Get the number of assets which are still loading
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern UInt32 ovrAvatar2Asset_GetNumberOutstandingAssets();
+
+        // Get the number of assets which are still loading for an entity
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern UInt32 ovrAvatar2Asset_GetEntityPendingCount(ovrAvatar2EntityId entityId);
+
+        //-----------------------------------------------------------------
+        //
+        // Load Request
+        //
+        //
+
+        public enum ovrAvatar2LoadRequestState : Int32
+        {
+            /// Invalid state
+            None = 0
+
+            ,
+
+            /// User Avatar specification requested
+            PendingSpecification
+
+            ,
+
+            /// CDN asset load in progress (network or from cache)
+            CdnLoad
+
+            ,
+
+            /// Loading assets from URI
+            LoadFromUri
+
+            ,
+
+            /// Loading assets from memory
+            LoadFromMemory
+
+            ,
+
+            /// Parsing asset files after load
+            ParseAsset
+
+            ,
+
+            /// Awaiting "ready to render" from client
+            ClientLoad
+
+            ,
+
+            /// Load successful; assets have been applied to the entity. hierarchyVersion/allNodesVersion likely changed
+            Success
+
+            ,
+
+            /// Failed; see LoadFailedReason for details
+            Failed
+
+            ,
+
+            /// Cancelled before completion (e.g. entity destroyed)
+            Cancelled
+        }
+
+        public enum ovrAvatar2LoadRequestFailure : Int32
+        {
+            None = 0
+            , CdnLoadInProgress
+            , NoAssetProfile
+            , SpecRequestFailed
+            , SpecParseFailed
+            , SpecRequestCancelled
+            , MissingAvatar
+            , SpecHadInvalidAnimSet
+            , SpecHadInvalidModel
+            , InvalidUri
+            , AssetNotFound
+            , MismatchedLoadFilters
+            , HttpLoadFailed
+            , CacheLoadFailed
+            , DiskLoadFailed
+            , ParseFailed
+            , Unknown
+            ,
+        }
+
+        public enum ovrAvatar2LoadRequestType : Int32
+        {
+            User
+            , Memory
+            , Uri
+            ,
+        }
+
+        ///
+        /// Provides information about the state of an asset load from a specific call to LoadUser
+        /// or a similar function.
+        public struct ovrAvatar2LoadRequestInfo
+        {
+            public ovrAvatar2LoadRequestId id;
+            public ovrAvatar2EntityId entityId;
+            public ovrAvatar2LoadRequestState state;
+            public ovrAvatar2LoadRequestFailure failedReason;
+            public ovrAvatar2LoadRequestType type;
+            public Int64 responseCode;
+        }
+
+        ///
+        /// Get the state of a specific load request
+        /// \param loadRequestId to load - obtained from a call to LoadUser or similar function
+        /// \param loadRequestInfo (out)
+        /// \return result code
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Asset_GetLoadRequestInfo(
+            ovrAvatar2LoadRequestId loadRequestId, out ovrAvatar2LoadRequestInfo loadRequestInfo);
+
+        //-----------------------------------------------------------------
+        //
+        // Resource
+        //
+        //
+
+        public enum ovrAvatar2AssetStatus : Int32
+        {
+            ovrAvatar2AssetStatus_LoadFailed = 0
+            , ovrAvatar2AssetStatus_Loaded
+            , ovrAvatar2AssetStatus_Unloaded
+            , ovrAvatar2AssetStatus_Updated
+            ,
+        }
+
+        [StructLayout(LayoutKind.Sequential)]
+        public struct ovrAvatar2Asset_Resource
+        {
+            public ovrAvatar2AssetStatus status;
+            public ovrAvatar2Id assetID; // The asset id
+        }
+
+        /// Release a resource after it has been loaded by the client application
+        /// Can be called from any thread
+        /// \param resourceID provided by ovrAvatar2Asset_ResourceCallback
+        /// \return result code
+        ///
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern ovrAvatar2Result ovrAvatar2Asset_ReleaseResource(ovrAvatar2Id resourceId);
+
+        /// Notify the Avatar runtime that the client application is ready to render the resource
+        /// Can be called from any thread
+        /// \param resourceID provided by ovrAvatar2Asset_ResourceCallback
+        /// \return result code
+        ///
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern ovrAvatar2Result ovrAvatar2Asset_ResourceReadyToRender(ovrAvatar2Id resourceID);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
+        public static extern unsafe ovrAvatar2Result ovrAvatar2Asset_GetName(
+            ovrAvatar2Id id, byte* nameBuffer, UInt32 bufferByteSize);
+
+
+        internal static bool OvrAvatarAsset_ReleaseResource(ovrAvatar2Id resourceId)
+        {
+            return ovrAvatar2Asset_ReleaseResource(resourceId)
+                .EnsureSuccess("ovrAvatar2Asset_ReleaseResource");
+        }
+
+        internal static bool OvrAvatarAsset_ResourceReadyToRender(ovrAvatar2Id resourceId)
+        {
+            // TODO: Call this at an appropriate later time when all Unity side processing is complete
+            return ovrAvatar2Asset_ResourceReadyToRender(resourceId)
+                .EnsureSuccess("ovrAvatar2Asset_ResourceReadyToRender");
+        }
+
+
+        //
+        // Primitive
+        //
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Asset_GetPrimitiveCount(
+            ovrAvatar2Id resourceId, out UInt32 count);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Asset_GetPrimitiveByIndex(
+            ovrAvatar2Id resourceId, UInt32 primitiveIndex, out ovrAvatar2Primitive primitive);
+
+        //
+        // Image
+        //
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Asset_GetImageCount(
+            ovrAvatar2Id resourceId, out UInt32 count);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Asset_GetImageByIndex(
+            ovrAvatar2Id resourceId, UInt32 imageIndex, out ovrAvatar2Image image);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Asset_GetImageDataByIndex(
+            ovrAvatar2Id resourceId, UInt32 imageIndex, /*byte[]*/ IntPtr buffer, UInt32 bufferSize);
+
+
+        //-----------------------------------------------------------------
+        //
+        // Image
+        //
+        //
+
+        /**
+         * Enumerates the possible image formats
+         * for an avatar texture.
+         * @see ovrAvatar2Image
+         */
+        public enum ovrAvatar2ImageFormat : UInt32
+        {
+            ///<summary>Invalid image format</summary>
+            Invalid = 0,
+            RGBA32 = 0xe3dd9a1e, ///< RGBA 32bit uncompressed texture
+            DXT1 = 0xb9ee766e, ///< DXT1/BC1 compressed texture
+            DXT5 = 0x9a853814, ///< DXT5/BC3 compressed texture
+            BC5U = 0xcee1cf1a, ///< BC5 compressed texture (unsigned)
+            BC5S = 0x57c603f3, ///< BC5 compressed texture (signed)
+            BC7U = 0xaa33790d, ///< BC7 compressed texture (unsigned)
+            ASTC_RGBA_4x4 = 0xdc2b8f4c, ///< ASTC 4x4 compressed texture
+            ASTC_RGBA_6x6 = 0xbd4fed74, ///< ASTC 6x6 compressed texture
+            ASTC_RGBA_8x8 = 0xa75606e7, ///< ASTC 8x8 compressed texture
+            ASTC_RGBA_12x12 = 0x7dfcb3d0, ///< ASTC 12x12 compressed texture
+        }
+
+        /**
+         * Describes a 2D image used by an avatar asset.
+         * The image is referenced by a unique ID.
+         * @see ovrAvatar2ImageFormat
+         */
+        [StructLayout(LayoutKind.Sequential)]
+        public struct ovrAvatar2Image
+        {
+            public ovrAvatar2Id id; ///< unique id used to reference this image
+            public ovrAvatar2ImageFormat format; ///< Image format type
+
+            public UInt32 sizeX; ///< Image X dimension
+            public UInt32 sizeY; ///< Image Y dimension
+            public UInt32 mipCount; ///< Number of mipmap levels
+            public UInt32 imageDataSize; ///< Image buffer size
+        };
+
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
+        public static extern unsafe CAPI.ovrAvatar2Result ovrAvatar2Asset_GetImageName(
+            ovrAvatar2Id primitiveId, byte* nameBuffer, UInt32 bufferByteSize);
+
+        //-----------------------------------------------------------------
+        //
+        // Primitive
+        //
+        //
+
+        public enum ovrAvatar2AlphaMode : Int32
+        {
+            Opaque = 0,
+            Mask = 1,
+            Blend = 2,
+        }
+
+        [StructLayout(LayoutKind.Sequential)]
+        public struct ovrAvatar2Primitive
+        {
+            public ovrAvatar2Id id; // unique id used to reference this mesh
+            public ovrAvatar2VertexBufferId vertexBufferId;
+            public ovrAvatar2MorphTargetBufferId morphTargetBufferId;
+            public UInt32 indexCount;
+            public UInt16 minIndexValue;
+            public UInt16 maxIndexValue;
+            public ovrAvatar2AlphaMode alphaMode; // The alpha rendering mode of the primitive
+            public UInt32 textureCount;
+            public UInt32 jointCount;
+            public UInt32 skeleton;
+        }
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
+        public static extern unsafe CAPI.ovrAvatar2Result ovrAvatar2Asset_GetPrimitiveName(
+            ovrAvatar2Id primitiveId, byte* nameBuffer, UInt32 bufferByteSize);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern CAPI.ovrAvatar2Result ovrAvatar2Primitive_GetMinMaxPosition(
+            ovrAvatar2Id primitiveId, out ovrAvatar2Vector3f minPosition, out ovrAvatar2Vector3f maxPosition);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern CAPI.ovrAvatar2Result ovrAvatar2Primitive_GetSkinnedMinMaxPosition(
+            ovrAvatar2Id primitiveId, out ovrAvatar2Vector3f minPosition, out ovrAvatar2Vector3f maxPosition);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern CAPI.ovrAvatar2Result ovrAvatar2Asset_GetLodFlags(
+            ovrAvatar2Id primitiveId,
+            out ovrAvatar2EntityLODFlags lodFlags);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern CAPI.ovrAvatar2Result ovrAvatar2Asset_GetManifestationFlags(
+            ovrAvatar2Id primitiveId, out ovrAvatar2EntityManifestationFlags manifestationFlags);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern CAPI.ovrAvatar2Result ovrAvatar2Asset_GetViewFlags(
+            ovrAvatar2Id primitiveId, out ovrAvatar2EntityViewFlags viewFlags);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern CAPI.ovrAvatar2Result ovrAvatar2Asset_GetSubMeshInclusionFlags(
+            ovrAvatar2Id primitiveId, out ovrAvatar2EntitySubMeshInclusionFlags subMeshInclusionFlags);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern CAPI.ovrAvatar2Result ovrAvatar2Asset_GetHighQualityFlags(
+            ovrAvatar2Id primitiveId, out ovrAvatar2EntityHighQualityFlags highQualityFlags);
+
+        //-----------------------------------------------------------------
+        //
+        // Index Buffer
+        //
+        //
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static unsafe extern CAPI.ovrAvatar2Result ovrAvatar2Primitive_GetIndexData(
+            ovrAvatar2Id primitiveId, UInt16* indexBuffer, UInt32 bytes);
+
+        public static bool OvrAvatar2Primitive_GetIndexData(
+            ovrAvatar2Id primitiveId, in NativeArray<UInt16> indexBuffer, UInt32 bytes)
+        {
+            unsafe
+            {
+                return ovrAvatar2Primitive_GetIndexData(primitiveId, indexBuffer.GetPtr(), bytes)
+                    .EnsureSuccess("ovrAvatar2Primitive_GetIndexData", assetLogScope);
+            }
+        }
+
+
+        //-----------------------------------------------------------------
+        //
+        // Vertex Buffer
+        //
+        //
+
+        // Get the vertex count of a vertex buffer
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern CAPI.ovrAvatar2Result ovrAvatar2VertexBuffer_GetVertexCount(
+            ovrAvatar2VertexBufferId id, out UInt32 vertexCount);
+
+        // Get the minimum x,y,z and maximum x,y,z values for positions in the vertex buffer
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern CAPI.ovrAvatar2Result ovrAvatar2VertexBuffer_GetMinMaxPosition(
+            ovrAvatar2VertexBufferId id,
+            out ovrAvatar2Vector3f minPosition,
+            out ovrAvatar2Vector3f maxPosition);
+
+        // Get vertex buffer position data
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern CAPI.ovrAvatar2Result ovrAvatar2VertexBuffer_GetPositions(
+            ovrAvatar2VertexBufferId vertexBufferId, /*ovrAvatar2Vector3f[]*/ IntPtr buffer, UInt32 bytes,
+            UInt32 stride);
+
+        // Get vertex buffer normal data
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern CAPI.ovrAvatar2Result ovrAvatar2VertexBuffer_GetNormals(
+            ovrAvatar2VertexBufferId vertexBufferId, /*ovrAvatar2Vector3f[]*/ IntPtr buffer, UInt32 bytes,
+            UInt32 stride);
+
+        // Get vertex buffer tangent data
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern CAPI.ovrAvatar2Result ovrAvatar2VertexBuffer_GetTangents(
+            ovrAvatar2VertexBufferId vertexBufferId, /*ovrAvatar2Vector4f[]*/ IntPtr buffer, UInt32 bytes,
+            UInt32 stride);
+
+        // Get vertex buffer color data
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern CAPI.ovrAvatar2Result ovrAvatar2VertexBuffer_GetColors(
+            ovrAvatar2VertexBufferId vertexBufferId, /*ovrAvatar2Vector4f[]*/ IntPtr buffer, UInt32 bytes,
+            UInt32 stride);
+
+        // Get vertex buffer ORMT "color" data
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern CAPI.ovrAvatar2Result ovrAvatar2VertexBuffer_GetColorsORMT(
+            ovrAvatar2VertexBufferId vertexBufferId, /*ovrAvatar2Vector4f[]*/ IntPtr buffer, UInt32 bytes,
+            UInt32 stride);
+
+        // Get vertex buffer texcoord0 data
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern CAPI.ovrAvatar2Result ovrAvatar2VertexBuffer_GetTexCoord0(
+            ovrAvatar2VertexBufferId vertexBufferId, /*ovrAvatar2Vector2f[]*/ IntPtr buffer, UInt32 bytes,
+            UInt32 stride);
+
+        // Get vertex buffer joint index data
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern CAPI.ovrAvatar2Result ovrAvatar2VertexBuffer_GetJointIndices(
+            ovrAvatar2VertexBufferId vertexBufferId, /*ovrAvatar2Vector4us[]*/ IntPtr buffer, UInt32 bytes,
+            UInt32 stride);
+
+        // Get vertex buffer joint weight data
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern CAPI.ovrAvatar2Result ovrAvatar2VertexBuffer_GetJointWeights(
+            ovrAvatar2VertexBufferId vertexBufferId, /*ovrAvatar2Vector4f[]*/ IntPtr buffer, UInt32 bytes,
+            UInt32 stride);
+
+        // Get vertex buffer submesh ID data
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern CAPI.ovrAvatar2Result ovrAvatar2VertexBuffer_GetSubMeshIds(
+            ovrAvatar2Id primitiveId, ovrAvatar2VertexBufferId vertexBufferId, /*Uint32[]*/ IntPtr buffer, UInt32 bytes,
+            UInt32 stride);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern CAPI.ovrAvatar2Result ovrAvatar2VertexBuffer_GetSubMeshIdsFloat(
+            ovrAvatar2Id primitiveId, ovrAvatar2VertexBufferId vertexBufferId, /*float[]*/ IntPtr buffer, UInt32 bytes,
+            UInt32 stride);
+
+        // Get vertex buffer submesh types
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern CAPI.ovrAvatar2Result ovrAvatar2VertexBuffer_GetSubMeshTypes(
+            ovrAvatar2Id primitiveId, ovrAvatar2VertexBufferId vertexBufferId, /*Uint32[]*/ IntPtr buffer, UInt32 bytes,
+            UInt32 stride);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern CAPI.ovrAvatar2Result ovrAvatar2VertexBuffer_GetSubMeshTypesFloat(
+            ovrAvatar2Id primitiveId, ovrAvatar2VertexBufferId vertexBufferId, /*float[]*/ IntPtr buffer, UInt32 bytes,
+            UInt32 stride);
+
+        //-----------------------------------------------------------------
+        //
+        // Morph Target
+        //
+        //
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
+        public static extern CAPI.ovrAvatar2Result ovrAvatar2MorphTarget_GetByName(
+            ovrAvatar2MorphTargetBufferId primitiveId, string name, out UInt32 morphTargetIndex);
+
+
+        // Get the morph target count of a vertex buffer
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2VertexBuffer_GetMorphTargetCount(
+            ovrAvatar2MorphTargetBufferId id, out UInt32 morphTargetCount);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern unsafe ovrAvatar2Result ovrAvatar2MorphTarget_GetVertexPositions(
+            ovrAvatar2MorphTargetBufferId primitiveId, UInt32 morphTargetIndex, ovrAvatar2Vector3f* buffer,
+            UInt32 bytes, UInt32 stride);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern unsafe CAPI.ovrAvatar2Result ovrAvatar2MorphTarget_GetVertexNormals(
+            ovrAvatar2MorphTargetBufferId primitiveId, UInt32 morphTargetIndex, ovrAvatar2Vector3f* buffer,
+            UInt32 bytes, UInt32 stride);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern unsafe CAPI.ovrAvatar2Result ovrAvatar2MorphTarget_GetVertexTangents(
+            ovrAvatar2MorphTargetBufferId primitiveId, UInt32 morphTargetIndex, ovrAvatar2Vector3f* buffer,
+            UInt32 bytes, UInt32 stride);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
+        internal static extern unsafe CAPI.ovrAvatar2Result ovrAvatar2Asset_GetMorphTargetName(
+            ovrAvatar2MorphTargetBufferId primitiveId, UInt32 morphTargetIndex, byte* nameBuffer,
+            UInt32 bufferByteSize);
+
+
+        //-----------------------------------------------------------------
+        //
+        // Material
+        //
+        //
+
+        public enum ovrAvatar2MaterialTextureType : Int32
+        {
+            BaseColor = 0, // sRGB color space, linear alpha
+            Normal = 1, // Linear color space
+            Occulusion = 2, // Linear color space
+            MetallicRoughness = 3, // Linear color space
+            Emissive = 4, // sRGB color space, linear alpha
+            UsedInExtension = 5, // Handled by material extensions
+        }
+
+        // For MetallicRoughness, x = metallic factor, y = roughness factor
+        [StructLayout(LayoutKind.Sequential)]
+        public struct ovrAvatar2MaterialTexture
+        {
+            public ovrAvatar2MaterialTextureType type;
+            public ovrAvatar2Vector4f factor;
+            public ovrAvatar2Id imageId; // id of the image for the texture
+        };
+
+        public enum ovrAvatar2MaterialExtensionEntryType : Int32
+        {
+            ImageId = 0,
+            Float = 1,
+            Int = 2,
+            Vector3f = 3,
+            Vector4f = 4,
+            Invalid = Int32.MaxValue,
+        };
+
+        [StructLayout(LayoutKind.Sequential)]
+        public struct ovrAvatar2MaterialExtensionEntry
+        {
+            public ovrAvatar2MaterialExtensionEntryType entryType;
+            public UInt32 nameBufferSize; // with null terminator
+            public UInt32 dataBufferSize;
+        };
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern CAPI.ovrAvatar2Result ovrAvatar2Primitive_GetMaterialTextureByIndex(
+            ovrAvatar2Id primitiveId, UInt32 materialTextureIndex, out ovrAvatar2MaterialTexture materialTexture);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
+        public static extern unsafe CAPI.ovrAvatar2Result ovrAvatar2Asset_GetPrimitiveMaterialName(
+            ovrAvatar2Id primitiveId, byte* nameBuffer, UInt32 bufferByteSize);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
+        public static extern unsafe CAPI.ovrAvatar2Result ovrAvatar2Asset_DeduceMaterialSubMeshFromName(
+            ovrAvatar2EntitySubMeshInclusionFlags* destType, byte* matName);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern unsafe CAPI.ovrAvatar2Result ovrAvatar2Primitive_GetNumMaterialExtensions(
+            ovrAvatar2Id id,
+            out UInt32 numExtensions);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
+        public static extern unsafe ovrAvatar2Result ovrAvatar2Primitive_GetMaterialExtensionName(
+            ovrAvatar2Id id,
+            UInt32 extensionIndex,
+            byte* nameBuffer,
+            UInt32* nameBufferSize);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Primitive_GetNumEntriesInMaterialExtensionByIndex(
+            ovrAvatar2Id id,
+            UInt32 extensionIndex,
+            out UInt32 numEntries);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern ovrAvatar2Result ovrAvatar2Primitive_MaterialExtensionEntryMetaDataByIndex(
+            ovrAvatar2Id id,
+            UInt32 materialExtensionIndex,
+            UInt32 entryIndex,
+            out ovrAvatar2MaterialExtensionEntry entry);
+
+
+        public static bool OvrAvatar2Primitive_MaterialExtensionEntryMetaDataByIndex(
+            ovrAvatar2Id id,
+            UInt32 materialExtensionIndex,
+            UInt32 entryIndex,
+            out ovrAvatar2MaterialExtensionEntry entry)
+        {
+            return ovrAvatar2Primitive_MaterialExtensionEntryMetaDataByIndex(id, materialExtensionIndex, entryIndex,
+                out entry).EnsureSuccess("ovrAvatar2Primitive_MaterialExtensionEntryMetaDataByIndex", assetLogScope);
+        }
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern unsafe ovrAvatar2Result ovrAvatar2Primitive_MaterialExtensionEntryDataByIndex(
+            ovrAvatar2Id id,
+            UInt32 materialExtensionIndex,
+            UInt32 entryIndex,
+            byte* nameBuffer,
+            UInt32 nameBufferSize,
+            byte* dataBuffer,
+            UInt32 dataBufferSize);
+
+        public static unsafe bool OvrAvatar2Primitive_MaterialExtensionEntryDataByIndex(
+            ovrAvatar2Id id,
+            UInt32 materialExtensionIndex,
+            UInt32 entryIndex,
+            byte* nameBuffer,
+            UInt32 nameBufferSize,
+            byte* dataBuffer,
+            UInt32 dataBufferSize)
+        {
+            return ovrAvatar2Primitive_MaterialExtensionEntryDataByIndex(id, materialExtensionIndex, entryIndex,
+                    nameBuffer, nameBufferSize, dataBuffer, dataBufferSize)
+                .EnsureSuccess("ovrAvatar2Primitive_MaterialExtensionEntryDataByIndex", assetLogScope);
+        }
+
+        //-----------------------------------------------------------------
+        //
+        // Joints
+        //
+        //
+
+        [StructLayout(LayoutKind.Sequential)]
+        public struct ovrAvatar2JointInfo
+        {
+            public Int32 jointIndex;
+            public ovrAvatar2Matrix4f inverseBind;
+        }
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern unsafe ovrAvatar2Result ovrAvatar2Primitive_GetJointInfo(
+            ovrAvatar2Id primitiveId, ovrAvatar2JointInfo* buffer, UInt32 bytes);
+
+
+        //-----------------------------------------------------------------
+        //
+        // SubMesh
+        //
+        //
+
+        [StructLayout(LayoutKind.Sequential)]
+        public struct ovrAvatar2PrimitiveSubmesh
+        {
+            public UInt32 indexStart; // index into the index buffer of first primitive
+            public UInt32 indexCount; // number of indices to use from the index buffer
+            public UInt16 vertexStart; // lowest index value
+            public UInt16 vertexCount; // highest index value - lowest index value
+            public ovrAvatar2Vector2f minUVValues; // vertex UV min values
+            public ovrAvatar2Vector2f maxUVValues; // vertex UV max values
+            public ovrAvatar2Vector3f minValues; // vertex position min values
+            public ovrAvatar2Vector3f maxValues; // vertex position max values
+
+            public ovrAvatar2EntitySubMeshInclusionFlags
+                inclusionFlags; // indicates the type of content in this submesh
+        }
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        internal static extern ovrAvatar2Result ovrAvatar2Primitive_GetSubMeshCount(
+            ovrAvatar2Id primitiveId, out UInt32 subMeshCount);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        internal static extern ovrAvatar2Result ovrAvatar2Primitive_GetSubMeshByIndex(
+            ovrAvatar2Id primitiveId, UInt32 subMeshIndex, out ovrAvatar2PrimitiveSubmesh subMesh);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
+        internal static extern unsafe ovrAvatar2Result ovrAvatar2Primitive_GetSubMeshMaterialName(
+            ovrAvatar2Id primitiveId, UInt32 subMeshIndex, byte* nameBuffer, UInt32 bufferByteSize);
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Asset.cs.meta b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Asset.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..3ea0715ec7da4997658bac7c40db8470b758ac71
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Asset.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 5ebe59e138d39de4abb062bbe9cb2fa0
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Avatar.cs b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Avatar.cs
new file mode 100644
index 0000000000000000000000000000000000000000..d8cbf103486cde2eb8b61dbe0d3b893422673964
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Avatar.cs
@@ -0,0 +1,621 @@
+using System;
+using System.Runtime.InteropServices;
+
+using Unity.Collections.LowLevel.Unsafe;
+
+namespace Oculus.Avatar2
+{
+    public partial class CAPI
+    {
+        private const string AvatarCapiLogScope = "OvrAvatarAPI_Avatar";
+
+        private const string ScriptBinaryMismatchResolution = "update c-sharp scripts to match libovravatar2 version";
+
+        // Native libovravatar2 version this integration was built against
+        private static FBVersionNumber TargetLibVersion;
+
+        /* Debug string indicating the native libovravatar2 version this manager is targeting */
+        public static string TargetAvatarLibVersionString
+            => $"{TargetLibProductVersion}.{TargetLibMajorVersion}.{TargetLibMinorVersion}.{TargetLibPatchVersion}";
+
+        // Native libovravatar2 version this integration was built against
+        private static int TargetLibProductVersion = -1;
+        private static int TargetLibMajorVersion = -1;
+        private static int TargetLibMinorVersion = -1;
+        private static int TargetLibPatchVersion = -1;
+
+        internal const string LibFile =
+#if UNITY_EDITOR || !UNITY_IOS
+#if UNITY_EDITOR_OSX
+        OvrAvatarPlugin.FullPluginFolderPath + "libovravatar2.framework/libovravatar2";
+#else
+        OvrAvatarManager.IsAndroidStandalone ? "ovravatar2" : "libovravatar2";
+#endif  // UNITY_EDITOR_OSX
+#else   // !UNITY_EDITOR && UNITY_IOS
+        "__Internal";
+#endif  // !UNITY_EDITOR && UNITY_IOS
+
+        // TODO: Add "INITIALIZED" frame count and assert in update when update called w/out init
+        private const uint AVATAR_UPDATE_UNINITIALIZED_FRAME_COUNT = 0;
+
+        //-----------------------------------------------------------------
+        //
+        // Forwards
+        //
+        //
+
+        public const float DefaultAvatarColorRed = (30 / 255.0f);
+        public const float DefaultAvatarColorGreen = (157 / 255.0f);
+        public const float DefaultAvatarColorBlue = (255 / 255.0f);
+
+        // Network thread update frequency.
+        // 256 hz is updating every 0.004ms which we think is optimal in most use cases
+        public const uint DefaultNetworkWorkerUpdateFrequency = 256;
+
+        //-----------------------------------------------------------------
+        //
+        // Callback functions
+        //
+        //
+
+        // Avatar Logging Level
+        // Matches the Android Log Levels
+        public enum ovrAvatar2LogLevel : Int32
+        {
+            Unknown = 0,
+            Default,
+            Verbose,
+            Debug,
+            Info,
+            Warn,
+            Error,
+            Fatal,
+            Silent,
+        };
+
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
+        public delegate void LoggingDelegate(ovrAvatar2LogLevel prio, string msg, IntPtr context);
+
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        public delegate void MemAllocDelegate(UInt64 byteCount, IntPtr context);
+
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        public delegate void MemFreeDelegate(IntPtr buffer, IntPtr context);
+
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        public delegate void ResourceDelegate(in ovrAvatar2Asset_Resource resource, IntPtr context);
+
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        public delegate void RequestDelegate(ovrAvatar2RequestId requestId, ovrAvatar2Result status, IntPtr userContext);
+
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
+        public delegate IntPtr FileOpenDelegate(IntPtr context, string filename);
+
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        [return: MarshalAs(UnmanagedType.U1)]
+        // TODO: Change return type, bool is an unreliable return type for an unmanaged function pointer
+        public delegate bool FileReadDelegate(IntPtr context, IntPtr fileHandle, out IntPtr fileDataPtr, out UInt64 fileSize);
+
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        [return: MarshalAs(UnmanagedType.U1)]
+        // TODO: Change return type, bool is an unreliable return type for an unmanaged function pointer
+        public delegate bool FileCloseDelegate(IntPtr context, IntPtr fileHandle);
+
+        //-----------------------------------------------------------------
+        //
+        // Initialization
+        //
+        //
+
+        public enum ovrAvatar2Platform : Int32
+        {
+            Invalid = 0,
+            PC = 1,
+            Quest = 2,
+            Quest2 = 3,
+            QuestPro = 4,
+
+
+            First = PC,
+
+            Last = QuestPro,
+
+            Count = (Last - First) + 1,
+            Num = Last + 1,
+        }
+
+        public static string ovrAvatar2PlatformToString(ovrAvatar2Platform platform)
+        {
+            switch (platform)
+            {
+                case ovrAvatar2Platform.Invalid:
+                    return "Invalid";
+                case ovrAvatar2Platform.PC:
+                    return "PC";
+                case ovrAvatar2Platform.Quest:
+                    return "Quest";
+                case ovrAvatar2Platform.Quest2:
+                    return "Quest2";
+                case ovrAvatar2Platform.QuestPro:
+                    return "QuestPro";
+            }
+
+            return "Unknown";
+        }
+
+        [Flags]
+        public enum ovrAvatar2InitializeFlags : Int32
+        {
+            ///< When set, ovrAvatar2_Shutdown() may return ovrAvatar2Result_MemoryLeak to indicate a
+            ///< detected memory leak
+            CheckMemoryLeaks = 1 << 0,
+            UseDefaultImage = 1 << 1,
+            // When set, skinningOrigin in ovrAvatar2PrimitiveRenderState is set with the skinning origin
+            // and the skinning matrices root will be the skinning Origin
+            EnableSkinningOrigin = 1 << 3,
+
+            First = CheckMemoryLeaks,
+            Last = EnableSkinningOrigin,
+        }
+
+        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+        // This needs to be the csharp equivalent of ovrAvatar2InitializeInfo in Avatar.h
+        public struct ovrAvatar2InitializeInfo
+        {
+            public FBVersionNumber versionNumber;
+            public string clientVersion; // client version string for the user app (ex. "Unity v2019.2")
+            public ovrAvatar2Platform platform;
+
+            public ovrAvatar2InitializeFlags flags;
+
+            public ovrAvatar2LogLevel loggingLevel; // logging threshold to control what is logged
+            public LoggingDelegate loggingCallback; ///< override logging with user defined function. This
+                                                    ///< function may be called from multiple threads
+            public IntPtr loggingContext;
+            public MemAllocDelegate memAllocCallback; // override memory management
+            public MemFreeDelegate memFreeCallback; // override memory management
+            public IntPtr memoryContext; // user context for memory callbacks
+
+            public RequestDelegate requestCallback;
+
+            public FileOpenDelegate fileOpenCallback; // used to open a file
+            public FileReadDelegate fileReadCallback; // used to load file contents
+            public FileCloseDelegate fileCloseCallback; // used to close a file
+            public IntPtr fileReaderContext; // context for above file callbacks
+
+            public ResourceDelegate resourceLoadCallback; // resource load callback
+            public IntPtr resourceLoadContext;
+
+            public string fallbackPathToOvrAvatar2AssetsZip;
+
+            public UInt32 numWorkerThreads;
+
+            public Int64 maxNetworkRequests;
+            public Int64 maxNetworkSendBytesPerSecond;
+            public Int64 maxNetworkReceiveBytesPerSecond;
+
+            public ovrAvatar2Vector3f defaultModelColor;
+            ///
+            /// Defines the right axis in the game engine coordinate system.
+            /// For the Avatar SDK and Unity, this is (1, 0, 0)
+            ///
+            public ovrAvatar2Vector3f clientSpaceRightAxis;
+
+            ///
+            /// Defines the up axis in the game engine coordinate system.
+            /// For the Avatar SDK and Unity, this is (0, 1, 0)
+            ///
+            public ovrAvatar2Vector3f clientSpaceUpAxis;
+
+            ///
+            /// Defines the forward axis in the game engine coordinate system.
+            /// For the Avatar SDK this is (0, 0, -1). For Unity, it is (0, 0, 1)
+            ///
+            public ovrAvatar2Vector3f clientSpaceForwardAxis;
+
+            public string clientName;
+
+            // 0 for running network update on the main thread
+            public UInt32 networkWorkerUpdateFrequency;
+
+            private IntPtr reserved1;       // Reserved  / internal use only
+            private UInt32 reserved2;       // Reserved  / internal use only
+        }
+
+        internal static ovrAvatar2InitializeInfo OvrAvatar_DefaultInitInfo(string clientVersion, ovrAvatar2Platform platform)
+        {
+            // TODO: T86822707, This should be a method in the loaderShim/ovravatar2 lib
+            // return ovrAvatar2_DefaultInitInfo(clientVersion, platform);
+
+            // Copied from //arvr/libraries/avatar/Libraries/api/include/OvrAvatar/Avatar.h
+            ovrAvatar2InitializeInfo info = default;
+            info.versionNumber = SDKVersionInfo.CurrentVersion();
+            info.flags = ovrAvatar2InitializeFlags.UseDefaultImage;
+            info.clientVersion = clientVersion;
+            info.platform = platform;
+            info.loggingLevel = ovrAvatar2LogLevel.Warn;
+            info.numWorkerThreads = 1;
+            info.maxNetworkRequests = -1;
+            info.maxNetworkSendBytesPerSecond = -1;
+            info.maxNetworkReceiveBytesPerSecond = -1;
+
+            // Default color of the default/blank avatar
+            info.defaultModelColor.x = DefaultAvatarColorRed;
+            info.defaultModelColor.y = DefaultAvatarColorGreen;
+            info.defaultModelColor.z = DefaultAvatarColorBlue;
+#if OVR_AVATAR_ENABLE_CLIENT_XFORM
+            info.clientSpaceRightAxis = UnityEngine.Vector3.right;
+            info.clientSpaceUpAxis = UnityEngine.Vector3.up;
+            info.clientSpaceForwardAxis = UnityEngine.Vector3.forward;
+#else
+            info.clientSpaceRightAxis = UnityEngine.Vector3.right;
+            info.clientSpaceUpAxis = UnityEngine.Vector3.up;
+            info.clientSpaceForwardAxis = -UnityEngine.Vector3.forward;
+#endif
+
+            info.clientName = "unknown_unity";
+
+            info.networkWorkerUpdateFrequency = DefaultNetworkWorkerUpdateFrequency;
+
+            return info;
+        }
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern ovrAvatar2Result ovrAvatar2_Initialize(in ovrAvatar2InitializeInfo infoPtr);
+
+        public static bool OvrAvatar_Initialize(in ovrAvatar2InitializeInfo infoPtr)
+        {
+            if (ovrAvatar2_Initialize(in infoPtr)
+                .EnsureSuccess("ovrAvatar2_Initialize"))
+            {
+                TargetLibVersion = infoPtr.versionNumber;
+                return true;
+            }
+            return false;
+        }
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern ovrAvatar2Result ovrAvatar2_Shutdown();
+
+        internal static bool OvrAvatar_Shutdown()
+        {
+            OvrAvatar_Shutdown(out var result);
+            return result.EnsureSuccess("ovrAvatar2_Shutdown");
+        }
+        internal static void OvrAvatar_Shutdown(out ovrAvatar2Result result)
+        {
+            avatarUpdateCount = AVATAR_UPDATE_UNINITIALIZED_FRAME_COUNT;
+            result = ovrAvatar2_Shutdown();
+        }
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
+        public static extern ovrAvatar2Result ovrAvatar2_UpdateAccessToken(string token);
+
+        /// Update the network settings
+        /// \param maxNetworkSendBytesPerSecond -1 for no limit
+        /// \param maxNetworkReceiveBytesPerSecond -1 for no limit
+        ///
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        internal static extern ovrAvatar2Result ovrAvatar2_UpdateNetworkSettings(
+            Int64 maxNetworkSendBytesPerSecond, Int64 maxNetworkReceiveBytesPerSecond, Int64 maxNetworkRequests);
+
+        //-----------------------------------------------------------------
+        //
+        // Work
+        //
+        //
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern ovrAvatar2Result ovrAvatar2_Update(float deltaSeconds);
+
+        // TODO: Could this just be 0.0f?
+        private const float AVATAR_UPDATE_SMALL_STEP = 0.1f;
+        internal static uint avatarUpdateCount { get; private set; } = AVATAR_UPDATE_UNINITIALIZED_FRAME_COUNT;
+        internal static bool OvrAvatar2_Update(float deltaSeconds = AVATAR_UPDATE_SMALL_STEP)
+        {
+            var result = ovrAvatar2_Update(deltaSeconds);
+            if (result.EnsureSuccess("ovrAvatar2_Update"))
+            {
+                ++avatarUpdateCount;
+                return true;
+            }
+            return false;
+        }
+
+        /// Run a single task from the avatar task system.
+        /// Return ovrAvatar2Result_Success upon completion after running a task.
+        /// Return ovrAvatar2Result_NotFound when no tasks are currently in the queue.
+        /// Return ovrAvatar2Result_Unsupported when library is configured to use worker threads.
+        ///
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        internal static extern ovrAvatar2Result ovrAvatar2_RunTask();
+
+        //-----------------------------------------------------------------
+        //
+        // Query
+        //
+        //
+
+        /// Query to see if a user has an avatar
+        /// ovrAvatar2_RequestCallback is called when the request is fulfilled
+        /// Request status:
+        ///   ovrAvatar2Result_Success - request succeeded
+        ///   ovrAvatar2Result_Unknown - error while querying user avatar status
+        /// ovrAvatar2_GetRequestBool() result
+        ///   true - user has an avatar
+        ///   false - user does not have an avatar
+        ///
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern ovrAvatar2Result ovrAvatar2_HasAvatar(
+            UInt64 userId, out ovrAvatar2RequestId requestId, IntPtr userContext);
+
+        internal static bool OvrAvatar_HasAvatar(
+            UInt64 userId, out ovrAvatar2RequestId requestId, IntPtr userContext)
+        {
+            var result = ovrAvatar2_HasAvatar(userId, out requestId, userContext);
+            if (result.EnsureSuccess("ovrAvatar2_HasAvatar"))
+            {
+                return true;
+            }
+            requestId = ovrAvatar2RequestId.Invalid;
+            return false;
+        }
+
+        /// Query to see if an entity's avatar has changed
+        /// ovrAvatar2_RequestCallback is called when the request is fulfilled
+        /// Request status:
+        ///   ovrAvatar2Result_Success - request succeeded
+        ///   ovrAvatar2Result_Unknown - error while querying user avatar status
+        ///   ovrAvatar2Result_InvalidEntity - entity is no longer valid
+        /// ovrAvatar2_GetRequestBool() result
+        ///   true - user avatar has changed
+        ///   false - user avatar has not changed
+        ///
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern ovrAvatar2Result ovrAvatar2_HasAvatarChanged(
+            ovrAvatar2EntityId entityId, out ovrAvatar2RequestId requestId, IntPtr userContext);
+
+        internal static bool OvrAvatar2_HasAvatarChanged(
+            ovrAvatar2EntityId entityId, out ovrAvatar2RequestId requestId, IntPtr userContext)
+        {
+            var result = ovrAvatar2_HasAvatarChanged(entityId, out requestId, userContext);
+            if (result.EnsureSuccess("ovrAvatar2_HasAvatarChanged"))
+            {
+                return true;
+            }
+            requestId = ovrAvatar2RequestId.Invalid;
+            return false;
+        }
+
+        /// Get the result of a reqeust
+        /// Should be called in ovrAvatar2_RequestCallback
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern ovrAvatar2Result ovrAvatar2_GetRequestBool(
+            ovrAvatar2RequestId requestId, [MarshalAs(UnmanagedType.U1)] out bool result);
+
+        /* Query result of ovrAvatar2RequestId from `RequestCallback` */
+        internal static bool OvrAvatar_GetRequestBool(
+            ovrAvatar2RequestId requestId, out bool result)
+        {
+            if (ovrAvatar2_GetRequestBool(requestId, out result)
+                .EnsureSuccess("ovrAvatar2_GetRequestBool"))
+            {
+                return true;
+            }
+            result = false;
+            return false;
+        }
+
+        //-----------------------------------------------------------------
+        //
+        // Asset Sources
+        //
+        //
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
+        public static extern ovrAvatar2Result ovrAvatar2_AddZipSourceFile(string filename);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
+        public static extern ovrAvatar2Result ovrAvatar2_RemoveZipSource(string filename);
+
+
+        //-----------------------------------------------------------------
+        //
+        // Stats
+        //
+        //
+
+        // Avatar memory statistics
+
+        [StructLayout(LayoutKind.Sequential)]
+        public struct ovrAvatar2MemoryStats
+        {
+            public UInt64 currBytesUsed;
+            public UInt64 currAllocationCount;
+            public UInt64 maxBytesUsed;
+            public UInt64 maxAllocationCount;
+            public UInt64 totalBytesUsed;
+            public UInt64 totalAllocationCount;
+        }
+
+        /// Updates the given memory stats struct with the current statistics
+        /// \param pointer to a stats structure to update
+        /// \param logging context, used if an error is encountered
+        /// \return true for success, false if an error was encountered (most likely ovrAvatar2 is not initialized)
+        public static bool OvrAvatar2_QueryMemoryStats(out ovrAvatar2MemoryStats stats
+            , UnityEngine.Object logContext = null)
+        {
+            var statsStructSize = (UInt32)UnsafeUtility.SizeOf<ovrAvatar2MemoryStats>();
+            var result = ovrAvatar2_QueryMemoryStats(out stats, statsStructSize, out var bytesUpdated);
+            return result.EnsureSuccessOrWarning(
+                ovrAvatar2Result.BufferTooSmall, ovrAvatar2Result.BufferLargerThanExpected
+                , ScriptBinaryMismatchResolution, "ovrAvatar2_QueryMemoryStats", AvatarCapiLogScope
+                , logContext);
+        }
+
+        [StructLayout(LayoutKind.Sequential)]
+        internal struct ovrAvatar2NetworkStats
+        {
+            public UInt64 downloadTotalBytes;
+            public UInt64 downloadSpeed;
+            public UInt64 totalRequests;
+            public UInt64 activeRequests;
+        }
+
+        /// Updates the given network stats struct with the current statistics
+        /// \param pointer to a stats structure to update
+        /// \param logging context, used if an error is encountered
+        /// \return true for success, false if an error was encountered (most likely ovrAvatar2 is not initialized)
+        internal static bool OvrAvatar2_QueryNetworkStats(out ovrAvatar2NetworkStats stats
+            , UnityEngine.Object logContext = null)
+        {
+            var statsStructSize = (UInt32)UnsafeUtility.SizeOf<ovrAvatar2NetworkStats>();
+            var result = ovrAvatar2_QueryNetworkStats(out stats, statsStructSize, out var bytesUpdated);
+            return result.EnsureSuccessOrWarning(
+                ovrAvatar2Result.BufferTooSmall, ovrAvatar2Result.BufferLargerThanExpected
+                , ScriptBinaryMismatchResolution, "ovrAvatar2_QueryNetworkStats"
+                , AvatarCapiLogScope, logContext);
+        }
+
+        /// Avatar task statistics
+        ///
+
+        public const int TaskHistogramSize = 32;
+
+        [StructLayout(LayoutKind.Sequential)]
+        public unsafe struct ovrAvatar2TaskStats
+        {
+            // UInt32 fixed size array of 32 items
+            public fixed UInt32 histogram[TaskHistogramSize];
+            public UInt32 pending;
+        }
+
+        /// Updates the given task stats struct with the current statistics
+        /// \param pointer to a stats structure to update
+        /// \param logging context, used if an error is encountered
+        /// \return true for success, false if an error was encountered (most likely ovrAvatar2 is not initialized)
+        internal static bool OvrAvatar2_QueryTaskStats(out ovrAvatar2TaskStats stats
+            , UnityEngine.Object logContext = null)
+        {
+            var statsStructSize = (UInt32)UnsafeUtility.SizeOf<ovrAvatar2TaskStats>();
+            var result = ovrAvatar2_QueryTaskStats(out stats, statsStructSize, out var bytesUpdated);
+            return result.EnsureSuccessOrWarning(
+                ovrAvatar2Result.BufferTooSmall, ovrAvatar2Result.BufferLargerThanExpected
+                , ScriptBinaryMismatchResolution, "ovrAvatar2_QueryTaskStats", AvatarCapiLogScope, logContext);
+        }
+
+        //-----------------------------------------------------------------
+        //
+        // Misc
+        //
+        //
+
+        /// <summary>
+        /// Get the string representation of an ovrAvatar2Result code
+        /// </summary>
+        /// <param name="result">The return code you want the string for</param>
+        /// <param name="buffer">The buffer to return the string in</param>
+        /// <param name="size">The size of the buffer</param>
+        /// <returns>Success unless the result provided is out of range (BadParameter), or the buffer is
+        /// null or too small (BufferTooSmall)</returns>
+        ///
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern unsafe ovrAvatar2Result ovrAvatar2_GetResultString(ovrAvatar2Result result, char* buffer, UInt32* size);
+
+        /// Get the avatar API version string
+        /// \param versionBuffer string to populate with the version string
+        /// \param bufferSize length of the version buffer
+        ///
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
+        private static extern unsafe ovrAvatar2Result ovrAvatar2_GetVersionString(byte* versionBuffer, UInt32 bufferSize);
+
+        internal static string OvrAvatar_GetVersionString()
+        {
+            unsafe
+            {
+                const int bufferSize = 1024;
+                var versionBuffer = stackalloc byte[bufferSize];
+                var result = ovrAvatar2_GetVersionString(versionBuffer, bufferSize);
+                if (result.EnsureSuccess("ovrAvatar2_GetVersionString"))
+                {
+                    return Marshal.PtrToStringAnsi((IntPtr)versionBuffer);
+                }
+            }
+
+            return string.Empty;
+        }
+
+        /// <summary>
+        /// Enables the link from the native runtime to Avatar developer tools.
+        /// </summary>
+        /// <returns>Returns success</returns>
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2_EnableDevToolsLink();
+
+        /// <summary>
+        /// Disables the link from the native runtime to Avatar developer tools.
+        /// </summary>
+        /// <returns>Returns success</returns>
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2_DisableDevToolsLink();
+
+
+
+        //-----------------------------------------------------------------
+        //
+        // CAPI Bindings
+        //
+        //
+
+        /// Updates the given stats struct with the current statistics
+        /// \param pointer to a stats structure to update
+        /// \param size of the stats structure to update
+        /// \param number of bytes updated in `stats`
+        /// Returns result codes:
+        ///   ovrAvatar2Result_Success - stats updated successfully
+        ///   ovrAvatar2Result_DataNotAvailable - stats tracking unavailable
+        ///   ovrAvatar2Result_BadParameter - stats is null or statsStructSize is 0
+        ///   ovrAvatar2Result_BufferTooSmall - statsStructSize is smaller than expected
+        ///   ovrAvatar2Result_BufferLargerThanExpected - statsStructSize is larger than expected
+        ///     (note: Invoking `ovrAvatar2_Initialize` establishes primary thread)
+        ///   ovrAvatar2Result_NotInitialized - ovrAvatar2 is currently not initialized
+        ///
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern ovrAvatar2Result ovrAvatar2_QueryMemoryStats(out ovrAvatar2MemoryStats stats,
+            UInt32 statsStructSize, out UInt32 bytesUpdated);
+
+        /// Updates the given stats struct with the current statistics
+        /// \param pointer to a stats structure to update
+        /// \param size of the stats structure to update
+        /// \param number of bytes updated in `stats`
+        /// Returns result codes:
+        ///   ovrAvatar2Result_Success - stats updated successfully
+        ///   ovrAvatar2Result_DataNotAvailable - stats tracking unavailable
+        ///   ovrAvatar2Result_BadParameter - stats is null or statsStructSize is 0
+        ///   ovrAvatar2Result_BufferTooSmall - statsStructSize is smaller than expected
+        ///   ovrAvatar2Result_BufferLargerThanExpected - statsStructSize is larger than expected
+        ///     (note: Invoking `ovrAvatar2_Initialize` establishes primary thread)
+        ///   ovrAvatar2Result_NotInitialized - ovrAvatar2 is currently not initialized
+        ///
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
+        private static extern ovrAvatar2Result ovrAvatar2_QueryNetworkStats(out ovrAvatar2NetworkStats stats
+            , UInt32 statsStructSize, out UInt32 bytesUpdated);
+
+        /// Update the given stats struct with the current task statistics
+        /// \param pointer to a stats structure to update
+        /// \param size of the stats structure to update
+        /// \param number of bytes updated in `stats`
+        /// Returns result codes:
+        ///   ovrAvatar2Result_Success - stats updated successfully
+        ///   ovrAvatar2Result_DataNotAvailable - stats tracking unavailable
+        ///   ovrAvatar2Result_BadParameter - stats is null or statsStructSize is 0
+        ///   ovrAvatar2Result_BufferTooSmall - statsStructSize is smaller than expected
+        ///   ovrAvatar2Result_BufferLargerThanExpected - statsStructSize is larger than expected
+        ///     (note: Invoking `ovrAvatar2_Initialize` establishes primary thread)
+        ///   ovrAvatar2Result_NotInitialized - ovrAvatar2 is currently not initialized
+        ///
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern ovrAvatar2Result ovrAvatar2_QueryTaskStats(out ovrAvatar2TaskStats stats
+            , UInt32 statsStructSize, out UInt32 bytesUpdated);
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Avatar.cs.meta b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Avatar.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..b50147347aee1d401d1c68a76f087061a7755e21
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Avatar.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 03bd0fa68ce0ee747913e348ec503815
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Behavior.cs b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Behavior.cs
new file mode 100644
index 0000000000000000000000000000000000000000..aeb2c656e5d24bdf694961642a20bf9b1834424d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Behavior.cs
@@ -0,0 +1,85 @@
+
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace Oculus.Avatar2
+{
+
+    public partial class CAPI
+    {
+        //-----------------------------------------------------------------
+        //
+        // Gaze Targets
+        //
+        //
+
+        public enum ovrAvatar2GazeTargetType : Int32
+        {
+            AvatarHead,
+            AvatarHand,
+            Object,
+            ObjectStatic,
+
+            Count
+        }
+
+        [StructLayout(LayoutKind.Sequential)]
+        internal struct ovrAvatar2GazeTarget
+        {
+            public ovrAvatar2Id id;
+            public ovrAvatar2Vector3f worldPosition;
+            public ovrAvatar2GazeTargetType type;
+        }
+
+        /// Create gaze targets.
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        internal static extern ovrAvatar2Result ovrAvatar2Behavior_CreateGazeTargets(IntPtr targets, int targetCount);
+
+        /// Destroy gaze targets.
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        internal static extern ovrAvatar2Result ovrAvatar2Behavior_DestroyGazeTargets(IntPtr targets, int targetCount);
+
+        /// Update position of gaze targets.
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        internal unsafe static extern ovrAvatar2Result ovrAvatar2Behavior_UpdateGazeTargetPositions(CAPI.ovrAvatar2GazeTarget* targets, int targetCount);
+
+        /// Get the position the avatar is looking at
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        internal static extern ovrAvatar2Result ovrAvatar2Behavior_GetGazePos(
+            ovrAvatar2EntityId entityId, ref ovrAvatar2Vector3f outPos);
+
+
+        //-----------------------------------------------------------------
+        //
+        // Custom Hands
+        //
+        //
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern ovrAvatar2Result
+            ovrAvatar2_SetCustomWristOffset(
+                ovrAvatar2EntityId entityId,
+                ovrAvatar2Side side,
+                in ovrAvatar2Transform offset);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern ovrAvatar2Result
+            ovrAvatar2_SetCustomHandSkeleton(
+                ovrAvatar2EntityId entityId,
+                ovrAvatar2Side side,
+                in ovrAvatar2TrackingBodySkeleton skeleton);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern ovrAvatar2Result
+        ovrAvatar2_SetCustomHandPose(
+            ovrAvatar2EntityId entityId,
+            ovrAvatar2Side side,
+            in ovrAvatar2TrackingBodyPose pose);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern ovrAvatar2Result
+        ovrAvatar2_ClearCustomHandPose(ovrAvatar2EntityId entityId, ovrAvatar2Side side);
+
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Behavior.cs.meta b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Behavior.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..f8e8c93aef550c1d51e493680cca66384d06eea8
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Behavior.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 4f86d2891e0ae2248ad2382f9effe471
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Entity.cs b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Entity.cs
new file mode 100644
index 0000000000000000000000000000000000000000..b9f9df732c5659fad9fc5645f0d157f95ad496b0
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Entity.cs
@@ -0,0 +1,761 @@
+using System;
+using System.Runtime.InteropServices;
+
+using Unity.Collections;
+using Unity.Collections.LowLevel.Unsafe;
+
+using UnityEngine;
+
+using static Oculus.Avatar2.OvrAvatarHelperExtensions;
+/// @file OvrAvatarAPI_Entity.cs
+
+namespace Oculus.Avatar2
+{
+    // TODO: This should be a static class
+    public partial class CAPI
+    {
+        private const string entityLogScope = "OvrAvatarAPI_Entity";
+
+        internal static ovrAvatar2EntityLoadNetworkSettings SpecificationNetworkSettings = ovrAvatar2_DefaultEntityNetworkSettings();
+        internal static ovrAvatar2EntityLoadNetworkSettings AssetNetworkSettings = ovrAvatar2_DefaultEntityNetworkSettings();
+
+        //-----------------------------------------------------------------
+        //
+        // Creation / Destruction
+        //
+        //
+
+        [Flags]
+        [System.Serializable]
+        ///
+        /// Describes avatar rendering and animation capabilities.
+        ///
+        public enum ovrAvatar2EntityFeatures : Int32
+        {
+            // Empty features flag, usually used for error signaling
+            /* None value isn't needed in C# and conflicts w/ some Unity inspector logic for Flags */
+            // None = 0,
+
+            // Reserved for future use
+            [InspectorName(null)]
+            ReservedExtra = 1 << 0,
+
+            /// Render avatar geometry
+            Rendering_Prims = 1 << 1,
+
+            /// Perform skinning on avatar
+            Rendering_SkinningMatrices = 1 << 2,
+
+            /// Calculate object space transforms (needed for GPU Skinning)
+            Rendering_ObjectSpaceTransforms = 1 << 3,
+
+            /// Allow avatar animation
+            Animation = 1 << 4,
+
+            ///  Use default avatar model
+            UseDefaultModel = 1 << 5,
+
+            /// Use default animation hierarchy
+            UseDefaultAnimHierarchy = 1 << 6,
+
+            ///  Do not use.
+            AnalyticIk = 1 << 7,
+
+            /// Use default facial animations
+            UseDefaultFaceAnimations = 1 << 8,
+
+            /// Display controllers in avatar hands (not implemented yet)
+            ShowControllers = 1 << 9,
+
+            /// Reproportions avatar hand bones according to tracking information in hand tracking mode
+            HandScaling = 1 << 10,
+
+            /// Allows to control the leg end-effector transforms using a two-bone IK solver.
+            LegIk = 1 << 11,
+
+            // Base set of features needed for entity rendering
+            Rendering = Rendering_Prims | Rendering_SkinningMatrices,
+
+            // Collection of all current feature flags
+            All = Rendering_Prims | Rendering_SkinningMatrices | Rendering_ObjectSpaceTransforms | Animation
+                         | UseDefaultModel | UseDefaultAnimHierarchy | AnalyticIk | UseDefaultFaceAnimations
+                         | ShowControllers | HandScaling | LegIk,
+
+            // Preset collection of feature flags for standard local avatar use case
+            Preset_Default = Rendering | Animation | UseDefaultModel | UseDefaultAnimHierarchy
+                             | UseDefaultFaceAnimations | HandScaling,
+
+            // Preset collection for using AnalyticIk/SimpleIk
+            Preset_AllIk = Preset_Default | AnalyticIk,
+
+            // Preset for minimum functional local avatar
+            Preset_Minimal = Rendering | Animation,
+
+            // Preset for common remote avatar usage
+            Preset_Remote = Rendering | UseDefaultModel,
+
+            [InspectorName(null)]
+            First = ReservedExtra,
+            [InspectorName(null)]
+            Last = LegIk,
+        }
+
+        ///
+        /// Configures what characteristics will be loaded (level of detail, rendering characteristics, point of view).
+        /// Exclusion from this filter will mean the setting is not loaded, saving memory and load time.
+        /// NOTE: These values cannot be changed after OvrAvatarEntity.CreateEntity is called.
+        /// @see ovrAvatar2EntityLODFlags
+        /// @see ovrAvatar2EntityManifestationFlags
+        /// @see ovrAvatar2EntityViewFlags
+        ///
+        [System.Serializable]
+        [StructLayout(LayoutKind.Sequential)]
+        public struct ovrAvatar2EntityFilters
+        {
+            [EnumMask]
+            public ovrAvatar2EntityLODFlags lodFlags; // unsigned ovrAvatar2EntityLODFlags
+            [EnumMask]
+            public ovrAvatar2EntityManifestationFlags manifestationFlags; // unsigned ovrAvatar2EntityManifestationFlags
+            [EnumMask]
+            public ovrAvatar2EntityViewFlags viewFlags; // unsigned ovrAvatar2EntityViewFlags
+            [EnumMask]
+            public ovrAvatar2EntitySubMeshInclusionFlags subMeshInclusionFlags; // unsigned ovrAvatar2EntitySubMeshInclusionFlags
+            [EnumMask]
+            public ovrAvatar2EntityHighQualityFlags highQualityFlags; // unsigned ovrAvatar2EntityHighQaulityFlags
+        }
+
+
+        ///
+        /// Avatar creation configuration.
+        /// Specifies overall level of detail, body parts to display,
+        /// rendering and animation characteristics and the
+        /// avatar's point of view.
+        /// @see ovrAvatar2EntityLODFlags
+        /// @see ovrAvatar2EntityManifestationFlags
+        /// @see ovrAvatar2EntityFeatures
+        /// @see ovrAvatar2EntityFilters
+        ///
+        [System.Serializable]
+        [StructLayout(LayoutKind.Sequential)]
+        public struct ovrAvatar2EntityCreateInfo
+        {
+            [EnumMask]
+            public ovrAvatar2EntityFeatures features;
+            public ovrAvatar2EntityFilters renderFilters;
+
+            // Abstract exact structure a bit for dependent code... since this is still public :X
+            public ovrAvatar2EntityLODFlags lodFlags
+            {
+                get => renderFilters.lodFlags;
+                set => renderFilters.lodFlags = value;
+            }
+
+            // TODO: Check for sensible input settings
+            public bool IsValid => true;
+        }
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern ovrAvatar2Result ovrAvatar2Entity_Create(in ovrAvatar2EntityCreateInfo info, out ovrAvatar2EntityId entityId);
+
+        /// Destroy an entity, releasing all related memory
+        /// \param entity to destroy
+        /// \return result code
+        ///
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern ovrAvatar2Result ovrAvatar2Entity_Destroy(ovrAvatar2EntityId entityId);
+
+
+        //-----------------------------------------------------------------
+        //
+        // LODs
+        //
+        //
+
+        /// Gets the available level of details of the entity
+        /// \param entity to query
+        /// \param level of detail flags
+        /// \return result code
+        ///
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Entity_GetAvailableLodFlags(
+            ovrAvatar2EntityId entityId, out UInt32 lodFlags);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Entity_GetLodFlags(
+            ovrAvatar2EntityId entityId, out ovrAvatar2EntityLODFlags lodFlags);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Entity_SetLodFlags(
+            ovrAvatar2EntityId entityId, ovrAvatar2EntityLODFlags lodflags);
+
+        //-----------------------------------------------------------------
+        //
+        // Manifestations
+        //
+        //
+
+        /// Gets the available manifestations of the entity
+        /// \param entity to query
+        /// \param manifestation flags
+        /// \return result code
+        ///
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Entity_GetAvailableManifestationFlags(
+            ovrAvatar2EntityId entityId, out UInt32 manifestationFlags);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Entity_GetManifestationFlags(
+            ovrAvatar2EntityId entityId, out ovrAvatar2EntityManifestationFlags manifestationflags);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Entity_SetManifestationFlags(
+            ovrAvatar2EntityId entityId, ovrAvatar2EntityManifestationFlags manifestation);
+
+        //-----------------------------------------------------------------
+        //
+        // View
+        //
+        //
+
+        /// Gets the available views of the entity
+        /// \param entity to query
+        /// \param pointer to view flags
+        /// \return result code
+        ///
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Entity_GetAvailableViewFlags(
+            ovrAvatar2EntityId entityId, out UInt32 viewFlags);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Entity_GetViewFlags(
+            ovrAvatar2EntityId entityId, out ovrAvatar2EntityViewFlags viewFlags);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Entity_SetViewFlags(
+            ovrAvatar2EntityId entityId, ovrAvatar2EntityViewFlags viewflags);
+
+        //-----------------------------------------------------------------
+        //
+        // SubMeshInclusions
+        //
+        //
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Entity_GetSubMeshInclusionFlags(
+            ovrAvatar2EntityId entityId, out ovrAvatar2EntitySubMeshInclusionFlags subMeshInclusionFlags);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Entity_SetSubMeshInclusionFlags(
+            ovrAvatar2EntityId entityId, ovrAvatar2EntitySubMeshInclusionFlags subMeshInclusionFlags);
+
+        //-----------------------------------------------------------------
+        //
+        // HighQuality
+        //
+        //
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Entity_GetHighQualityFlags(
+            ovrAvatar2EntityId entityId, out ovrAvatar2EntityHighQualityFlags highQualityFlags);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Entity_SetHighQualityFlags(
+            ovrAvatar2EntityId entityId, ovrAvatar2EntityHighQualityFlags highQualityFlags);
+
+        //-----------------------------------------------------------------
+        //
+        // Pose
+        //
+        //
+
+        /// Query the current pose for the an entity
+        /// \param entity to query
+        /// \param pose structure to fill out
+        /// \param pose version to fill out (scoped to the entity)
+        /// \return result code
+        ///
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Entity_GetPose(
+            ovrAvatar2EntityId entityId, out ovrAvatar2Pose posePtr, out ovrAvatar2HierarchyVersion hierarchyVersion);
+
+        /// Updates the current pose for an entity.
+        /// Does not add or remove joints, but will update the joints.
+        /// You can provide only partian information like just the transforms to update only those parts.
+        /// \param entity to query
+        /// \param pose data
+        /// \return result code
+        ///
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Entity_UpdatePose(ovrAvatar2EntityId entityId, in ovrAvatar2Pose posePtr);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Entity_SetRoot(ovrAvatar2EntityId entityId, ovrAvatar2Transform root);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern unsafe ovrAvatar2Result ovrAvatar2Entity_SetRoots(ovrAvatar2EntityId* entityIds, ovrAvatar2Transform* roots, uint numEntities);
+
+        //-----------------------------------------------------------------
+        //
+        // Loading / Unloading
+        //
+        //
+
+        internal struct ovrAvatar2EntityLoadNetworkSettings
+        {
+            public UInt32 timeoutMS; // If a request is not completed in this time, retry
+            public UInt32 lowSpeedTimeSeconds; // If download speed is below lowSpeedLimitBytesPerSecond for this long, retry
+            public UInt32 lowSpeedLimitBytesPerSecond; // see lowSpeedTimeSeconds
+        }
+
+        /// Setup ovrAvatar2EntityLoadNetworkSettings info with default values.
+        ///
+        private static ovrAvatar2EntityLoadNetworkSettings ovrAvatar2_DefaultEntityNetworkSettings()
+        {
+            ovrAvatar2EntityLoadNetworkSettings settings;
+            settings.timeoutMS = 0U;
+            settings.lowSpeedTimeSeconds = 30U;
+            settings.lowSpeedLimitBytesPerSecond = 60U;
+            return settings;
+        }
+
+        /// Note that loadFilters and numLodsWithMorphs work together. Morph targets will only be
+        /// loaded for the highest numLodsWithMorphs. This is taken from the available lods
+        /// specified by loadFilters.lodFlags. i.e. if loadFilters.lodFlags was to specify only
+        /// loading lods 0,2, and 4, and numLodsWithMorphs was 2. Then lods 0 and 2 would have
+        /// morph targets, and lod 4 would not.
+        ///
+        internal struct ovrAvatar2EntityLoadSettings
+        {
+            public ovrAvatar2EntityFilters loadFilters;
+            public ovrAvatar2EntityLoadNetworkSettings loadSpecificationNetworkSettings;
+            public ovrAvatar2EntityLoadNetworkSettings loadAssetNetworkSettings;
+            // 0 for no textures, minimum 80*1024 if > 0, UINT_MAX for no limit
+            public UInt32 maxTextureMemoryBytes;
+            public UInt32 numLodsWithMorphs;
+        }
+
+        /// Setup ovrAvatar2EntityLoadSettings info with default values.
+        ///
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern ovrAvatar2EntityLoadSettings ovrAvatar2Entity_DefaultLoadSettings();
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern ovrAvatar2EntityLoadSettings ovrAvatar2Entity_MinimumLoadSettings();
+
+        internal static ovrAvatar2EntityFilters OvrAvatar2_DefaultLoadFilters()
+        {
+            return ovrAvatar2_DefaultEntityLoadFilters();
+        }
+
+        static private ovrAvatar2EntityFilters ovrAvatar2_DefaultEntityLoadFilters()
+        {
+            return new ovrAvatar2EntityFilters
+            {
+                lodFlags = ovrAvatar2EntityLODFlags.All,
+                manifestationFlags = ovrAvatar2EntityManifestationFlags.All,
+                viewFlags = ovrAvatar2EntityViewFlags.All,
+                subMeshInclusionFlags = ovrAvatar2EntitySubMeshInclusionFlags.All,
+                highQualityFlags = CAPI.ovrAvatar2EntityHighQualityFlags.None
+            };
+        }
+
+        internal static ovrAvatar2EntityLoadSettings OvrAvatar2_GetLoadSettings()
+        {
+            var loadSettings = ovrAvatar2Entity_DefaultLoadSettings();
+            loadSettings.loadSpecificationNetworkSettings = SpecificationNetworkSettings;
+            loadSettings.loadAssetNetworkSettings = AssetNetworkSettings;
+            return loadSettings;
+        }
+
+        internal static ovrAvatar2EntityLoadSettings OvrAvatar2_GetFastLoadSettings()
+        {
+            var loadSettings = ovrAvatar2Entity_MinimumLoadSettings();
+            loadSettings.loadSpecificationNetworkSettings = SpecificationNetworkSettings;
+            loadSettings.loadAssetNetworkSettings = AssetNetworkSettings;
+            return loadSettings;
+        }
+
+        /// Load assets described in a GLB file into the entity
+        /// \param ovrAvatar2Entity to load into
+        /// \param the uri to the GLB file
+        ///        From file: "file://<path.glb>"
+        ///        From zip file: "zip://<path.glb>"
+        ///        From content delivery network (cdn): "cdn://<path.glb>"
+        /// \param loadSettings for this load
+        /// \param (out) loadRequestID to retrieve status from ovrAvatar2Asset_GetLoadRequestInfo
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
+        private static extern ovrAvatar2Result ovrAvatar2Entity_LoadUri(
+            ovrAvatar2EntityId entityId, string path, ovrAvatar2EntityLoadSettings loadSettings, out ovrAvatar2LoadRequestId requestId);
+
+        /// Load assets described in a GLB file into the entity
+        /// \param ovrAvatar2Entity to load into
+        /// \param the uri to the GLB file
+        ///        From file: "file://<path.glb>"
+        ///        From zip file: "zip://<path.glb>"
+        ///        From content delivery network (cdn): "cdn://<path.glb>"
+        /// \param (out) loadRequestID to retrieve status from ovrAvatar2Asset_GetLoadRequestInfo
+        public static ovrAvatar2Result OvrAvatarEntity_LoadUri(ovrAvatar2EntityId entityId, string path, out ovrAvatar2LoadRequestId requestId)
+        {
+            var defaultLoadSettings = OvrAvatar2_GetLoadSettings();
+            return ovrAvatar2Entity_LoadUri(entityId, path, defaultLoadSettings, out requestId);
+        }
+
+
+        /// \param the uri to the GLB file
+        ///        From file: "file://<path.glb>"
+        ///        From zip file: "zip://<path.glb>"
+        ///        From content delivery network (cdn): "cdn://<path.glb>"
+        /// \param load filters for this load
+        /// \param (out) loadRequestID to retrieve status from ovrAvatar2Asset_GetLoadRequestInfo
+        public static ovrAvatar2Result OvrAvatarEntity_LoadUriWithFilters(ovrAvatar2EntityId entityId, string uri, in ovrAvatar2EntityFilters loadFilters, out ovrAvatar2LoadRequestId requestId)
+        {
+            var loadSettings = OvrAvatar2_GetLoadSettings();
+            loadSettings.loadFilters = loadFilters;
+            return ovrAvatar2Entity_LoadUri(entityId, uri, loadSettings, out requestId);
+        }
+
+        /// \param the uri to the GLB file
+        ///        From file: "file://<path.glb>"
+        ///        From zip file: "zip://<path.glb>"
+        ///        From content delivery network (cdn): "cdn://<path.glb>"
+        /// \param load filters for this load
+        /// \param (out) loadRequestID to retrieve status from ovrAvatar2Asset_GetLoadRequestInfo
+        public static ovrAvatar2Result OvrAvatarEntity_LoadUriWithFiltersFast(ovrAvatar2EntityId entityId, string uri, in ovrAvatar2EntityFilters loadFilters, out ovrAvatar2LoadRequestId requestId)
+        {
+            var loadSettings = OvrAvatar2_GetFastLoadSettings();
+            loadSettings.loadFilters.manifestationFlags = loadFilters.manifestationFlags;
+            loadSettings.loadFilters.subMeshInclusionFlags = loadFilters.subMeshInclusionFlags;
+            loadSettings.loadFilters.viewFlags = loadFilters.viewFlags;
+            loadSettings.loadFilters.highQualityFlags = CAPI.ovrAvatar2EntityHighQualityFlags.None;
+            return ovrAvatar2Entity_LoadUri(entityId, uri, loadSettings, out requestId);
+        }
+
+        /// \param the uri to the GLB file
+        ///        From file: "file://<path.glb>"
+        ///        From zip file: "zip://<path.glb>"
+        ///        From content delivery network (cdn): "cdn://<path.glb>"
+        /// \param LODFlags representing which LODs to load
+        /// \param (out) loadRequestID to retrieve status from ovrAvatar2Asset_GetLoadRequestInfo
+        public static ovrAvatar2Result OvrAvatarEntity_LoadUriWithLODFilter(ovrAvatar2EntityId entityId, string uri, ovrAvatar2EntityLODFlags lodFilter, out ovrAvatar2LoadRequestId requestId)
+        {
+            var renderFilter = ovrAvatar2_DefaultEntityLoadFilters();
+            renderFilter.lodFlags = lodFilter;
+            return OvrAvatarEntity_LoadUriWithFilters(entityId, uri, in renderFilter, out requestId);
+        }
+
+        /// Load assets described in an in-memory GLB file into the entity.
+        /// \param ovrAvatar2Entity to load into
+        /// \param pointer to the beginning of a memory buffer
+        /// \param the length of the memory buffer
+        /// \param name what should be used to reference this glb
+        /// \param loadSettings for this load
+        /// \param (out) loadRequestID to retrieve status from ovrAvatar2Asset_GetLoadRequestInfo
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
+        private static extern ovrAvatar2Result ovrAvatar2Entity_LoadMemory(
+            ovrAvatar2EntityId entityId, IntPtr data, UInt32 dataSize, string name, ovrAvatar2EntityLoadSettings loadSettings, out ovrAvatar2LoadRequestId requestId);
+
+        public static ovrAvatar2Result OvrAvatarEntity_LoadMemory(
+            ovrAvatar2EntityId entityId, IntPtr data, UInt32 dataSize, string name, out ovrAvatar2LoadRequestId requestId)
+        {
+            var defaultLoadSettings = OvrAvatar2_GetLoadSettings();
+            return ovrAvatar2Entity_LoadMemory(entityId, data, dataSize, name, defaultLoadSettings, out requestId);
+        }
+
+        public static ovrAvatar2Result OvrAvatarEntity_LoadMemoryWithFilters(
+            ovrAvatar2EntityId entityId, IntPtr data, UInt32 dataSize, string name, in ovrAvatar2EntityFilters loadFilters, out ovrAvatar2LoadRequestId requestId)
+        {
+            var loadSettings = OvrAvatar2_GetLoadSettings();
+            loadSettings.loadFilters = loadFilters;
+            return ovrAvatar2Entity_LoadMemory(entityId, data, dataSize, name, loadSettings, out requestId);
+        }
+
+        /// Load assets described by a user's specification.
+        /// Note: Only one user's assets may be on an entity at a time.
+        /// Note: This can be called to load updated assets after ovrAvatar2_HasAvatarChanged returns true
+        /// \param ovrAvatar2Entity to load into
+        /// \param userID to load from
+        /// \param loadSettings for this load
+        /// \param (out) loadRequestID to retrieve status from ovrAvatar2Asset_GetLoadRequestInfo
+        /// \return result code
+        ///
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern ovrAvatar2Result ovrAvatar2Entity_LoadUser(
+            ovrAvatar2EntityId entityId, UInt64 userId, ovrAvatar2EntityLoadSettings loadSettings, out ovrAvatar2LoadRequestId requestId);
+
+        public static ovrAvatar2Result OvrAvatarEntity_LoadUser(ovrAvatar2EntityId entityId, UInt64 userId, out ovrAvatar2LoadRequestId requestId)
+        {
+            var defaultLoadSettings = OvrAvatar2_GetLoadSettings();
+            return ovrAvatar2Entity_LoadUser(entityId, userId, defaultLoadSettings, out requestId);
+        }
+
+        public static ovrAvatar2Result OvrAvatarEntity_LoadUserWithFilters(ovrAvatar2EntityId entityId, UInt64 userId, in ovrAvatar2EntityFilters loadFilters, out ovrAvatar2LoadRequestId requestId)
+        {
+            var loadSettings = OvrAvatar2_GetLoadSettings();
+            loadSettings.loadFilters = loadFilters;
+            return ovrAvatar2Entity_LoadUser(entityId, userId, loadSettings, out requestId);
+        }
+
+        public static ovrAvatar2Result OvrAvatarEntity_LoadUserWithFiltersFast(ovrAvatar2EntityId entityId, UInt64 userId, in ovrAvatar2EntityFilters loadFilters, out ovrAvatar2LoadRequestId requestId)
+        {
+            var loadSettings = OvrAvatar2_GetFastLoadSettings();
+            loadSettings.loadFilters.manifestationFlags = loadFilters.manifestationFlags;
+            loadSettings.loadFilters.subMeshInclusionFlags = loadFilters.subMeshInclusionFlags;
+            loadSettings.loadFilters.viewFlags = loadFilters.viewFlags;
+            loadSettings.loadFilters.highQualityFlags = CAPI.ovrAvatar2EntityHighQualityFlags.None;
+            return ovrAvatar2Entity_LoadUser(entityId, userId, loadSettings, out requestId);
+        }
+
+        /// Unload the default model from the entity
+        /// \param ovrAvatar2Entity to unload the default model from
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern ovrAvatar2Result ovrAvatar2Entity_UnloadDefaultModel(ovrAvatar2EntityId entityId);
+
+        public static bool OvrAvatar2Entity_UnloadDefaultModel(ovrAvatar2EntityId entityId)
+        {
+            return ovrAvatar2Entity_UnloadDefaultModel(entityId)
+                .EnsureSuccess("ovrAvatar2Entity_UnloadDefaultModel");
+        }
+
+        /// Unload an asset loaded via ovrAvatar2Entity_LoadUri()
+        /// \param ovrAvatar2Entity to unload the asset from
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
+        private static extern ovrAvatar2Result ovrAvatar2Entity_UnloadUri(ovrAvatar2EntityId entityId, string uri);
+        public static bool OvrAvatar2Entity_UnloadUri(ovrAvatar2EntityId entityId, string uri)
+        {
+            return ovrAvatar2Entity_UnloadUri(entityId, uri)
+                .EnsureSuccess("ovrAvatar2Entity_UnloadUri");
+        }
+
+        /// Unload an asset loaded via ovrAvatar2Entity_LoadMemory()
+        /// \param ovrAvatar2Entity to unload the asset from
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
+        private unsafe static extern ovrAvatar2Result ovrAvatar2Entity_UnloadMemory(ovrAvatar2EntityId entityId, /*const*/ char* name);
+
+        /// Unload an asset loaded via ovrAvatar2Entity_LoadUser()
+        /// \param ovrAvatar2Entity to unload the asset from
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern ovrAvatar2Result ovrAvatar2Entity_UnloadUser(ovrAvatar2EntityId entityId);
+
+        //-----------------------------------------------------------------
+        //
+        // Queries
+        //
+        //
+
+        /// Get the number of loaded assets on an entity
+        /// \param ovrAvatar2Entity to query
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern UInt32 ovrAvatar2Entity_GetNumLoadedAssets(ovrAvatar2EntityId entityId);
+
+        public static uint OvrAvatar2Entity_GetNumLoadedAssets(ovrAvatar2EntityId entityId)
+        {
+            return ovrAvatar2Entity_GetNumLoadedAssets(entityId);
+        }
+
+        [System.Serializable]
+        public enum ovrAvatar2EntityAssetType : Int32
+        {
+            SystemDefaultModel = 0,
+            SystemOther = 1,
+            Other = 2,
+        }
+
+        /// Get the asset types of the loaded assets on an entity
+        /// \param ovrAvatar2Entity to query
+        /// \param pointer to array to populate (should be sized by ovrAvatar2Entity_GetNumLoadedAssets)
+        /// \param size of the typesBuffer
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        private unsafe static extern ovrAvatar2Result ovrAvatar2Entity_GetLoadedAssetTypes(
+            ovrAvatar2EntityId entityId,
+            ovrAvatar2EntityAssetType* typesBuffer,
+            UInt32 bufferSize);
+
+        internal static NativeArrayDisposeWrapper<ovrAvatar2EntityAssetType>
+            OvrAvatar2Entity_GetLoadedAssetTypes_NativeArray(ovrAvatar2EntityId entityId)
+        {
+            uint numAssets = OvrAvatar2Entity_GetNumLoadedAssets(entityId);
+            if (numAssets > 0)
+            {
+                var assetTypes = new NativeArray<ovrAvatar2EntityAssetType>((int)numAssets
+                    , Allocator.Temp, NativeArrayOptions.UninitializedMemory);
+                try
+                {
+                    unsafe
+                    {
+                        var assetTypesPtr = (ovrAvatar2EntityAssetType*)assetTypes.GetUnsafePtr();
+                        var bufferSize = assetTypes.GetEnumBufferSize(sizeof(ovrAvatar2EntityAssetType));
+                        if (ovrAvatar2Entity_GetLoadedAssetTypes(entityId, assetTypesPtr, bufferSize)
+                            .EnsureSuccess("ovrAvatar2Entity_GetLoadedAssetTypes"))
+                        {
+                            return assetTypes;
+                        }
+                    }
+                }
+                catch { }
+                assetTypes.Dispose();
+            }
+            return default;
+        }
+
+        public static ovrAvatar2EntityAssetType[] OvrAvatar2Entity_GetLoadedAssetTypes(ovrAvatar2EntityId entityId)
+        {
+            using (var assetTypes
+                = OvrAvatar2Entity_GetLoadedAssetTypes_NativeArray(entityId))
+            {
+                return assetTypes.ToArray();
+            }
+        }
+
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Entity_GetStatus(ovrAvatar2EntityId entityId);
+
+
+        //-----------------------------------------------------------------
+        //
+        // Active
+        //
+        //
+
+        /// Set whether the an entity will update its render prims.
+        /// \param entity to get status of
+        /// \param acitve value
+        /// \return result code
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Entity_SetActive(
+            ovrAvatar2EntityId entityId,
+            [MarshalAs(UnmanagedType.U1)]
+            bool active);
+
+        /// Get whether the an entity will update its render prims.
+        /// \param entity to get status of
+        /// \param pointer to the where the active flag should be stored
+        /// \return result code
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Entity_GetActive(
+            ovrAvatar2EntityId entityId,
+            [MarshalAs(UnmanagedType.U1)]
+            out bool isActive);
+
+        /// Get whether the an entity will update its render prims.
+        /// \param entity to get status of
+        /// \param pointer to the where the active flag should be stored
+        /// \return result code
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern unsafe ovrAvatar2Result ovrAvatar2Entity_GetActives(
+            ovrAvatar2EntityId* entityIds,
+            [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1)]
+            bool* isActives,
+            uint numIds);
+
+        //-----------------------------------------------------------------
+        //
+        // Debug
+        //
+        //
+
+        /// Get name for provided `nodeId`
+        /// \param entity to get status of
+        /// \param `ovrAvatar2NodeId` with unknown name
+        /// \param `char*` output buffer for name
+        /// \param length in bytes of name buffer
+        /// \return result code
+        ///
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
+        private static extern unsafe ovrAvatar2Result ovrAvatar2Entity_GetNodeName(
+            ovrAvatar2EntityId entityId,
+            ovrAvatar2NodeId nodeId,
+            byte* nameBuffer,
+            UInt32 nameBufferSize,
+            out UInt32 nameLength);
+
+        public static string OvrAvatar2Entity_GetNodeName(ovrAvatar2EntityId entityId, ovrAvatar2NodeId nodeId)
+        {
+            unsafe
+            {
+                const int bufferSize = 256;
+                var nameBuffer = stackalloc byte[bufferSize];
+                if (ovrAvatar2Entity_GetNodeName(entityId, nodeId, nameBuffer, bufferSize, out var nameLen)
+                    .EnsureSuccess("ovrAvatar2Entity_GetNodeName"))
+                {
+                    return Marshal.PtrToStringAnsi((IntPtr)nameBuffer, (int)nameLen);
+                }
+            }
+
+            return null;
+        }
+
+        /// Query `ovrAvatar2NodeId`s for provided `jointTypes`
+        /// \param entity to get status of
+        /// \param pointer to JointType values to query
+        /// \param length of `jointTypes` array
+        /// \param pointer to output array of `ovrAvatar2NodeId`s,
+        ///     length must be greater than `jointTypeCount`
+        /// \return result code
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        private unsafe static extern ovrAvatar2Result ovrAvatar2Entity_QueryJointTypeNodes(
+            ovrAvatar2EntityId entityId,
+            /*const*/ ovrAvatar2JointType* jointTypes,
+            UInt32 jointTypeCount,
+            ovrAvatar2NodeId* nodeIds);
+
+        public static ovrAvatar2NodeId[] OvrAvatar2Entity_QueryJointTypeNodes(ovrAvatar2EntityId entityId, ovrAvatar2JointType[] jointTypes, UnityEngine.Object logContext = null)
+        {
+            var jointTypesLen = jointTypes.Length;
+            var jointTypesHandle = GCHandle.Alloc(jointTypes, GCHandleType.Pinned);
+            try
+            {
+                unsafe
+                {
+                    var jointTypesPtr = (ovrAvatar2JointType*)jointTypesHandle.AddrOfPinnedObject();
+                    using var result =
+                        OvrAvatar2Entity_QueryJointTypeNodes(entityId, jointTypesPtr, jointTypesLen, logContext);
+                    return result.ToArray();
+                }
+            }
+            finally
+            {
+                jointTypesHandle.Free();
+            }
+        }
+
+        public static ovrAvatar2NodeId[] OvrAvatar2Entity_QueryJointTypeNodes(
+            ovrAvatar2EntityId entityId, NativeSlice<ovrAvatar2JointType> jointTypes, UnityEngine.Object logContext = null)
+        {
+            var jointTypesLen = jointTypes.Length;
+            unsafe
+            {
+                var jointTypesPtr = (ovrAvatar2JointType*)jointTypes.GetUnsafeReadOnlyPtr();
+                using var result = OvrAvatar2Entity_QueryJointTypeNodes(entityId, jointTypesPtr, jointTypesLen, logContext);
+                return result.ToArray();
+            }
+        }
+
+        public static NativeArrayDisposeWrapper<ovrAvatar2NodeId> OvrAvatar2Entity_QueryJointTypeNodes_NativeArray(ovrAvatar2EntityId entityId, in NativeArray<ovrAvatar2JointType> jointTypes, UnityEngine.Object logContext = null)
+        {
+            unsafe
+            {
+                return OvrAvatar2Entity_QueryJointTypeNodes(entityId, jointTypes.GetPtr(), jointTypes.Length, logContext);
+            }
+        }
+
+        private static unsafe NativeArrayDisposeWrapper<ovrAvatar2NodeId> OvrAvatar2Entity_QueryJointTypeNodes(
+            ovrAvatar2EntityId entityId, ovrAvatar2JointType* jointTypesPtr, int jointTypesLen, UnityEngine.Object logContext = null)
+        {
+            var nodeIdsOutput = new NativeArray<ovrAvatar2NodeId>(jointTypesLen, Allocator.Temp, NativeArrayOptions.UninitializedMemory);
+            var queryResult = ovrAvatar2Entity_QueryJointTypeNodes(entityId, jointTypesPtr, (UInt32)jointTypesLen, nodeIdsOutput.GetPtr());
+
+            // Log appropriate error/warning, if error - return null
+            if (!queryResult.EnsureSuccessOrWarning(ovrAvatar2Result.LegacyJointTypeFallback
+                , "enable `ovrAvatar2EntityFeatures.UseDefaultAnimHierarchy` in `OvrAvatarEntity.creationInfo.features`"
+                , "ovrAvatar2Entity_QueryJointTypeNodes", entityLogScope, logContext)
+                && queryResult != ovrAvatar2Result.LegacyJointTypeFallback)
+            {
+                nodeIdsOutput.Dispose();
+                return default; // effectively, `null`
+            }
+
+            return nodeIdsOutput;
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Entity.cs.meta b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Entity.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..234ff5a79d7bbfb0883d051539457e361ee2d9c0
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Entity.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: d0cd431c058b63a4090d003e6e19e08d
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Importance.cs b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Importance.cs
new file mode 100644
index 0000000000000000000000000000000000000000..ab09473148f3edc3e6b4cdec7593c17939615c3e
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Importance.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Oculus.Avatar2
+{
+    public partial class CAPI
+    {
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern CAPI.ovrAvatar2Result ovrAvatar2Importance_SetBudget(UInt32 maxActiveEntities, UInt32 budget);
+        
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern CAPI.ovrAvatar2Result ovrAvatar2Importance_SetImportanceAndCost(ovrAvatar2EntityId entityId, float importance, UInt32 cost);
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Importance.cs.meta b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Importance.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..a93bd64e9c263c4b111f14a1b46e8ee544ccf4f9
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Importance.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 1bd319ea8f269d34697293db348e0cc2
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Lod.cs b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Lod.cs
new file mode 100644
index 0000000000000000000000000000000000000000..8a0da45655111c035927f227a5178e2bf04e8032
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Lod.cs
@@ -0,0 +1,121 @@
+using System;
+using System.Runtime.InteropServices;
+using UnityEngine.Assertions;
+
+namespace Oculus.Avatar2
+{
+
+    public partial class CAPI
+    {
+        // Ease of use managed structure for registering an avatar with the LOD system.
+        // At time of registration we'll make the struct below.
+        public struct ovrAvatar2LODRegistration
+        {
+            public Int32 avatarId; // Caller defined identifier for the avatar instance
+            public Int32[] lodWeights; // Weights of LODs and count
+            public Int32 lodThreshold;  // Max lod level permitted
+        };
+
+        // What we supply to the runtime C API, from the above
+        // Runtime copies data out of here, so there is no need for this data to be persistent
+        [StructLayout(LayoutKind.Sequential)]
+        private struct ovrAvatar2LODRegistrationNative
+        {
+            public Int32 avatarId; // Caller defined identifier for the avatar instance
+            public IntPtr lodWeights; // Weights of LODs and count
+            public Int32 lodWeightCount; // Weight count
+            public Int32 lodThreshold;  // Max lod level permitted
+        };
+
+        [StructLayout(LayoutKind.Sequential)]
+        public struct ovrAvatar2LODUpdate
+        {
+            public Int32 avatarId; // Caller defined identifier for the avatar instance
+            public bool isPlayer;  // This avatar is the player
+            public bool isCulled;  // This avatar has been culled by some visbility system
+            public Int32 importanceScore; // User defined importance (eg distance from cam)
+        };
+
+        [StructLayout(LayoutKind.Sequential)]
+        public struct ovrAvatar2LODResult
+        {
+            public Int32 avatarId; // Caller defined identifier for the avatar instance
+            public Int32 assignedLOD; // LOD level assigned to this avatar
+        };
+
+        // Register / unregister / query avatar
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        internal static extern ovrAvatar2Result ovrAvatar2LOD_UnregisterAvatar(Int32 id);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        internal static extern ovrAvatar2Result ovrAvatar2LOD_AvatarRegistered(Int32 id);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ovrAvatar2LOD_RegisterAvatar")]
+        internal static extern ovrAvatar2Result ovrAvatar2LOD_RegisterAvatarNative(IntPtr recrord);
+
+        internal static ovrAvatar2Result ovrAvatar2LOD_RegisterAvatar(ovrAvatar2LODRegistration record)
+        {
+            unsafe
+            {
+                fixed (Int32* weightPtr = record.lodWeights)
+                {
+                    ovrAvatar2LODRegistrationNative nativeRecord;
+                    nativeRecord.avatarId = record.avatarId;
+                    nativeRecord.lodWeights = (IntPtr)weightPtr;
+                    nativeRecord.lodWeightCount = record.lodWeights.Length;
+                    nativeRecord.lodThreshold = record.lodThreshold;
+
+                    return ovrAvatar2LOD_RegisterAvatarNative(new IntPtr(&nativeRecord));
+                }
+            }
+        }
+
+        // Calculate LOD distribution
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        internal static extern void ovrAvatar2LOD_SetDistribution(Int32 maxWeightValue, float exponent);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ovrAvatar2LOD_GenerateDistribution")]
+        unsafe internal static extern ovrAvatar2Result ovrAvatar2LOD_GenerateDistributionNative(
+            Int32* weightDistribution,
+            Int32 distributionCount,
+            ovrAvatar2LODUpdate* lodUpdates,
+            ovrAvatar2LODResult* lodResults,
+            Int32 avatarCount,
+            Int32* totalAssignedWeight);
+
+        internal static ovrAvatar2Result ovrAvatar2LOD_GenerateDistribution(
+            Int32[] weightDistribution,
+            ovrAvatar2LODUpdate[] lodUpdates,
+            ref ovrAvatar2LODResult[] lodResults,
+            out Int32 totalAssignedWeightOut)
+        {
+            Assert.IsNotNull(lodResults);
+
+            ovrAvatar2Result result;
+            Int32 totalAssignedWeight;
+            unsafe
+            {
+                fixed (Int32* weightDistributionPtr = weightDistribution)
+                {
+                    fixed (ovrAvatar2LODUpdate* updatesPtr = lodUpdates)
+                    {
+                        fixed (ovrAvatar2LODResult* resultsPtr = lodResults)
+                        {
+                            result = ovrAvatar2LOD_GenerateDistributionNative(
+                                weightDistributionPtr,
+                                weightDistribution.Length,
+                                updatesPtr,
+                                resultsPtr,
+                                lodUpdates.Length,
+                                &totalAssignedWeight);
+                        }
+                    }
+                }
+            }
+            totalAssignedWeightOut = totalAssignedWeight;
+            return result;
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Lod.cs.meta b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Lod.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..9735b6fcb4916ff39ecdc8ba867609c5d5163af4
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Lod.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 8a7e2bfdd337f154fbb544e8fb245f9b
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Render.cs b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Render.cs
new file mode 100644
index 0000000000000000000000000000000000000000..4755f6f0262921fc57ae8c571e84cbc056ebe03a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Render.cs
@@ -0,0 +1,97 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Oculus.Avatar2
+{
+    public partial class CAPI
+    {
+        [StructLayout(LayoutKind.Sequential)]
+        public unsafe readonly ref struct ovrAvatar2EntityRenderState
+        {
+            public readonly ovrAvatar2Transform rootTransform;
+            public readonly UInt32 primitiveCount;
+            public readonly ovrAvatar2HierarchyVersion hierarchyVersion; ///< see ovrAvatar2Entity_GetPose
+            public readonly ovrAvatar2EntityRenderStateVersion allNodesVersion; ///< changes when allMeshNodes changes
+            public readonly ovrAvatar2EntityRenderStateVersion visibleNodesVersion; ///< changes when visibleMeshNodes changes
+            public readonly ovrAvatar2NodeId* allMeshNodes;
+            public readonly ovrAvatar2NodeId* visibleMeshNodes;
+            public readonly UInt32 allMeshNodesCount;
+            public readonly UInt32 visibleMeshNodesCount;
+
+            public ovrAvatar2NodeId GetAllMeshNodeAtIdx(UInt32 index)
+            {
+                if (index >= allMeshNodesCount)
+                {
+
+                    throw new ArgumentOutOfRangeException(
+                        $"Index {index} is out of range of allMeshNodes array of size {allMeshNodesCount}");
+                }
+
+                unsafe
+                {
+                    return allMeshNodes[index];
+                }
+            }
+
+            public ovrAvatar2NodeId GetVisibleMeshNodeAtIdx(UInt32 index)
+            {
+                if (index >= visibleMeshNodesCount)
+                {
+
+                    throw new ArgumentOutOfRangeException(
+                        $"Index {index} is out of range of visibleMeshNodes array of size {visibleMeshNodesCount}");
+                }
+
+                unsafe
+                {
+                    return visibleMeshNodes[index];
+                }
+            }
+        }
+
+        public enum ovrAvatar2PrimitiveRenderInstanceID : Int32
+        {
+            Invalid = 0
+        }
+
+        [StructLayout(LayoutKind.Sequential)]
+        public unsafe readonly struct ovrAvatar2PrimitiveRenderState
+        {
+            public readonly ovrAvatar2PrimitiveRenderInstanceID id; // unique id of the instance of the primitive to be rendered
+            public readonly ovrAvatar2Id primitiveId; // primitive to be rendered
+            public readonly ovrAvatar2NodeId meshNodeId;
+            public readonly ovrAvatar2Transform localTransform; // local transform of this prim relative to root
+            public readonly ovrAvatar2Transform worldTransform; // world transform of this prim
+            public readonly ovrAvatar2Pose pose; // current pose
+            public readonly UInt32 morphTargetCount; // number of blend shapes values
+            public readonly ovrAvatar2Transform skinningOrigin; // root transform of the skinning matrices
+        };
+
+        // Query the render state for an entity
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern CAPI.ovrAvatar2Result ovrAvatar2Render_QueryRenderState(
+            ovrAvatar2EntityId entityId, out ovrAvatar2EntityRenderState outState);
+
+        // Query the render state for a primitive in an entity by index
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern CAPI.ovrAvatar2Result ovrAvatar2Render_GetPrimitiveRenderStateByIndex(
+            ovrAvatar2EntityId entityId, UInt32 primitiveRenderStateIndex,
+            out ovrAvatar2PrimitiveRenderState outState);
+
+        // Query the render states for a primitive in an entity by index
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern unsafe CAPI.ovrAvatar2Result ovrAvatar2Render_GetPrimitiveRenderStatesByIndex(
+            ovrAvatar2EntityId entityId, UInt32* primitiveRenderStateIndices,
+            ovrAvatar2PrimitiveRenderState* outState,
+            UInt32 numRenderStates);
+
+        // Retrieve the skin transforms for a primitive render state
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern CAPI.ovrAvatar2Result ovrAvatar2Render_GetSkinTransforms(
+            ovrAvatar2EntityId entityId, ovrAvatar2PrimitiveRenderInstanceID instanceId, /*ovrAvatar2Matrix4f[]*/ IntPtr skinTransforms, UInt32 bytes, bool interleaveNormalMatrices);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern CAPI.ovrAvatar2Result ovrAvatar2Render_GetMorphTargetWeights(
+            ovrAvatar2EntityId entityId, ovrAvatar2PrimitiveRenderInstanceID instanceId, /*float[]*/ IntPtr morphTargetWeights, UInt32 bytes);
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Render.cs.meta b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Render.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..7d3abd61a9d1116427feb59494d97de2ad6718ce
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Render.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 372a5df073afec946aa93ac4fc22c8a8
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_SDKVersionInfo.cs b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_SDKVersionInfo.cs
new file mode 100644
index 0000000000000000000000000000000000000000..3528aa981f2ca9b264df707ca5793ba808a4fa77
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_SDKVersionInfo.cs
@@ -0,0 +1,46 @@
+// Avatar SDK runtime version file
+// @generated on 06/27/2023, at 22:17:05 UTC from AndroidManifest.xml
+//
+// DO NOT MODIFY THIS FILE BY HAND, IT IS AUTOGENERATED
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace Oculus.Avatar2
+{
+
+  [StructLayout(LayoutKind.Sequential)]
+  public struct FBVersionNumber
+  {
+    public UInt32 releaseVersion;
+    public UInt32 hotfixVersion;
+    public UInt32 experimentationVersion;
+    public UInt32 betaVersion;
+    public UInt32 alphaVersion;
+
+    public override string ToString() {
+      return $"{releaseVersion}.{hotfixVersion}.{experimentationVersion}.{betaVersion}.{alphaVersion}";
+    }
+  };
+
+  public static class SDKVersionInfo
+  {
+    public const UInt32 AVATAR2_RELEASE_VERSION = 20;
+    public const UInt32 AVATAR2_HOTFIX_VERSION = 3;
+    public const UInt32 AVATAR2_EXPERIMENTATION_VERSION = 0;
+    public const UInt32 AVATAR2_BETA_VERSION = 17;
+    public const UInt32 AVATAR2_ALPHA_VERSION = 0;
+
+    static public FBVersionNumber CurrentVersion()
+    {
+        FBVersionNumber versionNumber;
+        versionNumber.releaseVersion = AVATAR2_RELEASE_VERSION;
+        versionNumber.hotfixVersion = AVATAR2_HOTFIX_VERSION;
+        versionNumber.experimentationVersion = AVATAR2_EXPERIMENTATION_VERSION;
+        versionNumber.betaVersion = AVATAR2_BETA_VERSION;
+        versionNumber.alphaVersion = AVATAR2_ALPHA_VERSION;
+
+        return versionNumber;
+    }
+  }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_SDKVersionInfo.cs.meta b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_SDKVersionInfo.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..5ec6cd71eccd867645b0c579706c38550b9ae5bd
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_SDKVersionInfo.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 6349ce8779c00a4478e9255257610169
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Streaming.cs b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Streaming.cs
new file mode 100644
index 0000000000000000000000000000000000000000..4aac0758b01d856d708ea2b818371dcec314d486
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Streaming.cs
@@ -0,0 +1,111 @@
+using System;
+using System.Runtime.InteropServices;
+
+using UnityEngine;
+
+namespace Oculus.Avatar2
+{
+    public partial class CAPI
+    {
+        private const string StreamingCapiLogScope = "OvrAvatarAPI_Streaming";
+        //-----------------------------------------------------------------
+        //
+        // State
+        //
+        //
+
+        [StructLayout(LayoutKind.Sequential)]
+        public struct ovrAvatar2StreamingPlaybackState
+        {
+            public UInt32 numSamples; // Number of samples in the playback buffer
+            public float interpolationBlendWeight; // Interpolation blend between the oldest 2 samples
+            public UInt64 oldestSampleTime; // Time in microseconds of the oldest sample
+            public UInt64 latestSampleTime; // Time in microseconds of the newest sample
+            public UInt64 remoteTime; ///< Time in microseconds of the remote time (for snapshot playback)
+            public UInt64 localTime; ///< Time in microseconds of local time (for snapshot playback)
+            public UInt64 recordingPlaybackTime; ///< Time in microseconds of recordingPlayback time (for recording playback)
+            [MarshalAs(UnmanagedType.U1)]
+            bool poseValid; ///< Whether the playback pose is valid
+        }
+
+        //-----------------------------------------------------------------
+        //
+        // Record
+        //
+        //
+
+        public enum ovrAvatar2StreamLOD : Int32
+        {
+            Full, // Full avatar state with lossless compression
+            High, // Full avatar state with lossy compression
+            Medium, // Partial avatar state with lossy compression
+            Low, // Minimal avatar state with lossy compression
+        }
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern CAPI.ovrAvatar2Result ovrAvatar2Streaming_RecordStart(ovrAvatar2EntityId entityId);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern CAPI.ovrAvatar2Result ovrAvatar2Streaming_RecordStop(ovrAvatar2EntityId entityId);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern CAPI.ovrAvatar2Result ovrAvatar2Streaming_RecordSnapshot(ovrAvatar2EntityId entityId);
+
+        public static unsafe bool OvrAvatar2Streaming_SerializeRecording(
+            ovrAvatar2EntityId entityId, ovrAvatar2StreamLOD lod, byte* destinationPtr, ref UInt64 bytes)
+        {
+            return ovrAvatar2Streaming_SerializeRecording(entityId, lod, destinationPtr, ref bytes)
+                .EnsureSuccess("ovrAvatar2Streaming_SerializeRecording", StreamingCapiLogScope);
+        }
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static unsafe extern CAPI.ovrAvatar2Result ovrAvatar2Streaming_SerializeRecording(
+            ovrAvatar2EntityId entityId, ovrAvatar2StreamLOD lod, byte* destinationPtr, ref UInt64 bytes);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern CAPI.ovrAvatar2Result ovrAvatar2Streaming_GetRecordingSize(
+            ovrAvatar2EntityId entityId, ovrAvatar2StreamLOD lod, out UInt64 bytes);
+
+        //-----------------------------------------------------------------
+        //
+        // Playback
+        //
+        //
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern CAPI.ovrAvatar2Result ovrAvatar2Streaming_PlaybackStart(ovrAvatar2EntityId entityId);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern CAPI.ovrAvatar2Result ovrAvatar2Streaming_PlaybackStop(ovrAvatar2EntityId entityId);
+
+        public static unsafe bool OvrAvatar2Streaming_DeserializeRecording(
+            ovrAvatar2EntityId entityId, byte* sourceBuffer, UInt64 bytes, UnityEngine.Object context)
+        {
+            Debug.Assert(entityId != ovrAvatar2EntityId.Invalid);
+            Debug.Assert(sourceBuffer != null);
+            Debug.Assert(bytes > 0);
+
+            var result = ovrAvatar2Streaming_DeserializeRecording(entityId, sourceBuffer, bytes);
+            return result.EnsureSuccessOrLogVerbose(
+                CAPI.ovrAvatar2Result.DeserializationPending, "skeleton is not loaded",
+                "ovrAvatar2Streaming_DeserializeRecording", StreamingCapiLogScope, context);
+        }
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern CAPI.ovrAvatar2Result ovrAvatar2Streaming_SetPlaybackTimeDelay(
+            ovrAvatar2EntityId entityId, float delaySeconds);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern CAPI.ovrAvatar2Result ovrAvatar2Streaming_GetPlaybackState(
+            ovrAvatar2EntityId entityId, out ovrAvatar2StreamingPlaybackState playbackState);
+
+        //-----------------------------------------------------------------
+        //
+        // Dll Bindings
+        //
+        //
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern unsafe CAPI.ovrAvatar2Result ovrAvatar2Streaming_DeserializeRecording(
+            ovrAvatar2EntityId entityId, byte* sourceBuffer, UInt64 bytes);
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Streaming.cs.meta b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Streaming.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..e5bfd54adb6ad4dfcce3ecdae20caf58bfe053d6
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Streaming.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: b473fd5078d06684bb48a3d08cee700e
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Tracking.cs b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Tracking.cs
new file mode 100644
index 0000000000000000000000000000000000000000..6db44768bbde9c35a92a69670afd43120888abbb
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Tracking.cs
@@ -0,0 +1,784 @@
+using System;
+using System.Runtime.InteropServices;
+
+///
+/// @file OvrAvatarAPI_Tracking.cs
+/// The structures and enums in this file overlay corresponding
+/// data maintained in the native avatar SDK implementation.
+///
+namespace Oculus.Avatar2
+{
+    public partial class CAPI
+    {
+        #region Input
+        ///
+        /// Flags that indicate which controller buttons are pressed.
+        /// @see ovrAvatar2Touch
+        ///
+        [Flags]
+        public enum ovrAvatar2Button : Int32
+        {
+            /// X/A pressed
+            One = 0x0001,
+
+            /// Y/B pressed
+            Two = 0x0002,
+
+            /// Select/Oculus button pressed
+            Three = 0x0004,
+
+            /// Joystick button pressed
+            Joystick = 0x0008,
+        }
+
+        ///
+        /// Flags that indicate which parts of the controller are touched.
+        /// @see ovrAvatar2Touch
+        ///
+        [Flags]
+        public enum ovrAvatar2Touch : Int32
+        {
+            /// Capacitive touch for X/A button
+            One = 0x0001,
+
+            /// Capacitive touch for Y/B button
+            Two = 0x0002,
+
+            /// Capacitive touch for thumbstick
+            Joystick = 0x0004,
+
+            /// Capacitive touch for thumb rest
+            ThumbRest = 0x0008,
+
+            /// Capacitive touch for index trigger
+            Index = 0x0010,
+
+            /// Index finger is pointing
+            Pointing = 0x0040,
+
+            /// Thumb is up
+            ThumbUp = 0x0080,
+        }
+
+        ///
+        /// Designates the type of controller being used.
+        ///
+        public enum ovrAvatar2ControllerType : Int32
+        {
+            /// Invalid or unknown controller
+            Invalid = -1,
+
+            ///  Oculus Rift controller
+            Rift = 0,
+
+            /// Oculus Touch controller
+            Touch = 1,
+
+            /// Oculus Quest 2 controller
+            Quest2 = 2,
+
+            /// Meta Quest Pro controller
+            QuestPro = 3,
+        }
+
+        ///
+        /// Describes native controller state.
+        ///
+        [StructLayout(LayoutKind.Sequential)]
+        public struct ovrAvatar2ControllerState
+        {
+            ///
+            /// Flags specifying which buttons are currently pressed.
+            /// @see ovrAvatar2Button
+            ///
+            public ovrAvatar2Button buttonMask;
+
+            ///
+            /// Flags mask specifying which portions of the controller are currently touched.
+            /// @see ovrAvatar2Touch
+            ///
+            public ovrAvatar2Touch touchMask;
+
+            /// X-axis position of the thumbstick.
+            public float joystickX;
+
+            /// Y-axis position of the thumbstick.
+            public float joystickY;
+
+            /// Current value of the index finger trigger.
+            public float indexTrigger;
+
+            /// Current value of the hand trigger (underneath the middle finger).
+            public float handTrigger;
+        }
+
+        ///
+        /// Collects the input state for both left and right controllers.
+        /// @see ovrAvatar2ControllerState
+        /// @see ovrAvatar2ControllerType
+        ///
+        [StructLayout(LayoutKind.Sequential)]
+        public struct ovrAvatar2InputControlState
+        {
+            /// Type of controller being used.
+            public ovrAvatar2ControllerType type;
+
+            // the next two entries match 'ovrAvatar2ControllerState controllerState[ovrAvatar2Side_Count]'
+            // in the c++ implementation (can't use 'fixed' for ovrAvatar2ControllerState)
+
+            /// Input state of the left controller.
+            public ovrAvatar2ControllerState leftControllerState;
+
+            /// Input state of the right controller.
+            public ovrAvatar2ControllerState rightControllerState;
+        }
+
+        ///
+        /// Collects the current position, orientation and scale for
+        /// the headset and controllers.
+        /// @see ovrAvatar2InputControlState
+        ///
+        [StructLayout(LayoutKind.Sequential)]
+        public struct ovrAvatar2InputTrackingState
+        {
+            // True if the headset is currently tracked and valid.
+            [MarshalAs(UnmanagedType.U1)]
+            public bool headsetActive;
+
+            /// True if the left controller is currently tracked and valid.
+            [MarshalAs(UnmanagedType.U1)]
+            public bool leftControllerActive;
+
+            /// True if the right controller is currently tracked and valid.
+            [MarshalAs(UnmanagedType.U1)]
+            public bool rightControllerActive;
+
+            ///
+            /// True if the controller model should be shown.
+            /// Must also have *ovrAvatar2EntityFeatures.ShowControllers* set on the entity.
+            /// @see ovrAvatar2EntityFeatures
+            /// @see OvrAvatarEntity.CreateEntity
+            /// @see ovrAvatar2EntityCreateInfo
+            ///
+            [MarshalAs(UnmanagedType.U1)]
+            public bool leftControllerVisible;
+
+            ///
+            /// True if the controller model should be shown.
+            /// Must also have *ovrAvatar2EntityFeatures.ShowControllers* set on the entity.
+            /// @see ovrAvatar2EntityFeatures
+            /// @see OvrAvatarEntity.CreateEntity
+            /// @see ovrAvatar2EntityCreateInfo
+            ///
+            [MarshalAs(UnmanagedType.U1)]
+            public bool rightControllerVisible;
+
+            ///
+            public ovrAvatar2Transform headset;
+
+            // the next two entries match 'ovrAvatar2Transform controller[ovrAvatar2Side_Count]'
+            // in the c++ implementation (can't use 'fixed' for ovrAvatar2Transform)
+
+            /// Transform with position, orientation and scale for the left controller.
+            public ovrAvatar2Transform leftController;
+
+            /// Transform with position, orientation and scale for the right controller.
+            public ovrAvatar2Transform rightController;
+        }
+        #endregion
+
+        #region Tracking
+
+        ///
+        /// Estimate of the user's overall body pose
+        /// (sitting or standing).
+        ///
+        public enum ovrAvatar2TrackingBodyModality : Int32
+        {
+            /// Avatar modality unknown.
+            Unknown = 0,
+
+            // TODO: verify this is correct
+            ///  User is in a seated position.
+            Sitting = 1,
+
+            // TODO: verify this is correct
+            ///  User is in a standing position.
+            Standing = 2,
+        };
+
+        // TODO: more explanation of what tracking confidence level means
+        ///
+        /// Tracking confidence level.
+        ///
+        public enum ovrAvatar2TrackingConfidence : Int32
+        {
+            // Low tracking confidence level
+            Low = 0,
+
+            // High tracking confidence level
+            High = 0x3f800000,
+        };
+
+        ///
+        /// Indicates the coordinate space of a joint / bone.
+        ///
+        public enum ovrAvatar2Space : Int32
+        {
+            /// Local coordinates, with respect to parent joint
+            Local = 0,
+
+            /// Object coordinates, with respect to the avatar entity
+            Object = 1,
+
+            /// Coordinate space not known
+            Unknown = 2,
+        }
+
+        ///
+        /// Contains bone ID for this bone and the index of it's parent bone.
+        /// An array of these defines the structure of a skeleton.
+        /// The name and order of the tracking skeleton bones are fixed but
+        /// the hierarchy is not.
+        ///
+        /// @see ovrAvatar2JointType
+        ///
+        [StructLayout(LayoutKind.Sequential)]
+        public struct ovrAvatar2Bone
+        {
+            /// Bone ID for this bone.
+            public ovrAvatar2JointType boneId;
+
+            /// Zero-based index of this bone's parent bone.
+            /// It will be -1 if the bone has no parent.
+            public Int16 parentBoneIndex;
+        };
+
+        ///
+        /// Contains the transforms for the bones in the tracking skeleton.
+        ///
+        [StructLayout(LayoutKind.Sequential)]
+        internal unsafe ref struct ovrAvatar2TrackingBodyPose
+        {
+            /// Number of bones in the tracking skeleton.
+            public readonly UInt32 numBones;
+
+            // TODO: Make this field readonly
+            /// Coordinate space of the pose.
+            public ovrAvatar2Space space;
+
+            /// Position, orientation and scale for each bone in the skeleton.
+            public readonly ovrAvatar2Transform* bones;
+
+            /// Scale value of left hand.
+            public readonly float leftHandScale;
+
+            /// Scale value of right hand.
+            public readonly float rightHandScale;
+
+            public ovrAvatar2TrackingBodyPose(ovrAvatar2Transform* bones, UInt32 numBones, ovrAvatar2Space space = ovrAvatar2Space.Unknown, float leftHandScale = 1.0f, float rightHandScale = 1.0f)
+            {
+                this.space = space;
+                this.numBones = numBones;
+                this.bones = bones;
+                this.leftHandScale = leftHandScale;
+                this.rightHandScale = rightHandScale;
+            }
+        };
+
+        /**
+         * @struct ovrAvatar2TrackingBodySkeleton
+         * Contains the description of the body tracking skeleton.
+         */
+        [StructLayout(LayoutKind.Sequential)]
+        internal unsafe ref struct ovrAvatar2TrackingBodySkeleton
+        {
+            /// Number of bones in the bones array.
+            public readonly UInt32 numBones;
+
+            /// Vector with the forward bone direction.
+            public ovrAvatar2Vector3f forwardDir;
+
+            /// Array of bone IDs and their parent bones.
+            /// Describes the structure of the skeleton.
+            public readonly ovrAvatar2Bone* bones;
+
+            /// Reference pose of the skeleton. This is the pose
+            /// which places the skeleton in a T with arms horizontal.
+            ///
+            public ovrAvatar2TrackingBodyPose referencePose;
+
+            ///
+            /// Constructs a native body tracking skeleton from a bone hierarchy and initial pose.
+            /// @param bones     Array of bone IDs and parent bone indices.
+            /// @param numBones  Number of bones in the array.
+            /// @param pose      Native pose description.
+            /// @see ovrAvatar2Bone
+            /// @see ovrAvatar2TrackingBodyPose
+            ///
+            public ovrAvatar2TrackingBodySkeleton(ovrAvatar2Bone* bones, UInt32 numBones, ovrAvatar2TrackingBodyPose pose)
+            {
+                this.bones = bones;
+                this.numBones = numBones;
+                forwardDir = new ovrAvatar2Vector3f();
+                referencePose = pose;
+            }
+        }
+
+        ///
+        /// Type of input used for hand tracking.
+        ///
+        public enum ovrAvatar2HandInputType : Int32
+        {
+            /// Controller used to get hand position and orientation
+            Controller = 0,
+
+            /// Headset sensors are used to get hand position and orientation
+            Tracking = 1,
+
+            /// Custom hand tracking implementation
+            Custom = 2,
+
+            /// Hand tracking input type unknown
+            Unknown = 3,
+        }
+
+        ///
+        /// Collects the state of the body tracker.
+        /// The tracking state includes the position, orientation and scale
+        /// for the headset and controllers, buttons pressed, the type of
+        /// Hand tracking desired and whether the avatar is sitting or standing.
+        /// @see ovrAvatar2InputControlState
+        /// @see ovrAvatar2HandInputType
+        /// @see ovrAvatar2TrackingBodyModality
+        ///
+        [StructLayout(LayoutKind.Sequential)]
+        public ref struct ovrAvatar2TrackingBodyState
+        {
+            /// Position, orientation and scale of headset and left and right controllers.
+            public ovrAvatar2InputTrackingState inputTrackingState;
+
+            /// Input state for left and right controllers.
+            public ovrAvatar2InputControlState inputControlState;
+
+            /// Type of hand tracking input for left hand.
+            public ovrAvatar2HandInputType leftHandInputType;
+
+            /// Type of hand tracking input for right hand.
+            public ovrAvatar2HandInputType rightHandInputType;
+
+            /// Version number of tracking skeleton.
+            public Int32 skeletonVersion;
+
+            /// Number of bones in tracking skeleton.
+            public Int32 numBones;
+
+            /// Avatar body modality.
+            public ovrAvatar2TrackingBodyModality bodyModality;
+
+            /// Scale value of left hand.
+            public float leftHandScale;
+
+            /// Scale value of right hand.
+            public float rightHandScale;
+        }
+
+        ///
+        /// C# callback function invoked from the native code to update the body tracking state.
+        /// @param bodyState   C# object to get the updated tracking state.
+        /// @param userContext handle to native object originating the call.
+        /// @see ovrAvatar2TrackingBodyState
+        ///
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        [return: MarshalAs(UnmanagedType.U1)]
+        internal delegate bool BodyStateCallback(out ovrAvatar2TrackingBodyState bodyState, IntPtr userContext);
+
+        ///
+        /// C# callback function invoked from the native code to update the body tracking skeleton.
+        /// @param skeleton    C# object to get the updated skeleton.
+        /// @param userContext C++ pointer to native object originating the call.
+        /// @see ovrAvatar2TrackingBodySkeleton
+        ///
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        [return: MarshalAs(UnmanagedType.U1)]
+        internal delegate bool BodySkeletonCallback(ref ovrAvatar2TrackingBodySkeleton skeleton, IntPtr userContext);
+
+        ///
+        /// C# callback function invoked from the native code to update the body tracking pose.
+        /// @param pose        C# object to get the updated pose.
+        /// @param userContext handle to native object originating the call.
+        /// @see ovrAvatar2TrackingBodyPose
+        ///
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        [return: MarshalAs(UnmanagedType.U1)]
+        internal delegate bool BodyPoseCallback(ref ovrAvatar2TrackingBodyPose pose, IntPtr userContext);
+
+        ///
+        /// Collects the C# callbacks and the pointer to the native body tracking implementation.
+        /// This is the *context* passed to @ref BodyPoseCallback(),
+        /// @ref BodySkeletonCallback() and @ref BodyStateCallback().
+        ///
+        [StructLayout(LayoutKind.Sequential)]
+        internal struct ovrAvatar2TrackingDataContext
+        {
+            /// Native handle to the body tracking implementation.
+            public IntPtr context;
+
+            /// C# function called by native code to update body tracking state.
+            public BodyStateCallback bodyStateCallback;
+
+            /// C# function called by native code to update body tracking skeleton.
+            public BodySkeletonCallback bodySkeletonCallback;
+
+            /// C# function called by native code to update body tracking pose.
+            public BodyPoseCallback bodyPoseCallback;
+        }
+
+        ///
+        /// Collects the native handles to the C# callbacks and the
+        /// body tracking implementation.
+        ///
+        [StructLayout(LayoutKind.Sequential)]
+        internal struct ovrAvatar2TrackingDataContextNative
+        {
+            /// Native handle to the native body tracking implementation.
+            public IntPtr context;
+
+            ///
+            /// Native handle to the body state callback.
+            /// @see BodyStateCallback
+            ///
+            public IntPtr bodyStateCallback;
+
+            ///
+            /// Native handle to the body skeleton callback.
+            /// @see BodySkeletonCallback
+            ///
+            public IntPtr bodySkeletonCallback;
+
+            ///
+            /// Native handle to the body pose callback.
+            /// @see BodyPoseCallback
+            ///
+            public IntPtr bodyPoseCallback;
+        }
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        internal static extern ovrAvatar2Result ovrAvatar2Tracking_SetBodyTrackingContext(ovrAvatar2EntityId entityId, in ovrAvatar2TrackingDataContext context);
+
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ovrAvatar2Tracking_SetBodyTrackingContext")]
+        internal static extern ovrAvatar2Result ovrAvatar2Tracking_SetBodyTrackingContextNative(ovrAvatar2EntityId entityId, in ovrAvatar2TrackingDataContextNative context);
+
+        #endregion Tracking
+
+
+        #region LipSync
+
+        ///
+        /// Avatar visemes used for synchronizing avatar lip motion with audio speech.
+        ///
+        public enum ovrAvatar2Viseme : Int32
+        {
+            /// Silent viseme
+            sil = 0,
+            /// PP viseme (corresponds to p,b,m phonemes in worlds like \a put , \a bat, \a mat)
+            PP = 1,
+            /// FF viseme (corrseponds to f,v phonemes in the worlds like \a fat, \a vat)
+            FF = 2,
+            /// TH viseme (corresponds to th phoneme in words like \a think, \a that)
+            TH = 3,
+            /// DD viseme (corresponds to t,d phonemes in words like \a tip or \a doll)
+            DD = 4,
+            /// kk viseme (corresponds to k,g phonemes in words like \a call or \a gas)
+            kk = 5,
+            /// CH viseme (corresponds to tS,dZ,S phonemes in words like \a chair, \a join, \a she)
+            CH = 6,
+            /// SS viseme (corresponds to s,z phonemes in words like \a sir or \a zeal)
+            SS = 7,
+            /// nn viseme (corresponds to n,l phonemes in worlds like \a lot or \a not)
+            nn = 8,
+            /// RR viseme (corresponds to r phoneme in worlds like \a red)
+            RR = 9,
+            /// aa viseme (corresponds to A: phoneme in worlds like \a car)
+            aa = 10,
+            /// E viseme (corresponds to e phoneme in worlds like \a bed)
+            E = 11,
+            /// I viseme (corresponds to ih phoneme in worlds like \a tip)
+            ih = 12,
+            /// O viseme (corresponds to oh phoneme in worlds like \a toe)
+            oh = 13,
+            /// U viseme (corresponds to ou phoneme in worlds like \a book)
+            ou = 14,
+
+            /// Total number of visemes
+            Count = 15
+        }
+
+        // TODO: reference lipsync from SDK
+        ///
+        /// Collects the viseme weights from the lip tracker.
+        ///
+        [StructLayout(LayoutKind.Sequential)]
+        internal unsafe struct ovrAvatar2LipSyncState
+        {
+            /// Array of weights for each viseme.
+            public fixed float visemes[(int)ovrAvatar2Viseme.Count];
+
+            // TODO: figure out what this is
+            public float laughterScore;
+        }
+
+        ///
+        /// C# callback function invoked from the native code to update
+        /// the lip sync viseme weights.
+        /// @param lipSyncState    C# object to get the updated viseme weights.
+        /// @param userContext     handle to native object originating the call.
+        /// @see ovrAvatar2LipSyncState
+        ////
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        internal delegate bool LipSyncCallback(out ovrAvatar2LipSyncState lipSyncState, IntPtr userContext);
+
+        ///
+        /// Collects the C# callback and the pointer to the
+        /// native lip sync implementation.
+        /// This is the *context* passed to @ref LipSyncCallback().
+        ///
+        [StructLayout(LayoutKind.Sequential)]
+        internal struct ovrAvatar2LipSyncContext
+        {
+            /// handle to the native body tracking implementation.
+            public IntPtr context;
+
+            /// C# function to called by native code to update lip sync viseme weights.
+            public LipSyncCallback lipSyncCallback;
+        }
+
+        ///
+        /// Collects native handle for the C# callback and the pointer to the
+        /// native lip sync implementation.
+        /// This is the *context* passed to @ref LipSyncCallback().
+        ///
+        [StructLayout(LayoutKind.Sequential)]
+        internal struct ovrAvatar2LipSyncContextNative
+        {
+            /// handle to the native lip sync implementation.
+            public IntPtr context;
+
+            /// Native handle to the lip sync callback function.
+            public IntPtr lipSyncCallback;
+        }
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        internal static extern ovrAvatar2Result ovrAvatar2Tracking_SetLipSyncContext(ovrAvatar2EntityId entityId, in ovrAvatar2LipSyncContext context);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ovrAvatar2Tracking_SetLipSyncContext")]
+        internal static extern ovrAvatar2Result ovrAvatar2Tracking_SetLipSyncContextNative(ovrAvatar2EntityId entityId, in ovrAvatar2LipSyncContextNative context);
+
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Tracking_GetVisemes(ovrAvatar2EntityId entityId, Int32 numVisemeValues, IntPtr visemeValues);
+
+
+        //-----------------------------------------------------------------
+        //
+        // Pose
+        //
+        //
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Tracking_GetPose(ovrAvatar2EntityId entityId, out ovrAvatar2Pose outPose);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Tracking_GetPoseValid(ovrAvatar2EntityId entityId, [MarshalAs(UnmanagedType.U1)] out bool isValid);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
+        internal static extern unsafe ovrAvatar2Result ovrAvatar2Tracking_GetNameAtIndex(
+        ovrAvatar2EntityId entityId, int index, byte* nameBuffer, UInt32 bufferSize);
+        #endregion LipSync
+
+
+        #region Face
+        //-----------------------------------------------------------------
+        //
+        // Face
+        //
+        //
+
+        public enum ovrAvatar2FaceExpression : Int32
+        {
+            BrowLowererL = 0,
+            BrowLowererR = 1,
+
+            CheekPuffL = 2,
+            CheekPuffR = 3,
+            CheekRaiserL = 4,
+            CheekRaiserR = 5,
+            CheekSuckL = 6,
+            CheekSuckR = 7,
+
+            ChinRaiserL = 8,
+            ChinRaiserR = 9,
+
+            DimplerL = 10,
+            DimplerR = 11,
+
+            EyesClosedL = 12,
+            EyesClosedR = 13,
+            EyesLookDownL = 14,
+            EyesLookDownR = 15,
+            EyesLookLeftL = 16,
+            EyesLookLeftR = 17,
+            EyesLookRightL = 18,
+            EyesLookRightR = 19,
+            EyesLookUpL = 20,
+            EyesLookUpR = 21,
+
+            InnerBrowRaiserL = 22,
+            InnerBrowRaiserR = 23,
+
+            JawDrop = 24,
+            JawSidewaysLeft = 25,
+            JawSidewaysRight = 26,
+            JawThrust = 27,
+
+            LidTightenerL = 28,
+            LidTightenerR = 29,
+
+            LipCornerDepressorL = 30,
+            LipCornerDepressorR = 31,
+            LipCornerPullerL = 32,
+            LipCornerPullerR = 33,
+            LipFunnelerLB = 34,
+            LipFunnelerLT = 35,
+            LipFunnelerRB = 36,
+            LipFunnelerRT = 37,
+            LipPressorL = 38,
+            LipPressorR = 39,
+            LipPuckerL = 40,
+            LipPuckerR = 41,
+            LipStretcherL = 42,
+            LipStretcherR = 43,
+            LipSuckLB = 44,
+            LipSuckLT = 45,
+            LipSuckRB = 46,
+            LipSuckRT = 47,
+            LipTightenerL = 48,
+            LipTightenerR = 49,
+            LipsTowardLB = 50,
+            LipsTowardLT = 51,
+            LipsTowardRB = 52,
+            LipsTowardRT = 53,
+            LowerLipDepressorL = 54,
+            LowerLipDepressorR = 55,
+
+            MouthLeft = 56,
+            MouthRight = 57,
+
+            NasiolabialFurrowL = 58,
+            NasiolabialFurrowR = 59,
+
+            NoseWrinklerL = 60,
+            NoseWrinklerR = 61,
+            NostrilCompressorL = 62,
+            NostrilCompressorR = 63,
+            NostrilDilatorL = 64,
+            NostrilDilatorR = 65,
+
+            OuterBrowRaiserL = 66,
+            OuterBrowRaiserR = 67,
+
+            UpperLidRaiserL = 68,
+            UpperLidRaiserR = 69,
+            UpperLipRaiserL = 70,
+            UpperLipRaiserR = 71,
+
+            Count = 72
+        }
+
+        // TODO: Investigate why fixed float array marshalling is only failing on Mac M1. Implementation is swithed over to dynamic float[] to work around the issue, but using fixed array has
+        // the benefit of fixing array size to ensure tight coupling btn managed and unmanaged code. Fixed array in a struct is also pre-initialized without the need for explicit array initialization.
+        [StructLayout(LayoutKind.Sequential)]
+        internal struct ovrAvatar2FacePose
+        {
+            // expressionWeights and expressionConfidence are expected to have size equal to ovrAvatar2FaceExpression.Count
+            [MarshalAs(UnmanagedType.ByValArray, SizeConst = (int)ovrAvatar2FaceExpression.Count)]
+            public float[] expressionWeights;
+            [MarshalAs(UnmanagedType.ByValArray, SizeConst = (int)ovrAvatar2FaceExpression.Count)]
+            public float[] expressionConfidence;
+        }
+
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        [return: MarshalAs(UnmanagedType.U1)]
+        internal delegate bool FacePoseCallback(out ovrAvatar2FacePose facePose, IntPtr userContext);
+
+        [StructLayout(LayoutKind.Sequential)]
+        internal struct ovrAvatar2FacePoseProvider
+        {
+            public IntPtr provider;
+            public FacePoseCallback facePoseCallback;
+        }
+
+        [StructLayout(LayoutKind.Sequential)]
+        internal struct ovrAvatar2FacePoseProviderNative
+        {
+            public IntPtr provider;
+            public IntPtr facePoseCallback;
+        }
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        internal static extern ovrAvatar2Result ovrAvatar2Input_SetFacePoseProvider(ovrAvatar2EntityId entityId
+            , in ovrAvatar2FacePoseProvider provider);
+
+        [DllImport(
+            LibFile, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ovrAvatar2Input_SetFacePoseProvider")]
+        internal static extern ovrAvatar2Result ovrAvatar2Input_SetFacePoseProviderNative(ovrAvatar2EntityId entityId
+            , in ovrAvatar2FacePoseProviderNative provider);
+        #endregion Face
+
+        #region Eye
+        [StructLayout(LayoutKind.Sequential)]
+        public struct ovrAvatar2EyePose
+        {
+            public ovrAvatar2Quatf orientation;
+            public ovrAvatar2Vector3f position;
+            [MarshalAs(UnmanagedType.U1)]
+            public bool isValid;
+        }
+
+        [StructLayout(LayoutKind.Sequential)]
+        internal struct ovrAvatar2EyesPose
+        {
+            public ovrAvatar2EyePose leftEye;
+            public ovrAvatar2EyePose rightEye;
+        }
+
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        [return: MarshalAs(UnmanagedType.U1)]
+        internal delegate bool EyePoseCallback(out ovrAvatar2EyesPose eyePose, IntPtr userContext);
+
+        [StructLayout(LayoutKind.Sequential)]
+        internal struct ovrAvatar2EyePoseProvider
+        {
+            public IntPtr provider;
+            public EyePoseCallback eyePoseCallback;
+        }
+
+        [StructLayout(LayoutKind.Sequential)]
+        internal struct ovrAvatar2EyePoseProviderNative
+        {
+            public IntPtr provider;
+            public IntPtr eyePoseCallback;
+        }
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        internal static extern ovrAvatar2Result ovrAvatar2Input_SetEyePoseProvider(ovrAvatar2EntityId entityId
+            , in ovrAvatar2EyePoseProvider context);
+
+        [DllImport(
+            LibFile, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ovrAvatar2Input_SetEyePoseProvider")]
+        internal static extern ovrAvatar2Result ovrAvatar2Input_SetEyePoseProviderNative(ovrAvatar2EntityId entityId
+            , in ovrAvatar2EyePoseProviderNative context);
+        #endregion Eye
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Tracking.cs.meta b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Tracking.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..d8a5906b1ca3d92020fa25463acc146ef415c08d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Tracking.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 06b1d17177696b74dbd5232fe31a52f1
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Types.cs b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Types.cs
new file mode 100644
index 0000000000000000000000000000000000000000..776de2a8056e6f44c46fee17d9c4414db8f484ce
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Types.cs
@@ -0,0 +1,755 @@
+using System;
+using System.Runtime.InteropServices;
+
+using UnityEngine;
+
+/**
+ * @file OvrAvatarAPI_Types.cs
+ */
+namespace Oculus.Avatar2
+{
+    /**
+     * @class CAPI
+     * Encapsulates C# entry points for Avatar SDK native implementation.
+     */
+    public partial class CAPI
+    {
+        //-----------------------------------------------------------------
+        //
+        // Opaque ID types
+        //
+        //
+
+        public enum ovrAvatar2EntityId : Int32
+        {
+            Invalid = 0
+        }
+
+        public enum ovrAvatar2RequestId : Int32
+        {
+            Invalid = 0
+        }
+
+        // TODO: This is Int32 in native
+        public enum ovrAvatar2Id : Int32
+        {
+            Invalid = 0
+        }
+
+        public enum ovrAvatar2VertexBufferId : Int32
+        {
+            Invalid = 0,
+        }
+
+        public enum ovrAvatar2MorphTargetBufferId : Int32
+        {
+            Invalid = 0,
+        }
+
+        public enum ovrAvatar2NodeId : Int32
+        {
+            Invalid = 0,
+        }
+
+        public enum ovrAvatar2LoadRequestId : Int32
+        {
+            Invalid = 0,
+        }
+
+        //-----------------------------------------------------------------
+        //
+        // Opaque version types
+        //
+        //
+
+        public enum ovrAvatar2HierarchyVersion : Int32
+        {
+            Invalid = 0,
+        }
+
+        public enum ovrAvatar2EntityRenderStateVersion : Int32
+        {
+            Invalid = 0,
+        }
+
+
+        //-----------------------------------------------------------------
+        //
+        // Flags
+        //
+        //
+
+        /**
+         * Configures avatar level of detail.
+         * One or more flags may be set.
+         *
+         * @see ovrAvatar2EntityCreateInfo
+         */
+        [Flags]
+        [System.Serializable]
+        public enum ovrAvatar2EntityLODFlags : Int32
+        {
+            /// level of detail 0 (highest fidelity)
+            LOD_0 = 1 << 0,
+
+            /// level of detail 1
+            LOD_1 = 1 << 1,
+
+            /// level of detail 2
+            LOD_2 = 1 << 2,
+
+            /// level of detail 3
+            LOD_3 = 1 << 3,
+
+            /// level of detail 4 (lowest level)
+            LOD_4 = 1 << 4,
+
+            /// All levels of detail
+            All = LOD_0 | LOD_1 | LOD_2 | LOD_3 | LOD_4,
+        }
+        public const uint ovrAvatar2EntityLODFlagsCount = 5;
+
+        /**
+         * Configures how the avatar is manifested
+         * (full body, head and hands only).
+         * NOTE: Only Half is currently available
+         *
+         * @see ovrAvatar2EntityCreateInfo
+         */
+        [Flags]
+        [System.Serializable]
+        public enum ovrAvatar2EntityManifestationFlags : Int32
+        {
+            /// No avatar parts manifested
+            None = 0,
+
+            /// All body parts
+            Full = 1 << 0,
+
+            /// Upper body only
+            Half = 1 << 1,
+
+            /// Head and hands only
+            HeadHands = 1 << 2,
+
+            /// Head only
+            Head = 1 << 3,
+
+            /// Hands only
+            Hands = 1 << 4,
+
+            ///  All manifestations requested.
+            All = Full | Half | HeadHands | Head | Hands,
+        }
+
+        /**
+         * Configures how the avatar is viewed
+         * (first person, third person).
+         *
+         * @see ovrAvatar2EntityCreateInfo
+         */
+        [Flags]
+        [System.Serializable]
+        public enum ovrAvatar2EntityViewFlags : Int32
+        {
+            None = 0,
+
+            /// First person view
+            FirstPerson = 1 << 0,
+
+            /// Third person view
+            ThirdPerson = 1 << 1,
+
+            /// All views
+            All = FirstPerson | ThirdPerson
+        }
+
+        /**
+         * Configures what sub-meshes of the avatar
+         * will show.
+         *
+         * @see ovrAvatar2EntityMaterialTypes_
+         */
+        [Flags]
+        [System.Serializable]
+        public enum ovrAvatar2EntitySubMeshInclusionFlags : Int32
+        {
+            None = 0,
+
+            /// Outfit only
+            Outfit = 1 << 0,
+
+            /// Body only
+            Body = 1 << 1,
+
+            /// Head only
+            Head = 1 << 2,
+
+            /// Hair only
+            Hair = 1 << 3,
+
+            /// Eyebrow only
+            Eyebrow = 1 << 4,
+
+            /// L Eye only
+            L_Eye = 1 << 5,
+
+            /// R Eye only
+            R_Eye = 1 << 6,
+
+            /// Lashes only
+            Lashes = 1 << 7,
+
+            /// Facial hair only
+            FacialHair = 1 << 8,
+
+            /// Headwear only
+            Headwear = 1 << 9,
+
+            /// Earrings only
+            Earrings = 1 << 10,
+
+            ///  All manifestations requested.
+            All = Outfit | Body | Head | Hair | Eyebrow | L_Eye | R_Eye | Lashes | FacialHair | Headwear | Earrings,
+
+            ///  Works both as a test and also might be useful in some real applications.
+            BothEyes = L_Eye | R_Eye,
+        }
+
+
+        /**
+         * Configures how the avatar is loaded and displayed
+         *
+         * @see ovrAvatar2EntityCreateInfo
+         */
+        [Flags]
+        [System.Serializable]
+        public enum ovrAvatar2EntityHighQualityFlags : Int32
+        {
+            None = 0,
+
+            /// Normal maps
+            NormalMaps = 1 << 0,
+
+            /// Property map will be encoded as a hair map
+            PropertyHairMap = 1 << 1,
+
+            /// All views
+            All = NormalMaps | PropertyHairMap
+        }
+
+        //-----------------------------------------------------------------
+        //
+        // Math
+        //
+        //
+
+        /// 2D Vector Type
+        [StructLayout(LayoutKind.Sequential)]
+        public struct ovrAvatar2Vector2f
+        {
+            public float x;
+            public float y;
+
+            public static implicit operator ovrAvatar2Vector2f(Vector2 v)
+            {
+                ovrAvatar2Vector2f result = new ovrAvatar2Vector2f();
+                result.x = v.x;
+                result.y = v.y;
+                return result;
+            }
+
+            public static implicit operator Vector2(ovrAvatar2Vector2f v)
+            {
+                return new Vector3(v.x, v.y);
+            }
+        };
+
+        [StructLayout(LayoutKind.Sequential)]
+        public readonly struct ovrAvatar2Vector2u
+        {
+            public readonly UInt32 x;
+            public readonly UInt32 y;
+
+            public ovrAvatar2Vector2u(UInt32 X, UInt32 Y) { x = X; y = Y; }
+        };
+
+        /// 3D Vector Type
+        [StructLayout(LayoutKind.Sequential)]
+        public struct ovrAvatar2Vector3f
+        {
+            public float x;
+            public float y;
+            public float z;
+
+            public float Length => Mathf.Sqrt(LengthSquared);
+            public float LengthSquared => (x * x + y * y + z * z);
+
+            public ovrAvatar2Vector3f(float x_, float y_, float z_)
+            {
+                x = x_;
+                y = y_;
+                z = z_;
+            }
+
+            public static implicit operator ovrAvatar2Vector3f(in Vector3 v)
+            {
+                return new ovrAvatar2Vector3f(v.x, v.y, v.z);
+            }
+
+            public static implicit operator Vector3(in ovrAvatar2Vector3f v)
+            {
+                return new Vector3(v.x, v.y, v.z);
+            }
+
+            public static ovrAvatar2Vector3f operator +(in ovrAvatar2Vector3f lhs, in ovrAvatar2Vector3f rhs)
+            {
+                return new ovrAvatar2Vector3f
+                (
+                    lhs.x + rhs.x,
+                    lhs.y + rhs.y,
+                    lhs.z + rhs.z
+                );
+            }
+            public static ovrAvatar2Vector3f operator -(in ovrAvatar2Vector3f lhs, in ovrAvatar2Vector3f rhs)
+            {
+                return new ovrAvatar2Vector3f
+                (
+                    lhs.x - rhs.x,
+                    lhs.y - rhs.y,
+                    lhs.z - rhs.z
+                );
+            }
+
+            public static ovrAvatar2Vector3f operator *(in ovrAvatar2Vector3f vec, float scale)
+            {
+                return new ovrAvatar2Vector3f
+                (
+                    vec.x * scale,
+                    vec.y * scale,
+                    vec.z * scale
+                );
+            }
+            public static ovrAvatar2Vector3f operator /(in ovrAvatar2Vector3f numer, float denom)
+            {
+                return numer * (1.0f / denom);
+            }
+        };
+
+        /// 4D Vector Type
+        [StructLayout(LayoutKind.Sequential)]
+        public struct ovrAvatar2Vector4f
+        {
+            public float x;
+            public float y;
+            public float z;
+            public float w;
+
+            public ovrAvatar2Vector4f(float x_, float y_, float z_, float w_)
+            {
+                x = x_;
+                y = y_;
+                z = z_;
+                w = w_;
+            }
+
+            public static implicit operator ovrAvatar2Vector4f(in Vector4 v)
+            {
+                return new ovrAvatar2Vector4f(v.x, v.y, v.z, v.w);
+            }
+
+            public static implicit operator Vector4(in ovrAvatar2Vector4f v)
+            {
+                return new Vector4(v.x, v.y, v.z, v.w);
+            }
+
+            public static implicit operator Color(in ovrAvatar2Vector4f v)
+            {
+                return new Color(v.x, v.y, v.z, v.w);
+            }
+        };
+
+        /// 4D Vector Type
+        [StructLayout(LayoutKind.Sequential)]
+        public struct ovrAvatar2Vector4ub
+        {
+            public Byte x;
+            public Byte y;
+            public Byte z;
+            public Byte w;
+        };
+
+        /// 4D Vector Type
+        [StructLayout(LayoutKind.Sequential)]
+        public struct ovrAvatar2Vector4us
+        {
+            public UInt16 x;
+            public UInt16 y;
+            public UInt16 z;
+            public UInt16 w;
+        };
+
+        /// Quaternion Type
+        [StructLayout(LayoutKind.Sequential)]
+        public struct ovrAvatar2Quatf
+        {
+            public float x;
+            public float y;
+            public float z;
+            public float w;
+
+            public float Length => Mathf.Sqrt(LengthSquared);
+            public float LengthSquared => ((x * x) + (y * y) + (z * z) + (w * w));
+
+            public ovrAvatar2Quatf(float x_, float y_, float z_, float w_)
+            {
+                x = x_;
+                y = y_;
+                z = z_;
+                w = w_;
+            }
+
+            public static implicit operator ovrAvatar2Quatf(in Quaternion q)
+            {
+                return new ovrAvatar2Quatf(q.x, q.y, q.z, q.w);
+            }
+
+            public static implicit operator Quaternion(in ovrAvatar2Quatf q)
+            {
+                return new Quaternion(q.x, q.y, q.z, q.w);
+            }
+        };
+
+
+        /// Transform Type
+        [StructLayout(LayoutKind.Sequential)]
+        public struct ovrAvatar2Transform
+        {
+            public ovrAvatar2Vector3f position;
+            public ovrAvatar2Quatf orientation;
+            public ovrAvatar2Vector3f scale;
+
+            public ovrAvatar2Transform(in ovrAvatar2Vector3f position_, in ovrAvatar2Quatf orientation_)
+            {
+                position = position_;
+                orientation = orientation_;
+                scale.x = scale.y = scale.z = 1.0f;
+            }
+
+            public ovrAvatar2Transform(in ovrAvatar2Vector3f position_
+                , in ovrAvatar2Quatf orientation_, in ovrAvatar2Vector3f scale_)
+            {
+                position = position_;
+                orientation = orientation_;
+                scale = scale_;
+            }
+
+            public ovrAvatar2Transform(in Vector3 position_
+                , in Quaternion orientation_, in Vector3 scale_)
+            {
+                position = position_;
+                orientation = orientation_;
+                scale = scale_;
+            }
+
+            public static explicit operator ovrAvatar2Transform(Transform t)
+            {
+                return new ovrAvatar2Transform(t.localPosition, t.localRotation, t.localScale);
+            }
+        };
+
+        // Matrix Type
+        [StructLayout(LayoutKind.Sequential)]
+        public struct ovrAvatar2Matrix4f
+        {
+            internal float m00, m10, m20, m30;
+            internal float m01, m11, m21, m31;
+            internal float m02, m12, m22, m32;
+            internal float m03, m13, m23, m33;
+
+            public float this[int index]
+            {
+                get
+                {
+                    switch (index)
+                    {
+                        case 0:
+                            return this.m00;
+                        case 1:
+                            return this.m10;
+                        case 2:
+                            return this.m20;
+                        case 3:
+                            return this.m30;
+                        case 4:
+                            return this.m01;
+                        case 5:
+                            return this.m11;
+                        case 6:
+                            return this.m21;
+                        case 7:
+                            return this.m31;
+                        case 8:
+                            return this.m02;
+                        case 9:
+                            return this.m12;
+                        case 10:
+                            return this.m22;
+                        case 11:
+                            return this.m32;
+                        case 12:
+                            return this.m03;
+                        case 13:
+                            return this.m13;
+                        case 14:
+                            return this.m23;
+                        case 15:
+                            return this.m33;
+                        default:
+                            throw new IndexOutOfRangeException("Invalid matrix index!");
+                    }
+                }
+                set
+                {
+                    switch (index)
+                    {
+                        case 0:
+                            this.m00 = value;
+                            break;
+                        case 1:
+                            this.m10 = value;
+                            break;
+                        case 2:
+                            this.m20 = value;
+                            break;
+                        case 3:
+                            this.m30 = value;
+                            break;
+                        case 4:
+                            this.m01 = value;
+                            break;
+                        case 5:
+                            this.m11 = value;
+                            break;
+                        case 6:
+                            this.m21 = value;
+                            break;
+                        case 7:
+                            this.m31 = value;
+                            break;
+                        case 8:
+                            this.m02 = value;
+                            break;
+                        case 9:
+                            this.m12 = value;
+                            break;
+                        case 10:
+                            this.m22 = value;
+                            break;
+                        case 11:
+                            this.m32 = value;
+                            break;
+                        case 12:
+                            this.m03 = value;
+                            break;
+                        case 13:
+                            this.m13 = value;
+                            break;
+                        case 14:
+                            this.m23 = value;
+                            break;
+                        case 15:
+                            this.m33 = value;
+                            break;
+                        default:
+                            throw new IndexOutOfRangeException("Invalid matrix index!");
+                    }
+                }
+            }
+
+            public static explicit operator ovrAvatar2Matrix4f(in Matrix4x4 m) =>  m.ToAvatarMatrix();
+            public static explicit operator Matrix4x4(in ovrAvatar2Matrix4f m) => m.ToUnityMatrix();
+        }
+
+        [StructLayout(LayoutKind.Sequential)]
+        public readonly unsafe struct ovrAvatar2Pose
+        {
+            public readonly UInt32 jointCount;
+            public readonly ovrAvatar2Transform* localTransforms; // Array of ovrAvatar2Transforms
+            public readonly ovrAvatar2Transform* objectTransforms; // Array of ovrAvatar2Transforms relative to root
+            private readonly Int32* parents; // Array of Int32
+            private readonly ovrAvatar2NodeId* nodeIds; ///< Array of node ids
+
+            public Int32 GetParentIndex(Int32 childIndex)
+            {
+                if (childIndex < 0)
+                {
+                    throw new ArgumentOutOfRangeException(
+                        $"Index {childIndex} is out of range of pose parent array of size {jointCount}");
+                }
+                return GetParentIndex((UInt32)childIndex);
+            }
+
+            public Int32 GetParentIndex(UInt32 childIndex)
+            {
+                if (childIndex >= jointCount)
+                {
+                    throw new ArgumentOutOfRangeException(
+                        $"Index {childIndex} is over range of pose parent array of size {jointCount}");
+                }
+                if (parents == null)
+                {
+                    throw new NullReferenceException("parents array is null");
+                }
+                unsafe { return parents[childIndex]; }
+            }
+
+            public ovrAvatar2NodeId GetNodeIdAtIndex(Int32 index)
+            {
+                if (index < 0)
+                {
+                    throw new ArgumentOutOfRangeException(
+                        $"Index {index} is out of range of pose nodeIds array of size {jointCount}");
+                }
+                return GetNodeIdAtIndex((UInt32)index);
+            }
+            public ovrAvatar2NodeId GetNodeIdAtIndex(UInt32 index)
+            {
+                if (index >= jointCount)
+                {
+                    throw new ArgumentOutOfRangeException(
+                        $"Index {index} is over range of pose nodeIds array of size {jointCount}");
+                }
+                if (nodeIds == null)
+                {
+                    throw new NullReferenceException("nodeIds array is null");
+                }
+                Debug.Assert(index < jointCount);
+                unsafe { return nodeIds[index]; }
+            }
+        };
+        public enum ovrAvatar2Side : Int32
+        {
+            Left = 0,
+            Right = 1,
+            Count = 2
+        }
+
+        //-----------------------------------------------------------------
+        //
+        // Results
+        //
+        //
+
+        public enum ovrAvatar2Result : Int32
+        {
+            Success = 0,
+            Unknown = 1,
+            OutOfMemory = 2,
+            NotInitialized = 3,
+            AlreadyInitialized = 4,
+            BadParameter = 5,
+            Unsupported = 6,
+            NotFound = 7,
+            AlreadyExists = 8,
+            IndexOutOfRange = 9,
+            InvalidEntity = 10,
+            InvalidThread = 11,
+            BufferTooSmall = 12,
+            DataNotAvailable = 13,
+            InvalidData = 14,
+            SkeletonMismatch = 15,
+            LibraryLoadFailed = 16,
+            Pending = 17,
+            MissingAccessToken = 18,
+            MemoryLeak = 19,
+            RequestCallbackNotSet = 20,
+            UnmatchedLoadFilters = 21,
+            DeserializationPending = 22,
+            LegacyJointTypeFallback = 23,
+            UnableToConnectToDevTools = 24,
+            RequestCancelled = 25,
+            BufferLargerThanExpected = 26,
+
+            Count,
+        }
+
+        //-----------------------------------------------------------------
+        //
+        // Joints
+        //
+
+        public enum ovrAvatar2JointType : Int32
+        {
+            Invalid = -1,
+
+            Root = 0,
+            Hips = 1,
+            LeftLegUpper = 2,
+            LeftLegLower = 3,
+            LeftFootAnkle = 4,
+            LeftFootBall = 5,
+            RightLegUpper = 6,
+            RightLegLower = 7,
+            RightFootAnkle = 8,
+            RightFootBall = 9,
+            SpineLower = 10,
+            SpineMiddle = 11,
+            SpineUpper = 12,
+            Chest = 13,
+            Neck = 14,
+            Head = 15,
+            LeftShoulder = 16,
+            LeftArmUpper = 17,
+            LeftArmLower = 18,
+            LeftHandWrist = 19,
+            RightShoulder = 20,
+            RightArmUpper = 21,
+            RightArmLower = 22,
+            RightHandWrist = 23,
+            LeftHandThumbTrapezium = 24,
+            LeftHandThumbMeta = 25,
+            LeftHandThumbProximal = 26,
+            LeftHandThumbDistal = 27,
+            LeftHandIndexMeta = 28,
+            LeftHandIndexProximal = 29,
+            LeftHandIndexIntermediate = 30,
+            LeftHandIndexDistal = 31,
+            LeftHandMiddleMeta = 32,
+            LeftHandMiddleProximal = 33,
+            LeftHandMiddleIntermediate = 34,
+            LeftHandMiddleDistal = 35,
+            LeftHandRingMeta = 36,
+            LeftHandRingProximal = 37,
+            LeftHandRingIntermediate = 38,
+            LeftHandRingDistal = 39,
+            LeftHandPinkyMeta = 40,
+            LeftHandPinkyProximal = 41,
+            LeftHandPinkyIntermediate = 42,
+            LeftHandPinkyDistal = 43,
+            RightHandThumbTrapezium = 44,
+            RightHandThumbMeta = 45,
+            RightHandThumbProximal = 46,
+            RightHandThumbDistal = 47,
+            RightHandIndexMeta = 48,
+            RightHandIndexProximal = 49,
+            RightHandIndexIntermediate = 50,
+            RightHandIndexDistal = 51,
+            RightHandMiddleMeta = 52,
+            RightHandMiddleProximal = 53,
+            RightHandMiddleIntermediate = 54,
+            RightHandMiddleDistal = 55,
+            RightHandRingMeta = 56,
+            RightHandRingProximal = 57,
+            RightHandRingIntermediate = 58,
+            RightHandRingDistal = 59,
+            RightHandPinkyMeta = 60,
+            RightHandPinkyProximal = 61,
+            RightHandPinkyIntermediate = 62,
+            RightHandPinkyDistal = 63,
+
+            Count
+        }
+
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Types.cs.meta b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Types.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..58df2f3b1a36e9b3e3882e677b7f3d56a410908e
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrAvatarAPI_Types.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: aa7c5b587f6cc464bb8728380c967eae
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/CAPI/OvrGpuSkinningAPI_Main.cs b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrGpuSkinningAPI_Main.cs
new file mode 100644
index 0000000000000000000000000000000000000000..24da7ef852c6aae026729a15c49182d87fc7ca0f
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrGpuSkinningAPI_Main.cs
@@ -0,0 +1,1143 @@
+//#define OVR_GPUSKINNING_USE_NATIVE_SKINNER
+
+/* CAPI wrapper for gpuskinning.[dll/so]
+ * version: 0.0.1
+ * */
+
+using Oculus.Skinning;
+
+using System;
+using System.Runtime.InteropServices;
+
+namespace Oculus.Avatar2
+{
+    public partial class CAPI
+    {
+        // enum only used for type-safety
+        public enum ovrGpuSkinningHandle : Int32 { AtlasPackerId_Invalid = -1 };
+
+        public enum ovrGpuSkinningEncodingPrecision : Int32 { ENCODING_PRECISION_FLOAT, ENCODING_PRECISION_HALF, ENCODING_PRECISION_10_10_10_2, ENCODING_PRECISION_UINT16, ENCODING_PRECISION_UINT8 };
+
+        // Result codes for GpuSkinning CAPI methods
+        [System.Flags]
+        public enum ovrGpuSkinningResult : Int32
+        {
+            Success = 0,
+            Failure = 1 << 0,
+
+            InvalidId = 1 << 1,
+            InvalidHandle = 1 << 2,
+            InvalidParameter = 1 << 3,
+
+            NullParameter = 1 << 4,
+            InsufficientBuffer = 1 << 5,
+
+            SystemUninitialized = 1 << 8,
+            SubSystemUninitialized = 1 << 9,
+
+            Unknown = 1 << 16,
+        };
+
+#if UNITY_EDITOR || !UNITY_IOS
+#if UNITY_EDITOR_OSX
+        private const string GpuSkinningLibFile = OvrAvatarPlugin.FullPluginFolderPath + "libovrgpuskinning.framework/libovrgpuskinning";
+#else
+        private const string GpuSkinningLibFile = OvrAvatarManager.IsAndroidStandalone ? "ovrgpuskinning" : "libovrgpuskinning";
+#endif  // UNITY_EDITOR_OSX
+#else   // !UNITY_EDITOR && UNITY_IOS
+        private const string GpuSkinningLibFile = "__Internal";
+#endif  // !UNITY_EDITOR && UNITY_IOS
+
+        [StructLayout(LayoutKind.Sequential)]
+        public struct ovrGpuSkinningTextureDesc
+        {
+            public UInt32 width, height, dataSize;
+        }
+
+        [StructLayout(LayoutKind.Sequential)]
+        public struct ovrGpuSkinningBufferDesc
+        {
+            public UInt32 dataSize, numVerts;
+            public ovrGpuSkinningEncodingPrecision precision;
+        }
+
+        //-----------------------------------------------------------------
+        //
+        // GpuSkinningDLL Management
+        //
+        public static bool OvrGpuSkinning_Initialize()
+        {
+            return ovrGpuSkinning_Initialize()
+                .EnsureSuccess("ovrGpuSkinning_Initialize");
+        }
+        public static bool OvrGpuSkinning_Shutdown()
+        {
+            return ovrGpuSkinning_Shutdown()
+                .EnsureSuccess("ovrGpuSkinning_Shutdown");
+        }
+
+        public static AtlasPackerId OvrGpuSkinning_AtlasPackerCreate(
+            UInt32 atlasWidth,
+            UInt32 atlasHeight,
+            AtlasPackerPackingAlgortihm packingAlgorithm
+        )
+        {
+            if (ovrGpuSkinning_AtlasPackerCreate(
+                atlasWidth, atlasHeight, packingAlgorithm, out var newId)
+                .EnsureSuccess("ovrGpuSkinning_AtlasPackerCreate"))
+            {
+                return newId;
+            }
+            return AtlasPackerId.Invalid;
+        }
+
+        public static ovrGpuSkinningHandle OvrGpuSkinning_AtlasPackerAddBlock(
+            AtlasPackerId id,
+            UInt32 width,
+            UInt32 height)
+        {
+            if (ovrGpuSkinning_AtlasPackerAddBlock(id, width, height, out var newHandle)
+                .EnsureSuccess("ovrGpuSkinning_AtlasPackerAddBlock"))
+            {
+                return newHandle;
+            }
+            return ovrGpuSkinningHandle.AtlasPackerId_Invalid;
+        }
+
+        //-----------------------------------------------------------------
+        //
+        // GpuMorphTargetTextureInfo
+        //
+
+        [StructLayout(LayoutKind.Sequential)]
+        public readonly struct ovrGpuMorphTargetTextureDesc
+        {
+            public readonly UInt32 textureDataSize;
+            public readonly UInt32 numVerts;
+            public readonly UInt32 numAffectedVerts;
+            public readonly UInt32 firstAffectedVert;
+            public readonly UInt32 lastAffectedVert;
+
+            public readonly UInt32 texWidth;
+            public readonly UInt32 texHeight;
+            public readonly UInt32 numRowsPerMorphTarget;
+            public readonly UInt32 numMorphTargets;
+
+            public readonly ovrAvatar2Vector3f positionRange;
+            public readonly ovrAvatar2Vector3f normalRange;
+            public readonly ovrAvatar2Vector3f tangentRange;
+        }
+
+        [StructLayout(LayoutKind.Sequential)]
+        public readonly struct ovrGpuMorphTargetBufferDesc
+        {
+            public readonly UInt32 bufferDataSize;
+            public readonly UInt32 numMorphedVerts;
+
+            public readonly ovrAvatar2Vector3f positionScale;
+            public readonly ovrAvatar2Vector3f normalScale;
+            public readonly ovrAvatar2Vector3f tangentScale;
+
+            public readonly UInt32 numMorphedVertsFourJoints;
+            public readonly UInt32 numMorphedVertsThreeJoints;
+            public readonly UInt32 numMorphedVertsTwoJoints;
+            public readonly UInt32 numMorphedVertsOneJoint;
+            public readonly UInt32 numMorphedVertsNoJoints;
+
+            public readonly UInt32 numVertsFourJointsOnly;
+            public readonly UInt32 numVertsThreeJointsOnly;
+            public readonly UInt32 numVertsTwoJointsOnly;
+            public readonly UInt32 numVertsOneJointOnly;
+            public readonly UInt32 numVertsNoJointsOrMorphs;
+
+            public readonly UInt32 numMorphTargets;
+            public readonly ovrGpuSkinningEncodingPrecision encodingPrecision;
+        }
+
+        public static ovrGpuMorphTargetTextureDesc OvrGpuSkinning_MorphTargetEncodeMeshVertToAffectedVert(
+            UInt32 maxTexDimension,
+            UInt32 numMeshVerts,
+            UInt32 numMorphTargets,
+            ovrGpuSkinningEncodingPrecision texType,
+            IntPtr deltaPositionsArray, // ovrAvatar2Vector3f**
+            IntPtr deltaNormalsArray,    // ovrAvatar2Vector3f**
+            IntPtr meshVertToAffectedVert
+        )
+        {
+            unsafe
+            {
+                if (ovrGpuSkinning_MorphTargetEncodeMeshVertToAffectedVert(
+                    maxTexDimension, numMeshVerts, numMorphTargets, texType
+                    , deltaPositionsArray, deltaNormalsArray, (Int32*)meshVertToAffectedVert
+                    , out var newTextureDesc)
+                    .EnsureSuccess("ovrGpuSkinning_GpuMorphTargetTextureInfoCreate"))
+                {
+                    return newTextureDesc;
+                }
+            }
+
+            return new ovrGpuMorphTargetTextureDesc();
+        }
+
+        public static ovrGpuMorphTargetTextureDesc OvrGpuSkinning_MorphTargetEncodeMeshVertToAffectedVertWithTangents(
+            UInt32 maxTexDimension,
+            UInt32 numMeshVerts,
+            UInt32 numMorphTargets,
+            ovrGpuSkinningEncodingPrecision texType,
+            IntPtr deltaPositionsArray, // ovrAvatar2Vector3f**
+            IntPtr deltaNormalsArray,    // ovrAvatar2Vector3f**
+            IntPtr deltaTangentsArray,    // ovrAvatar2Vector3f**
+            IntPtr meshVertToAffectedVert
+        )
+        {
+            unsafe
+            {
+                if (ovrGpuSkinning_MorphTargetEncodeMeshVertToAffectedVertWithTangents(
+                    maxTexDimension, numMeshVerts, numMorphTargets, texType
+                    , deltaPositionsArray, deltaNormalsArray, deltaTangentsArray, (Int32*)meshVertToAffectedVert
+                    , out var newTextureDesc)
+                    .EnsureSuccess("ovrGpuSkinning_GpuMorphTargetTextureInfoCreateWithTangents"))
+                {
+                    return newTextureDesc;
+                }
+            }
+
+            return new ovrGpuMorphTargetTextureDesc();
+        }
+
+        public static bool OvrGpuSkinning_MorphTargetEncodeTextureData(
+            in ovrGpuMorphTargetTextureDesc morphTargetDesc,
+            IntPtr meshVertToAffectedVert,
+            ovrGpuSkinningEncodingPrecision texType,
+            IntPtr deltaPositionsArray, // ovrAvatar2Vector3f**
+            IntPtr deltaNormalsArray,    // ovrAvatar2Vector3f**
+            IntPtr resultBuffer
+        )
+        {
+            unsafe
+            {
+                return ovrGpuSkinning_MorphTargetEncodeTextureData(
+                    morphTargetDesc, (Int32*)meshVertToAffectedVert, texType
+                    , deltaPositionsArray, deltaNormalsArray, (byte*)resultBuffer)
+                    .EnsureSuccess("OvrGpuSkinning_GpuMorphTargetTextureInfoCreateTextureData");
+            }
+        }
+
+        public static bool OvrGpuSkinning_MorphTargetEncodeTextureDataWithTangents(
+            in ovrGpuMorphTargetTextureDesc morphTargetDesc,
+            IntPtr meshVertToAffectedVert,
+            ovrGpuSkinningEncodingPrecision texType,
+            IntPtr deltaPositionsArray, // ovrAvatar2Vector3f**
+            IntPtr deltaNormalsArray,    // ovrAvatar2Vector3f**
+            IntPtr deltaTangentsArray,    // ovrAvatar2Vector3f**
+            IntPtr resultBuffer
+        )
+        {
+            unsafe
+            {
+                return ovrGpuSkinning_MorphTargetEncodeTextureDataWithTangents(
+                    morphTargetDesc, (Int32*)meshVertToAffectedVert, texType
+                    , deltaPositionsArray, deltaNormalsArray, deltaTangentsArray, (byte*)resultBuffer)
+                    .EnsureSuccess("OvrGpuSkinning_GpuMorphTargetTextureInfoCreateTextureData");
+            }
+        }
+
+        public static ovrGpuMorphTargetBufferDesc OvrGpuSkinning_MorphTargetGetTextureBufferMetaData(
+            UInt32 numMeshVerts,
+            UInt32 numMorphTargets,
+            ovrGpuSkinningEncodingPrecision encodingPrecision,
+            IntPtr deltaPositionsArray, // ovrAvatar2Vector3f**
+            IntPtr deltaNormalsArray,    // ovrAvatar2Vector3f**
+            IntPtr jointWeights, // float*
+            IntPtr vertexIndexReordering // UInt16*
+        )
+        {
+            unsafe
+            {
+                if (ovrGpuSkinning_MorphTargetGetBufferMetaData(
+                        numMeshVerts,
+                        numMorphTargets,
+                        encodingPrecision,
+                        deltaPositionsArray,
+                        deltaNormalsArray,
+                        jointWeights,
+                        (UInt16*)vertexIndexReordering,
+                        out var newBufferDesc)
+                    .EnsureSuccess("ovrGpuSkinning_MorphTargetGetBufferMetaData"))
+                {
+                    return newBufferDesc;
+                }
+            }
+
+            return new ovrGpuMorphTargetBufferDesc();
+        }
+
+        public static ovrGpuMorphTargetBufferDesc OvrGpuSkinning_MorphTargetGetTextureBufferMetaDataWithTangents(
+            UInt32 numMeshVerts,
+            UInt32 numMorphTargets,
+            ovrGpuSkinningEncodingPrecision encodingPrecision,
+            IntPtr deltaPositionsArray, // ovrAvatar2Vector3f**
+            IntPtr deltaNormalsArray,   // ovrAvatar2Vector3f**
+            IntPtr deltaTangentsArray,  // ovrAvatar2Vector3f**
+            IntPtr jointWeights, // float*
+            IntPtr vertexIndexReordering // UInt16*
+        )
+        {
+            unsafe
+            {
+                if (ovrGpuSkinning_MorphTargetGetBufferMetaDataWithTangents(
+                        numMeshVerts,
+                        numMorphTargets,
+                        encodingPrecision,
+                        deltaPositionsArray,
+                        deltaNormalsArray,
+                        deltaTangentsArray,
+                        jointWeights,
+                        (UInt16*)vertexIndexReordering,
+                        out var newBufferDesc)
+                    .EnsureSuccess("ovrGpuSkinning_MorphTargetGetBufferMetaDataWithTangents"))
+                {
+                    return newBufferDesc;
+                }
+            }
+
+            return new ovrGpuMorphTargetBufferDesc();
+        }
+
+        public static bool OvrGpuSkinning_MorphTargetEncodeBufferData(
+            in ovrGpuMorphTargetBufferDesc morphTargetDesc,
+            /* const */ IntPtr vertexIndexReordering, // UInt16*
+            IntPtr deltaPositionsArray, // ovrAvatar2Vector3f**
+            IntPtr deltaNormalsArray,   // ovrAvatar2Vector3f**
+            IntPtr resultBuffer
+        )
+        {
+            unsafe
+            {
+                return ovrGpuSkinning_MorphTargetEncodeBufferData(
+                        morphTargetDesc, (UInt16*)vertexIndexReordering, deltaPositionsArray, deltaNormalsArray, (byte*)resultBuffer)
+                    .EnsureSuccess("OvrGpuSkinning_MorphTargetEncodeBufferData");
+            }
+        }
+
+        public static bool OvrGpuSkinning_MorphTargetEncodeBufferDataWithTangents(
+            in ovrGpuMorphTargetBufferDesc morphTargetDesc,
+            /* const */ IntPtr vertexIndexReordering, // UInt16*
+            IntPtr deltaPositionsArray, // ovrAvatar2Vector3f**
+            IntPtr deltaNormalsArray,   // ovrAvatar2Vector3f**
+            IntPtr deltaTangentsArray,  // ovrAvatar2Vector3f**
+            IntPtr resultBuffer
+        )
+        {
+            unsafe
+            {
+                return ovrGpuSkinning_MorphTargetEncodeBufferDataWithTangents(
+                        morphTargetDesc, (UInt16*)vertexIndexReordering, deltaPositionsArray, deltaNormalsArray, deltaTangentsArray, (byte*)resultBuffer)
+                    .EnsureSuccess("OvrGpuSkinning_MorphTargetEncodeBufferDataWithTangents");
+            }
+        }
+
+        //-----------------------------------------------------------------
+        //
+        // GpuSkinningIndirectionTextureInfo
+        //
+
+        public static UInt32 OvrGpuSkinning_IndirectionTextureInfoTexCoordsSizeInBytes()
+        {
+            if (ovrGpuSkinning_IndirectionTextureInfoTexCoordsSizeInBytes(out var coordSizeInBytes)
+                .EnsureSuccess("ovrGpuSkinning_IndirectionTextureInfoTexCoordsSizeInBytes"))
+            {
+                return coordSizeInBytes;
+            }
+            return 0;
+        }
+
+        public static UInt32 OvrGpuSkinning_IndirectionTextureInfoTexelSizeInBytes()
+        {
+            if (ovrGpuSkinning_IndirectionTextureInfoTexelSizeInBytes(out var texelSize)
+                .EnsureSuccess("ovrGpuSkinning_IndirectionTextureInfoTexelSizeInBytes"))
+            {
+                return texelSize;
+            }
+            return 0;
+        }
+
+        public static bool OvrGpuSkinning_IndirectionTextureInfoPopulateTextureCoordinateArrays(
+            in ovrGpuSkinningRecti texelsInCombinedTex,
+            UInt32 combinedTexSlice,
+            UInt32 combinedTexWidth,
+            UInt32 combinedTexHeight,
+            in ovrAvatar2Vector3f unaffectedVertTexCoordInCombinedTex,
+            UInt32 meshVertCount,
+            UInt32 morphTargetAffectedVertCount,
+            /*const*/ IntPtr meshVertIndexToAffectedVertIndex,  // Int32[]
+            IntPtr resultPositionTexCoords, // float results in a raw byte array
+            IntPtr resultNormalTexCoords    // float results in a raw byte array
+        )
+        {
+            unsafe
+            {
+                return ovrGpuSkinning_IndirectionTextureInfoPopulateTextureCoordinateArrays(in texelsInCombinedTex
+                    , combinedTexSlice, combinedTexWidth, combinedTexHeight, in unaffectedVertTexCoordInCombinedTex
+                    , meshVertCount, morphTargetAffectedVertCount, (Int32*)meshVertIndexToAffectedVertIndex
+                    , (float*)resultPositionTexCoords, (float*)resultNormalTexCoords)
+                    .EnsureSuccess("ovrGpuSkinning_IndirectionTextureInfoPopulateTextureCoordinateArrays");
+            }
+        }
+
+        public static bool OvrGpuSkinning_IndirectionTextureInfoPopulateTextureCoordinateArraysWithTangents(
+            in ovrGpuSkinningRecti texelsInCombinedTex,
+            UInt32 combinedTexSlice,
+            UInt32 combinedTexWidth,
+            UInt32 combinedTexHeight,
+            in ovrAvatar2Vector3f unaffectedVertTexCoordInCombinedTex,
+            UInt32 meshVertCount,
+            UInt32 morphTargetAffectedVertCount,
+            /*const*/ IntPtr meshVertIndexToAffectedVertIndex,  // Int32[]
+            IntPtr resultPositionTexCoords, // float results in a raw byte array
+            IntPtr resultNormalTexCoords,    // float results in a raw byte array
+            IntPtr resultTangentTexCoords    // float results in a raw byte array
+        )
+        {
+            unsafe
+            {
+                return ovrGpuSkinning_IndirectionTextureInfoPopulateTextureCoordinateArraysWithTangents(in texelsInCombinedTex
+                    , combinedTexSlice, combinedTexWidth, combinedTexHeight, in unaffectedVertTexCoordInCombinedTex
+                    , meshVertCount, morphTargetAffectedVertCount, (Int32*)meshVertIndexToAffectedVertIndex
+                    , (float*)resultPositionTexCoords, (float*)resultNormalTexCoords, (float*)resultTangentTexCoords)
+                    .EnsureSuccess("ovrGpuSkinning_IndirectionTextureInfoPopulateTextureCoordinateArraysWithTangents");
+            }
+        }
+
+        public static bool OvrGpuSkinning_IndirectionTextureInfoPopulateTextureData(
+            UInt32 texWidth,
+            UInt32 texHeight,
+            UInt32 meshVertCount,
+            /*const*/ IntPtr positionTexCoords,   // float[]
+            /*const*/ IntPtr normalTexCoords,     // float[]
+            IntPtr resultBuffer,   // byte results in a raw byte array
+            UInt32 resultBufferSize
+        )
+        {
+            unsafe
+            {
+                return ovrGpuSkinning_IndirectionTextureInfoPopulateTextureData(texWidth, texHeight, meshVertCount
+                    , (float*)positionTexCoords, (float*)normalTexCoords
+                    , (byte*)resultBuffer, resultBufferSize)
+                    .EnsureSuccess("ovrGpuSkinning_IndirectionTextureInfoPopulateTextureData");
+            }
+        }
+
+        public static bool OvrGpuSkinning_IndirectionTextureInfoPopulateTextureDataWithTangents(
+            UInt32 texWidth,
+            UInt32 texHeight,
+            UInt32 meshVertCount,
+            /*const*/ IntPtr positionTexCoords,   // float[]
+            /*const*/ IntPtr normalTexCoords,     // float[]
+            /*const*/ IntPtr tangentTexCoords,    // float[]
+            IntPtr resultBuffer,   // byte results in a raw byte array
+            UInt32 resultBufferSize
+        )
+        {
+            unsafe
+            {
+                return ovrGpuSkinning_IndirectionTextureInfoPopulateTextureDataWithTangents(texWidth, texHeight, meshVertCount
+                    , (float*)positionTexCoords, (float*)normalTexCoords, (float*)tangentTexCoords
+                    , (byte*)resultBuffer, resultBufferSize)
+                    .EnsureSuccess("ovrGpuSkinning_IndirectionTextureInfoPopulateTextureDataWithTangents");
+            }
+        }
+
+
+        public static ovrGpuSkinningTextureDesc OvrGpuSkinning_JointTextureDesc(
+            UInt32 maxTexDimension,
+            UInt32 numMeshVerts
+        )
+        {
+            if (ovrGpuSkinning_JointTextureDesc(maxTexDimension, numMeshVerts, out var texDesc)
+                .EnsureSuccess("ovrGpuSkinning_JointTextureDesc"))
+            {
+                return texDesc;
+            }
+            return default;
+        }
+
+
+        public static bool OvrGpuSkinning_JointEncodeTextureData(
+            in ovrGpuSkinningTextureDesc desc,
+            UInt32 numMeshVerts,
+            IntPtr jointIndices, // ovrAvatar2Vector4us[]
+            IntPtr jointWeights,  // ovrAvatar2Vector4f[]
+            IntPtr resultBuffer,
+            UInt32 resultBufferSize
+        )
+        {
+            unsafe
+            {
+                return (ovrGpuSkinning_JointEncodeTextureData(in desc, numMeshVerts
+                    , (ovrAvatar2Vector4us*)jointIndices, (ovrAvatar2Vector4f*)jointWeights, (byte*)resultBuffer, resultBufferSize)
+                    .EnsureSuccess("ovrGpuSkinning_JointEncodeTextureData"));
+            }
+        }
+
+        public static ovrGpuSkinningBufferDesc OvrGpuSkinning_JointWeightsBufferDesc(
+            UInt32 numMeshVerts
+        )
+        {
+            if (ovrGpuSkinning_JointWeightsBufferDesc(numMeshVerts, out var bufferDesc)
+                .EnsureSuccess("ovrGpuSkinning_JointWeightsBufferDesc"))
+            {
+                return bufferDesc;
+            }
+
+            return default;
+        }
+
+
+        public static ovrGpuSkinningBufferDesc OvrGpuSkinning_JointIndicesBufferDesc(
+            UInt32 numMeshVerts, ovrGpuSkinningEncodingPrecision encodingPrecision
+        )
+        {
+            if (ovrGpuSkinning_JointIndicesBufferDesc(numMeshVerts, encodingPrecision, out var bufferDesc)
+                .EnsureSuccess("ovrGpuSkinning_JointIndicesBufferDesc"))
+            {
+                return bufferDesc;
+            }
+
+            return default;
+        }
+
+        public static bool OvrGpuSkinning_EncodeJointWeightsBufferData(
+            in ovrGpuSkinningBufferDesc desc,
+            /* const */ IntPtr jointWeights, // ovrAvatar2Vector4f[]
+            /* const */ IntPtr vertexIndexReordering, // UInt16*
+            IntPtr resultBuffer // byte*
+        )
+        {
+            unsafe
+            {
+                return ovrGpuSkinning_EncodeJointWeightsBufferData(
+                        desc,
+                        (ovrAvatar2Vector4f*)jointWeights,
+                        (UInt16*)vertexIndexReordering,
+                        (byte*)resultBuffer)
+                    .EnsureSuccess("ovrGpuSkinning_EncodeJointWeightsBufferData");
+            }
+        }
+
+        public static bool OvrGpuSkinning_EncodeJointIndicesBufferData(
+            in ovrGpuSkinningBufferDesc desc,
+            /* const */ IntPtr jointIndices, // ovrAvatar2Vector4us[]
+            /* const */ IntPtr vertexIndexReordering, // UInt16*
+            IntPtr resultBuffer // byte*
+        )
+        {
+            unsafe
+            {
+                return ovrGpuSkinning_EncodeJointIndicesBufferData(
+                        desc,
+                        (ovrAvatar2Vector4us*)jointIndices,
+                        (UInt16*)vertexIndexReordering,
+                        (byte*)resultBuffer)
+                    .EnsureSuccess("ovrGpuSkinning_EncodeJointIndicesBufferData");
+            }
+        }
+
+        //-----------------------------------------------------------------
+        //
+        // Atlas Packer
+        //
+
+        // enum only used for type-safety
+        public enum AtlasPackerId : Int32 { Invalid = -1 };
+
+        // a copy of OVR::GpuSkinning::AtlasPacker::PackingAlgorithm
+        public enum AtlasPackerPackingAlgortihm : Int32
+        {
+            Runtime,
+            Preprocess,
+        };
+
+        [StructLayout(LayoutKind.Sequential)]
+        public struct ovrTextureLayoutResult
+        {
+            public Int32 x, y;
+            public Int32 w, h;
+            public UInt32 texSlice;
+
+            public static readonly ovrTextureLayoutResult INVALID_LAYOUT = new ovrTextureLayoutResult
+            {
+                x = 0,
+                y = 0,
+                w = 0,
+                h = 0,
+                texSlice = 0,
+            };
+
+            public ovrGpuSkinningRecti ExtractRectiOnly()
+            {
+                return new ovrGpuSkinningRecti(x, y, w, h);
+            }
+
+            public bool IsValid => x >= 0 && y >= 0 && w > 0 && h > 0;
+        }
+
+        //-----------------------------------------------------------------
+        //
+        // GpuSkinning_AtlasPacker
+        //
+
+        [StructLayout(LayoutKind.Sequential)]
+        public struct ovrGpuSkinningRecti
+        {
+            public int x, y;
+            public int w, h;
+
+            public ovrGpuSkinningRecti(int x_, int y_, int w_, int h_) { x = x_; y = y_; w = w_; h = h_; }
+        }
+
+        public static bool OvrGpuSkinning_AtlasPackerDestroy(
+            AtlasPackerId id
+        )
+        {
+            return ovrGpuSkinning_AtlasPackerDestroy(id)
+                .EnsureSuccess("ovrGpuSkinning_AtlasPackerDestroy");
+        }
+
+        public static ovrGpuSkinningHandle ovrGpuSkinning_AtlasPackerResultsForBlock(
+            AtlasPackerId id,
+            UInt32 width,
+            UInt32 height)
+        {
+            if (ovrGpuSkinning_AtlasPackerAddBlock(id, width, height, out var newHandle)
+                .EnsureSuccess("ovrGpuSkinning_AtlasPackerAddBlock"))
+            {
+                return newHandle;
+            }
+            return ovrGpuSkinningHandle.AtlasPackerId_Invalid;
+        }
+
+        public static ovrTextureLayoutResult OvrGpuSkinning_AtlasPackerResultsForBlock(
+            AtlasPackerId id,
+            ovrGpuSkinningHandle handle)
+        {
+            if (ovrGpuSkinning_AtlasPackerResultsForBlock(id, handle, out var result)
+                .EnsureSuccess("ovrGpuSkinning_AtlasPackerResultsForBlock"))
+            {
+                return result;
+            }
+            return default;
+        }
+
+        public static bool OvrGpuSkinning_AtlasPackerRemoveBlock(
+            AtlasPackerId id,
+            ovrGpuSkinningHandle handle)
+        {
+            return ovrGpuSkinning_AtlasPackerRemoveBlock(id, handle)
+                .EnsureSuccess("ovrGpuSkinning_AtlasPackerRemoveBlock");
+        }
+
+        //-----------------------------------------------------------------
+        //
+        // GpuSkinningJointTextureInfo
+        //
+
+        // enum only used for type-safety
+        public enum ovrGpuSkinningJointTextureInfoId : Int32
+        {
+            Invalid = 0
+        };
+
+        //-----------------------------------------------------------------
+        //
+        // GpuSkinningNeutralPoseEncoder
+        //
+
+        public static ovrGpuSkinningTextureDesc OvrGpuSkinning_NeutralPoseTextureDesc(
+            UInt32 maxTexDimension,
+            UInt32 numMeshVerts,
+            bool hasTangents)
+        {
+            if (ovrGpuSkinning_NeutralPoseTextureDesc(
+                maxTexDimension, numMeshVerts, hasTangents, out var newTexDesc)
+                .EnsureSuccess("ovrGpuSkinning_NeutralPoseTextureDesc"))
+            {
+                return newTexDesc;
+            }
+            return default;
+        }
+
+        public static bool OvrGpuSkinning_NeutralPoseEncodeTextureData(
+            in ovrGpuSkinningTextureDesc desc,
+            UInt32 numMeshVerts,
+            IntPtr neutralPositions, // ovrAvatar2Vector3f[]
+            IntPtr neutralNormals, // ovrAvatar2Vector3f[]
+            IntPtr neutralTangents, // ovrAvatar2Vector4f[]
+            IntPtr resultBuffer,
+            UInt32 resultBufferSize)
+        {
+            unsafe
+            {
+                return ovrGpuSkinning_NeutralPoseEncodeTextureData(
+                    in desc, numMeshVerts, (ovrAvatar2Vector3f*)neutralPositions, (ovrAvatar2Vector3f*)neutralNormals
+                    , (ovrAvatar2Vector4f*)neutralTangents, (byte*)resultBuffer, resultBufferSize)
+                    .EnsureSuccess("ovrGpuSkinning_NeutralPoseEncodeTextureData");
+            }
+        }
+
+        public static bool OvrGpuSkinning_NeutralPoseEncodeBufferData(
+            UInt32 numMeshVerts,
+            IntPtr neutralPositions, // ovrAvatar2Vector3f[]
+            IntPtr neutralNormals, // ovrAvatar2Vector3f[]
+            IntPtr neutralTangents, // ovrAvatar2Vector4f[]
+            ovrGpuSkinningEncodingPrecision precision,
+            bool alignVec4,
+            IntPtr resultBuffer,
+            UInt32 resultBufferSize)
+        {
+            unsafe
+            {
+                return ovrGpuSkinning_NeutralPoseEncodeBufferData(numMeshVerts,
+                        (ovrAvatar2Vector3f*)neutralPositions, (ovrAvatar2Vector3f*)neutralNormals,
+                        (ovrAvatar2Vector4f*)neutralTangents, precision, alignVec4, (byte*)resultBuffer,
+                        resultBufferSize)
+                    .EnsureSuccess("ovrGpuSkinning_NeutralPoseEncodeBufferData");
+            }
+        }
+
+        public static ovrGpuSkinningBufferDesc OvrGpuSkinning_NeutralPositionsBufferDesc(
+            UInt32 numMeshVerts,
+            ovrGpuSkinningEncodingPrecision encodingPrecision)
+        {
+            if (ovrGpuSkinning_NeutralPositionsBufferDesc(
+                    numMeshVerts,
+                    encodingPrecision,
+                    out var newDesc)
+                .EnsureSuccess("ovrGpuSkinning_NeutralPositionsBufferDesc"))
+            {
+                return newDesc;
+            }
+
+            return default;
+        }
+
+        public static ovrGpuSkinningBufferDesc OvrGpuSkinning_NeutralNormalsBufferDesc(
+            UInt32 numMeshVerts,
+            ovrGpuSkinningEncodingPrecision encodingPrecision)
+        {
+            if (ovrGpuSkinning_NeutralNormalsBufferDesc(
+                    numMeshVerts,
+                    encodingPrecision,
+                    out var newDesc)
+                .EnsureSuccess("ovrGpuSkinning_NeutralNormalsBufferDesc"))
+            {
+                return newDesc;
+            }
+
+            return default;
+        }
+
+        public static ovrGpuSkinningBufferDesc OvrGpuSkinning_NeutralTangentsBufferDesc(
+            UInt32 numMeshVerts,
+            ovrGpuSkinningEncodingPrecision encodingPrecision)
+        {
+            if (ovrGpuSkinning_NeutralTangentsBufferDesc(
+                    numMeshVerts,
+                    encodingPrecision,
+                    out var newDesc)
+                .EnsureSuccess("ovrGpuSkinning_NeutralTangentsBufferDesc"))
+            {
+                return newDesc;
+            }
+
+            return default;
+        }
+
+        public static bool OvrGpuSkinning_EncodeNeutralPositionsBufferData(
+            in ovrGpuSkinningBufferDesc desc,
+            /* const */ IntPtr neutralPositions, // ovrAvatar2Vector3f*
+            /* const */ IntPtr vertexIndexReordering, // UInt16*
+            IntPtr resultBuffer) // byte*
+        {
+            unsafe
+            {
+                return ovrGpuSkinning_EncodeNeutralPositionsBufferData(
+                        desc,
+                        (ovrAvatar2Vector3f*)neutralPositions,
+                        (UInt16*)vertexIndexReordering,
+                        (byte*)resultBuffer)
+                    .EnsureSuccess("ovrGpuSkinning_EncodeNeutralPositionsBufferData");
+            }
+        }
+
+        public static bool OvrGpuSkinning_EncodeNeutralNormalsBufferData(
+            in ovrGpuSkinningBufferDesc desc,
+            /* const */ IntPtr neutralNormals, // ovrAvatar2Vector3f*
+            /* const */ IntPtr vertexIndexReordering, // UInt16*
+            IntPtr resultBuffer) // byte*
+        {
+            unsafe
+            {
+                return ovrGpuSkinning_EncodeNeutralNormalsBufferData(
+                        desc,
+                        (ovrAvatar2Vector3f*)neutralNormals,
+                        (UInt16*)vertexIndexReordering,
+                        (byte*)resultBuffer)
+                    .EnsureSuccess("ovrGpuSkinning_EncodeNeutralNormalsBufferData");
+            }
+        }
+
+        public static bool OvrGpuSkinning_EncodeNeutralTangentsBufferData(
+            in ovrGpuSkinningBufferDesc desc,
+            /* const */ IntPtr neutralTangents, // ovrAvatar2Vector4f*
+            /* const */ IntPtr vertexIndexReordering, // UInt16*
+            IntPtr resultBuffer) // byte*
+        {
+            unsafe
+            {
+                return ovrGpuSkinning_EncodeNeutralTangentsBufferData(
+                        desc,
+                        (ovrAvatar2Vector4f*)neutralTangents,
+                        (UInt16*)vertexIndexReordering,
+                        (byte*)resultBuffer)
+                    .EnsureSuccess("ovrGpuSkinning_EncodeNeutralTangentsBufferData");
+            }
+        }
+
+        //-----------------------------------------------------------------
+        //
+        // GpuSkinningJointTextureInfo
+        //
+
+        #region extern methods
+
+        [DllImport(GpuSkinningLibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern ovrGpuSkinningResult ovrGpuSkinning_Initialize();
+
+        [DllImport(GpuSkinningLibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern ovrGpuSkinningResult ovrGpuSkinning_Shutdown();
+
+        [DllImport(GpuSkinningLibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern ovrGpuSkinningResult ovrGpuSkinning_AtlasPackerCreate(
+            UInt32 atlasWidth,
+            UInt32 atlasHeight,
+            AtlasPackerPackingAlgortihm packingAlgorithm,
+            out AtlasPackerId createdId
+        );
+
+        [DllImport(GpuSkinningLibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern ovrGpuSkinningResult ovrGpuSkinning_AtlasPackerDestroy(
+            AtlasPackerId id
+        );
+
+        [DllImport(GpuSkinningLibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern ovrGpuSkinningResult ovrGpuSkinning_AtlasPackerAddBlock(
+            AtlasPackerId id,
+            UInt32 width,
+            UInt32 height,
+            out ovrGpuSkinningHandle newHandle
+        );
+
+        [DllImport(GpuSkinningLibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern ovrGpuSkinningResult ovrGpuSkinning_AtlasPackerResultsForBlock(
+            AtlasPackerId id,
+            ovrGpuSkinningHandle handle,
+            out ovrTextureLayoutResult result
+        );
+
+        [DllImport(GpuSkinningLibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern ovrGpuSkinningResult ovrGpuSkinning_AtlasPackerRemoveBlock(
+            AtlasPackerId id,
+            ovrGpuSkinningHandle handle
+        );
+
+        //-----------------------------------------------------------------
+        //
+        // GpuMorphTargetTextureInfo
+        //
+
+        [DllImport(GpuSkinningLibFile, CallingConvention = CallingConvention.Cdecl)]
+        private unsafe static extern ovrGpuSkinningResult ovrGpuSkinning_MorphTargetEncodeMeshVertToAffectedVert(
+            UInt32 maxTexDimension,
+            UInt32 numMeshVerts,
+            UInt32 numMorphTargets,
+            ovrGpuSkinningEncodingPrecision texType,
+            /* const */ IntPtr deltaPositionsArray, // ovrAvatar2Vector3f**
+            /* const */ IntPtr deltaNormalsArray,    // ovrAvatar2Vector3f**
+            Int32* meshVertToAffectedVert,
+            out ovrGpuMorphTargetTextureDesc newMorphTargetInfo
+        );
+
+        [DllImport(GpuSkinningLibFile, CallingConvention = CallingConvention.Cdecl)]
+        private unsafe static extern ovrGpuSkinningResult ovrGpuSkinning_MorphTargetEncodeMeshVertToAffectedVertWithTangents(
+            UInt32 maxTexDimension,
+            UInt32 numMeshVerts,
+            UInt32 numMorphTargets,
+            ovrGpuSkinningEncodingPrecision texType,
+            /* const */ IntPtr deltaPositionsArray, // ovrAvatar2Vector3f**
+            /* const */ IntPtr deltaNormalsArray,    // ovrAvatar2Vector3f**
+            /* const */ IntPtr deltaTangentsArray,    // ovrAvatar2Vector3f**
+            Int32* meshVertToAffectedVert,
+            out ovrGpuMorphTargetTextureDesc newMorphTargetInfo
+        );
+
+        [DllImport(GpuSkinningLibFile, CallingConvention = CallingConvention.Cdecl)]
+        private unsafe static extern ovrGpuSkinningResult ovrGpuSkinning_MorphTargetEncodeTextureData(
+            /* const */ in ovrGpuMorphTargetTextureDesc newMorphTargetInfo,
+            /* const */ Int32* meshVertToAffectedVert,
+            ovrGpuSkinningEncodingPrecision texType,
+            /* const */ IntPtr deltaPositionsArray, // ovrAvatar2Vector3f**
+            /* const */ IntPtr deltaNormalsArray,    // ovrAvatar2Vector3f**
+            byte* result
+        );
+
+        [DllImport(GpuSkinningLibFile, CallingConvention = CallingConvention.Cdecl)]
+        private unsafe static extern ovrGpuSkinningResult ovrGpuSkinning_MorphTargetEncodeTextureDataWithTangents(
+            /* const */ in ovrGpuMorphTargetTextureDesc newMorphTargetInfo,
+            /* const */ Int32* meshVertToAffectedVert,
+            ovrGpuSkinningEncodingPrecision texType,
+            /* const */ IntPtr deltaPositionsArray, // ovrAvatar2Vector3f**
+            /* const */ IntPtr deltaNormalsArray,    // ovrAvatar2Vector3f**
+            /* const */ IntPtr deltaTangentsArray,    // ovrAvatar2Vector3f**
+            byte* result
+        );
+
+        [DllImport(GpuSkinningLibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern unsafe ovrGpuSkinningResult ovrGpuSkinning_MorphTargetGetBufferMetaData(
+            UInt32 numMeshVerts,
+            UInt32 numMorphTargets,
+            ovrGpuSkinningEncodingPrecision encodingPrecision,
+            /* const */ IntPtr deltaPositionsArray,
+            /* const */ IntPtr deltaNormalsArray,
+            /* const */ IntPtr jointWeights,
+            UInt16* vertexIndexReordering,
+            out ovrGpuMorphTargetBufferDesc newMorphTargetInfo);
+
+        [DllImport(GpuSkinningLibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern unsafe ovrGpuSkinningResult ovrGpuSkinning_MorphTargetGetBufferMetaDataWithTangents(
+            UInt32 numMeshVerts,
+            UInt32 numMorphTargets,
+            ovrGpuSkinningEncodingPrecision encodingPrecision,
+            /* const */ IntPtr deltaPositionsArray,
+            /* const */ IntPtr deltaNormalsArray,
+            /* const */ IntPtr deltaTangentsArray,
+            /* const */ IntPtr jointWeights,
+            UInt16* vertexIndexReordering,
+            out ovrGpuMorphTargetBufferDesc newMorphTargetInfo);
+
+        [DllImport(GpuSkinningLibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern unsafe ovrGpuSkinningResult ovrGpuSkinning_MorphTargetEncodeBufferData(
+            /* const */ in ovrGpuMorphTargetBufferDesc morphTargetInfo,
+            /* const */ UInt16* vertexIndexReordering,
+            /* const */ IntPtr deltaPositionsArray,
+            /* const */ IntPtr deltaNormalsArray,
+            byte* result);
+
+        [DllImport(GpuSkinningLibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern unsafe ovrGpuSkinningResult ovrGpuSkinning_MorphTargetEncodeBufferDataWithTangents(
+            /* const */ in ovrGpuMorphTargetBufferDesc morphTargetInfo,
+            /* const */ UInt16* vertexIndexReordering,
+            /* const */ IntPtr deltaPositionsArray,
+            /* const */ IntPtr deltaNormalsArray,
+            /* const */ IntPtr deltaTangentsArray,
+            byte* result);
+
+        //-----------------------------------------------------------------
+        //
+        // GpuSkinningIndirectionTextureInfo
+        //
+
+        [DllImport(GpuSkinningLibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern ovrGpuSkinningResult ovrGpuSkinning_IndirectionTextureInfoTexCoordsSizeInBytes(out UInt32 coordSizeInBytes);
+
+        [DllImport(GpuSkinningLibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern ovrGpuSkinningResult ovrGpuSkinning_IndirectionTextureInfoTexelSizeInBytes(out UInt32 texelSizeInBytes);
+
+        [DllImport(GpuSkinningLibFile, CallingConvention = CallingConvention.Cdecl)]
+        private unsafe static extern ovrGpuSkinningResult ovrGpuSkinning_IndirectionTextureInfoPopulateTextureCoordinateArrays(
+            in ovrGpuSkinningRecti texelsInCombinedTex,
+            UInt32 combinedTexSlice,
+            UInt32 combinedTexWidth,
+            UInt32 combinedTexHeight,
+            in ovrAvatar2Vector3f unaffectedVertTexCoordInCombinedTex,
+            UInt32 meshVertCount,
+            UInt32 morphTargetAffectedVertCount,
+            /*const*/ Int32* meshVertIndexToAffectedVertIndex,  // Int32[]
+            float* resultPositionTexCoords, // float results in a raw byte array
+            float* resultNormalTexCoords    // float results in a raw byte array
+        );
+
+        [DllImport(GpuSkinningLibFile, CallingConvention = CallingConvention.Cdecl)]
+        private unsafe static extern ovrGpuSkinningResult ovrGpuSkinning_IndirectionTextureInfoPopulateTextureCoordinateArraysWithTangents(
+            in ovrGpuSkinningRecti texelsInCombinedTex,
+            UInt32 combinedTexSlice,
+            UInt32 combinedTexWidth,
+            UInt32 combinedTexHeight,
+            in ovrAvatar2Vector3f unaffectedVertTexCoordInCombinedTex,
+            UInt32 meshVertCount,
+            UInt32 morphTargetAffectedVertCount,
+            /*const*/ Int32* meshVertIndexToAffectedVertIndex,  // Int32[]
+            float* resultPositionTexCoords, // float results in a raw byte array
+            float* resultNormalTexCoords,   // float results in a raw byte array
+            float* resultTangetTexCoords    // float results in a raw byte array
+        );
+
+        [DllImport(GpuSkinningLibFile, CallingConvention = CallingConvention.Cdecl)]
+        private unsafe static extern ovrGpuSkinningResult ovrGpuSkinning_IndirectionTextureInfoPopulateTextureData(
+            UInt32 texWidth,
+            UInt32 texHeight,
+            UInt32 meshVertCount,
+            /*const*/ float* positionTexCoords,   // float[]
+            /*const*/ float* normalTexCoords,     // float[]
+            byte* resultBuffer,   // byte results in a raw byte array
+            UInt32 resultBufferSize
+        );
+
+        [DllImport(GpuSkinningLibFile, CallingConvention = CallingConvention.Cdecl)]
+        private unsafe static extern ovrGpuSkinningResult ovrGpuSkinning_IndirectionTextureInfoPopulateTextureDataWithTangents(
+            UInt32 texWidth,
+            UInt32 texHeight,
+            UInt32 meshVertCount,
+            /*const*/ float* positionTexCoords,   // float[]
+            /*const*/ float* normalTexCoords,     // float[]
+            /*const*/ float* tangentTexCoords,    // float[]
+            byte* resultBuffer,   // byte results in a raw byte array
+            UInt32 resultBufferSize
+        );
+
+        //-----------------------------------------------------------------
+        //
+        // GpuSkinningNeutralPoseEncoder
+        //
+
+        [DllImport(GpuSkinningLibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern ovrGpuSkinningResult ovrGpuSkinning_NeutralPoseTextureDesc(
+            UInt32 maxTexDimension,
+            UInt32 numMeshVerts,
+            bool hasTangents,
+            out ovrGpuSkinningTextureDesc newTextureDesc
+        );
+
+        [DllImport(GpuSkinningLibFile, CallingConvention = CallingConvention.Cdecl)]
+        private unsafe static extern ovrGpuSkinningResult ovrGpuSkinning_NeutralPoseEncodeTextureData(
+            in ovrGpuSkinningTextureDesc desc,
+            UInt32 numMeshVerts,
+            /*const*/ ovrAvatar2Vector3f* neutralPositions, // ovrAvatar2Vector3f[]
+            /*const*/ ovrAvatar2Vector3f* neutralNormals, // ovrAvatar2Vector3f[]
+            /*const*/ ovrAvatar2Vector4f* neutralTangents, // ovrAvatar2Vector4f[]
+            byte* resultBuffer,
+            UInt32 resultBufferSize
+        );
+
+        [DllImport(GpuSkinningLibFile, CallingConvention = CallingConvention.Cdecl)]
+        private unsafe static extern ovrGpuSkinningResult ovrGpuSkinning_NeutralPoseEncodeBufferData(
+            UInt32 numMeshVerts,
+            /*const*/ ovrAvatar2Vector3f* neutralPositions, // ovrAvatar2Vector3f[]
+            /*const*/ ovrAvatar2Vector3f* neutralNormals, // ovrAvatar2Vector3f[]
+            /*const*/ ovrAvatar2Vector4f* neutralTangents, // ovrAvatar2Vector4f[]
+            ovrGpuSkinningEncodingPrecision precision,
+            bool alignVec4,
+            byte* resultBuffer,
+            UInt32 resultBufferSize
+        );
+
+        [DllImport(GpuSkinningLibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern ovrGpuSkinningResult ovrGpuSkinning_NeutralPositionsBufferDesc(
+            UInt32 numMeshVerts,
+            ovrGpuSkinningEncodingPrecision encodingPrecision,
+            out ovrGpuSkinningBufferDesc bufferDesc
+        );
+
+        [DllImport(GpuSkinningLibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern ovrGpuSkinningResult ovrGpuSkinning_NeutralNormalsBufferDesc(
+            UInt32 numMeshVerts,
+            ovrGpuSkinningEncodingPrecision encodingPrecision,
+            out ovrGpuSkinningBufferDesc bufferDesc
+        );
+
+        [DllImport(GpuSkinningLibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern ovrGpuSkinningResult ovrGpuSkinning_NeutralTangentsBufferDesc(
+            UInt32 numMeshVerts,
+            ovrGpuSkinningEncodingPrecision encodingPrecision,
+            out ovrGpuSkinningBufferDesc bufferDesc
+        );
+
+        [DllImport(GpuSkinningLibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern unsafe ovrGpuSkinningResult ovrGpuSkinning_EncodeNeutralPositionsBufferData(
+            in ovrGpuSkinningBufferDesc desc,
+            /* const */ ovrAvatar2Vector3f* neutralPositions,
+            /* const */ UInt16* vertexIndexReordering,
+            byte* resultBuffer
+        );
+
+        [DllImport(GpuSkinningLibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern unsafe ovrGpuSkinningResult ovrGpuSkinning_EncodeNeutralNormalsBufferData(
+            in ovrGpuSkinningBufferDesc desc,
+            /* const */ ovrAvatar2Vector3f* neutralNormals,
+            /* const */ UInt16* vertexIndexReordering,
+            byte* resultBuffer
+        );
+
+        [DllImport(GpuSkinningLibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern unsafe ovrGpuSkinningResult ovrGpuSkinning_EncodeNeutralTangentsBufferData(
+            in ovrGpuSkinningBufferDesc desc,
+            /* const */ ovrAvatar2Vector4f* neutralTangents,
+            /* const */ UInt16* vertexIndexReordering,
+            byte* resultBuffer
+        );
+
+        //-----------------------------------------------------------------
+        //
+        // GpuSkinningJointEncoder
+        //
+
+        [DllImport(GpuSkinningLibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern ovrGpuSkinningResult ovrGpuSkinning_JointTextureDesc(
+            UInt32 maxTexDimension,
+            UInt32 numMeshVerts,
+            out ovrGpuSkinningTextureDesc texDesc
+        );
+
+        [DllImport(GpuSkinningLibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern unsafe ovrGpuSkinningResult ovrGpuSkinning_JointEncodeTextureData(
+            in ovrGpuSkinningTextureDesc desc,
+            UInt32 numMeshVerts,
+            /* const */ ovrAvatar2Vector4us* jointIndices,
+            /* const */ ovrAvatar2Vector4f* jointWeights,
+            byte* resultBuffer,
+            UInt32 resultBufferSize
+        );
+
+        [DllImport(GpuSkinningLibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern ovrGpuSkinningResult ovrGpuSkinning_JointWeightsBufferDesc(
+            UInt32 numMeshVerts,
+            out ovrGpuSkinningBufferDesc bufferDesc
+        );
+
+        [DllImport(GpuSkinningLibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern ovrGpuSkinningResult ovrGpuSkinning_JointIndicesBufferDesc(
+            UInt32 numMeshVerts,
+            ovrGpuSkinningEncodingPrecision encodingPrecision,
+            out ovrGpuSkinningBufferDesc bufferDesc
+        );
+
+        [DllImport(GpuSkinningLibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern unsafe ovrGpuSkinningResult ovrGpuSkinning_EncodeJointWeightsBufferData(
+            in ovrGpuSkinningBufferDesc desc,
+            /* const */ ovrAvatar2Vector4f* jointWeights,  // ovrAvatar2Vector4f[]
+            /* const */ UInt16* vertexIndexReordering,
+            byte* resultBuffer
+        );
+
+        [DllImport(GpuSkinningLibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern unsafe ovrGpuSkinningResult ovrGpuSkinning_EncodeJointIndicesBufferData(
+            in ovrGpuSkinningBufferDesc desc,
+            /* const */ ovrAvatar2Vector4us* jointIndices, // ovrAvatar2Vector4us[]
+            /* const */ UInt16* vertexIndexReordering,
+            byte* resultBuffer
+        );
+
+
+        #endregion // extern methods
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/CAPI/OvrGpuSkinningAPI_Main.cs.meta b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrGpuSkinningAPI_Main.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..95c31db08a2619bafe7b9e4d90d7ca9baee6cb12
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/CAPI/OvrGpuSkinningAPI_Main.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 95e9851176f337e4e8f4e6ed308efe2a
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/CAPI/experimental.meta b/Assets/Oculus/Avatar2/Scripts/CAPI/experimental.meta
new file mode 100644
index 0000000000000000000000000000000000000000..4e4608ce0c322ddefbe7dd0eef67dba46344bc59
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/CAPI/experimental.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: cb9d3e7d8e22f0644a15cfad55ab56f6
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/CAPI/experimental/OvrAvatarAPI_AnimationInternal.cs b/Assets/Oculus/Avatar2/Scripts/CAPI/experimental/OvrAvatarAPI_AnimationInternal.cs
new file mode 100644
index 0000000000000000000000000000000000000000..de3fafadb9e3fab8d3d8ddcb3a10f7fb42de8c85
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/CAPI/experimental/OvrAvatarAPI_AnimationInternal.cs
@@ -0,0 +1,325 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Oculus.Avatar2.Experimental
+{
+    using EntityPtr = IntPtr;
+    /* Pointer to pinned float[] */
+    using FloatArrayPtr = IntPtr;
+    using MixerLayerPtr = IntPtr;
+    /* Pointer to pinned string[], [In] string[] is used instead */
+    // using StringArrayPtr = IntPtr;
+
+    using OvrAnimClipPtr = IntPtr;
+    using OvrAnimHierarchyPtr = IntPtr;
+    /* Pointer to pinned ovrAvatar2AnimationParameterId[]*/
+    using ParameterIdArrayPtr = IntPtr;
+
+    using ovrAvatar2Id = Avatar2.CAPI.ovrAvatar2Id;
+    using ovrAvatar2Result = Avatar2.CAPI.ovrAvatar2Result;
+    using ovrAvatar2EntityId = Avatar2.CAPI.ovrAvatar2EntityId;
+
+#pragma warning disable CA1401 // P/Invokes should not be visible
+#pragma warning disable IDE1006 // Naming Styles
+    public partial class CAPI
+    {
+        [DllImport(Avatar2.CAPI.LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Asset_LoadAnimHierarchy(
+    IntPtr data, UInt32 size, out ovrAvatar2Id assetId);
+
+        [DllImport(Avatar2.CAPI.LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Asset_UnloadAnimHierarchy(ovrAvatar2Id assetId);
+
+        [DllImport(Avatar2.CAPI.LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Asset_GetAnimHierarchy(
+            ovrAvatar2Id assetId,
+            OvrAnimHierarchyPtr hierarchyAsset);
+
+        [DllImport(Avatar2.CAPI.LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Asset_LoadAnimClip(
+            IntPtr data, UInt32 size, out ovrAvatar2Id assetId);
+
+        [DllImport(Avatar2.CAPI.LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Asset_UnloadAnimClip(ovrAvatar2Id assetId);
+
+        [DllImport(Avatar2.CAPI.LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Asset_GetAnimClip(
+            ovrAvatar2Id assetId, ref ovrAvatar2AnimClipAsset outClipAsset);
+
+        [StructLayout(LayoutKind.Sequential)]
+        public struct ovrAvatar2SampleAnimationClipParams
+        {
+            public float phase;
+            public Int32 numJoints;
+            public IntPtr jointTransformArray; // ovrAvatar2Transform[]
+            public Int32 numFloats;
+            public IntPtr floatChannels; // float[]
+        }
+
+        [DllImport(Avatar2.CAPI.LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Animation_SampleAnimationClip(
+            ovrAvatar2Id clipAssetId,
+            // TODO: Pass as `in` pointer
+            ovrAvatar2SampleAnimationClipParams sampleParams);
+
+        /// Assets
+
+        [DllImport(Avatar2.CAPI.LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Animation_SetMood(
+            ovrAvatar2EntityId entityId, ovrAvatar2Mood desiredMood);
+
+        [DllImport(Avatar2.CAPI.LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Animation_GetMood(
+            ovrAvatar2EntityId entityId, out ovrAvatar2Mood currentMood);
+
+        /// Loads an animation state machine definition asset from memory.
+        /// \param the json data
+        /// \param result id of the loaded asset
+        /// \return result code
+        ///
+        [DllImport(Avatar2.CAPI.LibFile, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Animation_LoadAnimStateMachineDefinitionFromJson(
+            string json, out ovrAvatar2AnimationStateMachineDefinitionId outAssetId);
+
+        [DllImport(Avatar2.CAPI.LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Animation_GetHierarchyId(ovrAvatar2EntityId entityId, out ovrAvatar2AnimationHierarchyId outHierarchyId);
+
+        [DllImport(Avatar2.CAPI.LibFile, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Animation_CreateMask(
+            ovrAvatar2AnimationId hierarchyAssetId,
+            string name,
+            [In] string[] includedJoints,
+            int numJoints,
+            [In] string[] includedFloats,
+            int numFloats,
+            out ovrAvatar2AnimationMaskId outAssetId);
+
+        // Layers
+
+        [DllImport(Avatar2.CAPI.LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Animation_SetLayerWeight(MixerLayerPtr mixerLayer, float weight);
+
+        [DllImport(Avatar2.CAPI.LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Animation_DestroyLayer(MixerLayerPtr mixerLayer);
+
+
+        // State layer
+
+        [DllImport(Avatar2.CAPI.LibFile, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Animation_GetParameterId(string paramName, out ovrAvatar2AnimationParameterId paramId);
+
+        [DllImport(Avatar2.CAPI.LibFile, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Animation_GetStateId(string stateName, out ovrAvatar2AnimationStateId paramId);
+
+        [DllImport(Avatar2.CAPI.LibFile, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Animation_GetTransitionId(string transitionName, out ovrAvatar2AnimationTransitionId paramId);
+
+        [DllImport(Avatar2.CAPI.LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Animation_CreateStateLayer(
+            ovrAvatar2EntityId entityId,
+            ovrAvatar2AnimationStateMachineDefinitionId stateMachineId,
+            int priority,
+            ovrAvatar2AnimationBlendMode blendMode,
+            out MixerLayerPtr layer);
+
+        [DllImport(Avatar2.CAPI.LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Animation_StateLayerSetFloatParameter(
+            MixerLayerPtr mixerLayer, ovrAvatar2AnimationParameterId param, float value);
+
+        [DllImport(Avatar2.CAPI.LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Animation_StateLayerSetFloatParameters(
+            MixerLayerPtr mixerLayer, int numParams, ParameterIdArrayPtr paramIds, FloatArrayPtr values);
+
+        [DllImport(Avatar2.CAPI.LibFile, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Animation_StateLayerSetNameParameter(
+            MixerLayerPtr mixerLayer, ovrAvatar2AnimationParameterId param, string name);
+
+        [DllImport(Avatar2.CAPI.LibFile, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Animation_StateLayerSetNameParameters(
+            MixerLayerPtr mixerLayer, int numParams, ParameterIdArrayPtr paramIds, [In] string[] names);
+
+
+        [DllImport(Avatar2.CAPI.LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Animation_StateLayerRequestTransition(
+            MixerLayerPtr mixerLayer, ovrAvatar2AnimationTransitionId transitionId);
+
+        [DllImport(Avatar2.CAPI.LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Animation_StateLayerRequestFadeToState(
+            MixerLayerPtr mixerLayer, ovrAvatar2AnimationStateId transitionId, float transitionTimeSec);
+
+
+        /// Clip Layer
+
+        [DllImport(Avatar2.CAPI.LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Animation_ClipLayerCreate(
+            ovrAvatar2EntityId entityId, int priority, out MixerLayerPtr newLayer);
+
+        [DllImport(Avatar2.CAPI.LibFile, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Animation_ClipLayerSetClipByName(
+            MixerLayerPtr mixerLayer, string animName);
+
+        [DllImport(Avatar2.CAPI.LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Animation_ClipLayerSetClipById(
+            MixerLayerPtr mixerLayer, ovrAvatar2AnimationClipId animId);
+
+        [DllImport(Avatar2.CAPI.LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Animation_ClipLayerSetRate(
+            MixerLayerPtr mixerLayer, float rate);
+
+        [DllImport(Avatar2.CAPI.LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Animation_ClipLayerSetPhase(
+            MixerLayerPtr mixerLayer, float phase);
+
+        /// Viseme Layer
+
+        [DllImport(Avatar2.CAPI.LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result ovrAvatar2Animation_CreateVisemeLayer(
+            ovrAvatar2EntityId entityId,
+            in ovrAvatar2AnimVisemeLayerParams visemeParams,
+            int numVisemeAnims,
+            [In] string[] visemeAnimNames,
+            out MixerLayerPtr layer);
+
+
+        /// Ik Layer
+        ///
+        [DllImport(Avatar2.CAPI.LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result
+           ovrAvatar2Animation_CreateIkLayer(
+               ovrAvatar2EntityId entityId,
+               int priority,
+               out MixerLayerPtr layer);
+
+        [DllImport(Avatar2.CAPI.LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result
+           ovrAvatar2Animation_CreateIkLayerFromParams(
+                ovrAvatar2EntityId entityId,
+                int priority,
+                in ovrAvatar2AnimationIkLayerParams parameters,
+                out MixerLayerPtr layer);
+
+
+        [DllImport(Avatar2.CAPI.LibFile, CallingConvention = CallingConvention.Cdecl)]
+        public static extern ovrAvatar2Result
+            ovrAvatar2Animation_IkLayerSetTargetWeight(
+                MixerLayerPtr layer,
+                ovrAvatar2AnimationIkTarget target,
+                float weight,
+                float timeSec);
+
+        [StructLayout(LayoutKind.Sequential)]
+        public struct ovrAvatar2AnimVisemeLayerParams
+        {
+            int priority;
+            ovrAvatar2AnimationVisemeFilterSettings filterSetiings;
+        }
+
+        [StructLayout(LayoutKind.Sequential)]
+        public struct ovrAvatar2AnimationVisemeFilterSettings
+        {
+            float saturationThreshold;
+            float onsetSpeed;
+            float falloffSpeed;
+        }
+
+        /// Ik Layer
+        ///
+
+        [StructLayout(LayoutKind.Sequential)]
+        public struct OvrAvatarAnimationLimbConfig
+        {
+            public int upperLimb;
+            public int lowerLimb;
+            public int extremity;
+            public int lowerLimbPartial;
+            public int extremityPartial;
+
+            public int numUpperLimbTwists;
+            public IntPtr upperLimbTwists;
+            public int numLowerLimbTwists;
+            public IntPtr lowerLimbTwists;
+        }
+
+        [StructLayout(LayoutKind.Sequential)]
+        public struct ovrAvatar2AnimationIkLayerParams
+        {
+            public int headTracker;
+            public int leftHandTracker;
+            public int rightHandTracker;
+
+            public int root;
+            public int hips;
+            public int chest;
+            public int neck;
+            public int head;
+
+            public int numSpineJoints;
+            public IntPtr spineJoints;
+
+            public int rightShoulder;
+            public int leftShoulder;
+
+            public OvrAvatarAnimationLimbConfig leftArm;
+            public OvrAvatarAnimationLimbConfig rightArm;
+            public OvrAvatarAnimationLimbConfig leftLeg;
+            public OvrAvatarAnimationLimbConfig rightLeg;
+        }
+
+        public enum ovrAvatar2AnimationStateMachineDefinitionId : Int32
+        {
+            Invalid = 0,
+        }
+
+        public enum ovrAvatar2AnimationMaskId : Int32
+        {
+            Invalid = 0,
+        }
+        public enum ovrAvatar2AnimationClipId : Int32
+        {
+            Invalid = 0,
+        }
+
+        // TODO: The size of ovrAvatar2AnimationBlendMode is not explicitly specified
+        public enum ovrAvatar2AnimationBlendMode
+        {
+            Override = 0,
+            Additive,
+        }
+
+        // TODO: The size of ovrAvatar2AnimationIkTarget is not explicitly specified
+        public enum ovrAvatar2AnimationIkTarget
+        {
+            Head = 0,
+            LeftHand = 1,
+            RightHand = 2,
+            Count
+        }
+
+        // TODO: The size is not explicitly specified
+        public enum ovrAvatar2Mood
+        {
+            Invalid = -1,
+            Neutral = 0,
+            Like,
+            VeryLike,
+            Happy,
+            Confused,
+            VeryConfused,
+            Dislike,
+            VeryDislike,
+            Unhappy,
+
+            Count
+        }
+
+        // typedefs (effectively)
+        public enum ovrAvatar2AnimationId : UInt64 { }
+        public enum ovrAvatar2AnimationHierarchyId : UInt64 { }
+        public enum ovrAvatar2AnimationParameterId : UInt64 { }
+        public enum ovrAvatar2AnimationTransitionId : UInt64 { }
+        public enum ovrAvatar2AnimationStateId : UInt64 { }
+
+    }
+#pragma warning restore IDE1006 // Naming Styles
+#pragma warning restore CA1401 // P/Invokes should not be visible
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/CAPI/experimental/OvrAvatarAPI_AnimationInternal.cs.meta b/Assets/Oculus/Avatar2/Scripts/CAPI/experimental/OvrAvatarAPI_AnimationInternal.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..82111a94a4d2a4d9d04c935d2716ea9667ba043c
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/CAPI/experimental/OvrAvatarAPI_AnimationInternal.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 098db9c8a37d74c428aad7ac18b4c5d0
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/CAPI/experimental/OvrAvatarAPI_AnimationTypes.cs b/Assets/Oculus/Avatar2/Scripts/CAPI/experimental/OvrAvatarAPI_AnimationTypes.cs
new file mode 100644
index 0000000000000000000000000000000000000000..016c873c1407bd73f49811bc1355dcc633ff016c
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/CAPI/experimental/OvrAvatarAPI_AnimationTypes.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Oculus.Avatar2.Experimental
+{
+    using ovrAvatar2Id = Avatar2.CAPI.ovrAvatar2Id;
+
+    public partial class CAPI
+    {
+        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+        public struct ovrAvatar2AnimHierarchyAsset
+        {
+            public ovrAvatar2Id id; // asset id of the hierarchy
+            public string name; // name of the hierarchy
+            public UInt64 hash; // name of the hierarchy
+            public UInt32 jointCount; // Number of joints in the joint hierarchy
+            public IntPtr jointTransform; // Joint transforms
+            public IntPtr jointParents; // joint parents
+            public IntPtr jointNames; // joint names
+            public UInt32 floatChannelCount; // number of float values
+            public IntPtr floatChannelNames; // array of float value names
+        }
+
+        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+        public struct ovrAvatar2AnimClipAsset
+        {
+            public ovrAvatar2Id id; // asset id of the animation clip
+            [MarshalAs(UnmanagedType.LPStr)]
+            public IntPtr name; // name of the animation clip
+            [MarshalAs(UnmanagedType.LPStr)]
+            public IntPtr hierarchyName; // name of the animation hierarchy
+            public UInt64 hierarchyHash; // hash of the animation hierarchy
+            public Int32 hierarchyJointCount; // number of joints in the hierarchy
+            public Int32 hierarchyFloatCount; // number of float channels in the hierarchy
+            public Int32 numSamples; // number of samples (Duration = (numSamples - 1)*sampleDeltaTimeSecs)
+            public float sampleDeltaTimeSecs; // sampleDeltaTime.
+            [MarshalAs(UnmanagedType.U1)]
+            public bool looping; // whether the animation is looping.
+            [MarshalAs(UnmanagedType.U1)]
+            public bool additive; // whether the animation is additive
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/CAPI/experimental/OvrAvatarAPI_AnimationTypes.cs.meta b/Assets/Oculus/Avatar2/Scripts/CAPI/experimental/OvrAvatarAPI_AnimationTypes.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..57c57ac541fac42de8e8386208d90d3ead65528b
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/CAPI/experimental/OvrAvatarAPI_AnimationTypes.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 71dd2d9bbb415d54990bced460168698
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/CAPI/tracking.meta b/Assets/Oculus/Avatar2/Scripts/CAPI/tracking.meta
new file mode 100644
index 0000000000000000000000000000000000000000..4bf859bc55abcc56333047073ec31c7d3ca24b87
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/CAPI/tracking.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: cd660d4ac61da1940be99c606d722a84
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/CAPI/tracking/OvrAvatarAPI_Body.cs b/Assets/Oculus/Avatar2/Scripts/CAPI/tracking/OvrAvatarAPI_Body.cs
new file mode 100644
index 0000000000000000000000000000000000000000..072f2adf896109aac023ffedd8cba8c2bbdfe8ee
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/CAPI/tracking/OvrAvatarAPI_Body.cs
@@ -0,0 +1,214 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Oculus.Avatar2
+{
+    public partial class CAPI
+    {
+        enum ovrAvatar2HandTrackingBoneId : Int32
+        {
+            Invalid = -1,
+
+            Start = 0,
+            LeftHandThumbTrapezium = Start + 0,
+            LeftHandThumbMeta = Start + 1,
+            LeftHandThumbProximal = Start + 2,
+            LeftHandThumbDistal = Start + 3,
+            LeftHandIndexProximal = Start + 4,
+            LeftHandIndexIntermediate = Start + 5,
+            LeftHandIndexDistal = Start + 6,
+            LeftHandMiddleProximal = Start + 7,
+            LeftHandMiddleIntermediate = Start + 8,
+            LeftHandMiddleDistal = Start + 9,
+            LeftHandRingProximal = Start + 10,
+            LeftHandRingIntermediate = Start + 11,
+            LeftHandRingDistal = Start + 12,
+            LeftHandPinkyMeta = Start + 13,
+            LeftHandPinkyProximal = Start + 14,
+            LeftHandPinkyIntermediate = Start + 15,
+            LeftHandPinkyDistal = Start + 16,
+            RightHandThumbTrapezium = Start + 17,
+            RightHandThumbMeta = Start + 18,
+            RightHandThumbProximal = Start + 19,
+            RightHandThumbDistal = Start + 20,
+            RightHandIndexProximal = Start + 21,
+            RightHandIndexIntermediate = Start + 22,
+            RightHandIndexDistal = Start + 23,
+            RightHandMiddleProximal = Start + 24,
+            RightHandMiddleIntermediate = Start + 25,
+            RightHandMiddleDistal = Start + 26,
+            RightHandRingProximal = Start + 27,
+            RightHandRingIntermediate = Start + 28,
+            RightHandRingDistal = Start + 29,
+            RightHandPinkyMeta = Start + 30,
+            RightHandPinkyProximal = Start + 31,
+            RightHandPinkyIntermediate = Start + 32,
+            RightHandPinkyDistal = Start + 33,
+            Count = Start + 34,
+        }
+
+        internal const int MaxHandBones = (int)ovrAvatar2HandTrackingBoneId.Count;
+
+        // TODO: Convert to unsafe fixed arrays
+        [StructLayout(LayoutKind.Sequential)]
+        public struct ovrAvatar2HandTrackingState
+        {
+            public ovrAvatar2Transform wristPosLeft;
+            public ovrAvatar2Transform wristPosRight;
+
+            public ovrAvatar2Quatf boneRotation0;
+            public ovrAvatar2Quatf boneRotation1;
+            public ovrAvatar2Quatf boneRotation2;
+            public ovrAvatar2Quatf boneRotation3;
+            public ovrAvatar2Quatf boneRotation4;
+            public ovrAvatar2Quatf boneRotation5;
+            public ovrAvatar2Quatf boneRotation6;
+            public ovrAvatar2Quatf boneRotation7;
+            public ovrAvatar2Quatf boneRotation8;
+            public ovrAvatar2Quatf boneRotation9;
+            public ovrAvatar2Quatf boneRotation10;
+            public ovrAvatar2Quatf boneRotation11;
+            public ovrAvatar2Quatf boneRotation12;
+            public ovrAvatar2Quatf boneRotation13;
+            public ovrAvatar2Quatf boneRotation14;
+            public ovrAvatar2Quatf boneRotation15;
+            public ovrAvatar2Quatf boneRotation16;
+            public ovrAvatar2Quatf boneRotation17;
+            public ovrAvatar2Quatf boneRotation18;
+            public ovrAvatar2Quatf boneRotation19;
+            public ovrAvatar2Quatf boneRotation20;
+            public ovrAvatar2Quatf boneRotation21;
+            public ovrAvatar2Quatf boneRotation22;
+            public ovrAvatar2Quatf boneRotation23;
+            public ovrAvatar2Quatf boneRotation24;
+            public ovrAvatar2Quatf boneRotation25;
+            public ovrAvatar2Quatf boneRotation26;
+            public ovrAvatar2Quatf boneRotation27;
+            public ovrAvatar2Quatf boneRotation28;
+            public ovrAvatar2Quatf boneRotation29;
+            public ovrAvatar2Quatf boneRotation30;
+            public ovrAvatar2Quatf boneRotation31;
+            public ovrAvatar2Quatf boneRotation32;
+            public ovrAvatar2Quatf boneRotation33;
+
+            public float handScaleLeft;
+            public float handScaleRight;
+            [MarshalAs(UnmanagedType.U1)] public bool isTrackedLeft;
+            [MarshalAs(UnmanagedType.U1)] public bool isTrackedRight;
+            [MarshalAs(UnmanagedType.U1)] public bool isConfidentLeft;
+            [MarshalAs(UnmanagedType.U1)] public bool isConfidentRight;
+        }
+
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        public delegate bool HandStateCallback(out ovrAvatar2HandTrackingState skeleton, IntPtr userContext);
+
+        [StructLayout(LayoutKind.Sequential)]
+        public struct ovrAvatar2HandTrackingDataContext
+        {
+            public IntPtr context;
+            public HandStateCallback handTrackingCallback;
+        }
+
+        [StructLayout(LayoutKind.Sequential)]
+        internal struct ovrAvatar2HandTrackingDataContextNative
+        {
+            public IntPtr context;
+            public IntPtr handTrackingCallback;
+        }
+
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        [return: MarshalAs(UnmanagedType.U1)]
+        internal delegate bool InputControlCallback(out ovrAvatar2InputControlState inputControlState, IntPtr userContext);
+
+        [StructLayout(LayoutKind.Sequential)]
+        internal struct ovrAvatar2InputControlContext
+        {
+            public IntPtr context;
+            public InputControlCallback inputControlCallback;
+        }
+
+        [StructLayout(LayoutKind.Sequential)]
+        internal struct ovrAvatar2InputControlContextNative
+        {
+            public IntPtr context;
+            public IntPtr inputControlCallback;
+        }
+
+        [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
+        [return: MarshalAs(UnmanagedType.U1)]
+        internal delegate bool InputTrackingCallback(out ovrAvatar2InputTrackingState inputTrackingState, IntPtr userContext);
+
+        [StructLayout(LayoutKind.Sequential)]
+        internal struct ovrAvatar2InputTrackingContext
+        {
+            public IntPtr context;
+            public InputTrackingCallback inputTrackingCallback;
+        }
+
+        [StructLayout(LayoutKind.Sequential)]
+        internal struct ovrAvatar2InputTrackingContextNative
+        {
+            public IntPtr context;
+            public IntPtr inputTrackingCallback;
+        }
+
+        public enum ovrAvatar2BodyMarkerTypes : Int32
+        {
+            Hmd,
+            LeftController,
+            RightController,
+            LeftHand,
+            RightHand,
+        }
+
+        [Flags]
+        public enum ovrAvatar2BodyProviderCreateFlags : Int32
+        {
+            ///< When set, the body context will run in a background thread.
+            RunAsync = 1 << 0,
+        }
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        internal static extern ovrAvatar2Result ovrAvatar2Body_CreateProvider(
+            ovrAvatar2BodyProviderCreateFlags flags, out IntPtr bodyTrackingContext);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        internal static extern ovrAvatar2Result ovrAvatar2Body_DestroyProvider(IntPtr bodyTrackingContext);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        internal static extern ovrAvatar2Result ovrAvatar2Body_SetOffset(
+            IntPtr bodyTrackingContext,
+            ovrAvatar2BodyMarkerTypes type,
+            in ovrAvatar2Transform inputTransforms);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        internal static extern ovrAvatar2Result ovrAvatar2Body_SetHandTrackingContext(IntPtr bodyTrackingContext,
+            in ovrAvatar2HandTrackingDataContext handContext);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl,
+            EntryPoint = "ovrAvatar2Body_SetHandTrackingContext")]
+        internal static extern ovrAvatar2Result ovrAvatar2Body_SetHandTrackingContextNative(
+            IntPtr bodyTrackingContext, in ovrAvatar2HandTrackingDataContextNative handContext);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        internal static extern ovrAvatar2Result ovrAvatar2Body_SetInputControlContext(IntPtr bodyTrackingContext, in ovrAvatar2InputControlContext context);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ovrAvatar2Body_SetInputControlContext")]
+        internal static extern ovrAvatar2Result ovrAvatar2Body_SetInputControlContextNative(IntPtr bodyTrackingContext, in ovrAvatar2InputControlContextNative context);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        internal static extern ovrAvatar2Result ovrAvatar2Body_SetInputTrackingContext(IntPtr bodyTrackingContext, in ovrAvatar2InputTrackingContext context);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ovrAvatar2Body_SetInputTrackingContext")]
+        internal static extern ovrAvatar2Result ovrAvatar2Body_SetInputTrackingContextNative(IntPtr bodyTrackingContext, in ovrAvatar2InputTrackingContextNative context);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        internal static extern ovrAvatar2Result ovrAvatar2Body_InitializeDataContext(
+            IntPtr bodyTrackingContext, out ovrAvatar2TrackingDataContext dataContext);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl,
+            EntryPoint = "ovrAvatar2Body_InitializeDataContext")]
+        internal static extern ovrAvatar2Result ovrAvatar2Body_InitializeDataContextNative(
+            IntPtr bodyTrackingContext, out ovrAvatar2TrackingDataContextNative dataContext);
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/CAPI/tracking/OvrAvatarAPI_Body.cs.meta b/Assets/Oculus/Avatar2/Scripts/CAPI/tracking/OvrAvatarAPI_Body.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..3c3ff499e8e51c0537bf56aadaa3f79014dd62cf
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/CAPI/tracking/OvrAvatarAPI_Body.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: e7d40db578ae4a439bc25958276e9cc5
+timeCreated: 1605739984
\ No newline at end of file
diff --git a/Assets/Oculus/Avatar2/Scripts/CAPI/tracking/OvrAvatarAPI_LipSync.cs b/Assets/Oculus/Avatar2/Scripts/CAPI/tracking/OvrAvatarAPI_LipSync.cs
new file mode 100644
index 0000000000000000000000000000000000000000..d2066d27ec52335469566b03785510ea476f743d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/CAPI/tracking/OvrAvatarAPI_LipSync.cs
@@ -0,0 +1,81 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Oculus.Avatar2
+{
+    public partial class CAPI
+    {
+
+        public enum ovrAvatar2LipSyncMode : Int32
+        {
+            Original = 0,
+            Enhanced = 1,
+            EnhancedWithLaughter = 2
+        }
+
+        [StructLayout(LayoutKind.Sequential)]
+        public struct ovrAvatar2LipSyncProviderConfig
+        {
+            public ovrAvatar2LipSyncMode mode;
+            public UInt32 audioSampleRate;
+            public UInt32 audioBufferSize;
+        }
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        internal static extern ovrAvatar2Result ovrAvatar2LipSync_CreateProvider(
+            ref ovrAvatar2LipSyncProviderConfig config, ref IntPtr lipsyncProvider);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        internal static extern ovrAvatar2Result ovrAvatar2LipSync_ReconfigureProvider(
+            IntPtr lipsyncProvider, ref ovrAvatar2LipSyncProviderConfig config);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        internal static extern ovrAvatar2Result ovrAvatar2LipSync_DestroyProvider(IntPtr lipsyncProvider);
+
+        public enum ovrAvatar2AudioDataFormat : Int32
+        {
+            S16_Mono, // Signed 16-bit integer mono audio stream
+            S16_Stereo, // Signed 16-bit integer stereo audio stream
+            F32_Mono, // Signed 32-bit float mono data type
+            F32_Stereo, // Signed 32-bit float stereo data type
+        }
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        internal static extern ovrAvatar2Result ovrAvatar2LipSync_FeedAudio(IntPtr lipsyncProvider,
+            ovrAvatar2AudioDataFormat format, IntPtr data, UInt32 numSamples);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        internal static extern ovrAvatar2Result
+        ovrAvatar2LipSync_SetLaughter(IntPtr lipsyncProvider, Int32 amount);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        internal static extern ovrAvatar2Result
+        ovrAvatar2LipSync_SetSmoothing(IntPtr lipsyncProvider, Int32 smoothing);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        internal static extern ovrAvatar2Result
+        ovrAvatar2LipSync_EnableViseme(IntPtr lipsyncProvider, ovrAvatar2Viseme viseme);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        internal static extern ovrAvatar2Result
+        ovrAvatar2LipSync_DisableViseme(IntPtr lipsyncProvider, ovrAvatar2Viseme viseme);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        internal static extern ovrAvatar2Result
+        ovrAvatar2LipSync_SetViseme(
+            IntPtr lipsyncProvider,
+            ovrAvatar2Viseme viseme,
+            Int32 amount);
+
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        internal static extern ovrAvatar2Result ovrAvatar2LipSync_InitializeContext(
+            IntPtr lipsyncProvider, ref ovrAvatar2LipSyncContext lipSyncContext);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl,
+            EntryPoint = "ovrAvatar2LipSync_InitializeContext")]
+        internal static extern ovrAvatar2Result ovrAvatar2LipSync_InitializeContextNative(
+            IntPtr lipsyncProvider, out ovrAvatar2LipSyncContextNative lipSyncContext);
+
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/CAPI/tracking/OvrAvatarAPI_LipSync.cs.meta b/Assets/Oculus/Avatar2/Scripts/CAPI/tracking/OvrAvatarAPI_LipSync.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..056cbba454117e770b5cbf6d473ac6880dc57ec6
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/CAPI/tracking/OvrAvatarAPI_LipSync.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: ad10098be99e41b48a84ba28e315d68e
+timeCreated: 1605740313
\ No newline at end of file
diff --git a/Assets/Oculus/Avatar2/Scripts/Common.meta b/Assets/Oculus/Avatar2/Scripts/Common.meta
new file mode 100644
index 0000000000000000000000000000000000000000..0515acd7e8b3b2ea1b60802b3da3ab3c04c2fc0c
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Common.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 7231f017e5d300e4e946d472a26b88b9
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Common/Extensions.cs b/Assets/Oculus/Avatar2/Scripts/Common/Extensions.cs
new file mode 100644
index 0000000000000000000000000000000000000000..ee5ece77676a0fd76e3fbba3f61320c58f6f8e7c
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Common/Extensions.cs
@@ -0,0 +1,67 @@
+using System.Collections.Generic;
+
+using UnityEngine;
+
+namespace Oculus.Avatar2
+{
+    public static class UnityExtensions
+    {
+        public static T ToNullIfDestroyed<T>(this T obj) where T : UnityEngine.Object
+            => obj is null || obj == null ? null : obj;
+
+        public static T GetComponentOrNull<T>(this GameObject obj) where T : Component
+            => obj.GetComponent<T>().ToNullIfDestroyed();
+
+        public static T GetComponentOrNull<T>(this Component c) where T : Component
+            => c.gameObject.GetComponentOrNull<T>();
+
+        public static T GetOrAddComponent<T>(this GameObject obj) where T : Component
+            => obj.GetComponentOrNull<T>() ?? obj.AddComponent<T>();
+    }
+
+    public static class ListExtensions
+    {
+        /// <summary>
+        /// Insert an item into a sorted list using BinarySearch.
+        /// </summary>
+        public static void AddSorted<T>(this List<T> list, T item, IComparer<T> comparer)
+        {
+            int index = list.BinarySearch(item, comparer);
+            list.Insert(index < 0 ? ~index : index, item);
+        }
+
+        /// <summary>
+        /// Removes an item in a sorted list using BinarySearch.
+        /// </summary>
+        public static bool RemoveSorted<T>(this List<T> list, T item, IComparer<T> comparer)
+        {
+            int index = list.BinarySearch(item, comparer);
+            if (index < 0)
+            {
+                return false;
+            }
+
+            list.RemoveAt(index);
+            return true;
+        }
+
+        /// <summary>
+        /// Insert an item into a sorted list range using BinarySearch.
+        /// </summary>
+        public static void AddSorted<T>(this List<T> list, int start, int count, T item, IComparer<T> comparer)
+        {
+            int index = list.BinarySearch(start, count, item, comparer);
+            list.Insert(index < 0 ? ~index : index, item);
+        }
+    }
+
+    public static class FloatExtenstions
+    {
+        private const float DEFAULT_EPS = 1e-30f;
+
+        public static bool IsApproximatelyZero(this float x, float eps = DEFAULT_EPS)
+        {
+            return Mathf.Abs(x) <= eps;
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Common/Extensions.cs.meta b/Assets/Oculus/Avatar2/Scripts/Common/Extensions.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..d76d5a30f1f9a625b6927c57315e1fceece3151e
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Common/Extensions.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: d0c1260748c803043a8b2ae7a9f4ab9e
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Common/OvrSingletonBehaviour.cs b/Assets/Oculus/Avatar2/Scripts/Common/OvrSingletonBehaviour.cs
new file mode 100644
index 0000000000000000000000000000000000000000..bef54ec178021cb4f141e83b126794d78d1285bb
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Common/OvrSingletonBehaviour.cs
@@ -0,0 +1,264 @@
+using UnityEngine;
+
+namespace Oculus.Avatar2
+{
+    //https://wiki.unity3d.com/index.php/Singleton
+    public class OvrSingletonBehaviour<T> : MonoBehaviour where T : OvrSingletonBehaviour<T>
+    {
+        private const string logScope = nameof(T);
+
+        public static bool hasInstance => !shuttingDown && Instance != null && !Instance._willShutdown;
+
+        // Get the currently created Singleton instance, neither creates nor finds instances
+        public static T Instance { get; private set; } = null;
+
+        public static bool shuttingDown { get; private set; } = false;
+
+        // If this Singleton is not instantiated, create it - try to fix Instantiation order instead
+        public static void EnsureInstantiated() { if (Instance is null) { Instantiate(); } }
+
+        // Should be called only once per Singleton Type, before it is used
+        public static void Instantiate()
+        {
+            if (hasInstance)
+            {
+                OvrAvatarLog.LogError($"Instantiate called when singleton of {typeof(T).Name} already exists"
+                    , logScope, Instance);
+            }
+            else if (initializing)
+            {
+                OvrAvatarLog.LogError(
+                    $"Instantiate called when singleton of {typeof(T).Name} is already initializing"
+                    , logScope, Instance);
+            }
+            else if (shuttingDown)
+            {
+                OvrAvatarLog.LogError($"Instantiate called when singleton of {typeof(T).Name} is already shutting down"
+                    , logScope, Instance);
+            }
+
+            Debug.Assert(!hasInstance && !initializing && !shuttingDown);
+
+            if (!initializing && !hasInstance)
+            {
+                // Search for existing instance. Do not assign to _instance, as it may not have Awoken yet
+                var sceneInstance = FindObjectOfType<T>();
+
+                // Create new instance if one doesn't already exist.
+                if (sceneInstance == null || sceneInstance._willShutdown)
+                {
+                    if (!shuttingDown)
+                    {
+                        // Need to create a new GameObject to attach the singleton to.
+                        var singletonObject = new GameObject(typeof(T) + " (Singleton)", typeof(T));
+                        OvrAvatarLog.LogDebug($"Singleton instance not found in scene, created {singletonObject.name}."
+                            , logScope, singletonObject);
+
+                        singletonObject.GetComponent<T>().CheckStartup();
+                    }
+                    else
+                    {
+                        OvrAvatarLog.LogError(
+                            $"Singleton '{typeof(T).Name}' attempted spawn during shutdown. Ignoring."
+                            , logScope, sceneInstance);
+                    }
+                }
+                else if (!sceneInstance._hasStarted)
+                {
+                    if (sceneInstance.enabled && sceneInstance.gameObject.activeInHierarchy)
+                    {
+                        sceneInstance.ExecuteStartup();
+                    }
+                    else
+                    {
+                        OvrAvatarLog.LogError(
+                            $"Singleton '{typeof(T).Name}' is in scene but disabled, no instance created."
+                            , logScope, sceneInstance);
+                    }
+                }
+                else
+                {
+                    OvrAvatarLog.LogWarning(
+                        $"Singleton `{typeof(T).Name}` was started before Instantiate!"
+                        , logScope, sceneInstance);
+                }
+            }
+        }
+
+        // Subclasses may implement custom startup logic in `Initialize`
+        protected virtual void Initialize() { }
+        // Subclasses may implement custom shutdown logic in `Shutdown`
+        protected virtual void Shutdown() { }
+
+        // Derived singleton classes can not reliably implement Awake
+        protected void Awake() => CheckStartup();
+
+        // Derived classes can not reliably implement Awake or Start,
+        // but we don't have anything to do in Start and implementing it will add overhead at runtime
+        // TODO: Better way to accomplish this goal
+#if UNITY_EDITOR
+        protected void Start() { }
+#endif
+
+        protected void OnApplicationQuit() => CheckShutdown(false);
+        protected void OnDestroy() => CheckShutdown(true);
+
+        // For error detection
+
+        private static bool initializing = false;
+        protected bool IsSingletonInstance => Instance == this;
+        private bool _hasStarted = false;
+        private bool _willShutdown = false;
+        private bool _hasShutdown = false;
+
+        private void CheckStartup()
+        {
+            Debug.Assert(!initializing);
+
+            // May have been jump started by `Instance` access
+            if (_hasStarted) { return; }
+
+            if (Instance == null)
+            {
+                ExecuteStartup();
+            }
+            else
+            {
+                OvrAvatarLog.LogWarning($"Duplicate `{typeof(T).Name}` instance created on {gameObject.name}, destroying", logScope);
+                Destroy(this);
+            }
+        }
+        private void CheckShutdown(bool isDestroy)
+        {
+            _willShutdown = true;
+            if (!_hasStarted || _hasShutdown) { return; }
+
+            Debug.Assert(Instance == this || Instance is null);
+            if (Instance == this)
+            {
+                ExecuteShutdown(isDestroy);
+            }
+        }
+
+        private void ExecuteStartup()
+        {
+            initializing = true;
+            try
+            {
+                Initialize();
+
+#if UNITY_EDITOR
+                UnityEditor.EditorApplication.quitting += EmergencyShutdown;
+                UnityEditor.AssemblyReloadEvents.beforeAssemblyReload += EmergencyShutdown;
+
+                if (Application.isPlaying)
+#endif
+                {
+                    // `DontDestroyOnLoad` requires root parent
+                    if (transform.parent == null)
+                    {
+                        OvrAvatarLog.LogDebug("Marking DontDestroyOnLoad on singleton"
+                            , logScope, this);
+
+                        OvrSingletonBehaviour<T>.DontDestroyOnLoad(this);
+                    }
+                    else
+                    {
+                        OvrAvatarLog.LogInfo("Root gameObject required for DontDestroyOnLoad call"
+                            , logScope, this);
+                    }
+                }
+                Instance = (T)this;
+
+                _hasStarted = true;
+            }
+            catch (System.Exception e)
+            {
+                OvrAvatarLog.LogException("initialize", e, logScope, this);
+            }
+            initializing = false;
+        }
+
+        private void ExecuteShutdown(bool isDestroy)
+        {
+            shuttingDown = true;
+            try
+            {
+                Shutdown();
+            }
+            catch (System.Exception e)
+            {
+                OvrAvatarLog.LogException("shutdown", e, logScope, this);
+            }
+
+            Instance = null;
+            _hasShutdown = true;
+
+#if UNITY_EDITOR
+            UnityEditor.EditorApplication.quitting -= EmergencyShutdown;
+            UnityEditor.AssemblyReloadEvents.beforeAssemblyReload -= EmergencyShutdown;
+#endif
+
+            if (!isDestroy && this != null)
+            {
+#if UNITY_EDITOR
+                if (!Application.isPlaying)
+                {
+                    // Don't want to destroy prefabs, only scene instances
+                    if (!UnityEditor.PrefabUtility.IsPartOfAnyPrefab(this))
+                    {
+                        // Can't call Destroy from edit mode
+                        OvrSingletonBehaviour<T>.DestroyImmediate(this);
+                    }
+                }
+                else
+#endif
+                {
+                    OvrSingletonBehaviour<T>.Destroy(this);
+                }
+            }
+
+            shuttingDown = false;
+        }
+
+#if UNITY_EDITOR
+        // Used for editor close and assembly reload
+        // Simply removes the `isDestroy` param to match those event signatures
+        private void EmergencyShutdown() => CheckShutdown(false);
+#endif
+
+        // Should be called only by `OvrAvatarManager` during shutdown
+        internal protected static void _AvatarManagerCheckShutdown<U>(OvrAvatarManager manager, U instance)
+            where U : OvrSingletonBehaviour<U>
+        {
+            if (instance is null) { return; }
+
+            OvrAvatarLog.Assert(manager != null, logScope, Instance);
+            OvrAvatarLog.Assert(manager._willShutdown, logScope, Instance);
+            OvrAvatarLog.Assert(manager == OvrAvatarManager.Instance, logScope, Instance);
+
+            if (instance.IsSingletonInstance)
+            {
+                instance.CheckShutdown(false);
+            }
+        }
+
+        ///
+        /// Shuts down the instance and asserts that it's null.
+        /// This should be called by unit tests during teardown to reset this singleton's state.
+        ///
+        public static void ResetInstance()
+        {
+            if (!(Instance is null))
+            {
+                if (Instance != null)
+                {
+                    Instance.CheckShutdown(false);
+                }
+                OvrAvatarLog.Assert(Instance == null, logScope, Instance);
+                Instance = null;
+            }
+        }
+
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Common/OvrSingletonBehaviour.cs.meta b/Assets/Oculus/Avatar2/Scripts/Common/OvrSingletonBehaviour.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..e6ffc1a455474519aa16382119c196bf6410909a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Common/OvrSingletonBehaviour.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 78788c53050e12a429065b45cd02878b
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Common/OvrStateMachine.cs b/Assets/Oculus/Avatar2/Scripts/Common/OvrStateMachine.cs
new file mode 100644
index 0000000000000000000000000000000000000000..f2cced18db2af9aff4104913bb86075ce5600b76
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Common/OvrStateMachine.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+
+namespace Oculus.Avatar2
+{
+    public class OvrStateMachine<T> where T : System.Enum
+    {
+        public T currentState;
+
+        public delegate bool CanEnterDelegate(T nextState);
+        public delegate void StateChangedDelegate(T nextState, T prevState);
+        public CanEnterDelegate canEnter;
+        public StateChangedDelegate onStateChange;
+        public bool SetState(T nextState)
+        {
+            if(canEnter != null && !canEnter(nextState))
+            {
+                return false;
+            }
+            T prevState = currentState;
+            currentState = nextState;
+            if(onStateChange != null)
+            {
+                onStateChange(currentState, prevState);
+            }
+            return true;
+        }
+
+
+        public bool IsState(T checkState)
+        {
+            return Compare(currentState, checkState);
+        }
+
+        public bool Compare(T x, T y)
+        {
+            return EqualityComparer<T>.Default.Equals(x, y);
+        }
+
+        public string GetStateString()
+        {
+            return currentState.ToString();
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Common/OvrStateMachine.cs.meta b/Assets/Oculus/Avatar2/Scripts/Common/OvrStateMachine.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..71f97a91b04d9c643278f40edd3abf0b1916056c
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Common/OvrStateMachine.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: e7b74a725fa1dce459fba1953ccb3a4a
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Common/OvrTime.cs b/Assets/Oculus/Avatar2/Scripts/Common/OvrTime.cs
new file mode 100644
index 0000000000000000000000000000000000000000..9066ae280bb3280bd3f34bf9359a3cb5b97f66a1
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Common/OvrTime.cs
@@ -0,0 +1,412 @@
+// #define OVR_TIME_SLICER_CHECK_SLICE_TIME
+
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using UnityEngine;
+
+using Stopwatch = System.Diagnostics.Stopwatch;
+
+#if UNITY_EDITOR
+using System.Runtime.CompilerServices;
+[assembly: InternalsVisibleTo("AvatarSDK.PlayModeTests")]
+#endif
+
+namespace Oculus.Avatar2
+{
+    // This class is 1000% *NOT* threadsafe aside from post methods.
+    // When it doubt, post it out. (PostAllocToUnityMainThread or PostCleanupToUnityMainThread)
+
+    public static class SliceExtensions
+    {
+        // Invalidate target SliceHandle instance
+        internal static void Clear(this ref OvrTime.SliceHandle handle)
+        {
+            handle = default;
+        }
+
+        internal static bool Cancel(this ref OvrTime.SliceHandle handle)
+        {
+            Debug.Assert(handle.IsValid);
+            // This is marked obsolete to discourage direct calls, since we can't scope it properly
+#pragma warning disable CS0618 // Type or member is obsolete
+            bool didCancel = handle._Stop();
+#pragma warning restore CS0618 // Type or member is obsolete
+
+            handle.Clear();
+
+            return didCancel;
+        }
+    }
+
+    // A manager class that coroutines can use to time slice their work.
+    // Handles nested calls to Slice and multiple timesliced functions waiting to run at once
+    // TODO: Investigate a final solution to async loading. Investigate threading/Tasks (has issues with Unity though)
+    // TODO: Analyze GC alloc of coroutines vs tasks and how it can be optimized in either case
+    // TODO: Allow slices to wait on other SliceHandles - reduce number of redundant status checks
+    // TODO: List is awful for the queue, tree? (sooner/later/dependent)
+    // TODO: Better delay logic - slowest enumerators should move to end of queue and stay there
+    // -> Track "delay region" and insert new delays earlier - repeated delays move to end of region
+    // ^ Hmmm a tree would do that nicely...
+    internal static class OvrTime
+    {
+        private const string logScope = "OvrTime";
+
+        // TODO: The split between static and local members isn't very fun
+        // - but I'm not sure how else to make this reasonably runtime configurable via Unity editor?
+        // NOTE: Could do a "copyOver" at the start of UpdateInternal or some such, but I'd prefer to support mid-slice budget changes
+        // TODO: Put more thought into what the correct amount here should be
+        // Minimum amount of work to run per frame (in miilliseconds) while sliced work remains
+        private static uint _minWorkPerFrameMS = uint.MaxValue;
+
+        internal static UInt16 minWorkPerFrameMS
+        {
+            get => _minWorkPerFrameMS <= UInt16.MaxValue ? (UInt16)_minWorkPerFrameMS : UInt16.MaxValue;
+            set => _minWorkPerFrameMS = value;
+        }
+
+        // `uint.MaxValue` can only be set from static ctor, external API only accepts UInt16
+        internal static bool HasLimitedBudget => _minWorkPerFrameMS < uint.MaxValue;
+        internal static void ResetInitialBudget() { _minWorkPerFrameMS = uint.MaxValue; }
+
+        // TODO: "Bleed-over" time? If an update goes long, shorten the duration of the next update?
+
+        private static readonly Stopwatch _watch = new Stopwatch();
+#if OVR_TIME_SLICER_CHECK_SLICE_TIME
+        private readonly Stopwatch _sliceCheck = new Stopwatch();
+#endif
+
+        // TODO: Switch to exapandable ring buffer
+        /// <summary>
+        /// Sliced IEnumerators which are potentially eligable for performing work
+        /// </summary>
+        private static readonly List<IEnumerator<SliceStep>> _slicerQueue = new List<IEnumerator<SliceStep>>();
+        /// <summary>
+        /// Cleanup actions are prioritized before alloc actions - under the assumption they will reduce resource pressure
+        /// </summary>
+        private static readonly ConcurrentQueue<Action> _postedCleanupActions = new ConcurrentQueue<Action>();
+        /// <summary>
+        /// Posted actions which will increase resource pressure.
+        /// </summary>
+        private static readonly ConcurrentQueue<Action> _postedAllocActions = new ConcurrentQueue<Action>();
+
+        private static bool _isSlicing = false;
+
+        public enum SliceStep
+        {
+            /* Continue working on this Slicer, for deferring step logic to helper functions
+            * NOTE: When continuing work after checking CanContinue/ShouldHold, prefer to avoid `yield return` entirely */
+            Continue = 0,
+            /* End slicer updates this frame, used when budget overrun has been detected */
+            Hold = 1,
+            /* Defer time to next Slicer, but do not change queue order - ie: "frame delay" */
+            Wait = 2,
+            /* Stop working on this slicer, move it to end of queue, "indefinite delay" */
+            Delay = 3,
+            /* The next operation is expected to be slow - run it on a frame by itself
+             NOTE: Exact behavior is undefined, but this should be avoided if unnecessary */
+            Stall = 4,
+            /* Cancel this slicer, equivalent to `yield break`, for deferring step logic to helper functions
+             NOTE: `yield break` may be preferrable to returning `Cancel`*/
+            Cancel = 5,
+        }
+
+        public struct SliceHandle
+        {
+            public bool IsValid => _enumerator != null;
+
+            // Safe to call from Finalizer to prevent OvrTime stall - implicitly logs a warning
+            // Do not call under expected conditions, should almost never be called from Unity.Main thread
+            // NOTE: This method indicates failure is expected, it will likely never be 100% bulletproof
+            internal void EmergencyShutdown()
+            {
+                // TODO: Confirm finalizer thread? Non-main thread?
+                // TODO: Attempt to stop current slice? Block until not slicing and reevaluate?
+                OvrAvatarLog.LogError($"EmergencyShutdown activated for enumerator {_enumerator}", logScope);
+                Debug.Assert(IsValid);
+                var self = this;
+                PostCleanupToUnityMainThread(() => self.Cancel());
+            }
+
+            internal void WasCancelled()
+            {
+                Debug.Assert(IsValid);
+                OvrTime.IsEnumeratorQueued(_enumerator);
+            }
+
+            [Obsolete("Call `Cancel` instead which will auto-invalidate the handle")]
+            internal bool _Stop()
+            {
+                Debug.Assert(IsValid);
+                return StopEnumerator(_enumerator);
+            }
+
+            internal static SliceHandle GenerateHandle(IEnumerator<SliceStep> enumerator, object queueContext)
+            {
+                Debug.Assert(queueContext == _slicerQueue);
+                return new SliceHandle(enumerator);
+            }
+            private SliceHandle(IEnumerator<SliceStep> enumerator) => _enumerator = enumerator;
+            private readonly IEnumerator<SliceStep> _enumerator;
+        }
+
+        #region Threadsafe Methods
+        public static void PostToUnityMainThread(Action action) => PostAllocToUnityMainThread(action);
+        public static void PostAllocToUnityMainThread(Action action)
+        {
+            _postedAllocActions.Enqueue(action);
+        }
+        public static void PostCleanupToUnityMainThread(Action action)
+        {
+            _postedCleanupActions.Enqueue(action);
+        }
+        #endregion // Threadsafe Methods
+
+
+        //:: Public Helpers
+        #region Public Helper Methods
+        // Evaluate this action now, shorting budget against the next update
+        public static void Rush(Action timedOp)
+        {
+            Debug.Assert(timedOp != null);
+            RunNow(timedOp);
+        }
+
+        // Run slicer when time permits - will be run over multiple frames
+        public static SliceHandle Slice(IEnumerator<SliceStep> slicer)
+        {
+            Debug.Assert(slicer != null);
+            Debug.Assert(!_slicerQueue.Contains(slicer));
+
+            _slicerQueue.Add(slicer);
+
+            return SliceHandle.GenerateHandle(slicer, _slicerQueue);
+        }
+
+        // Checks if work should Hold (stop for this frame)
+        // This should only be called from sliced tasks
+        public static bool ShouldHold
+        {
+            get
+            {
+                // ShouldHold must only be used from w/in Sliced methods
+                // this indicates invocation from an unexpected location (attempt at sync load?)
+                Debug.Assert(_isSlicing);
+                return !HasFrameBudget;
+            }
+        }
+
+        internal static bool HasWork
+        {
+            get
+            {
+                return _slicerQueue.Count > 0 || !_postedAllocActions.IsEmpty || !_postedCleanupActions.IsEmpty;
+            }
+        }
+
+        // NOTE: This should only be called as a failsafe after all Slices are expected to be cancelled, ie: shutdown
+        internal static void CancelAll()
+        {
+            OvrAvatarLog.AssertStaticBuilder(_slicerQueue.Count == 0, Debug_SlicerQueue, logScope);
+            Debug.Assert(!HasWork);
+
+            while (_postedCleanupActions.TryDequeue(out var result))
+            {
+                result();
+            }
+            while (_postedAllocActions.TryDequeue(out var ignore)) ;
+            _slicerQueue.Clear();
+        }
+
+        internal static string Debug_SlicerQueue()
+        {
+            string buildStr = string.Empty;
+            string sep = string.Empty;
+            foreach (var slicer in _slicerQueue)
+            {
+                buildStr += sep + slicer.ToString();
+                sep = ", ";
+            }
+            return buildStr;
+        }
+
+        #endregion // Public Helper Methods
+
+        //:: Private Helpers
+        #region Private Helper Methods
+        private static bool StopEnumerator(IEnumerator<SliceStep> enumerator)
+        {
+            // Stopping Slicer is invalid during slice, return Cancel instead
+            OvrAvatarLog.AssertParam(!_isSlicing, in enumerator, _StopEnumeratorSliceAssertBuilder, logScope);
+            bool didRemove = _slicerQueue.Remove(enumerator);
+            OvrAvatarLog.AssertParam(didRemove, in enumerator, _CancelNotFoundAssertBuilder, logScope);
+            return didRemove;
+        }
+        private static bool IsEnumeratorQueued(IEnumerator<SliceStep> enumerator)
+        {
+            // Checking queue status during slice only leads to other bad habits
+            OvrAvatarLog.AssertParam(!_isSlicing, in enumerator, _IsQueuedSliceAssertBuilder, logScope);
+            return _slicerQueue.Contains(enumerator);
+        }
+
+        private static string _StopEnumeratorSliceAssertBuilder(in IEnumerator<SliceStep> enumer)
+            => $"StopEnumerator {enumer} while slicing";
+        private static string _CancelNotFoundAssertBuilder(in IEnumerator<SliceStep> enumer)
+            => $"Cancelled slicer {enumer} which is not running";
+        private static string _IsQueuedSliceAssertBuilder(in IEnumerator<SliceStep> enumer)
+            => $"IsEnumeratorQueued {enumer} called while slicing";
+
+        private static bool HasFrameBudget => _watch.ElapsedMilliseconds < (Int64)_minWorkPerFrameMS;
+
+        private static void StartNewFrame()
+        {
+            _watch.Reset();
+        }
+
+        private static void RunNow(Action op)
+        {
+            Debug.Assert(!_isSlicing);
+            _watch.Start();
+            op();
+            _watch.Stop();
+        }
+
+        // Check for and resolve posted actions in Update,
+        // also, handle scene change edge cases to ensure no tasks are dropped
+        internal static void InternalUpdate()
+        {
+            if (HasFrameBudget)
+            {
+                _watch.Start();
+                RunWork();
+                _watch.Stop();
+            }
+            StartNewFrame();
+        }
+
+        private static void RunWork()
+        {
+            // Cleanup actions should reduce memory usage
+            while (_postedCleanupActions.TryDequeue(out var cleanupAction))
+            {
+                cleanupAction.Invoke();
+                if (!HasFrameBudget) { return; }
+            }
+
+            _isSlicing = true;
+            bool hasBudget = RunSlices();
+            _isSlicing = false;
+
+            if (hasBudget)
+            {
+                // Alloc actions will increase memory usage and spawn additional slices
+                while (_postedAllocActions.TryDequeue(out var allocAction))
+                {
+                    allocAction.Invoke();
+                    if (!HasFrameBudget) { return; }
+                }
+            }
+        }
+
+        // Run sliced work
+        private static bool RunSlices()
+        {
+            var currentIndex = 0;
+            var stopIndex = _slicerQueue.Count;
+            // Process existing slices
+            while (currentIndex < stopIndex)
+            {
+                Debug.Assert(stopIndex <= _slicerQueue.Count);
+                if (!CutSlice(ref currentIndex, ref stopIndex) || !HasFrameBudget) { return false; }
+            }
+            return true;
+        }
+
+        // Coroutine host which actually executes slicing
+        private static bool CutSlice(ref int index, ref int stopIndex)
+        {
+            bool continueWorking = true;
+            var sliceTask = _slicerQueue[index];
+
+#if OVR_TIME_SLICER_CHECK_SLICE_TIME
+        _sliceCheck.Reset();
+        _sliceCheck.Start();
+#endif
+            var step = sliceTask.MoveNext() ? sliceTask.Current : SliceStep.Cancel;
+#if OVR_TIME_SLICER_CHECK_SLICE_TIME
+        _sliceCheck.Stop();
+        if (_sliceCheck.ElapsedMilliseconds > _minWorkPerFrameMS)
+        {
+            OvrAvatarLog.LogWarning($"Slice from {sliceTask} was overbudget ({_sliceCheck.ElapsedMilliseconds} > {_minWorkPerFrameMS})", logScope, this);
+
+            if (step != SliceStep.Cancel)
+            {
+                // DEBUG: Run next slice to step into and see where the task is
+                step = sliceTask.MoveNext() ? sliceTask.Current : SliceStep.Cancel;
+            }
+        }
+#endif
+
+            // Queue order should not change during sliced work
+            Debug.Assert(_slicerQueue[index] == sliceTask && _slicerQueue.IndexOf(sliceTask) == index);
+            switch (step)
+            {
+                case SliceStep.Continue:
+                    {
+                        // Continue working on this slice - generally this is discouraged but handy when deferring to helper methods
+                    }
+                    break;
+
+                case SliceStep.Hold:
+                    {
+                        // Stop processing slices this frame disregarding budget - slice has detected we are out of budget
+                        continueWorking = false;
+                    }
+                    break;
+
+                case SliceStep.Wait:
+                    {
+                        // Continue executing additional slices, time permitting
+                        index++;
+                    }
+                    break;
+
+                case SliceStep.Delay:
+                    {
+                        // Undefined delay - move to end of queue, stop working if last task
+                        stopIndex--;
+                        _slicerQueue.RemoveAt(index);
+                        _slicerQueue.Add(sliceTask);
+                    }
+                    break;
+
+                case SliceStep.Stall:
+                    {
+                        // Hold slicing, move to front of queue to be first next frame presumably filling it
+                        continueWorking = false;
+                        // Shift to front of queue
+                        var shifter = sliceTask;
+                        for (int idx = 0; idx <= index; ++idx)
+                        {
+                            var nextShifter = _slicerQueue[idx];
+                            _slicerQueue[idx] = shifter;
+                            shifter = nextShifter;
+                        }
+                    }
+                    break;
+
+                case SliceStep.Cancel:
+                    {
+                        stopIndex--;
+                        _slicerQueue.RemoveAt(index);
+                    }
+                    break;
+
+                default:
+                    throw new ArgumentOutOfRangeException($"OvrTime.SliceStep value {step} unexpected");
+            }
+            return continueWorking;
+        }
+    }
+    #endregion // Private Helper Methods
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Common/OvrTime.cs.meta b/Assets/Oculus/Avatar2/Scripts/Common/OvrTime.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..18e73eba33480320215aacfb81ac8009dc73f231
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Common/OvrTime.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 356ebd869e403094ab4fb5909ff1e425
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Config.meta b/Assets/Oculus/Avatar2/Scripts/Config.meta
new file mode 100644
index 0000000000000000000000000000000000000000..d7d096c292831f1380f670b97557b9f348657239
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Config.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 66c597a21725b824eba4adff3021c387
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Config/OvrConfigTypes.cs b/Assets/Oculus/Avatar2/Scripts/Config/OvrConfigTypes.cs
new file mode 100644
index 0000000000000000000000000000000000000000..7447f3abce75ceae489267d442b0046373ccd75c
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Config/OvrConfigTypes.cs
@@ -0,0 +1,18 @@
+namespace Oculus.Avatar2
+{
+    public enum DefaultableBool : sbyte
+    {
+        Off = 0,
+        On = 1,
+        Default = -1,
+    }
+
+    public static class DefaultableExtensions
+    {
+        public static bool GetValue(this DefaultableBool defaultableBool, bool defaultValue = false)
+        {
+            return (defaultableBool == DefaultableBool.On) ||
+                   (defaultableBool == DefaultableBool.Default && defaultValue);
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Config/OvrConfigTypes.cs.meta b/Assets/Oculus/Avatar2/Scripts/Config/OvrConfigTypes.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..bf8e024c955e4dc49dd445fc8c5c0fbb9dad0af2
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Config/OvrConfigTypes.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 2fd0e108189bc37409bc344ece9a49dd
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Custom Hand Poses.meta b/Assets/Oculus/Avatar2/Scripts/Custom Hand Poses.meta
new file mode 100644
index 0000000000000000000000000000000000000000..8b450f4328b284d0371850fd85ae54c5a940c24a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Custom Hand Poses.meta	
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: cbf3e757ab9def047aa0fe8e77dbd3f6
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Custom Hand Poses/OvrAvatarCustomHandPose.cs b/Assets/Oculus/Avatar2/Scripts/Custom Hand Poses/OvrAvatarCustomHandPose.cs
new file mode 100644
index 0000000000000000000000000000000000000000..0d7d1c54e9eb066a0cd71e493c311141ae3ba744
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Custom Hand Poses/OvrAvatarCustomHandPose.cs	
@@ -0,0 +1,324 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+
+using Unity.Collections;
+using Unity.Collections.LowLevel.Unsafe;
+
+using UnityEngine;
+
+using HandJointType = OvrAvatarHandJointType.HandJointType;
+
+namespace Oculus.Avatar2
+{
+    [RequireComponent(typeof(OvrAvatarEntity))]
+    public class OvrAvatarCustomHandPose : MonoBehaviour
+    {
+        private const string logscope = "customHandPose";
+        private readonly Quaternion _leftDefaultRotation = Quaternion.Euler(0, 270, 90);
+        private readonly Quaternion _rightDefaultRotation = Quaternion.Euler(0, 90, 90);
+
+        private struct JointTransform
+        {
+            public CAPI.ovrAvatar2JointType type;
+            public Transform transform;
+            public int parentIndex;
+        }
+
+        [SerializeField]
+        [Tooltip("Which avatar hand is controlled by this component")]
+        private CAPI.ovrAvatar2Side _side = CAPI.ovrAvatar2Side.Left;
+
+        [SerializeField]
+        [Tooltip("The hand skeleton to use, kept in it's default pose")]
+        private GameObject _handSkeleton = null;
+
+        [SerializeField]
+        [Tooltip("The hand skeleton's forward vector in local space")]
+        private Vector3 _handSkelLocalForward = new Vector3(0, 0, 1);
+
+        [SerializeField]
+        [Tooltip("The instance of the hand skeleton that will be used to set the custom hand pose\nIf null, handSkeleton will be instantiated")]
+        public GameObject _handPose = null;
+
+        [SerializeField]
+        public Transform _wristOffset = null;
+
+        public bool setHandPose = true;
+        public bool setWristOffset = true;
+
+        [SerializeField]
+        [HideInInspector]
+        private OvrAvatarEntity _entity;
+
+        private (CAPI.ovrAvatar2Bone[] bones, CAPI.ovrAvatar2Transform[] pose) _skeletonData;
+        private List<JointTransform> _poseJointTransforms = new List<JointTransform>();
+        private int _wristIndex;
+
+        private bool _skeletonIsSet = false;
+        private bool _initialized = false;
+
+        private bool _entityPreviouslyReady = false;
+
+#if UNITY_EDITOR
+        protected virtual void OnValidate()
+        {
+            _entity = GetComponent<OvrAvatarEntity>();
+        }
+#endif
+
+        protected virtual void OnEnable()
+        {
+            Initialize();
+        }
+
+        protected virtual void OnDisable()
+        {
+            ClearHandPose();
+        }
+
+        protected virtual void Update()
+        {
+            if (setHandPose) { UpdateHandPose(); }
+            if (setWristOffset) { UpdateWristOffset(); }
+        }
+
+        public void Initialize()
+        {
+            if (_initialized) return;
+
+            OvrAvatarLog.AssertConstMessage(_entity, "No associated entity", logscope, this);
+            _skeletonData = GetSkeletonData();
+
+            if (_handPose == null)
+            {
+                _handPose = Instantiate(_handSkeleton);
+            }
+
+            _poseJointTransforms = GetJointTransforms(_handPose, out _wristIndex);
+            _initialized = true;
+        }
+
+        public void SetHandSkeleton()
+        {
+            Initialize();
+
+            // Wait until the entity is loaded with an anim hierarchy
+            if (!EntityIsReady()) return;
+
+            var skelPoseHandle = GCHandle.Alloc(_skeletonData.pose, GCHandleType.Pinned);
+            var skelBonesHandle = GCHandle.Alloc(_skeletonData.bones, GCHandleType.Pinned);
+
+            try
+            {
+                unsafe
+                {
+                    CAPI.ovrAvatar2TrackingBodyPose cSkelPose = new CAPI.ovrAvatar2TrackingBodyPose(
+                        (CAPI.ovrAvatar2Transform*)skelPoseHandle.AddrOfPinnedObject(), (uint)_skeletonData.pose.Length,
+                        CAPI.ovrAvatar2Space.Local);
+
+                    CAPI.ovrAvatar2TrackingBodySkeleton cSkel = new CAPI.ovrAvatar2TrackingBodySkeleton(
+                        (CAPI.ovrAvatar2Bone*)skelBonesHandle.AddrOfPinnedObject(), (uint)_skeletonData.bones.Length,
+                        cSkelPose);
+                    cSkel.forwardDir = _handSkeleton.transform.TransformDirection(_handSkelLocalForward);
+                    cSkel.forwardDir = cSkel.forwardDir.ConvertSpace();
+
+                    if (_entity.SetCustomHandSkeleton(_side, in cSkel))
+                    {
+                        _skeletonIsSet = true;
+                    }
+                }
+            }
+            finally
+            {
+                skelPoseHandle.Free();
+                skelBonesHandle.Free();
+            }
+        }
+
+        // Can be called directly to update to a static hand pose even when component is disabled
+        public void UpdateHandPose()
+        {
+            // Wait until the entity is loaded with an anim hierarchy
+            if (!EntityIsReady()) { return; }
+            if (!_skeletonIsSet)
+            {
+                SetHandSkeleton();
+                if (!_skeletonIsSet) { return; }
+            }
+
+            // Hand pose
+            var poseTransforms = new NativeArray<CAPI.ovrAvatar2Transform>(_poseJointTransforms.Count, Allocator.Temp,
+                NativeArrayOptions.UninitializedMemory);
+            GetPoseTransformsNative(_poseJointTransforms, poseTransforms);
+
+            try
+            {
+                unsafe
+                {
+                    var cPose = new CAPI.ovrAvatar2TrackingBodyPose(
+                        (CAPI.ovrAvatar2Transform*)poseTransforms.GetUnsafePtr(), (uint)poseTransforms.Length,
+                        CAPI.ovrAvatar2Space.Local);
+
+                    _entity.SetCustomHandPose(_side, in cPose);
+                }
+            }
+            finally
+            {
+                poseTransforms.Dispose();
+            }
+        }
+
+        public void UpdateWristOffset()
+        {
+            if (!EntityIsReady()) return;
+
+            // Wrist offset
+            if (_wristOffset != null && _wristIndex >= 0)
+            {
+                var wristX = _poseJointTransforms[_wristIndex].transform;
+
+                CAPI.ovrAvatar2Vector3f position = wristX.InverseTransformPoint(_wristOffset.position);
+                position.x = -position.x;
+                CAPI.ovrAvatar2Quatf rotation = Quaternion.Inverse(wristX.rotation) * _wristOffset.rotation;
+
+                var xform = new CAPI.ovrAvatar2Transform(position, rotation);
+                _entity.SetCustomWristOffset(_side, in xform);
+            }
+        }
+
+        public void ClearHandPose()
+        {
+            if (_entity && _entity.IsCreated)
+            {
+                _entity.ClearCustomHandPose(_side);
+            }
+        }
+
+        private bool EntityIsReady()
+        {
+            bool isReady = _entity && (_entity.CurrentState == OvrAvatarEntity.AvatarState.DefaultAvatar
+                                       || _entity.CurrentState == OvrAvatarEntity.AvatarState.FastLoad
+                                       || _entity.CurrentState == OvrAvatarEntity.AvatarState.UserAvatar);
+
+            if (!isReady && _entityPreviouslyReady)
+            {
+                ClearHandPose();
+            }
+
+            _entityPreviouslyReady = isReady;
+            return isReady;
+        }
+
+        // Take list of joint transforms from hand and convert to native space ovrAvatar2Transforms
+        private static void GetPoseTransformsNative(List<JointTransform> joints, NativeArray<CAPI.ovrAvatar2Transform> xforms)
+        {
+            OvrAvatarLog.AssertConstMessage(joints.Count == xforms.Length, "Joint array and native array mismatch",
+                logscope);
+            for (var i = 0; i < joints.Count; i++)
+            {
+                // TODO: Handle `OVR_AVATAR_ENABLE_CLIENT_XFORM` in `ConvertSpace()`?
+#if OVR_AVATAR_ENABLE_CLIENT_XFORM
+                xforms[i] = (CAPI.ovrAvatar2Transform)joints[i].transform;
+#else
+                xforms[i] = joints[i].transform.ConvertSpace();
+#endif
+            }
+
+        }
+
+        // Take list of joint transforms from hand and convert to native space ovrAvatar2Transforms
+        private static CAPI.ovrAvatar2Transform[] GetPoseTransforms(List<JointTransform> joints)
+        {
+            var xforms = new CAPI.ovrAvatar2Transform[joints.Count];
+
+            for (var i = 0; i < joints.Count; i++)
+            {
+                // TODO: Handle `OVR_AVATAR_ENABLE_CLIENT_XFORM` in `ConvertSpace()`?
+#if OVR_AVATAR_ENABLE_CLIENT_XFORM
+                xforms[i] = (CAPI.ovrAvatar2Transform)joints[i].transform;
+#else
+                xforms[i] = joints[i].transform.ConvertSpace();
+#endif
+            }
+
+            return xforms;
+        }
+
+        private (CAPI.ovrAvatar2Bone[], CAPI.ovrAvatar2Transform[]) GetSkeletonData()
+        {
+            var jointTransforms = GetJointTransforms(_handSkeleton);
+
+            var bones = new CAPI.ovrAvatar2Bone[jointTransforms.Count];
+            for (var i = 0; i < jointTransforms.Count; i++)
+            {
+                bones[i] = new CAPI.ovrAvatar2Bone
+                {
+                    boneId = jointTransforms[i].type,
+                    parentBoneIndex = (short)jointTransforms[i].parentIndex
+                };
+            }
+            return (bones, GetPoseTransforms(jointTransforms));
+        }
+
+        // Get ordered list of joints from the hand
+        private List<JointTransform> GetJointTransforms(GameObject gob)
+        {
+            return GetJointTransforms(gob, out int wristIndexIgnored);
+        }
+
+        private List<JointTransform> GetJointTransforms(GameObject gob, out int wristIndex)
+        {
+            wristIndex = -1;
+
+            var joints = new List<JointTransform>();
+            var transformToIndex = new Dictionary<Transform, int>();
+            foreach (var xform in gob.GetComponentsInChildren<Transform>())
+            {
+                int parentIndex = -1;
+                if (xform.parent && !transformToIndex.TryGetValue(xform.parent, out parentIndex))
+                {
+                    parentIndex = -1;
+                }
+
+                var typeComponent = xform.GetComponent<OvrAvatarHandJointType>();
+                var type = typeComponent ? typeComponent.jointType : HandJointType.Invalid;
+                if (type == HandJointType.Wrist) { wristIndex = joints.Count; }
+
+                joints.Add(new JointTransform
+                {
+                    type = JointTypeFromHandType(type, _side),
+                    transform = xform,
+                    parentIndex = parentIndex
+                });
+
+                transformToIndex.Add(xform, joints.Count - 1);
+            }
+
+            return joints;
+        }
+
+        private static CAPI.ovrAvatar2JointType JointTypeFromHandType(
+            HandJointType hand, CAPI.ovrAvatar2Side side)
+        {
+            if (hand == HandJointType.Invalid)
+            {
+                return CAPI.ovrAvatar2JointType.Invalid;
+            }
+
+            if (hand == HandJointType.Wrist)
+            {
+                return side == CAPI.ovrAvatar2Side.Left
+                    ? CAPI.ovrAvatar2JointType.LeftHandWrist
+                    : CAPI.ovrAvatar2JointType.RightHandWrist;
+            }
+
+            var handBase = side == CAPI.ovrAvatar2Side.Left
+                ? CAPI.ovrAvatar2JointType.LeftHandThumbTrapezium
+                : CAPI.ovrAvatar2JointType.RightHandThumbTrapezium;
+
+            return handBase + (int)hand - 1;
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Custom Hand Poses/OvrAvatarCustomHandPose.cs.meta b/Assets/Oculus/Avatar2/Scripts/Custom Hand Poses/OvrAvatarCustomHandPose.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..dfe57be164f5d6f685536e66652382240e46487b
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Custom Hand Poses/OvrAvatarCustomHandPose.cs.meta	
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 32f3cd2ab93d875418350b1b6f51d33f
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Custom Hand Poses/OvrAvatarHandJointType.cs b/Assets/Oculus/Avatar2/Scripts/Custom Hand Poses/OvrAvatarHandJointType.cs
new file mode 100644
index 0000000000000000000000000000000000000000..2fdfe43569e65c76bf8f4e5e51c416ed1c1861f4
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Custom Hand Poses/OvrAvatarHandJointType.cs	
@@ -0,0 +1,38 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using Oculus.Avatar2;
+using UnityEngine;
+
+public class OvrAvatarHandJointType : MonoBehaviour
+{
+    public enum HandJointType : Int32 {
+        Invalid = -1,
+            
+        Wrist = 0,
+        ThumbTrapezium,
+        ThumbMeta,
+        ThumbProximal,
+        ThumbDistal,
+        IndexMeta,
+        IndexProximal,
+        IndexIntermediate,
+        IndexDistal,
+        MiddleMeta,
+        MiddleProximal,
+        MiddleIntermediate,
+        MiddleDistal,
+        RingMeta,
+        RingProximal,
+        RingIntermediate,
+        RingDistal,
+        PinkyMeta,
+        PinkyProximal,
+        PinkyIntermediate,
+        PinkyDistal,
+
+        Count,
+    }
+    
+    public HandJointType jointType;
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Custom Hand Poses/OvrAvatarHandJointType.cs.meta b/Assets/Oculus/Avatar2/Scripts/Custom Hand Poses/OvrAvatarHandJointType.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..7fc76fa3fa6342038054004b6dd2166465a636df
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Custom Hand Poses/OvrAvatarHandJointType.cs.meta	
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 04a0706dc00d22946b853246bd3f1e88
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/IOvrAvatarNativeBodyTracking.cs b/Assets/Oculus/Avatar2/Scripts/IOvrAvatarNativeBodyTracking.cs
new file mode 100644
index 0000000000000000000000000000000000000000..9f710ba3dbc85c8e5474766feedae98c9ec41dce
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/IOvrAvatarNativeBodyTracking.cs
@@ -0,0 +1,10 @@
+namespace Oculus.Avatar2
+{
+    /// <summary>
+    /// An implementation of body tracking can implement this interface to reduce the marshaling overhead.
+    /// </summary>
+    internal interface IOvrAvatarNativeBodyTracking
+    {
+        CAPI.ovrAvatar2TrackingDataContextNative NativeDataContext { get; }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/IOvrAvatarNativeBodyTracking.cs.meta b/Assets/Oculus/Avatar2/Scripts/IOvrAvatarNativeBodyTracking.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..e5505e2270b4058c6cc18714d014cd45710c451f
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/IOvrAvatarNativeBodyTracking.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 331a12b6dc65712488e2487b2757a51f
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/IOvrAvatarNativeEyePose.cs b/Assets/Oculus/Avatar2/Scripts/IOvrAvatarNativeEyePose.cs
new file mode 100644
index 0000000000000000000000000000000000000000..3709a4892544ba5b36bae9b4e8275866f74a93d6
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/IOvrAvatarNativeEyePose.cs
@@ -0,0 +1,10 @@
+namespace Oculus.Avatar2
+{
+    /// <summary>
+    /// An implementation of eye pose can implement this interface to reduce the marshaling overhead.
+    /// </summary>
+    internal interface IOvrAvatarNativeEyePose
+    {
+        CAPI.ovrAvatar2EyePoseProviderNative NativeProvider { get; }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/IOvrAvatarNativeEyePose.cs.meta b/Assets/Oculus/Avatar2/Scripts/IOvrAvatarNativeEyePose.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..ad0f7155407d383a75bbbb9127ce66e52ded3bd8
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/IOvrAvatarNativeEyePose.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 1bd6b1f216c9b634baa493b67c3be320
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/IOvrAvatarNativeFacePose.cs b/Assets/Oculus/Avatar2/Scripts/IOvrAvatarNativeFacePose.cs
new file mode 100644
index 0000000000000000000000000000000000000000..639abcc53b39deeafec99d8b8763c3558dc06521
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/IOvrAvatarNativeFacePose.cs
@@ -0,0 +1,10 @@
+namespace Oculus.Avatar2
+{
+    /// <summary>
+    /// An implementation of face tracking can implement this interface to reduce the marshaling overhead.
+    /// </summary>
+    internal interface IOvrAvatarNativeFacePose
+    {
+        CAPI.ovrAvatar2FacePoseProviderNative NativeProvider { get; }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/IOvrAvatarNativeFacePose.cs.meta b/Assets/Oculus/Avatar2/Scripts/IOvrAvatarNativeFacePose.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..1d7d57fc264fb910440ac8edd2f5fdb01ae3492b
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/IOvrAvatarNativeFacePose.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 9960f9d313d9a7044a63cf88178747d5
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/IOvrAvatarNativeHandDelegate.cs b/Assets/Oculus/Avatar2/Scripts/IOvrAvatarNativeHandDelegate.cs
new file mode 100644
index 0000000000000000000000000000000000000000..02569299ffd3bbf226e6a751fc89cabc9212e191
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/IOvrAvatarNativeHandDelegate.cs
@@ -0,0 +1,10 @@
+namespace Oculus.Avatar2
+{
+    /// <summary>
+    /// An implementation of hand tracking can implement this interface to reduce the marshaling overhead.
+    /// </summary>
+    internal interface IOvrAvatarNativeHandDelegate
+    {
+        CAPI.ovrAvatar2HandTrackingDataContextNative NativeContext { get; }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/IOvrAvatarNativeHandDelegate.cs.meta b/Assets/Oculus/Avatar2/Scripts/IOvrAvatarNativeHandDelegate.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..c088bb99505ea110bfeb78237f6755cd7f65166c
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/IOvrAvatarNativeHandDelegate.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 3df024e41b2f4cd39b4b908f7de44ce3
+timeCreated: 1606871294
\ No newline at end of file
diff --git a/Assets/Oculus/Avatar2/Scripts/Interfaces.meta b/Assets/Oculus/Avatar2/Scripts/Interfaces.meta
new file mode 100644
index 0000000000000000000000000000000000000000..3848a7d13cfa65ab1ba560f4c50e747c245230f5
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Interfaces.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 4e93bedb7468ab6489fdfd4629e6c808
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Interfaces/IOvrAvatarHandTrackingDelegate.cs b/Assets/Oculus/Avatar2/Scripts/Interfaces/IOvrAvatarHandTrackingDelegate.cs
new file mode 100644
index 0000000000000000000000000000000000000000..702764da7ff851aaaab1844d228892b78028a9bb
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Interfaces/IOvrAvatarHandTrackingDelegate.cs
@@ -0,0 +1,10 @@
+namespace Oculus.Avatar2
+{
+    /// <summary>
+    /// Interface which allows the OvrAvatarBodyTrackingContext to read hand tracking data from clients.
+    /// </summary>
+    public interface IOvrAvatarHandTrackingDelegate
+    {
+        bool GetHandData(OvrAvatarTrackingHandsState handData);
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Interfaces/IOvrAvatarHandTrackingDelegate.cs.meta b/Assets/Oculus/Avatar2/Scripts/Interfaces/IOvrAvatarHandTrackingDelegate.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..ef8b864ca4ef6fd1511f832674409b40f6f93b4f
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Interfaces/IOvrAvatarHandTrackingDelegate.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 0e08cb2f9eee8634cafb9647ac5ef124
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Interfaces/IOvrAvatarInputControlDelegate.cs b/Assets/Oculus/Avatar2/Scripts/Interfaces/IOvrAvatarInputControlDelegate.cs
new file mode 100644
index 0000000000000000000000000000000000000000..7e1d2d8c570d8b520ee229e73278f4707ea3e2c5
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Interfaces/IOvrAvatarInputControlDelegate.cs
@@ -0,0 +1,10 @@
+namespace Oculus.Avatar2
+{
+    /// <summary>
+    /// Interface which allows the OvrAvatarBodyTrackingContext to read input control data from clients.
+    /// </summary>
+    public interface IOvrAvatarInputControlDelegate
+    {
+        bool GetInputControlState(out OvrAvatarInputControlState inputControlState);
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Interfaces/IOvrAvatarInputControlDelegate.cs.meta b/Assets/Oculus/Avatar2/Scripts/Interfaces/IOvrAvatarInputControlDelegate.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..b610d2b5ed50cfe6db675877913fd2ce6e17b4bf
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Interfaces/IOvrAvatarInputControlDelegate.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 75ee3ffed4604c14f990bcc234daa53a
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Interfaces/IOvrAvatarInputTrackingDelegate.cs b/Assets/Oculus/Avatar2/Scripts/Interfaces/IOvrAvatarInputTrackingDelegate.cs
new file mode 100644
index 0000000000000000000000000000000000000000..d1f7c148383bb3c8e5918ac6e35eac10acb1506e
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Interfaces/IOvrAvatarInputTrackingDelegate.cs
@@ -0,0 +1,10 @@
+namespace Oculus.Avatar2
+{
+    /// <summary>
+    /// Interface which allows the OvrAvatarBodyTrackingContext to read input tracking data from clients.
+    /// </summary>
+    public interface IOvrAvatarInputTrackingDelegate
+    {
+        bool GetInputTrackingState(out OvrAvatarInputTrackingState inputTrackingState);
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Interfaces/IOvrAvatarInputTrackingDelegate.cs.meta b/Assets/Oculus/Avatar2/Scripts/Interfaces/IOvrAvatarInputTrackingDelegate.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..173794c5e4b2363bb1cc2af918ceda122b6c3412
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Interfaces/IOvrAvatarInputTrackingDelegate.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 15f572e349a760b40ada55f6fbea376d
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/LOD.meta b/Assets/Oculus/Avatar2/Scripts/LOD.meta
new file mode 100644
index 0000000000000000000000000000000000000000..dc2fcaa47c714ca5cc0f62cfc0047193a7fc80e8
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/LOD.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: c3af908839e3e9740bb77b3ea6ecbe27
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLOD.cs b/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLOD.cs
new file mode 100644
index 0000000000000000000000000000000000000000..b3339971d9d70fc09cad9eb642d08fcf90a8a3b0
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLOD.cs
@@ -0,0 +1,572 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+
+using UnityEngine;
+using UnityEngine.Profiling;
+
+using CultureInfo = System.Globalization.CultureInfo;
+using Debug = UnityEngine.Debug;
+
+namespace Oculus.Avatar2
+{
+    /**
+     * Per-avatar LOD information.
+     * This component is added to every avatar managed by the LOD manager.
+     * It is informational and not intended to be changed by the application.
+     * @see OvrAvatarLODManager
+     */
+    public sealed class AvatarLOD : MonoBehaviour
+    {
+        private const string logScope = "AvatarLOD";
+
+        /// Cached transform for this AvatarLOD
+        public Transform CachedTransform { get; private set; } = null;
+
+        /// The entity whose LOD is managed by this instance
+        public OvrAvatarEntity Entity { get; internal set; } = null;
+
+        // Whether the entity associated with this AvatarLOD is non-null and active
+        public bool EntityActive => !(Entity is null) && Entity.isActiveAndEnabled;
+
+        /// True if the avatar has been culled by the LOD manager.
+        public bool culled { get; private set; }
+
+        /// If enabled, the overrideLevel value will be used instead of the calculated LOD.
+        public bool overrideLOD = false;
+
+        private bool _prevOverrideLOD = false;
+
+        /// Desired level of detail for this avatar.
+        public int overrideLevel
+        {
+            get => Mathf.Clamp(_overrideLevel, -1, maxLodLevel);
+            set => _overrideLevel = value;
+        }
+
+        // TODO: Initialize to `int.MinValue`?
+        private int _overrideLevel = default;
+        private int _prevOverrideLevel = default;
+
+        /// Transform on the avatar center joint.
+        public Transform centerXform;
+
+        public readonly List<Transform> extraXforms = new List<Transform>();
+        private readonly List<AvatarLODGroup> _lodGroups = new List<AvatarLODGroup>();
+
+        /// Vertex counts for each level of detail for this avatar.
+        public readonly List<int> vertexCounts = new List<int>();
+
+        /// Triangle counts for each level of detail for this avatar.
+        public readonly List<int> triangleCounts = new List<int>();
+
+        private int _minLodLevel = -1;
+
+        /// Minimum LOD level loaded for this avatar.
+        public int minLodLevel => _minLodLevel;
+
+        private int _maxLodLevel = -1;
+
+        /// Maximum LOD level loaded for this avatar.
+        public int maxLodLevel => _maxLodLevel;
+
+        /// Distance of avatar center joint from the LOD camera.
+        public float distance;
+
+        /// Screen percent occupied by the avatar (0 - 1).
+        public float screenPercent;
+
+        /// LOD level calculated based on screen percentage (before dynamic processing).
+        public int wantedLevel;
+
+        /// LOD level calculated after dynamic processing.
+        public int dynamicLevel;
+
+        ///
+        /// Importance of avatar for display purposes (geometric LOD).
+        /// This is from a logarithmic function by OvrAvatarLODManager.
+        /// @see OvrAvatarLODManager.dynamicLodWantedLogScale
+        ///
+        public float lodImportance;
+
+        ///
+        /// Importance of avatar for update (animation) purposes.
+        /// This is from a logarithmic function by OvrAvatarLODManager.
+        /// @see OvrAvatarLODManager.screenPercentToUpdateImportanceCurvePower
+        ///  @see OvrAvatarLODManager.screenPercentToUpdateImportanceCurveMultiplier.
+        ///
+        public float updateImportance;
+
+        /// Network streaming fidelity for this avatar.
+        public OvrAvatarEntity.StreamLOD dynamicStreamLod;
+
+        /// Event invoked when the avatar's cull status has changed.
+        /// This event is also available from OvrAvatarLODManager.
+        /// @see OvrAvatarLODManager.CullChangedEvent
+        public Action<bool> CulledChangedEvent;
+
+        /// Event invoked when the avatar's cull status has changed.
+        /// This event is also available from OvrAvatarLODManager.
+        /// @see OvrAvatarLODManager.OnCullChangedEvent
+        public event Action<AvatarLOD, bool> OnCulledChangedEvent;
+
+        private bool forceDisabled_;
+
+        private int _level;
+        private int _prevLevel;
+
+        public int Level
+        {
+            get => _level;
+            set
+            {
+                if (value == _prevLevel) { return; }
+                _level = value;
+
+                if (!overrideLOD)
+                {
+                    UpdateLOD();
+                    UpdateDebugLabel();
+                }
+
+                _prevLevel = _level;
+            }
+        }
+
+        public UInt32 UpdateCost
+        {
+            get
+            {
+                // Clear
+                // ASSUMPTION: Not more than 31 lods, so using int as bitfields is sufficient
+                int levelsWithCost = 0;
+
+                // Check costs for all lod groups
+                foreach (var lodGroup in _lodGroups) { levelsWithCost |= lodGroup.LevelsWithAnimationUpdateCost; }
+
+                UInt32 cost = 0;
+                for (int i = minLodLevel; i <= maxLodLevel; i++)
+                {
+                    cost += ((levelsWithCost & (i << i)) != 0) ? (UInt32)vertexCounts[i] : 0;
+                }
+
+                return cost;
+            }
+        }
+
+        private void Awake()
+        {
+#if UNITY_EDITOR || UNITY_DEVELOPMENT
+            _avatarId = ++_avatarIdSource;
+#endif // UNITY_EDITOR || UNITY_DEVELOPMENT
+
+            CachedTransform = transform;
+        }
+
+        private void Start()
+        {
+            AvatarLODManager.Instance.AddLOD(this);
+            if (centerXform == null) { centerXform = CachedTransform; }
+        }
+
+        private void OnDestroy()
+        {
+            DestroyLODParentIfOnlyChild();
+            AvatarLODManager.RemoveLOD(this);
+        }
+
+        // Returns true upon a state transition
+        public bool SetCulled(bool nextCulled)
+        {
+            if (nextCulled == culled) { return false; }
+
+            culled = nextCulled;
+            CulledChangedEvent?.Invoke(culled);
+            OnCulledChangedEvent?.Invoke(this, culled);
+            return true;
+        }
+
+        private static List<AvatarLODParent> _parentsCache = null;
+
+        private bool HasValidLODParent()
+        {
+            CachedTransform.parent.GetComponents(_parentsCache ??= new List<AvatarLODParent>());
+
+            bool found = false;
+            foreach (var lodParent in _parentsCache)
+            {
+                if (!lodParent.beingDestroyed)
+                {
+                    found = true;
+                    break;
+                }
+            }
+            _parentsCache.Clear();
+            return found;
+        }
+
+        private void DestroyLODParentIfOnlyChild()
+        {
+            var cachedParent = CachedTransform.parent;
+            if (cachedParent != null)
+            {
+                cachedParent.gameObject.GetComponents(_parentsCache ??= new List<AvatarLODParent>());
+                foreach (var lodParent in _parentsCache) { lodParent.DestroyIfOnlyLODChild(this); }
+                _parentsCache.Clear();
+            }
+        }
+
+
+        private void OnBeforeTransformParentChanged()
+        {
+            DestroyLODParentIfOnlyChild();
+        }
+
+        private void OnTransformParentChanged()
+        {
+            var parentTx = CachedTransform.parent;
+            if (parentTx != null && !HasValidLODParent()) { parentTx.gameObject.AddComponent<AvatarLODParent>(); }
+
+            AvatarLODManager.ParentStateChanged(this);
+        }
+
+        // This behaviour is manually updated at a specific time during OvrAvatarManager::Update()
+        // to prevent issues with Unity script update ordering
+        internal void UpdateOverride()
+        {
+            if (!isActiveAndEnabled || forceDisabled_) { return; }
+
+            Profiler.BeginSample("AvatarLOD::UpdateOverride");
+
+            bool needsUpdateLod = (overrideLOD && overrideLevel != _prevOverrideLevel) ||
+                                  (overrideLOD != _prevOverrideLOD);
+
+            _prevOverrideLevel = overrideLevel;
+            _prevOverrideLOD = overrideLOD;
+
+            if (needsUpdateLod) { UpdateLOD(); }
+
+#if UNITY_EDITOR || UNITY_DEVELOPMENT
+            var needsDebugLabelUpdate = AvatarLODManager.Instance.debug.displayLODLabels ||
+                                        AvatarLODManager.Instance.debug.displayAgeLabels;
+
+            if (needsDebugLabelUpdate || needsUpdateLod) { UpdateDebugLabel(); }
+#endif // UNITY_EDITOR || UNITY_DEVELOPMENT
+
+            Profiler.EndSample();
+        }
+
+        private void UpdateLOD()
+        {
+            if (forceDisabled_) { return; }
+
+            if (_lodGroups != null && _lodGroups.Count > 0)
+            {
+                foreach (var lodGroup in _lodGroups) { lodGroup.Level = overrideLOD ? overrideLevel : Level; }
+            }
+        }
+
+        private void AddLODGroup(AvatarLODGroup group)
+        {
+            _lodGroups.Add(group);
+            group.parentLOD = this;
+            group.Level = overrideLOD ? overrideLevel : Level;
+        }
+
+        internal void RemoveLODGroup(AvatarLODGroup group)
+        {
+            _lodGroups.Remove(group);
+        }
+
+        internal void ClearLODGameObjects()
+        {
+            // Vertex counts will be reset by this function.
+            vertexCounts.Clear();
+            triangleCounts.Clear();
+
+            _minLodLevel = -1;
+            _maxLodLevel = -1;
+
+#if UNITY_EDITOR || UNITY_DEVELOPMENT
+            CAPI.ovrAvatar2LOD_UnregisterAvatar(_avatarId);
+#endif // UNITY_EDITOR || UNITY_DEVELOPMENT
+        }
+
+        public void AddLODGameObjectGroupByAvatarSkinnedMeshRenderers(GameObject parentGameObject, Dictionary<string, List<GameObject>> suffixToObj)
+        {
+            foreach (var kvp in suffixToObj)
+            {
+                AvatarLODSkinnableGroup gameObjectGroup = parentGameObject.GetOrAddComponent<AvatarLODSkinnableGroup>();
+                gameObjectGroup.GameObjects = kvp.Value.ToArray();
+                AddLODGroup(gameObjectGroup);
+            }
+        }
+
+        public void AddLODGameObjectGroupBySdkRenderers(Dictionary<int, OvrAvatarEntity.LodData> lodObjects)
+        {
+            // Vertex counts will be reset by this function.
+            vertexCounts.Clear();
+            triangleCounts.Clear();
+
+            if (lodObjects.Count > 0)
+            {
+                // first see what the limits could be...
+                _minLodLevel = int.MaxValue;
+                _maxLodLevel = int.MinValue;
+
+                foreach (var entry in lodObjects)
+                {
+                    int lodIndex = entry.Key;
+                    if (_minLodLevel > lodIndex) { _minLodLevel = lodIndex; }
+                    if (_maxLodLevel < lodIndex) { _maxLodLevel = lodIndex; }
+                }
+
+                OvrAvatarLog.LogVerbose($"Set lod range (min:{_minLodLevel}, max:{_maxLodLevel})", logScope, this);
+            }
+            else
+            {
+                OvrAvatarLog.LogError("No LOD data specified", logScope, this);
+
+                _maxLodLevel = _minLodLevel = -1;
+            }
+
+            // first find common parent and children;
+            if (maxLodLevel >= vertexCounts.Count || maxLodLevel >= triangleCounts.Count)
+            {
+                vertexCounts.Capacity = maxLodLevel;
+                triangleCounts.Capacity = maxLodLevel;
+                while (maxLodLevel >= vertexCounts.Count) { vertexCounts.Add(0); }
+                while (maxLodLevel >= triangleCounts.Count) { triangleCounts.Add(0); }
+            }
+
+            GameObject[] children = new GameObject[maxLodLevel + 1];
+            Transform commonParent = null;
+            for (int lodIdx = minLodLevel; lodIdx <= maxLodLevel; ++lodIdx)
+            {
+                if (lodObjects.TryGetValue(lodIdx, out var lodData))
+                {
+                    vertexCounts[lodIdx] = lodData.vertexCount;
+                    triangleCounts[lodIdx] = lodData.triangleCount;
+
+                    children[lodIdx] = lodData.gameObject;
+
+                    var localParentTx = lodData.transform.parent;
+
+                    OvrAvatarLog.AssertConstMessage(commonParent == null || commonParent == localParentTx
+                        , "Expected all lodObjects to have the same parent object.", logScope, this);
+
+                    commonParent = localParentTx;
+                }
+            }
+
+            if (commonParent != null)
+            {
+                var gameObjectGroup = commonParent.gameObject.GetOrAddComponent<AvatarLODSkinnableGroup>();
+                gameObjectGroup.GameObjects = children;
+                AddLODGroup(gameObjectGroup);
+            }
+
+#if UNITY_EDITOR || UNITY_DEVELOPMENT
+            // Register avatar with native runtime LOD scheme
+            // Temporary for LOD editing bring up
+            CAPI.ovrAvatar2LODRegistration reg;
+            reg.avatarId = _avatarId;
+            reg.lodWeights = vertexCounts.ToArray();
+            reg.lodThreshold = _maxLodLevel;
+
+            CAPI.ovrAvatar2LOD_RegisterAvatar(reg);
+#endif // UNITY_EDITOR || UNITY_DEVELOPMENT
+        }
+
+        public AvatarLODActionGroup AddLODActionGroup(GameObject go, Action[] actions)
+        {
+            var actionLODGroup = go.GetOrAddComponent<AvatarLODActionGroup>();
+            if (actions?.Length > 0)
+            {
+                actionLODGroup.Actions = new List<Action>(actions);
+            }
+            AddLODGroup(actionLODGroup);
+            return actionLODGroup;
+        }
+
+        public AvatarLODActionGroup AddLODActionGroup(GameObject go, Action action, int levels)
+        {
+            var actions = new Action[levels];
+            if (action != null)
+            {
+                for (int i = 0; i < levels; i++)
+                {
+                    actions[i] = action;
+                }
+            }
+
+            return AddLODActionGroup(go, actions);
+        }
+
+        // Find a valid LOD near the requested one
+        public int CalcAdjustedLod(int lod)
+        {
+            var adjustedLod = Mathf.Clamp(lod, minLodLevel, maxLodLevel);
+            if (adjustedLod != -1 && vertexCounts[adjustedLod] == 0)
+            {
+                adjustedLod = GetNextLod(lod);
+                if (adjustedLod == -1) { adjustedLod = GetPreviousLod(lod); }
+            }
+            return adjustedLod;
+        }
+
+        private int GetNextLod(int lod)
+        {
+            if (maxLodLevel >= 0)
+            {
+                for (int nextLod = lod + 1; nextLod <= maxLodLevel; ++nextLod)
+                {
+                    if (vertexCounts[nextLod] != 0) { return nextLod; }
+                }
+            }
+            return -1;
+        }
+
+        internal int GetPreviousLod(int lod)
+        {
+            if (minLodLevel >= 0)
+            {
+                for (int prevLod = lod - 1; prevLod >= minLodLevel; --prevLod)
+                {
+                    if (vertexCounts[prevLod] != 0) { return prevLod; }
+                }
+            }
+            return -1;
+        }
+
+        // Returns true when the entity is active and the LODs have been setup.
+        public bool AreLodsActive()
+        {
+            return EntityActive && minLodLevel >= 0 && maxLodLevel >= 0;
+        }
+
+        public void Reset()
+        {
+            ResetXforms();
+        }
+
+        private void ResetXforms()
+        {
+            centerXform = transform;
+            extraXforms.Clear();
+        }
+
+#if UNITY_EDITOR || UNITY_DEVELOPMENT
+        // AvatarLODManager.Initialize doesn't run for all the avatars added
+        // in LODScene so assign a unique id internally on construction.
+        private static Int32 _avatarIdSource = default;
+
+        // Temporary to bring up runtime LOD system
+        // Unique ID for this avatar
+        private Int32 _avatarId;
+
+        /// Clock time since last update (in seconds).
+        public float lastUpdateAgeSeconds;
+
+        /// Total maximum age during previous two updates (in seconds).
+        public float previousUpdateAgeWindowSeconds;
+
+        private GameObject _debugCanvas;
+#endif // UNITY_EDITOR || UNITY_DEVELOPMENT
+
+        [Conditional("UNITY_DEVELOPMENT")]
+        [Conditional("UNITY_EDITOR")]
+        internal void TrackUpdateAge(float deltaTime)
+        {
+#if UNITY_EDITOR || UNITY_DEVELOPMENT
+            // Track of the last update time for debug tools
+            if (EntityActive)
+            {
+                previousUpdateAgeWindowSeconds = lastUpdateAgeSeconds + deltaTime;
+                lastUpdateAgeSeconds = 0;
+            }
+            else { lastUpdateAgeSeconds += Time.deltaTime; }
+#endif // UNITY_EDITOR || UNITY_DEVELOPMENT
+        }
+
+        [Conditional("UNITY_DEVELOPMENT")]
+        [Conditional("UNITY_EDITOR")]
+        public void UpdateDebugLabel()
+        {
+#if UNITY_EDITOR || UNITY_DEVELOPMENT
+            if (AvatarLODManager.Instance.debug.displayLODLabels || AvatarLODManager.Instance.debug.displayAgeLabels)
+            {
+                if (_debugCanvas == null && AvatarLODManager.Instance.avatarLodDebugCanvas != null)
+                {
+                    GameObject
+                        canvasPrefab
+                            = AvatarLODManager.Instance
+                                .avatarLodDebugCanvas; //LoadAssetWithFullPath<GameObject>($"{AvatarPaths.ASSET_SOURCE_PATH}/LOD/Prefabs/AVATAR_LOD_DEBUG_CANVAS.prefab");
+                    if (canvasPrefab != null)
+                    {
+                        _debugCanvas = Instantiate(canvasPrefab, centerXform);
+
+                        // Set position instead of localPosition to keep the label in a steady readable location.
+                        _debugCanvas.transform.position = _debugCanvas.transform.parent.position +
+                                                          AvatarLODManager.Instance.debug.displayLODLabelOffset;
+
+                        _debugCanvas.SetActive(true);
+                    }
+                    else
+                    {
+                        OvrAvatarLog.LogWarning(
+                            "DebugLOD will require the avatarLodDebugCanvas prefab to be specified. This has a simple UI card that allows for world space display of LOD.");
+                    }
+                }
+
+                if (_debugCanvas != null)
+                {
+                    var text = _debugCanvas.GetComponentInChildren<UnityEngine.UI.Text>();
+
+                    // Set position instead of localPosition to keep the label in a steady readable location.
+                    _debugCanvas.transform.position = _debugCanvas.transform.parent.position +
+                                                      AvatarLODManager.Instance.debug.displayLODLabelOffset;
+
+                    if (AvatarLODManager.Instance.debug.displayLODLabels)
+                    {
+                        int actualLevel = overrideLOD ? overrideLevel : Level;
+                        text.color = actualLevel == -1 ? Color.gray : AvatarLODManager.LOD_COLORS[actualLevel];
+                        text.text = actualLevel.ToString();
+                        text.fontSize = 40;
+                    }
+
+                    if (AvatarLODManager.Instance.debug.displayAgeLabels)
+                    {
+                        text.text = previousUpdateAgeWindowSeconds.ToString(CultureInfo.InvariantCulture);
+                        text.color = new Color(
+                            Math.Max(Math.Min(-1.0f + 2.0f * previousUpdateAgeWindowSeconds, 1.0f), 0.0f)
+                            , Math.Max(
+                                Math.Min(
+                                    previousUpdateAgeWindowSeconds * 2.0f, 2.0f - 2.0f * previousUpdateAgeWindowSeconds)
+                                , 0f), Math.Max(Math.Min(1.0f - 2.0f * previousUpdateAgeWindowSeconds, 1.0f), 0.0f));
+                        text.fontSize = 10;
+                    }
+                }
+            }
+            else
+            {
+                if (_debugCanvas != null)
+                {
+                    _debugCanvas.SetActive(false);
+                    _debugCanvas = null;
+                }
+            }
+#endif // UNITY_EDITOR || UNITY_DEVELOPMENT
+        }
+
+        [Conditional("UNITY_DEVELOPMENT")]
+        [Conditional("UNITY_EDITOR")]
+        internal void ForceUpdateLOD<T>()
+        {
+            foreach (var lodGroup in _lodGroups)
+            {
+                if (lodGroup is T) { lodGroup.UpdateLODGroup(); }
+            }
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLOD.cs.meta b/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLOD.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..b791dd1fa7474835a0c10af621cc517e16f89f54
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLOD.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 3fc0eb3583821e34299e8422834043b4
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODActionGroup.cs b/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODActionGroup.cs
new file mode 100644
index 0000000000000000000000000000000000000000..9f924ed46a9febbe6096f8f719a6c51984153700
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODActionGroup.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Collections.Generic;
+
+
+namespace Oculus.Avatar2 {
+  public class AvatarLODActionGroup : AvatarLODGroup {
+    private List<Action> actions_ = new List<Action>();
+
+    public Action outOfRangeAction = null;
+
+    public List<Action> Actions {
+      //Lambdas
+      set {
+        this.actions_ = value;
+        count = actions_.Count;
+        ResetLODGroup();
+      }
+    }
+
+    public void AddAction(Action action) {
+      this.actions_.Add(action);
+      count = actions_.Count;
+      ResetLODGroup();
+    }
+
+    public override void ResetLODGroup() {
+      UpdateAdjustedLevel();
+      UpdateLODGroup();
+    }
+
+    public override void UpdateLODGroup() {
+      if (adjustedLevel_ == -1) {
+        outOfRangeAction?.Invoke();
+      } else if(adjustedLevel_ < actions_.Count) {
+        actions_[adjustedLevel_]?.Invoke();
+      }
+
+      prevLevel_ = Level;
+      prevAdjustedLevel_ = adjustedLevel_;
+    }
+  }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODActionGroup.cs.meta b/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODActionGroup.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..26240d7da3d27916b1758f9b10e3445f11eda165
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODActionGroup.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: d338013290c8230469968e5547016936
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODBehaviourStateGroup.cs b/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODBehaviourStateGroup.cs
new file mode 100644
index 0000000000000000000000000000000000000000..ab75840a31e091cf1f2131c84e05b5eaa236f853
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODBehaviourStateGroup.cs
@@ -0,0 +1,81 @@
+using UnityEngine;
+using System.Collections.Generic;
+
+
+namespace Oculus.Avatar2 {
+  public class AvatarLODBehaviourStateGroup : AvatarLODGroup {
+    AvatarLODBehaviourStateGroup() {
+      enabledStates_ = new List<bool>() {true};
+      lodBehaviours_ = new List<Behaviour>();
+    }
+
+    public bool outOfRangeState = false;
+
+    private List<Behaviour> lodBehaviours_ = null;
+
+    public List<Behaviour> LodBehaviours {
+      set {
+        lodBehaviours_ = value;
+        UpdateLODGroup();
+      }
+    }
+
+    public void AddBehaviour(Behaviour behavior) {
+      lodBehaviours_.Add(behavior);
+      UpdateLODGroup();
+    }
+
+
+    private List<bool> enabledStates_;
+
+    public List<bool> EnabledStates {
+      set {
+        this.enabledStates_ = value;
+        count = enabledStates_.Count;
+        ResetLODGroup();
+      }
+    }
+
+    public void AddEnabledState(bool state) {
+      enabledStates_.Add(state);
+      count = enabledStates_.Count;
+      ResetLODGroup();
+    }
+
+
+    public override void ResetLODGroup() {
+      for (int i = 0; i < lodBehaviours_.Count; i++)
+      {
+        if (lodBehaviours_[i] != null) 
+        {
+          lodBehaviours_[i].enabled = false;
+        }
+      }
+
+      UpdateAdjustedLevel();
+      UpdateLODGroup();
+    }
+
+    public override void UpdateLODGroup() {
+      if (adjustedLevel_ < enabledStates_.Count) {
+        for (int i = 0; i < lodBehaviours_.Count; i++) {
+          if (lodBehaviours_[i] == null)
+          {
+            continue;
+          }
+          if (adjustedLevel_ == -1) 
+          {
+            lodBehaviours_[i].enabled = outOfRangeState;
+          }
+          else
+          { 
+            lodBehaviours_[i].enabled = enabledStates_[adjustedLevel_];
+          }
+        }
+      }
+
+      prevLevel_ = Level;
+      prevAdjustedLevel_ = adjustedLevel_;
+    }
+  }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODBehaviourStateGroup.cs.meta b/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODBehaviourStateGroup.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..700381badef1480613518f948c64eb3fbdf6d97a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODBehaviourStateGroup.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: dcfa6238718f9d94d9e86596933de38f
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODCostData.cs b/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODCostData.cs
new file mode 100644
index 0000000000000000000000000000000000000000..572dba9826b8e0aaaab2f093bffd6413900f58a3
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODCostData.cs
@@ -0,0 +1,58 @@
+using Debug = UnityEngine.Debug;
+
+namespace Oculus.Avatar2
+{
+    public struct AvatarLODCostData
+    {
+        /// Number of vertices in avatar mesh.
+        public readonly uint meshVertexCount;
+        // TODO: Deprecate, use triCount instead
+        /// Number of vertices in the morph targets.
+        public readonly uint morphVertexCount;
+        /// Number of triangles in the avatar mesh.
+        public readonly uint renderTriangleCount;
+        // TODO: Include number of skinned bones + num morph targets
+
+        private AvatarLODCostData(uint meshVertCount, uint morphVertCount, uint triCount)
+        {
+            meshVertexCount = meshVertCount;
+            morphVertexCount = morphVertCount;
+            renderTriangleCount = triCount;
+        }
+        internal AvatarLODCostData(OvrAvatarPrimitive prim)
+            : this(prim.meshVertexCount, prim.morphVertexCount, prim.triCount) { }
+        ///
+        /// Add the second LOD cost to the first and return
+        /// the combined cost of both LODs.
+        ///
+        /// @param total    first LodCostData to add.
+        /// @param add      second LodCostData to add.
+        /// @returns LodCostData with total cost of both LODs.
+        // TODO: inplace Increment/Decrement would be useful
+        public static AvatarLODCostData Sum(in AvatarLODCostData total, in AvatarLODCostData add)
+        {
+            return new AvatarLODCostData(
+                total.meshVertexCount + add.meshVertexCount,
+                total.morphVertexCount + add.morphVertexCount,
+                total.renderTriangleCount + add.renderTriangleCount
+            );
+        }
+
+        ///
+        /// Subtract the second LOD cost from the first and return
+        /// the difference between the LODs.
+        ///
+        /// @param total    LodCostData to subtract from.
+        /// @param sub      LodCostData to subtract.
+        /// @returns LodCostData with different between LODs.
+        public static AvatarLODCostData Subtract(in AvatarLODCostData total, in AvatarLODCostData sub)
+        {
+            Debug.Assert(total.meshVertexCount >= sub.meshVertexCount);
+            return new AvatarLODCostData(
+                total.meshVertexCount - sub.meshVertexCount,
+                total.morphVertexCount - sub.morphVertexCount,
+                total.renderTriangleCount - sub.renderTriangleCount
+            );
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODCostData.cs.meta b/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODCostData.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..8daa73efa806a94bdaf9e3590a7b243ea2bdecc4
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODCostData.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 1069a79bb77f48c7a38e0351a364355a
+timeCreated: 1664213700
\ No newline at end of file
diff --git a/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODGameObjectGroup.cs b/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODGameObjectGroup.cs
new file mode 100644
index 0000000000000000000000000000000000000000..560e35348ca49d68acf3e1788d8474404afaf6e0
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODGameObjectGroup.cs
@@ -0,0 +1,61 @@
+using System;
+using UnityEngine;
+
+
+namespace Oculus.Avatar2 {
+  public class AvatarLODGameObjectGroup : AvatarLODGroup {
+    private const string logScope = "AvatarLODGameObjectGroup";
+
+    [SerializeField]
+    private GameObject[] gameObjects_ = Array.Empty<GameObject>();
+
+    public GameObject[] GameObjects {
+      get { return this.gameObjects_; }
+      set {
+        this.gameObjects_ = value;
+        count = GameObjects.Length;
+        ResetLODGroup();
+      }
+    }
+
+    public override void ResetLODGroup() {
+      if (!Application.isPlaying) return;
+      for (int i = 0; i < GameObjects.Length; i++) {
+        if(GameObjects[i] == null) continue;
+        GameObjects[i].SetActive(false);
+      }
+
+      UpdateAdjustedLevel();
+      UpdateLODGroup();
+    }
+
+    public override void UpdateLODGroup() {
+      if (prevAdjustedLevel_ >= 0)
+      {
+        if (prevAdjustedLevel_ < GameObjects.Length)
+        {
+          GameObjects[prevAdjustedLevel_]?.SetActive(false);
+        }
+        else
+        {
+          OvrAvatarLog.LogWarning("prevAdjustedLevel outside bounds of GameObjects array", logScope, this);
+        }
+      }
+
+      if (adjustedLevel_ >= 0)
+      {
+        if (adjustedLevel_ < GameObjects.Length)
+        {
+          GameObjects[adjustedLevel_].SetActive(true);
+        }
+        else
+        {
+          OvrAvatarLog.LogWarning("adjustedLevel outside bounds of GameObjects array", logScope, this);
+        }
+      }
+
+      prevLevel_ = Level;
+      prevAdjustedLevel_ = adjustedLevel_;
+    }
+  }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODGameObjectGroup.cs.meta b/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODGameObjectGroup.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..60d50c4b5f6e8edc8c30a0183955fb9112b4b2ad
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODGameObjectGroup.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 6929a611690904f4b84c911a4aefd83e
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODGroup.cs b/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODGroup.cs
new file mode 100644
index 0000000000000000000000000000000000000000..1e3384c0e896d1d907942c0c649c9ff776d3a95b
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODGroup.cs
@@ -0,0 +1,75 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+
+namespace Oculus.Avatar2 {
+  public class AvatarLODGroup : MonoBehaviour, IDisposable {
+    protected int count;
+
+    public AvatarLOD parentLOD = null;
+
+    public bool remapInOrder = false;
+
+    protected int level_ = -1;
+    protected int prevLevel_ = -1;
+    protected int adjustedLevel_ = -1;
+
+    public int AdjustedLevel {
+      get { return this.adjustedLevel_; }
+    }
+
+    protected int prevAdjustedLevel_ = -1;
+
+    public int Level {
+      get { return this.level_; }
+      set {
+        if (value == prevLevel_ && value == prevAdjustedLevel_)
+            return;
+        this.level_ = value;
+
+        // Update if parentLOD previously did not have LODs loaded yet
+        UpdateAdjustedLevel();
+        if (adjustedLevel_ != prevAdjustedLevel_) {
+          UpdateLODGroup();
+        }
+      }
+    }
+
+    protected virtual void Start() {
+    }
+    /*
+    public virtual void Update() {
+
+    }
+    */
+
+    protected virtual void OnDestroy() {
+      Dispose();
+    }
+
+    public void Dispose() {
+      if (parentLOD) { parentLOD.RemoveLODGroup(this); }
+      parentLOD = null;
+    }
+
+    public virtual void ResetLODGroup() {
+    }
+
+    protected virtual void UpdateAdjustedLevel() {
+      if (Level < 0 || !parentLOD) {
+        adjustedLevel_ = -1;
+        return;
+      }
+
+      adjustedLevel_ = parentLOD.CalcAdjustedLod(Level);
+    }
+
+    public virtual void UpdateLODGroup() {
+      prevLevel_ = Level;
+      prevAdjustedLevel_ = adjustedLevel_;
+    }
+
+    internal virtual byte LevelsWithAnimationUpdateCost => (byte) (1 << Level);
+  }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODGroup.cs.meta b/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODGroup.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..fdddb93f789880f707dd74e6640d0902807f8b9c
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODGroup.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: bcc44648ff487c84a96643c9492f5b95
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODLookat.cs b/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODLookat.cs
new file mode 100644
index 0000000000000000000000000000000000000000..bd7d2752f216afd814cecdbb049a2a5bb32df36f
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODLookat.cs
@@ -0,0 +1,52 @@
+using UnityEngine;
+
+namespace Oculus.Avatar2.Utils {
+  public class AvatarLODLookat : MonoBehaviour {
+    [SerializeField]
+    public Transform xform;
+    [SerializeField]
+    public UpVectorMode upVectorMode = UpVectorMode.WORLD_UP;
+    [SerializeField]
+    public Vector3 upVector = Vector3.up;
+    [SerializeField]
+    public GameObject upObject = null;
+    private Transform camXform = null;
+
+    public enum UpVectorMode {
+      WORLD_UP, // World Pos Y  (Default)
+      OBJECT_UP, // Vector from self to upObject
+      OBJECT_AIM // Use upVector
+    }
+
+    protected virtual void Start() {
+    }
+
+    protected virtual void Update() {
+      if (xform == null)
+        xform = this.transform;
+
+      if (AvatarLODManager.Instance.CurrentCamera == null)
+        return;
+
+      camXform = AvatarLODManager.Instance.CurrentCamera.transform;
+
+      if (upVectorMode == UpVectorMode.WORLD_UP) {
+        xform.rotation = Quaternion.LookRotation(
+           camXform.position - xform.position,
+          upVector);
+      } else if (upVectorMode == UpVectorMode.OBJECT_UP) {
+        if (upObject == null) {
+          upObject = AvatarLODManager.Instance.CurrentCamera.gameObject;
+        }
+        xform.rotation = Quaternion.LookRotation(
+            camXform.position - xform.position,
+          upObject.transform.rotation * upVector);
+      } else if (upObject != null) {
+        // OBJECT_AIM
+        xform.rotation = Quaternion.LookRotation(
+            camXform.position - xform.position,
+          xform.position - upObject.transform.position);
+      }
+    }
+  }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODLookat.cs.meta b/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODLookat.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..156b5acd56044ead1f42576c4136df095c3e18ff
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODLookat.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 56032097b1ab3a540b3349af62f82310
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODManager.cs b/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODManager.cs
new file mode 100644
index 0000000000000000000000000000000000000000..2757ab9d516650bf937a6255e1a7d95f4cddc459
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODManager.cs
@@ -0,0 +1,1105 @@
+using UnityEngine;
+using System;
+using System.Collections.Generic;
+using UnityEngine.Profiling;
+#if UNITY_EDITOR
+using UnityEditor;
+
+#endif
+
+
+namespace Oculus.Avatar2
+{
+    /**
+     * Configures the Avatar SDK Level of Detail System which aims to keep the
+     * total render and animation time used by all the avatars in a scene under a specified budget.
+     * This system will adjust the geometry displayed for the avatars and how often
+     * they are updated (skinned and tracked) to remain within this budget.
+     *
+     * The LOD manager computations to select the appropriate LOD can be distributed over multiple frames
+     * (cycleProcessingOverFrames) or done all at once. The application can limit the number of LODs
+     * recalculated per frame (LODCountPerFrame) and specify the time duration in which all LODs
+     * should be refreshed (refreshSeconds).
+     *
+     * The number of LODs loaded impacts runtime memory usage. When using Unity Skinning the total size of
+     * Unity Mesh objects for the half manifestation third person mesh is a total of ~9.45MB per avatar.
+     * When using the Avatar GPU skinning solution, the morph target data size for a half manifestation 3rd
+     * third person mesh is ~9.4MB per avatar. These mesh sizes are dominated by the morph targets of the head.
+     * You can select which LODs are loaded for an avatar by setting _creationInfo flags before
+     * before calling [CreateEntity()](@ref OvrAvatarEntity.CreateEntity).
+     * Setting maxLodLevel can force lower levels of detail to be ignored.
+     *
+     * GEOMETRY LOD
+     * The geometric level of detail chosen depends upon the screen percentage occupied by the avatar.
+     * The system uses a logarithmic function to select the LOD:
+     * @code
+     *  LOD = -ln(screenPercentage * dynamicLodWantedLogScale)
+     * @endcode
+     *
+     * The LOD system can cull avatars based on their distance from the camera. This culling is
+     * performed based on the avatar's joint positions. The application can specify a set of
+     * cameras and a list of avatar joints. If all the joints are outside of all the cameras,
+     * the avatar is culled.
+     *
+     * If dynamic processing is enabled, the LOD system will further downgrade LOD levels
+     * to maintain a specified total triangle budget. The application can specify how many
+     * avatars that participate in this adjustment.
+     *
+     * UPDATE LOD
+     * In addition to using different geometry, how often an avatar is updated
+     * (skinned, tracked, animated) is varied based on the importance of the
+     * avatar. The update importance is computed using a logarithmic function based
+     * on the screen percentage occupied by the avatar:
+     * @code
+     *  screenPercentToUpdateImportanceCurveMultiplier * screenPercent ** screenPercentToUpdateImportanceCurvePower
+     * @endcode
+     * The application can also limit the number of avatars which are updated per frame (maxActiveAvatars).
+     *
+     * STREAMING LOD
+     * If enableDynamicStreaming is set, the LOD system can throttle how much tracking data is sent across the network
+     * for each avatar based on its importance. The application can specify the maximum bits per second for
+     * low, medium and high fidelity streaming and establish distances from the camera at which streaming
+     * switches fidelity. Avatars beyond a certain distance send no data.
+     *
+     * LOD EVENTS
+     * OvrAvatarManager invokes several events to signal changes to the system
+     * (these events are also available from each AvatarLOD.)
+     * - CulledChangedEvent         invoked when the cull status of an avatar changes.
+     * - AvatarLODCountChangedEvent invoked when the number of AvatarLODs managed by AvatarLODManager changes.
+     *
+     * The LOD system has debug capabilities which can color each avatar based on
+     * it's LOD (Debug.displayLODColors), or display a label (Debug.displayLODLabels).
+     * @see OvrAvatarEntity.CreateEntity
+     * @see AvatarLOD
+     */
+    // This far too problematic in practice, doesn't seem worth it. Uncomment if you need this for development.
+    // [ExecuteInEditMode]
+    public class AvatarLODManager : OvrSingletonBehaviour<AvatarLODManager>
+    {
+        private const uint MAX_AVATARS = OvrAvatarGpuSkinningController.MaxGpuSkinnedAvatars;
+        private const string logScope = "AvatarLODManager";
+
+        private static Color[] _lodColorsCache = default;
+
+        public static Color[] LOD_COLORS
+        {
+            get
+            {
+                if (_lodColorsCache == null)
+                {
+                    _lodColorsCache = new Color[] {
+                        new Color(204f / 256f, 10f / 256f, 20f / 256f), // red
+                        new Color(255f / 256f, 204f / 256f, 0f / 256f), // yellow
+                        new Color(90f / 256f, 180f / 256f, 0f / 256f),  // green
+                        new Color(0f / 256f, 80f / 256f, 255f / 256f),  // blue
+                        new Color(153 / 256f, 51f / 256f, 255f / 256f), // purple
+                        new Color(102f / 256f, 102f / 256f, 50f / 256f),// brown
+                        new Color(102f / 256f, 51f / 256f, 0f / 256f),  // dark red
+                        new Color(255f / 256f, 102f / 256f, 0f / 256f), // orange
+                        new Color(51f / 256f, 204f / 256f, 204f / 256f) // cyan
+                    };
+                }
+
+                return _lodColorsCache;
+            }
+        }
+
+#if UNITY_EDITOR
+        private EditorWindow lastWindow = null;
+#endif
+        [Header("Configuration")]
+
+        [Tooltip("Maximum LOD level, 0 based. Lower LODs exhibit higher quality.")]
+        /// Maximum LOD level, 0 based. Lower LODs exhibit higher quality.
+        public int MaxLodLevel = 4;
+
+        [Header("Performance of the Manager (CPU Overhead)")]
+
+        [Tooltip("Every time the manager reevaluates LODs it will do so for a subset according to this number. Set to 0 if all LODs need reevaluation every frame.")]
+        /// Number of LODs to re-evaluate per frame. 0 recomputes all avatar LODs every frame.
+        public int LODCountPerFrame = 8;
+
+        [Tooltip("All LODs will be recalculated at this refresh period. Set to 0 to conduct manager refresh every frame.")]
+        /// Time period during which all LODs should be re-evaluated.
+        public float refreshSeconds = 0.0f;
+        private float currentRefresh = 0.0f;
+
+        [Tooltip("Cycles between the different sub functions of the manager and runs one per frame rather than all at once.")]
+        /// If enabled, LOD computations are distributed across multiple frames.
+        public bool cycleProcessingOverFrames = true;
+
+        private ContributingCamera currentCamera_ = new ContributingCamera() { affectsCulling = true, affectsLod = true };
+
+        public Camera CurrentCamera => currentCamera_.camera;
+        [Header("Camera Setup")]
+        [Tooltip("Selects the camera used to calculate LOD distance.")]
+        [UnityEngine.Serialization.FormerlySerializedAs("lodCamera")]
+        [SerializeField]
+        private Camera _lodCamera = null;
+
+        private bool didWarnMissingCamera = false;
+        /// Returns the camera being used for LOD calculations.
+        public Camera ActiveLODCamera
+        {
+            get
+            {
+                if (!_lodCamera || !_lodCamera.isActiveAndEnabled)
+                {
+                    var mainCamera = Camera.main;
+                    if (mainCamera)
+                    {
+                        _lodCamera = mainCamera;
+                        OvrAvatarLog.LogDebug($"No LOD camera specified. Using `Camera.main`: {_lodCamera.name}"
+                            , logScope, this);
+                    }
+                    else
+                    {
+                        _lodCamera = null;
+                        // Avoid log spam in automated tests, which often have no camera
+                        if (!Application.isBatchMode || !didWarnMissingCamera)
+                        {
+                            didWarnMissingCamera = true;
+                            OvrAvatarLog.LogWarning("No LOD camera specified. `Camera.main` is null."
+                                , logScope, this);
+                        }
+                    }
+                }
+                return _lodCamera;
+            }
+        }
+
+        [Serializable]
+        public class ContributingCamera
+        {
+            public Camera camera;
+            public bool affectsCulling = true;
+            public bool affectsLod = false;
+
+            // camera properties to calculate once per update
+            public Vector3 position;
+            public Vector3 forward;
+            public float fieldOfView;
+            public readonly Plane[] frustumPlanes = new Plane[6];
+        }
+
+        /// Extra cameras for LOD distance calculations and joint based culling.
+        [Tooltip("Extra cameras to calculate LOD distance, used for render camera to textures.")]
+        [SerializeField]
+        private List<ContributingCamera> extraCameras = new List<ContributingCamera>();
+
+        /// Avatar bounding box used for culling.
+        [Tooltip("Empirically determined avatar bounding box to use for culling purposes.")]
+        public Bounds frustumBounds_ = new Bounds(Vector3.zero, new Vector3(0.35f, 1f, 0.35f));
+
+        // TODO: Fetch this value from the actual camera
+        public float CameraHeight = 1.6f;
+
+        /// Event invoked when an avatar's cull status changes.
+        public Action<AvatarLOD, bool> CulledChangedEvent;   // events are also available from each AvatarLOD. Use this event when the avatarLOD is not known by the caller.
+
+        /// Event invoked when the number of avatars being managed by the LOD system changes.
+        public Action AvatarLODCountChangedEvent;            // the config is held outside the LOD system. When the number of AvatarLODs managed by AvatarLODManager changes, this events gives the player system a chance to update the config.
+
+        /// Event invoked when LOD system configuration changes.
+        public Action<AvatarLODManager> UpdateConfigEvent;   // the config is held outside the LOD system. Everytime the ssytem needs to update, this function must accomodate
+
+        private bool prevDisplayAgeLabels_ = false;
+        private bool prevDisplayLODLabels_ = false;
+        private bool prevDisplayLodColors_ = false;
+
+        /// Describes the exponential function which computes update importance based on screen percentage.
+        /// More important avatars are updated every frame, less important ones are updated less often.
+        public float screenPercentToUpdateImportanceCurvePower = 0.25f;
+        public float screenPercentToUpdateImportanceCurveMultiplier = 1.5f;
+
+        [Header("Joint Based Culling")]
+        /// Also cull the parent of an avatar that is culled based on its joint positions.
+        [Tooltip("Disables the parent of an avatar that is culled based on its joint positions.")]
+        public bool cullingDisablesParentGameObject = false;
+
+        /// Also cull the children of an avatar that is culled based on its joint positions.
+        [Tooltip("Disables the children of an avatar that is culled based on its joint positions.")]
+        public bool cullingDisablesChildrenGameObjects = true;
+
+        /// Joint used to designate the center of the avatar.
+        [SerializeField]
+        [Tooltip("Avatar joint used to designate the center of the avatar.")]
+        private CAPI.ovrAvatar2JointType _jointTypeToCenterOn = CAPI.ovrAvatar2JointType.Hips;
+
+        [SerializeField]
+        /// Joint used for culling the avatar. The avatar will be culled if all of these
+        /// joints are outside all of the contributing cameras.
+        [Tooltip("List of joints to use for culling. If all of these joints are outside the camera, the avatar is culled.")]
+        private CAPI.ovrAvatar2JointType[] _jointTypesToCullOn = { CAPI.ovrAvatar2JointType.Head, CAPI.ovrAvatar2JointType.LeftHandWrist, CAPI.ovrAvatar2JointType.RightHandWrist };
+
+        /// Joint used to designate the center of the avatar.
+        public CAPI.ovrAvatar2JointType JointTypeToCenterOn => _jointTypeToCenterOn;
+        public IReadOnlyList<CAPI.ovrAvatar2JointType> JointTypesToCullOn => _jointTypesToCullOn;
+
+        internal CAPI.ovrAvatar2JointType[] jointTypesToCullOnArray => _jointTypesToCullOn;
+
+        [Header("Dynamic Performance")]
+
+        /// Modify some of the avatar LODs to keep the total avatar triangle count within budget.
+        [Tooltip("With dynamic performance, all LODs in front of the camera will be modulated based on a total sum of vertices.")]
+        public bool enableDynamicPerformance = true;
+
+        /// Exponential power of screen percentage to LOD selection function.
+        public float dynamicLodWantedLogScale = 1.3f;
+        public int numDynamicLods = 2;
+        [Tooltip("Number of rendered avatar triangles to target, may be exceeded when all avatars are at lowest quality LOD")]
+        public int dynamicLodMaxTrianglesToRender = 90000;
+
+        [Tooltip("Maximum number of avatar animation updates per frame, this should be at least 1/8 of the expected max avatar count")]
+        [Range(1, (int)OvrAvatarGpuSkinningController.MaxSkinnedAvatarsPerFrame)]
+        public int maxActiveAvatars = 5;
+        [Tooltip("Number of avatar vertices to skin per frame")]
+        public int maxVerticesToSkin = 30000;
+
+        [Header("Dynamic Streaming")]
+        [Tooltip("Enables dynamically changing network streaming fidelity (low, medium, high) based on avatar distance from the camera.")]
+        public bool enableDynamicStreaming = false;
+        public long[] dynamicStreamLodBitsPerSecond = new long[OvrAvatarEntity.StreamLODCount] { 0, 0, 0, 0 };
+        public long[] dynamicStreamLodMaxDistance = new long[OvrAvatarEntity.StreamLODCount - 1] { 1, 3, 9 };
+        public long dynamicStreamLodMaxBitsPerSecond = 3000000;
+
+        protected override void Initialize()
+        {
+            // Set up params for native LOD system - valuea are taken from Joe's test case
+            CAPI.ovrAvatar2LOD_SetDistribution(75000, 3.0f);
+
+            base.Initialize();
+
+            var lods = UnityEngine.Object.FindObjectsOfType<AvatarLOD>();
+            foreach (var lod in lods)
+            {
+                AddLOD(lod);
+            }
+        }
+
+        private List<AvatarLOD> inactiveAvatarLods = new List<AvatarLOD>((int)MAX_AVATARS); // all avatars, declares capacity not size
+
+        private List<AvatarLOD> avatarLods = new List<AvatarLOD>((int)MAX_AVATARS); // only active avatars, declares capacity not size
+
+        private List<AvatarLOD> avatarLodsPerFrame; // this will only be used as a reference to either avatarLods or avatarLodsPerFrameSubList
+
+        private readonly List<AvatarLOD> lodsCulled = new List<AvatarLOD>((int)MAX_AVATARS); // only active avatars, declares capacity not size
+        private readonly List<AvatarLOD> lodsAppeared = new List<AvatarLOD>((int)MAX_AVATARS); // only active avatars, declares capacity not size
+        private readonly List<AvatarLOD> lodsVisible = new List<AvatarLOD>((int)MAX_AVATARS); // only active avatars, declares capacity not size
+        private readonly List<AvatarLOD> lodsToProcess = new List<AvatarLOD>((int)MAX_AVATARS); // only active avatars, declares capacity not size
+        private int roundRobinLodIndex = 0;
+
+        private List<AvatarLOD> dynamicLodPriorityQueue = new List<AvatarLOD>((int)MAX_AVATARS); // working buffer for dynamic LOD priority queue
+
+        /// Total number of avatar LODs being managed by the LOD system.
+        public int AvatarLODsCount => avatarLods.Count;
+
+        [Header("First Person Avatar")]
+        public AvatarLOD firstPersonAvatarLod;
+        [Tooltip("Desired LOD for first person avatar")]
+        public int firstPersonAvatarLodLevel = 0;
+        public float firstPersonUpdateImportance = 10000;
+
+        [Header("Debugging")]
+
+        [Tooltip("This should reference a Unity prefab with a GUI canvas inside. This GUI is used to render numbers in the world space.")]
+        [SerializeField]
+        internal GameObject avatarLodDebugCanvas = null;
+
+        [System.Serializable]
+        public class Debug
+        {
+            [Tooltip("Use the Unity Editor Scene view camera for LOD debug display. Only works within the Unity Editor.")]
+            public bool sceneViewCamera = true;
+            [Tooltip("Display a label next to the avatar with the LOD number.")]
+            public bool displayLODLabels = false;
+            public bool displayAgeLabels = false;
+            [Tooltip("Color the avatar based on it's LOD.")]
+            public bool displayLODColors = false;
+            [Tooltip("Offset of the LOD label relative to the avatar.")]
+            public Vector3 displayLODLabelOffset = new Vector3(0.3f, 0.0f, 0.3f);
+        }
+
+        [SerializeField]
+        public AvatarLODManager.Debug debug = new AvatarLODManager.Debug();
+
+        // Since all 3 of these fields are public, this function only remains for backwards compatibility.
+        public void SetConfig(float refreshSec, int LODsPerFrame, int firstPersonLodLevel)
+        {
+            refreshSeconds = refreshSec;
+            LODCountPerFrame = LODsPerFrame;
+            firstPersonAvatarLodLevel = firstPersonLodLevel;
+        }
+
+        ///
+        /// Add an extra camera for LOD computations.
+        /// The camera can be used for joint based culling, distance
+        /// calculations or both. The screen percentage of an avatar
+        /// is the maximum percentage among all the cameras.
+        /// An avatar is culling when none of its joints are visible
+        /// in any of the cameras.
+        /// @param camera   camera to add.
+        /// @param affectsLod   If true, this camera will be used in LOD distance calculations.
+        /// @param affectsCulling If true, this camera will be used for joint-based culling.
+        /// @see RemoveExtraCamera
+        ///
+        public void AddExtraCamera(Camera camera, bool affectsLod = false, bool affectsCulling = true)
+        {
+            if (camera && extraCameras.Find(x => x.camera == camera) == null)
+            {
+                extraCameras.Add(new ContributingCamera() { camera = camera, affectsCulling = affectsCulling, affectsLod = affectsLod });
+            }
+        }
+
+        ///
+        /// Removes an extra camera previously added.
+        /// The camera can will no longer be used for joint based culling
+        /// or LOD distance calculations. You cannot remove the active LOD camera.
+        /// @param camera   camera to remove.
+        /// @see AddExtraCamera
+        ///
+        public void RemoveExtraCamera(Camera camera)
+        {
+            if (camera)
+            {
+                extraCameras.RemoveAll(x => x.camera == camera);
+            }
+        }
+
+        private bool AvatarIsInactiveByParentHierarchy(AvatarLOD avatarLod)
+        {
+            return avatarLod.transform.parent != null && !avatarLod.transform.parent.gameObject.activeInHierarchy;
+        }
+
+        public void AddLOD(AvatarLOD lod)
+        {
+            if (inactiveAvatarLods.Count == MAX_AVATARS)
+            {
+                OvrAvatarLog.LogError($"Attempting to add more than {MAX_AVATARS} Avatars to LODManager", logScope, this);
+                return;
+            }
+
+            if (AvatarIsInactiveByParentHierarchy(lod))
+            {
+                if (!inactiveAvatarLods.Contains(lod))
+                {
+                    inactiveAvatarLods.Add(lod);
+                    AvatarLODCountChangedEvent?.Invoke();
+                }
+            }
+            else
+            {
+                if (!avatarLods.Contains(lod))
+                {
+                    avatarLods.Add(lod);
+                    lodsCulled.Add(lod);
+                    AvatarLODCountChangedEvent?.Invoke();
+                }
+            }
+        }
+
+        public static void RemoveLOD(AvatarLOD lod)
+        {
+            if (!AvatarLODManager.shuttingDown && Instance)
+            {
+                Instance.inactiveAvatarLods.Remove(lod);
+                Instance.avatarLods.Remove(lod);
+                Instance.lodsCulled.Remove(lod);
+                Instance.lodsVisible.Remove(lod);
+                Instance.AvatarLODCountChangedEvent?.Invoke();
+            }
+        }
+
+        public static void ParentStateChanged(AvatarLOD lod)
+        {
+            if (!AvatarLODManager.shuttingDown && Instance)
+            {
+                // Puts the LOD in the right list (active or inactive) based on parent
+                RemoveLOD(lod);
+                if (lod != null)
+                {
+                    Instance.AddLOD(lod);
+                }
+            }
+        }
+
+        private enum LodManagerFunction : int
+        {
+            FIRST,
+            REFRESH_SCREEN_PERCENTS_AND_IMPORTANCE = FIRST,
+            REFRESH_DYNAMIC_GEOMETRIC_LODS,
+            REFRESH_DYNAMIC_STREAM_LODS,
+            MAX
+        }
+        private LodManagerFunction cyclingFunction_;
+
+        // This behaviour is manually updated at a specific time during OvrAvatarManager::Update()
+        // to prevent issues with Unity script update ordering
+        public void UpdateInternal()
+        {
+            if (!isActiveAndEnabled)
+            {
+                return;
+            }
+
+            Profiler.BeginSample("AvatarLODManager::UpdateInternal");
+
+            currentRefresh += Time.deltaTime;
+            if (currentRefresh > refreshSeconds)
+            {
+                RefreshImportanceBudget();
+                RefreshCameras();
+
+                CullAndCreateProcessListForFrame();
+
+                // The following 3 functions are either all called sequentially every frame,
+                // or if "cycleProcessingOverFrames" is true, they are spread over frames.
+                // Under initial measurements, each of these 3 functions takes about the same amount of time.
+                // However, RefreshScreenPercentsAndImportance can be modulated be setting LODCountPerFrame.
+                if (!cycleProcessingOverFrames || cyclingFunction_ == LodManagerFunction.REFRESH_SCREEN_PERCENTS_AND_IMPORTANCE)
+                {
+                    RefreshScreenPercentsAndImportance();
+                }
+                else if(firstPersonAvatarLod != null)
+                {
+                    UpdateFirstPersonLOD();
+                    // TODO: Consolidate this into a reused method
+                    var firstPersonEntity = firstPersonAvatarLod.Entity;
+                    firstPersonEntity.UpdateAvatarLODOverride();
+                    firstPersonEntity.SendImportanceAndCost();
+                    firstPersonEntity.TrackUpdateAge();
+                }
+
+                if (!cycleProcessingOverFrames || cyclingFunction_ == LodManagerFunction.REFRESH_DYNAMIC_GEOMETRIC_LODS)
+                {
+                    RefreshDynamicGeometricLods();
+                }
+                if (!cycleProcessingOverFrames || cyclingFunction_ == LodManagerFunction.REFRESH_DYNAMIC_STREAM_LODS)
+                {
+                    RefreshDynamicStreamLods();
+                }
+                if (cycleProcessingOverFrames)
+                {
+                    cyclingFunction_++;
+                    if (cyclingFunction_ >= LodManagerFunction.MAX)
+                    {
+                        cyclingFunction_ = LodManagerFunction.FIRST;
+                    }
+                }
+
+                RefreshDebugDisplays();
+
+                currentRefresh = 0.0f;
+            }
+
+            Profiler.EndSample();   // "AvatarLODManager::UpdateInternal"
+        }
+
+        private static float GetScreenPercent(float height, float distance, float fieldOfView)
+        {
+            return (Mathf.Atan((height / 2) / distance) * 2 * Mathf.Rad2Deg) / fieldOfView;
+        }
+
+        public bool CullAvatar(AvatarLOD avatarLod)
+        {
+            // Disable the bodies of all avatars that are behind all Cameras in the scene
+            // Can update this to use angle or frustum WorldToScreenPoint for more accuracy
+            bool inFront = IsLodInFrontAnyCamera(avatarLod);
+
+            // TODO: This should happen in avatarLod?
+            if (cullingDisablesParentGameObject)
+            {
+                avatarLod.gameObject.SetActive(inFront);
+            }
+
+            bool culled = !inFront;
+
+            bool culledChanged = avatarLod.SetCulled(culled);
+            if (culledChanged)
+            {
+                CulledChangedEvent?.Invoke(avatarLod, culled);
+            }
+
+            return culled;
+        }
+
+        private bool IsLodInFrontAnyCamera(AvatarLOD avatarLod)
+        {
+            // The VR camera (or possible Scene cam in Editor)
+            if (currentCamera_.camera != null && AreAnyPointsInFrustum(currentCamera_, avatarLod))
+            {
+                return true;
+            }
+
+            // Extra cameras
+            foreach (var cc in extraCameras)
+            {
+                if (cc.affectsCulling && cc.camera != currentCamera_.camera)
+                {
+                    if (AreAnyPointsInFrustum(cc, avatarLod))
+                    {
+                        return true;
+                    }
+                }
+            }
+
+            return false;
+        }
+
+        private bool IsPointInFrustum(ContributingCamera cc, Vector3 point)
+        {
+            frustumBounds_.center = point;
+            return GeometryUtility.TestPlanesAABB(cc.frustumPlanes, frustumBounds_);
+        }
+
+        private bool AreAnyPointsInFrustum(ContributingCamera camera, AvatarLOD avatarLod)
+        {
+            bool areAnyPointsInCameraFrustum = false;
+
+            // first test the centerXform point, as that is the most common and fallback in case no more are set
+            frustumBounds_.center = avatarLod.centerXform.position;
+            areAnyPointsInCameraFrustum |= GeometryUtility.TestPlanesAABB(camera.frustumPlanes, frustumBounds_);
+
+            // next, check all other culling points that will be typically found at the extreme leaf nodes of the skeleton
+            if (!areAnyPointsInCameraFrustum && avatarLod.extraXforms != null)
+            {
+                foreach (var extraXform in avatarLod.extraXforms)
+                {
+                    frustumBounds_.center = extraXform.position;
+                    areAnyPointsInCameraFrustum |= GeometryUtility.TestPlanesAABB(camera.frustumPlanes, frustumBounds_);
+                    if (areAnyPointsInCameraFrustum)
+                    {
+                        break;
+                    }
+                }
+            }
+
+            return areAnyPointsInCameraFrustum;
+        }
+
+        private void UpdateFirstPersonLOD()
+        {
+            System.Diagnostics.Debug.Assert(firstPersonAvatarLod != null);
+            firstPersonAvatarLod.wantedLevel = firstPersonAvatarLodLevel;
+            firstPersonAvatarLod.distance = 0.0f;
+            firstPersonAvatarLod.screenPercent = 1.0f;
+            firstPersonAvatarLod.updateImportance = firstPersonUpdateImportance;
+        }
+
+        private void RefreshScreenPercentsAndImportance()
+        {
+            Profiler.BeginSample("AvatarLODManager::RefreshScreenPercentsAndImportance");
+
+            if (!(currentCamera_.camera is null))
+            {
+                foreach (var avatarLod in avatarLodsPerFrame)
+                {
+                    var avatarEntity = avatarLod.Entity;
+                    System.Diagnostics.Debug.Assert(avatarEntity != null);
+                    System.Diagnostics.Debug.Assert(avatarEntity.isActiveAndEnabled);
+
+                    bool isFirstPersonAvatarLod = ReferenceEquals(avatarLod, firstPersonAvatarLod);
+                    if (isFirstPersonAvatarLod)
+                    {
+                        UpdateFirstPersonLOD();
+                    }
+                    else
+                    {
+                        avatarLod.distance = (Vector3.Distance(currentCamera_.position, avatarLod.centerXform.position));
+
+                        float realHeight = CameraHeight * avatarLod.transform.lossyScale.x;
+                        float screenPercent = GetScreenPercent(realHeight, avatarLod.distance, currentCamera_.fieldOfView);
+
+                        foreach (ContributingCamera cc in extraCameras)
+                        {
+                            if (cc.camera != currentCamera_.camera && cc.affectsLod)
+                            {
+                                float extraCameraDist = (Vector3.Distance(cc.position, avatarLod.centerXform.position));
+                                float extraCameraScreenPercent = GetScreenPercent(realHeight, extraCameraDist, cc.fieldOfView);
+
+                                if (extraCameraScreenPercent > screenPercent)
+                                {
+                                    screenPercent = extraCameraScreenPercent;
+                                }
+                            }
+                        }
+
+                        avatarLod.screenPercent = screenPercent;
+
+                        // convert screenPercent to animation importance scale using:
+                        //  importance = screenPercent ^ CurvePower * CurveMultiplier
+                        avatarLod.updateImportance = Mathf.Pow(screenPercent, screenPercentToUpdateImportanceCurvePower) *
+                                                     screenPercentToUpdateImportanceCurveMultiplier;
+                    }
+
+                    if (avatarEntity != null)
+                    {
+                        avatarEntity.UpdateAvatarLODOverride();
+                        avatarEntity.SendImportanceAndCost();
+                        avatarEntity.TrackUpdateAge();
+                    }
+                }
+            }
+            else
+            {
+                foreach (var avatarLod in avatarLodsPerFrame)
+                {
+                    avatarLod.distance = 0;
+                    avatarLod.screenPercent = 0;
+                    avatarLod.updateImportance = 0;
+
+                    var avatarEntity = avatarLod.Entity;
+                    avatarEntity.UpdateAvatarLODOverride();
+                    avatarEntity.SendImportanceAndCost();
+                    avatarEntity.TrackUpdateAge();
+                }
+            }
+
+            Profiler.EndSample(); // AvatarLODManager::RefreshScreenPercentsAndImportance
+        }
+
+        private void RefreshDebugDisplays()
+        {
+            Profiler.BeginSample("AvatarLODManager::RefreshDebugDisplays");
+
+            if (debug.displayLODLabels || debug.displayLODLabels != prevDisplayLODLabels_)
+            {
+                foreach (var avatarLod in avatarLods)
+                {
+                    avatarLod.UpdateDebugLabel();
+                }
+
+                prevDisplayLODLabels_ = debug.displayLODLabels;
+            }
+
+            if (debug.displayLODColors != prevDisplayLodColors_)
+            {
+                foreach (var avatarLod in avatarLods)
+                {
+                    avatarLod.ForceUpdateLOD<AvatarLODActionGroup>();
+                }
+
+                prevDisplayLodColors_ = debug.displayLODColors;
+            }
+
+            if (debug.displayAgeLabels || debug.displayAgeLabels != prevDisplayAgeLabels_)
+            {
+                foreach (var avatarLod in avatarLods)
+                {
+                    avatarLod.UpdateDebugLabel();
+                }
+
+                prevDisplayAgeLabels_ = debug.displayAgeLabels;
+            }
+
+            Profiler.EndSample(); // AvatarLODManager::RefreshDebugDisplays
+        }
+
+        public void CullAndCreateProcessListForFrame()
+        {
+            Profiler.BeginSample("AvatarLODManager::CullAndCreateProcessListForFrame");
+
+            avatarLodsPerFrame = lodsToProcess;
+            if (LODCountPerFrame <= 0)
+            {
+                lodsToProcess.Clear();
+                foreach (var avatarLod in avatarLods)
+                {
+                    // "Cull" inactive entities
+                    bool avatarCulled = !avatarLod.EntityActive || CullAvatar(avatarLod);
+                    if (avatarCulled)
+                    {
+                        if (cullingDisablesChildrenGameObjects)
+                        {
+                            avatarLod.Level = -1;
+                        }
+                    }
+                    else
+                    {
+                        lodsToProcess.Add(avatarLod);
+                    }
+                }
+            }
+            else
+            {
+                // Each frame that passes we need to fill up lodsToProcess from scratch
+                int processCountPerFrame = Math.Min(LODCountPerFrame, avatarLods.Count);
+                lodsToProcess.Clear();
+                lodsAppeared.Clear();
+
+                // first fill up lodsToProcess with anything newly appeared
+                for (int i = lodsCulled.Count - 1; i >= 0; i--)
+                {
+                    var avatarLod = lodsCulled[i];
+
+                    // Skip inactive entities
+                    if (!avatarLod.AreLodsActive()) { continue; }
+
+                    bool lodCulled = CullAvatar(avatarLod);
+                    if (!lodCulled) // if it became visible
+                    {
+                        lodsCulled.Remove(avatarLod);
+                        lodsAppeared.Add(avatarLod);
+                        lodsToProcess.Add(avatarLod);   // always add newly unculled LODs to the process list
+                        processCountPerFrame--;
+                    }
+                }
+
+                // if any space is left in the list for processing, we'll fill it up with round robin members from the lodsVisible list
+                processCountPerFrame = Math.Min(processCountPerFrame, lodsVisible.Count);
+                if (roundRobinLodIndex >= lodsVisible.Count)
+                {
+                    roundRobinLodIndex = 0;
+                }
+                int lodsVisibleToConsume = lodsVisible.Count;
+                while (processCountPerFrame > 0 && lodsVisibleToConsume > 0)
+                {
+                    var avatarLod = lodsVisible[roundRobinLodIndex % lodsVisible.Count];
+
+                    // "Cull" inactive entities
+                    bool lodCulled = !avatarLod.EntityActive || CullAvatar(avatarLod);
+                    if (lodCulled)
+                    {
+                        avatarLod.Level = -1;
+                        lodsVisible.Remove(avatarLod);
+                        lodsCulled.Add(avatarLod);
+                    }
+                    else
+                    {
+                        lodsToProcess.Add(avatarLod);
+                        roundRobinLodIndex++;
+                        processCountPerFrame--;
+                    }
+                    lodsVisibleToConsume--;
+                }
+
+                // at this point, the lodsToProcess list is ready for this frame, but insert the lodsAppeared into the lodsVisible for next frame
+                if (lodsVisible.Count > 0 && roundRobinLodIndex > 0)
+                {
+                    int lastSpace = (roundRobinLodIndex - 1) % lodsVisible.Count;
+                    foreach (var avatarLod in lodsAppeared)
+                    {
+                        lodsVisible.Insert(lastSpace, avatarLod);
+                        lastSpace++;
+                    }
+                }
+                else
+                {
+                    foreach (var avatarLod in lodsAppeared)
+                    {
+                        lodsVisible.Add(avatarLod);
+                    }
+                }
+
+                // Now decide if the round robin index will be set before or after those just appeared
+                roundRobinLodIndex += lodsAppeared.Count;
+            }
+
+            Profiler.EndSample();   // AvatarLODManager::CullAndCreateProcessListForFrame
+        }
+
+        private void RefreshImportanceBudget()
+        {
+            Profiler.BeginSample("AvatarLODManager::RefreshImportanceBudget");
+
+            if (OvrAvatarManager.initialized)
+            {
+                if (enableDynamicPerformance)
+                {
+                    CAPI.ovrAvatar2Importance_SetBudget((UInt32)maxActiveAvatars, (UInt32)maxVerticesToSkin);
+                }
+                else
+                {
+                    CAPI.ovrAvatar2Importance_SetBudget(0, 0);
+                }
+            }
+
+            Profiler.EndSample(); // AvatarLODManager::RefreshImportanceBudget
+        }
+
+        private void RefreshDynamicGeometricLods()
+        {
+            Profiler.BeginSample("AvatarLODManager::RefreshDynamicGeometricLods");
+
+            dynamicLodPriorityQueue.Clear();
+
+            int trianglesToRender = 0;
+            foreach (var avatarLod in avatarLods) // cannot use avatarLodsPerFrame in loop since it is summing all avatars in front of camera
+            {
+                if (avatarLod.triangleCounts.Count <= 0)
+                {
+                    continue;
+                }
+
+                avatarLod.wantedLevel = -1;
+                avatarLod.lodImportance = -1;
+                avatarLod.dynamicLevel = -1;
+
+                if (ReferenceEquals(avatarLod, firstPersonAvatarLod))
+                {
+                    avatarLod.wantedLevel = firstPersonAvatarLodLevel;
+                    avatarLod.Level = firstPersonAvatarLodLevel;
+                    avatarLod.updateImportance = firstPersonUpdateImportance;
+                    trianglesToRender += avatarLod.triangleCounts[firstPersonAvatarLodLevel];
+                    continue;
+                }
+
+                if (cullingDisablesChildrenGameObjects && CullAvatar(avatarLod))
+                {
+                    avatarLod.Level = -1;
+                    continue;
+                }
+
+                var lodFactor = -Mathf.Log(Mathf.Clamp(dynamicLodWantedLogScale * avatarLod.screenPercent, float.Epsilon, 1f));
+                var dir = Vector3.Normalize(avatarLod.centerXform.position - currentCamera_.position);
+                var gazeBoost = Mathf.Clamp(Vector3.Dot(currentCamera_.forward, dir), float.Epsilon, 1f);
+                const float gazeEpsilon = 1e-3f;
+                avatarLod.lodImportance = (1f + gazeBoost) / Mathf.Max(lodFactor, gazeEpsilon);
+
+                avatarLod.wantedLevel = Mathf.Clamp(Mathf.RoundToInt(lodFactor), avatarLod.minLodLevel, avatarLod.maxLodLevel);
+                avatarLod.dynamicLevel = avatarLod.CalcAdjustedLod(avatarLod.wantedLevel + numDynamicLods);
+
+                // TODO: This seems, erroneous?
+                if (avatarLod.dynamicLevel == -1)
+                {
+                    avatarLod.Level = -1;
+                    continue;
+                }
+
+                // NOTE numDynamicLods currently assumes that all LOD levels are loaded
+                trianglesToRender += avatarLod.triangleCounts[avatarLod.dynamicLevel];
+
+                if (avatarLod.dynamicLevel > avatarLod.wantedLevel)
+                {
+                    dynamicLodPriorityQueue.AddSorted(avatarLod, AvatarLodImportanceComparer.Instance);
+                }
+                else
+                {
+                    avatarLod.Level = avatarLod.dynamicLevel;
+                }
+            }
+
+            int lastTrisToRender = trianglesToRender;
+
+            // increase level starting with most import until wantedLevel is reached
+            while (trianglesToRender < dynamicLodMaxTrianglesToRender)
+            {
+                for (int i = 0; i < dynamicLodPriorityQueue.Count; i++)
+                {
+                    var avatarLod = dynamicLodPriorityQueue[i];
+                    // If the `avatarLod` is null, it has reached its maximum LOD fidelity
+                    if (avatarLod is null) { continue; }
+
+                    // Get next higher fidelity LOD available for this avatar
+                    var prevLod = avatarLod.GetPreviousLod(avatarLod.dynamicLevel);
+                    // If there is a higher fidelity LOD (!= -1) and it is above the wantedLevel
+                    // TODO: Consider going 1 "step" over wantedLevel?
+                    if (prevLod != -1 && prevLod >= avatarLod.wantedLevel)
+                    {
+                        var nextLevelTriIncrease = avatarLod.triangleCounts[prevLod] - avatarLod.triangleCounts[avatarLod.dynamicLevel];
+                        var triCostWithIncrease = trianglesToRender + nextLevelTriIncrease;
+
+                        // If tri increase fits within our budget
+                        if (triCostWithIncrease <= dynamicLodMaxTrianglesToRender)
+                        {
+                            // Lock in this LOD upgrade via `dynamicLevel`
+                            trianglesToRender = triCostWithIncrease;
+                            avatarLod.dynamicLevel = prevLod;
+
+                            // If dynamicLevel is still lower fidelity than wantedLevel - wait for more tris
+                            if (avatarLod.dynamicLevel > avatarLod.wantedLevel) { continue; }
+                        }
+                    }
+
+                    // no longer a candidate for adjustment
+                    avatarLod.Level = avatarLod.dynamicLevel;
+                    dynamicLodPriorityQueue[i] = null;
+                }
+
+                // When there are no additional LOD fidelity upgrades available, stop searching
+                if (lastTrisToRender == trianglesToRender) { break; }
+
+                // Mark starting point for next sweep
+                lastTrisToRender = trianglesToRender;
+            }
+
+            foreach (var avatarLod in dynamicLodPriorityQueue)
+            {
+                if (!(avatarLod is null))
+                {
+                    avatarLod.Level = avatarLod.dynamicLevel;
+                }
+            }
+
+            Profiler.EndSample(); // AvatarLODManager::RefreshDynamicGeometricLods
+        }
+
+        private void RefreshDynamicStreamLods()
+        {
+            if (!enableDynamicStreaming) { return; }
+
+            Profiler.BeginSample("AvatarLODManager::RefreshDynamicStreamLods");
+
+            dynamicLodPriorityQueue.Clear();
+
+            long bandwidthRequired = 0;
+            foreach (var avatarLod in avatarLods)
+            {
+                var avatar = avatarLod.Entity;
+                if (avatar.IsLocal)
+                {
+                    avatarLod.dynamicStreamLod = OvrAvatarEntity.StreamLOD.Full;
+                    continue;
+                }
+
+                avatarLod.dynamicStreamLod = OvrAvatarEntity.StreamLOD.Low;
+
+                if (avatarLod.Level == -1 || avatarLod.distance > dynamicStreamLodMaxDistance[(int)OvrAvatarEntity.StreamLOD.Medium])
+                {
+                    // default to low
+                    bandwidthRequired += dynamicStreamLodBitsPerSecond[(int)OvrAvatarEntity.StreamLOD.Low];
+                    avatar.ForceStreamLod(OvrAvatarEntity.StreamLOD.Low);
+                }
+                else
+                {
+                    dynamicLodPriorityQueue.AddSorted(avatarLod, AvatarLodImportanceComparer.Instance);
+                }
+            }
+
+            for (int prevStreamLod = (int)OvrAvatarEntity.StreamLOD.Medium; prevStreamLod >= (int)OvrAvatarEntity.StreamLOD.High; --prevStreamLod)
+            {
+                var bandwidthIncrease = dynamicStreamLodBitsPerSecond[prevStreamLod] - dynamicStreamLodBitsPerSecond[prevStreamLod + 1];
+                if (bandwidthRequired + bandwidthIncrease > dynamicStreamLodMaxBitsPerSecond)
+                {
+                    break;
+                }
+
+                for (int i = 0; i < dynamicLodPriorityQueue.Count; i++)
+                {
+                    var avatarLod = dynamicLodPriorityQueue[i];
+
+                    if (avatarLod.distance < dynamicStreamLodMaxDistance[prevStreamLod])
+                    {
+                        avatarLod.dynamicStreamLod = (OvrAvatarEntity.StreamLOD)prevStreamLod;
+                        bandwidthRequired += bandwidthIncrease;
+
+                        if (bandwidthRequired + bandwidthIncrease > dynamicStreamLodMaxBitsPerSecond)
+                        {
+                            break;
+                        }
+                    }
+                }
+            }
+
+            for (int i = 0; i < dynamicLodPriorityQueue.Count; i++)
+            {
+                var avatarLod = dynamicLodPriorityQueue[i];
+                var avatar = avatarLod.Entity;
+                avatar.ForceStreamLod(avatarLod.dynamicStreamLod);
+            }
+
+            Profiler.EndSample(); // AvatarLODManager::RefreshDynamicStreamLods
+        }
+
+        private Camera FindVRCamera()
+        {
+            for (int i = 0; i < Camera.allCamerasCount; i++)
+            {
+                if (Camera.allCameras[i].stereoTargetEye == StereoTargetEyeMask.Both)
+                    return Camera.allCameras[i];
+            }
+
+            return null;
+        }
+
+        private void RefreshCameras()
+        {
+            Profiler.BeginSample("AvatarLODManager::RefreshCameras");
+
+            // This is all the explicit cameras we tell the system to look at (active or not)
+            extraCameras.RemoveAll(x => (x.camera == null));
+
+            // If running editor, allow Scene camera to drive LODs when it has focus
+#if UNITY_EDITOR
+            if (debug.sceneViewCamera)
+            {
+                if (EditorWindow.focusedWindow != null && EditorWindow.focusedWindow != lastWindow)
+                {
+                    if (EditorWindow.focusedWindow is UnityEditor.SceneView)
+                    {
+                        currentCamera_.camera = UnityEditor.SceneView.lastActiveSceneView.camera;
+                    }
+                    else if (EditorWindow.focusedWindow.titleContent.text == "Game")
+                    {
+                        currentCamera_.camera = null; // cam will be set below
+                    }
+
+                    /*
+                     * else if (EditorWindow.focusedWindow is UnityEditor.GameView) {
+                     * Dammit UnityEditor.GameView is internal, can't reference class!
+                     * How can I switch when a certain view gets focus without GameView?
+                     * Test the title above (not great, but should be OK because it only happens when
+                     * a new window gets focus and only ever in Editor)
+                    }
+                    */
+                    lastWindow = EditorWindow.focusedWindow;
+                }
+            }
+            // Scene view camera is always considered inactive, so we'll make an editor-only exception here.
+            if (!currentCamera_.camera || (!currentCamera_.camera.isActiveAndEnabled && currentCamera_.camera != UnityEditor.SceneView.lastActiveSceneView?.camera))
+            {
+#endif
+                currentCamera_.camera = ActiveLODCamera;
+
+                if (!currentCamera_.camera)
+                {
+                    currentCamera_.camera = FindVRCamera();
+                }
+#if UNITY_EDITOR
+            }
+#endif
+
+            ComputeCameraProperties(currentCamera_);
+
+            for (int i = 0; i < extraCameras.Count; i++)
+            {
+                ComputeCameraProperties(extraCameras[i]);
+            }
+
+            Profiler.EndSample(); // AvatarLODManager::RefreshCameras
+        }
+
+        private void ComputeCameraProperties(ContributingCamera cc)
+        {
+            if (cc.camera != null)
+            {
+
+                if (cc.affectsLod)
+                {
+                    cc.fieldOfView = cc.camera.fieldOfView;
+                    cc.position = cc.camera.transform.position;
+                    cc.forward = cc.camera.transform.forward;
+                }
+
+                if (cc.affectsCulling)
+                {
+                    GeometryUtility.CalculateFrustumPlanes(cc.camera, cc.frustumPlanes);
+                }
+            }
+        }
+
+        private sealed class AvatarLodImportanceComparer : IComparer<AvatarLOD>
+        {
+            private static AvatarLodImportanceComparer _instance = default;
+            public static AvatarLodImportanceComparer Instance
+                => _instance ?? (_instance = new AvatarLodImportanceComparer());
+
+            public int Compare(AvatarLOD x, AvatarLOD y)
+            {
+                var xVal = x != null ? x.lodImportance : float.NaN;
+                var yVal = y != null ? y.lodImportance : float.NaN;
+                return yVal.CompareTo(xVal);
+            }
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODManager.cs.meta b/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODManager.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..6086cb84f9f35f717d036e74d7d0a136802b078c
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODManager.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: f892af86578f82443a9682973148029a
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODMaterials.cs b/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODMaterials.cs
new file mode 100644
index 0000000000000000000000000000000000000000..d8f21b170e0c71bb3b7fea3727c98440c79846cf
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODMaterials.cs
@@ -0,0 +1,94 @@
+using System.Collections.Generic;
+using System.IO;
+using UnityEngine;
+#if UNITY_EDITOR
+using UnityEditor;
+
+#endif
+
+
+namespace Oculus.Avatar2
+{
+    public class AvatarLODMaterials
+    {
+#if UNITY_EDITOR
+        // TODO: Move these materials into the Avatar SDK and reference them there:
+        public static readonly string AVATAR_LOD_MATERIAL_PATH = "Assets/Package/AvatarAssetsSrc/Res/LOD/Materials/";
+#endif
+
+#if UNITY_EDITOR
+        private static Material lodNoneMaterial_ = null;
+        public static Material LodNoneMaterial
+        {
+            get
+            {
+                if (lodNoneMaterial_ == null)
+                {
+                    lodNoneMaterial_ = GetOrCreateLODMaterial(AVATAR_LOD_MATERIAL_PATH + "LODNoneMaterial.mat", Color.white);
+                }
+                return lodNoneMaterial_;
+            }
+        }
+#else
+    public readonly static Material LodNoneMaterial = null;
+#endif
+
+
+#if UNITY_EDITOR
+        private static Material lodOutOfRangeMaterial_ = null;
+
+        public static Material LodOutOfRangeMaterial
+        {
+            get
+            {
+                if (lodOutOfRangeMaterial_ == null)
+                {
+                    lodOutOfRangeMaterial_ =
+                      GetOrCreateLODMaterial(AVATAR_LOD_MATERIAL_PATH + "LODOutOfRangeMaterial.mat", Color.white);
+                }
+                return lodOutOfRangeMaterial_;
+            }
+        }
+#else
+    public readonly static Material LodOutOfRangeMaterial = null;
+#endif
+
+        private static List<Material> lodMaterials_ = null;
+
+        public static List<Material> LodMaterials
+        {
+            get
+            {
+#if UNITY_EDITOR
+                if (lodMaterials_ == null)
+                {
+                    lodMaterials_ = new List<Material>();
+                    for (int i = 0; i < AvatarLODManager.LOD_COLORS.Length; i++)
+                    {
+                        lodMaterials_.Add(GetOrCreateLODMaterial(AVATAR_LOD_MATERIAL_PATH + "LOD" + i + "Material.mat",
+                          AvatarLODManager.LOD_COLORS[i]));
+                    }
+                }
+#endif
+                return lodMaterials_;
+            }
+        }
+
+        private static Material GetOrCreateLODMaterial(string materialPath, Color color)
+        {
+            Material lodMaterial = null;
+#if UNITY_EDITOR
+            if (File.Exists(materialPath))
+            {
+                lodMaterial = AssetDatabase.LoadAssetAtPath(materialPath, typeof(Material)) as Material;
+                Color col = lodMaterial.GetColor("_Color");
+                if (col != color)
+                {
+                    lodMaterial.SetColor("_Color", color);
+                }
+            }
+#endif
+            return lodMaterial;
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODMaterials.cs.meta b/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODMaterials.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..930e7879ca9759ce93f2e6b0061d26f7cdbd0dcb
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODMaterials.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 01e57d80d9792844eba6b88d0365606d
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODParent.cs b/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODParent.cs
new file mode 100644
index 0000000000000000000000000000000000000000..9dd7360bd1e2b4f113a71cfc624ec17034435135
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODParent.cs
@@ -0,0 +1,47 @@
+using UnityEngine;
+
+namespace Oculus.Avatar2 {
+  public class AvatarLODParent : MonoBehaviour {
+    public bool beingDestroyed;  // Because Destroy doesn't happend until end of frame
+
+    private void ResetLODChildrenParentState() {
+      if (beingDestroyed) {
+        return;
+      }
+      foreach (Transform child in transform) {
+        AvatarLOD lod = child.GetComponent<AvatarLOD>();
+        if (lod) {
+          AvatarLODManager.ParentStateChanged(lod);
+        }
+      }
+    }
+
+    public void DestroyIfOnlyLODChild(AvatarLOD inLod) {
+      if (beingDestroyed) {
+        return;
+      }
+      bool lodFound = false;
+      foreach (Transform child in transform) {
+        AvatarLOD lod = child.GetComponent<AvatarLOD>();
+        if (lod && lod != inLod) {
+          lodFound = true;
+          break;
+        }
+      }
+
+      if (!lodFound) {
+        beingDestroyed = true;  // Need this status immediately, not end of frame
+        Destroy(this);  // Can't use DestroyImmediate in build
+      }
+    }
+
+    protected virtual void OnEnable() {
+      ResetLODChildrenParentState();
+    }
+
+    protected virtual void OnDisable() {
+      ResetLODChildrenParentState();
+    }
+    
+  }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODParent.cs.meta b/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODParent.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..9224a3343993aa9ff14e6acda56d9b30fd6abd94
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODParent.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a0a6e9d54fdc990459a23803127346e9
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODSkinnableGroup.cs b/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODSkinnableGroup.cs
new file mode 100644
index 0000000000000000000000000000000000000000..3c4cc58f91e07456fed772d7767142857ff3a172
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODSkinnableGroup.cs
@@ -0,0 +1,250 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+
+namespace Oculus.Avatar2 {
+  public class AvatarLODSkinnableGroup : AvatarLODGroup {
+    private const string logScope = "AvatarLODSkinnableGroup";
+    private const int INVALID_LEVEL = -1;
+
+    private GameObject[] _gameObjects = Array.Empty<GameObject>();
+
+    private OvrAvatarSkinnedRenderable[][] _childRenderables = Array.Empty<OvrAvatarSkinnedRenderable[]>();
+    private int _activeAndEnabledLevel = INVALID_LEVEL;
+
+    private int _pendingTransitionLevel = INVALID_LEVEL;
+    private readonly HashSet<OvrAvatarSkinnedRenderable> _pendingTransitionIncompleteRenderables =
+      new HashSet<OvrAvatarSkinnedRenderable>();
+
+    private byte _animatingLevels;
+    internal override byte LevelsWithAnimationUpdateCost => _animatingLevels;
+
+    private void FindAndCacheChildRenderables()
+    {
+      _childRenderables = new OvrAvatarSkinnedRenderable[count][];
+      for (int i = 0; i < GameObjects.Length; i++) {
+        var gameObj = GameObjects[i];
+        if (gameObj != null) {
+          _childRenderables[i] = gameObj.GetComponentsInChildren<OvrAvatarSkinnedRenderable>();
+        } else {
+          _childRenderables[i] = Array.Empty<OvrAvatarSkinnedRenderable>();
+        }
+      }
+    }
+
+    public GameObject[] GameObjects {
+      get => _gameObjects;
+      set {
+        // Disable and pending transitions and stop listening for transition completion
+        // This must be done first, since the renderables will be destroyed
+        DisablePendingLevelRenderablesAnimationAndStopListening();
+        _pendingTransitionLevel = INVALID_LEVEL;
+
+        _gameObjects = value;
+        count = GameObjects.Length;
+
+        // Filter out the skinned renderables
+        FindAndCacheChildRenderables();
+        ResetLODGroup();
+      }
+    }
+
+    public override void ResetLODGroup() {
+      if (!Application.isPlaying) { return; }
+
+      // Not 100% sure what "Reset" means in this context,
+      // just using AvatarLODGameObjectGroup as an example
+
+      // Disable and pending transitions and stop listening
+      // for transition completion
+      DisablePendingLevelRenderablesAnimationAndStopListening();
+      _pendingTransitionLevel = INVALID_LEVEL;
+
+      // Deactive all game objects
+      foreach (GameObject t in GameObjects)
+      {
+        if(t == null) { continue; }
+        t.SetActive(false);
+      }
+      _activeAndEnabledLevel = INVALID_LEVEL;
+
+      // Stop all animations
+      foreach (var renderablesForLevel in _childRenderables)
+      {
+        foreach (var r in renderablesForLevel)
+        {
+          r.IsAnimationEnabled = false;
+        }
+      }
+      ClearAnimatingLevels();
+
+      UpdateAdjustedLevel();
+      UpdateLODGroup();
+    }
+
+    public override void UpdateLODGroup()
+    {
+      base.UpdateLODGroup();
+
+      // If same level that is pending is requested again, do nothing
+      bool isRequestedLevelAlreadyPending = adjustedLevel_ == _pendingTransitionLevel;
+      if (isRequestedLevelAlreadyPending)
+      {
+        return;
+      }
+
+      // If the requested level is already the level that is "active and enabled" (visible)
+      // then the pending transaction just needs to be cancelled
+      bool isRequestedLevelAlreadyActive = _activeAndEnabledLevel == adjustedLevel_;
+      if (isRequestedLevelAlreadyActive)
+      {
+        DisablePendingLevelRenderablesAnimationAndStopListening();
+        _pendingTransitionLevel = adjustedLevel_;
+      }
+      else
+      {
+        // Transition "out of" previous LOD and into a new one (if applicable)
+        if (adjustedLevel_ < GameObjects.Length)
+        {
+          DisablePendingLevelRenderablesAnimationAndStopListening();
+
+          _pendingTransitionLevel = adjustedLevel_;
+
+          EnableSkinnedRenderablesAnimationAndListenForCompletion();
+        }
+        else
+        {
+          OvrAvatarLog.LogWarning("adjustedLevel outside bounds of GameObjects array", logScope, this);
+        }
+      }
+    }
+
+    private bool IsTransitionComplete => _pendingTransitionIncompleteRenderables.Count == 0;
+
+    private void DisablePendingLevelRenderablesAnimationAndStopListening()
+    {
+      // Disable animation for previously pending level (if it was valid)
+      if (!IsTransitionComplete)
+      {
+        // Previously requested transition hasn't completed yet,
+        // set all renderables to have animation disabled and stop listening for animation data completion
+        RemoveFromAnimatingLevels(_pendingTransitionLevel);
+        var renderables = _childRenderables[_pendingTransitionLevel];
+        foreach (var r in renderables)
+        {
+          r.IsAnimationEnabled = false;
+          r.AnimationDataComplete -= OnRenderableAnimDataComplete;
+        }
+
+        _pendingTransitionIncompleteRenderables.Clear();
+      }
+    }
+
+    private void EnableSkinnedRenderablesAnimationAndListenForCompletion()
+    {
+      // Special case:
+      // If the active level is "invalid" (i.e. nothing visible)
+      // then no "transition" is required (just show something instead of potentially waiting some frames)
+      bool needsTransition = IsLevelValid(_activeAndEnabledLevel);
+
+      if (IsLevelValid(_pendingTransitionLevel))
+      {
+        AddToAnimatingLevels(_pendingTransitionLevel);
+        var renderables = _childRenderables[_pendingTransitionLevel];
+        foreach (var r in renderables)
+        {
+          r.IsAnimationEnabled = true;
+
+          // See if need to listen for data completion or not
+          if (!r.IsAnimationDataCompletelyValid && needsTransition)
+          {
+            _pendingTransitionIncompleteRenderables.Add(r);
+            r.AnimationDataComplete += OnRenderableAnimDataComplete;
+          }
+        }
+      }
+
+      // Edge case here where all data is already completely valid and thus, the transition
+      // is already complete
+      if (IsTransitionComplete)
+      {
+        OnLevelTransitionCompleted();
+      }
+    }
+
+    private void OnLevelTransitionCompleted()
+    {
+      // ASSUMPTION: The pending requests should never be completed
+      // for the already active level (this should be caught upstream)
+      Debug.Assert(_activeAndEnabledLevel != _pendingTransitionLevel);
+
+      // Deactivate old game object and active new one.
+      bool isOldLevelValid = IsLevelValid(_activeAndEnabledLevel);
+      if (isOldLevelValid)
+      {
+        DeactiveGameObjectForLevelAndDisableAnimation(_activeAndEnabledLevel);
+      }
+
+      // Enable the new level
+      if (IsLevelValid(_pendingTransitionLevel))
+      {
+        GameObjects[_pendingTransitionLevel].SetActive(true);
+      }
+
+      _activeAndEnabledLevel = _pendingTransitionLevel;
+    }
+
+    private void DeactiveGameObjectForLevelAndDisableAnimation(int level)
+    {
+      // ASSUMPTION: caller checks for level validity
+      GameObjects[level].SetActive(false);
+
+      // Disable animation for the skinned renderables as well
+      RemoveFromAnimatingLevels(level);
+      foreach (var r in _childRenderables[level])
+      {
+        r.IsAnimationEnabled = false;
+      }
+    }
+
+    private void OnRenderableAnimDataComplete(OvrAvatarSkinnedRenderable sender)
+    {
+      _pendingTransitionIncompleteRenderables.Remove(sender);
+      sender.AnimationDataComplete -= OnRenderableAnimDataComplete;
+
+      // See if all renderers are complete
+      if (IsTransitionComplete)
+      {
+        OnLevelTransitionCompleted();
+      }
+    }
+
+    private void ClearAnimatingLevels()
+    {
+      _animatingLevels = 0;
+    }
+
+    private void AddToAnimatingLevels(int lev)
+    {
+      if (IsLevelValid(lev))
+      {
+        _animatingLevels |= (byte)(1 << lev);
+      }
+    }
+
+    private void RemoveFromAnimatingLevels(int lev)
+    {
+      if (IsLevelValid(lev))
+      {
+        _animatingLevels &= (byte)~(1 << lev);
+      }
+    }
+
+    private bool IsLevelValid(int level)
+    {
+      return level != INVALID_LEVEL && level >= 0 && level < GameObjects.Length;
+    }
+
+  } // end class AvatarLODSkinnableGroup
+} // end namespace
diff --git a/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODSkinnableGroup.cs.meta b/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODSkinnableGroup.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..bea18a2d322a9be05be0576028ec72fc3cf0334a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/LOD/AvatarLODSkinnableGroup.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: d3ac76d71e534854d914a238b8110f3a
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarBodyTrackingBehavior.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarBodyTrackingBehavior.cs
new file mode 100644
index 0000000000000000000000000000000000000000..fc80781f4b3af8924b0a07ac9148c8804c1fb88d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarBodyTrackingBehavior.cs
@@ -0,0 +1,22 @@
+using UnityEngine;
+
+/**
+ * @file OvrAvatarBodyTrackingBehavior.cs
+ */
+
+namespace Oculus.Avatar2
+{
+    /**
+     * MonoBehavior which holds a body tracking context so it can be referenced in the inspector.
+     * @see OvrAvatarBodyTrackingContextBase
+     */
+    public abstract class OvrAvatarBodyTrackingBehavior : MonoBehaviour
+    {
+        /**
+         * Get the body tracking implementation.
+         * Subclasses must implement a getter for this property.
+         * @see OvrAvatarBodyTrackingContextBase
+         */
+        public abstract OvrAvatarBodyTrackingContextBase TrackingContext { get; }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarBodyTrackingBehavior.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarBodyTrackingBehavior.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..76a47115e186a34bd035d9c5d9d2ec62573f81e9
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarBodyTrackingBehavior.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 6d61e74d8296ba84e8ac9e57c6c3cde9
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarBodyTrackingContext.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarBodyTrackingContext.cs
new file mode 100644
index 0000000000000000000000000000000000000000..b84c654af5b86c3a05bca66c7384d8d5ca34b8c4
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarBodyTrackingContext.cs
@@ -0,0 +1,295 @@
+using AOT;
+using System;
+
+namespace Oculus.Avatar2
+{
+    ///
+    /// C# wrapper around OvrBody stand alone solver
+    ///
+    public sealed class OvrAvatarBodyTrackingContext : OvrAvatarBodyTrackingContextBase, IOvrAvatarNativeBodyTracking
+    {
+        private const string logScope = "BodyTrackingContext";
+
+        private IntPtr _context;
+        private IOvrAvatarHandTrackingDelegate _handTrackingDelegate;
+        private IOvrAvatarInputTrackingDelegate _inputTrackingDelegate;
+        private IOvrAvatarInputControlDelegate _inputControlDelegate;
+        private readonly CAPI.ovrAvatar2TrackingDataContext? _callbacks;
+        private readonly OvrAvatarTrackingHandsState _handState = new OvrAvatarTrackingHandsState();
+        private OvrAvatarInputControlState _inputControlState = new OvrAvatarInputControlState();
+        private OvrAvatarInputTrackingState _inputTrackingState = new OvrAvatarInputTrackingState();
+        private readonly CAPI.ovrAvatar2TrackingDataContextNative _nativeContext;
+
+        CAPI.ovrAvatar2TrackingDataContextNative IOvrAvatarNativeBodyTracking.NativeDataContext
+        {
+            get => _nativeContext;
+        }
+
+        public IntPtr BodyTrackingContextPtr => _context;
+
+        public IOvrAvatarHandTrackingDelegate HandTrackingDelegate
+        {
+            get => _handTrackingDelegate;
+            set
+            {
+                _handTrackingDelegate = value ?? OvrAvatarManager.Instance.DefaultHandTrackingDelegate;
+
+                if (_handTrackingDelegate is IOvrAvatarNativeHandDelegate nativeHandDelegate)
+                {
+                    var nativeContext = nativeHandDelegate.NativeContext;
+                    CAPI.ovrAvatar2Body_SetHandTrackingContextNative(_context, nativeContext)
+                        .EnsureSuccess("ovrAvatar2Body_SetHandTrackingContextNative", logScope);
+                }
+                else
+                {
+                    // Set hand callbacks
+                    var handContext = new CAPI.ovrAvatar2HandTrackingDataContext();
+                    if (_handTrackingDelegate != null)
+                    {
+                        handContext.context = new IntPtr(id);
+                        handContext.handTrackingCallback = HandTrackingCallback;
+                    };
+
+                    CAPI.ovrAvatar2Body_SetHandTrackingContext(_context, handContext)
+                        .EnsureSuccess("ovrAvatar2Body_SetHandTrackingContext", logScope);
+                }
+            }
+        }
+
+        public IOvrAvatarInputTrackingDelegate InputTrackingDelegate
+        {
+            get => _inputTrackingDelegate;
+            set
+            {
+                _inputTrackingDelegate = value;
+
+                {
+                    var inputContext = new CAPI.ovrAvatar2InputTrackingContext();
+                    if (_inputTrackingDelegate != null)
+                    {
+                        inputContext.context = new IntPtr(id);
+                        inputContext.inputTrackingCallback = InputTrackingCallback;
+                    };
+
+                    CAPI.ovrAvatar2Body_SetInputTrackingContext(_context, inputContext)
+                        .EnsureSuccess("ovrAvatar2Body_SetInputTrackingContext", logScope);
+                }
+            }
+        }
+
+        public OvrAvatarInputTrackingState InputTrackingState { get => _inputTrackingState; }
+
+        public IOvrAvatarInputControlDelegate InputControlDelegate
+        {
+            get => _inputControlDelegate;
+            set
+            {
+                _inputControlDelegate = value;
+
+                {
+                    var inputContext = new CAPI.ovrAvatar2InputControlContext();
+                    if (_inputControlDelegate != null)
+                    {
+                        inputContext.context = new IntPtr(id);
+                        inputContext.inputControlCallback = InputControlCallback;
+                    }
+
+                    CAPI.ovrAvatar2Body_SetInputControlContext(_context, inputContext)
+                        .EnsureSuccess("ovrAvatar2Body_SetInputControlContext", logScope);
+                }
+            }
+        }
+
+        public OvrAvatarInputControlState InputControlState { get => _inputControlState; }
+
+        public static OvrAvatarBodyTrackingContext Create(bool runAsync)
+        {
+            OvrAvatarBodyTrackingContext context = null;
+            try
+            {
+                context = new OvrAvatarBodyTrackingContext(runAsync);
+            }
+            catch (Exception)
+            {
+                context?.Dispose();
+                context = null;
+            }
+
+            return context;
+        }
+
+        private OvrAvatarBodyTrackingContext(bool runAsync)
+        {
+            if (!CAPI.ovrAvatar2Body_CreateProvider(runAsync ? CAPI.ovrAvatar2BodyProviderCreateFlags.RunAsync : 0, out _context)
+                    .EnsureSuccess("ovrAvatar2Body_CreateProvider", logScope))
+            {
+                // Not sure which exception type is best
+                throw new Exception("Failed to create body tracking context");
+            }
+
+            HandTrackingDelegate = OvrAvatarManager.Instance.DefaultHandTrackingDelegate;
+
+            _callbacks = CreateBodyDataContext();
+
+            if (CAPI.ovrAvatar2Body_InitializeDataContextNative(_context, out var nativeContext)
+                .EnsureSuccess("ovrAvatar2Body_InitializeDataContextNative", logScope))
+            {
+                _nativeContext = nativeContext;
+            }
+        }
+
+        public void SetTransformOffset(CAPI.ovrAvatar2BodyMarkerTypes type, ref CAPI.ovrAvatar2Transform offset)
+        {
+            CAPI.ovrAvatar2Body_SetOffset(_context, type, offset)
+                .EnsureSuccess("ovrAvatar2Body_SetOffset", logScope);
+        }
+
+        private CAPI.ovrAvatar2TrackingDataContext? CreateBodyDataContext()
+        {
+            if (CAPI.ovrAvatar2Body_InitializeDataContext(_context, out var trackingContext)
+                .EnsureSuccess("ovrAvatar2Body_InitializeDataContext", logScope))
+            {
+                return trackingContext;
+            }
+            else
+            {
+                return null;
+            }
+        }
+
+        private void ReleaseUnmanagedResources()
+        {
+            if (_context == IntPtr.Zero) return;
+            // Release unmanaged resources here
+            CAPI.ovrAvatar2Body_DestroyProvider(_context)
+                .EnsureSuccess("ovrAvatar2Body_DestroyProvider", logScope);
+
+            _context = IntPtr.Zero;
+        }
+
+        protected override void Dispose(bool disposing)
+        {
+            ReleaseUnmanagedResources();
+            base.Dispose(disposing);
+        }
+
+        [MonoPInvokeCallback(typeof(CAPI.HandStateCallback))]
+        private static bool HandTrackingCallback(out CAPI.ovrAvatar2HandTrackingState handsState, IntPtr context)
+        {
+            try
+            {
+                var bodyContext = GetInstance<OvrAvatarBodyTrackingContext>(context);
+                if (bodyContext?._handTrackingDelegate != null &&
+                    bodyContext._handTrackingDelegate.GetHandData(bodyContext._handState))
+                {
+                    handsState = bodyContext._handState.ToNative();
+                    return true;
+                }
+            }
+            catch (Exception e)
+            {
+                OvrAvatarLog.LogError(e.ToString());
+            }
+
+            handsState = default;
+            return false;
+        }
+
+
+        [MonoPInvokeCallback(typeof(CAPI.InputTrackingCallback))]
+        private static bool InputTrackingCallback(out CAPI.ovrAvatar2InputTrackingState trackingState, IntPtr userContext)
+        {
+            try
+            {
+                var bodyContext = GetInstance<OvrAvatarBodyTrackingContext>(userContext);
+                if (bodyContext?._inputTrackingDelegate != null &&
+                    bodyContext._inputTrackingDelegate.GetInputTrackingState(out bodyContext._inputTrackingState))
+                {
+                    trackingState = bodyContext._inputTrackingState.ToNative();
+                    return true;
+                }
+            }
+            catch (Exception e)
+            {
+                OvrAvatarLog.LogError(e.ToString());
+            }
+
+            trackingState = default;
+            return false;
+        }
+
+        [MonoPInvokeCallback(typeof(CAPI.InputControlCallback))]
+        private static bool InputControlCallback(out CAPI.ovrAvatar2InputControlState controlState, IntPtr userContext)
+        {
+            try
+            {
+                var bodyContext = GetInstance<OvrAvatarBodyTrackingContext>(userContext);
+                if (bodyContext?._inputControlDelegate != null &&
+                    bodyContext._inputControlDelegate.GetInputControlState(out bodyContext._inputControlState))
+                {
+                    controlState = bodyContext._inputControlState.ToNative();
+                    return true;
+                }
+            }
+            catch (Exception e)
+            {
+                OvrAvatarLog.LogError(e.ToString());
+            }
+
+            controlState = default;
+            return false;
+        }
+
+        // Provides a Body State by calling into the native Body Tracking implementation
+        protected override bool GetBodyState(OvrAvatarTrackingBodyState bodyState)
+        {
+            if (_callbacks.HasValue)
+            {
+                var cb = _callbacks.Value;
+                if (cb.bodyStateCallback(out var nativeBodyState, cb.context))
+                {
+                    bodyState.FromNative(ref nativeBodyState);
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        // Provides a Tracking Skeleton by calling into the native Body Tracking implementation
+        protected override bool GetBodySkeleton(ref OvrAvatarTrackingSkeleton skeleton)
+        {
+            if (_callbacks.HasValue)
+            {
+                var cb = _callbacks.Value;
+                if (cb.bodySkeletonCallback != null)
+                {
+                    var native = skeleton.GetNative();
+                    var result = cb.bodySkeletonCallback(ref native, cb.context);
+                    skeleton.CopyFromNative(ref native);
+                    return result;
+                }
+
+            }
+
+            return false;
+        }
+
+        // Provides a Body Pose by calling into the native Body Tracking implementation
+        protected override bool GetBodyPose(ref OvrAvatarTrackingPose pose)
+        {
+            if (_callbacks.HasValue)
+            {
+                var cb = _callbacks.Value;
+                if (cb.bodyPoseCallback != null)
+                {
+                    var native = pose.GetNative();
+                    var result = cb.bodyPoseCallback(ref native, cb.context);
+                    pose.CopyFromNative(ref native);
+                    return result;
+                }
+            }
+
+            return false;
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarBodyTrackingContext.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarBodyTrackingContext.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..c44094077c6e5e67e9057f0cdd832ba77177146b
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarBodyTrackingContext.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 08852f3853a546f3b91fd67f0fcfcb62
+timeCreated: 1605741417
\ No newline at end of file
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarBodyTrackingContextBase.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarBodyTrackingContextBase.cs
new file mode 100644
index 0000000000000000000000000000000000000000..d8b71dad723f25e35263b24f36237c8a07d25602
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarBodyTrackingContextBase.cs
@@ -0,0 +1,161 @@
+using AOT;
+using System;
+
+/**
+ * @file OvrAvatarBodyTrackingContextBase.cs
+ */
+namespace Oculus.Avatar2
+{
+    /**
+     * Base class for C# code to supply tracking data for avatar entities.
+     * Body tracking implementations derive from this class to transfer
+     * data between Unity body tracking state and data gathered from sensors
+     * from a C++ implementation.
+     * @see OvrAvatarEntity.SetBodyTracking
+     * @see OvrPluginTracking
+     * @see CAPI.ovrAvatar2TrackingDataContext
+     */
+    public abstract class OvrAvatarBodyTrackingContextBase : OvrAvatarCallbackContextBase
+    {
+        private readonly OvrAvatarTrackingBodyState _bodyState = new OvrAvatarTrackingBodyState();
+        internal CAPI.ovrAvatar2TrackingDataContext DataContext { get; }
+
+        /**
+         * Initializes the implementation-specific callbacks
+         * invoked by native code to collect the current
+         * tracking state, skeleton and pose.
+         * @see BodyStateCallback
+         * @see BodySkeletonCallback
+         * @see BodyPoseCallback
+         */
+        protected OvrAvatarBodyTrackingContextBase()
+        {
+            var dataContext = new CAPI.ovrAvatar2TrackingDataContext
+            {
+                context = new IntPtr(id),
+                bodyStateCallback = BodyStateCallback,
+                bodySkeletonCallback = BodySkeletonCallback,
+                bodyPoseCallback = BodyPoseCallback
+            };
+            DataContext = dataContext;
+        }
+
+        /**
+         * Callback from Avatar SDK for the application to provide the current
+         * Body Tracking State.
+         *
+         * The tracking state includes the position, orientation and scale
+         * for the headset and controllers, buttons pressed, the type of
+         * Hand tracking desired and whether the avatar is sitting or standing.
+         * Body tracking implementations must override this function to provide
+         * the current Body Tracking state to AvatarSDK each frame.
+         * @param bodyState  where to store the generated tracking state.
+         * @see OvrAvatarTrackingBodyState
+         * @see OvrAvatarTrackingPose
+         * @see OvrAvatarTrackingSkeleton
+         * @see GetBodyPose
+         */
+        protected abstract bool GetBodyState(OvrAvatarTrackingBodyState bodyState);
+
+        /**
+         * Callback from Avatar SDK for the application to provide a
+         * description of the skeleton used for Body Tracking, including
+         * a reference pose.
+         *
+         * The tracking skeleton may be different than the avatar skeleton (it
+         * might have a different number of bones) and the pose will be
+         * retargeted within the Avatar SDK animation system.
+         * Body tracking implementations must override this function to
+         * describe the tracking skeleton to AvatarSDK. It will be called
+         * whenever the skeleton changes (see "skeletonVersion" in
+         * OvrAvatarTrackingBodyState).
+         * @param skeleton  where to store the generated skeleton.
+         * @see OvrAvatarTrackingPose
+         * @see OvrAvatarTrackingSkeleton
+         * @see GetBodyPose
+         */
+        protected abstract bool GetBodySkeleton(ref OvrAvatarTrackingSkeleton skeleton);
+
+        /**
+         * Callback from Avatar SDK for the application to provide a Body Pose.
+         *
+         * Body tracking implementations must override this function to fill
+         * out the OvrAvatarTrackingPose struct to provide pose data each frame.
+         * @param pose  Where to set the generated pose.
+         * @see OvrAvatarTrackingPose
+         * @see OvrAvatarTrackingSkeleton
+         * @see GetBodySkeleton
+         */
+        protected abstract bool GetBodyPose(ref OvrAvatarTrackingPose pose);
+
+        #region Static Methods
+
+        [MonoPInvokeCallback(typeof(CAPI.BodyStateCallback))]
+        private static bool BodyStateCallback(out CAPI.ovrAvatar2TrackingBodyState bodyState, IntPtr userContext)
+        {
+            try
+            {
+                var context = GetInstance<OvrAvatarBodyTrackingContextBase>(userContext);
+                if (context != null)
+                {
+                    if (context.GetBodyState(context._bodyState))
+                    {
+                        bodyState = context._bodyState.ToNative();
+                        return true;
+                    }
+                    bodyState = new CAPI.ovrAvatar2TrackingBodyState();
+                    return false;
+                }
+            }
+            catch (Exception e)
+            {
+                OvrAvatarLog.LogError(e.ToString());
+            }
+
+            bodyState = new CAPI.ovrAvatar2TrackingBodyState();
+            return false;
+        }
+
+        [MonoPInvokeCallback(typeof(CAPI.BodySkeletonCallback))]
+        private static bool BodySkeletonCallback(ref CAPI.ovrAvatar2TrackingBodySkeleton nativeSkeleton, IntPtr userContext)
+        {
+            try
+            {
+                var context = GetInstance<OvrAvatarBodyTrackingContextBase>(userContext);
+                if (context == null) return false;
+                var skeleton = new OvrAvatarTrackingSkeleton(ref nativeSkeleton);
+                var result = context.GetBodySkeleton(ref skeleton);
+                skeleton.CopyToNative(ref nativeSkeleton);
+                return result;
+            }
+            catch (Exception e)
+            {
+                OvrAvatarLog.LogError(e.ToString());
+            }
+
+            return false;
+        }
+
+        [MonoPInvokeCallback(typeof(CAPI.BodyPoseCallback))]
+        private static bool BodyPoseCallback(ref CAPI.ovrAvatar2TrackingBodyPose nativePose, IntPtr userContext)
+        {
+            try
+            {
+                var context = GetInstance<OvrAvatarBodyTrackingContextBase>(userContext);
+                if (context == null) return false;
+                var pose = new OvrAvatarTrackingPose(ref nativePose);
+                var result = context.GetBodyPose(ref pose);
+                pose.CopyToNative(ref nativePose);
+                return result;
+            }
+            catch (Exception e)
+            {
+                OvrAvatarLog.LogError(e.ToString());
+            }
+
+            return false;
+        }
+
+        #endregion
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarBodyTrackingContextBase.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarBodyTrackingContextBase.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..fd526704ffb0563b8249c8d617a224b79dc65a05
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarBodyTrackingContextBase.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: dbec53b1d8244c52ae3228d33d594676
+timeCreated: 1605837544
\ No newline at end of file
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarCallbackContextBase.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarCallbackContextBase.cs
new file mode 100644
index 0000000000000000000000000000000000000000..caf508ccd8e27b5eddf75f244e350bdaec197776
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarCallbackContextBase.cs
@@ -0,0 +1,76 @@
+using System;
+using System.Collections.Generic;
+
+namespace Oculus.Avatar2
+{
+    /**
+     * Base class for C# code which is called from the native libovravatar2 lib.
+     * This class maintains a map between the native handle (a pointer) and the
+     * C# instances so the C# instance can be found given the native handle.
+     */
+    public abstract class OvrAvatarCallbackContextBase : IDisposable
+    {
+        private static Dictionary<int, OvrAvatarCallbackContextBase> instanceMap_ =
+            new Dictionary<int, OvrAvatarCallbackContextBase>();
+
+        private static Int32 nextId_;
+
+        protected readonly Int32 id;
+
+
+        protected OvrAvatarCallbackContextBase()
+        {
+            id = nextId_++;
+            instanceMap_.Add(id, this);
+        }
+
+        #region Static Methods
+
+        /**
+         * Gets the C# object given the native handle.
+         */
+        protected static T GetInstance<T>(IntPtr handle) where T : OvrAvatarCallbackContextBase
+        {
+            if (instanceMap_.TryGetValue(handle.ToInt32(), out var context))
+            {
+                return (T)context;
+            }
+
+            return null;
+        }
+
+        internal static void DisposeAll()
+        {
+            var map = instanceMap_;
+            instanceMap_ = new Dictionary<int, OvrAvatarCallbackContextBase>();
+            foreach (var kvp in map)
+            {
+                kvp.Value.Dispose();
+            }
+        }
+
+        #endregion
+
+        #region IDisposable Implementation
+
+        protected virtual void Dispose(bool disposing)
+        {
+            if (disposing)
+            {
+                instanceMap_.Remove(id);
+            }
+        }
+
+        public void Dispose()
+        {
+            Dispose(true);
+            GC.SuppressFinalize(this);
+        }
+
+        ~OvrAvatarCallbackContextBase()
+        {
+            Dispose(false);
+        }
+        #endregion
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarCallbackContextBase.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarCallbackContextBase.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..52a157e6dc54f44422d36b5bcc43f4dc0d05564d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarCallbackContextBase.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: b8bb665b7e404afdad99e2289102b3df
+timeCreated: 1605837529
\ No newline at end of file
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntitlement.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntitlement.cs
new file mode 100644
index 0000000000000000000000000000000000000000..b33d25d5af4d6c3f7861d2b1233dc87b820c7311
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntitlement.cs
@@ -0,0 +1,37 @@
+using System;
+
+namespace Oculus.Avatar2
+{
+    public static class OvrAvatarEntitlement
+    {
+        private static string logScope = "entitlement";
+        private static string _accessToken = "";
+
+        public static bool AccessTokenIsValid() => !String.IsNullOrEmpty(_accessToken);
+
+        public static void SetAccessToken(string token)
+        {
+            var result = CAPI.ovrAvatar2_UpdateAccessToken(token);
+            if (result.IsSuccess())
+            {
+                _accessToken = token;
+            }
+            else
+            {
+                OvrAvatarLog.LogError($"UpdateAccessToken Failed: {result}", logScope);
+            }
+        }
+
+        public static void ResendAccessToken()
+        {
+            if (AccessTokenIsValid())
+            {
+                SetAccessToken(_accessToken);
+            }
+            else
+            {
+                OvrAvatarLog.LogError("Cannot resend access token when there is no valid token.", logScope);
+            }
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntitlement.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntitlement.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..05d39ce1fd5ad0b61f4e2010c8b6417e904b139b
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntitlement.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: ff06d343bc931ae4191858d3f02760d5
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity.cs
new file mode 100644
index 0000000000000000000000000000000000000000..b89326a7c37ffec7e1195807f80babbecbd19813
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity.cs
@@ -0,0 +1,1821 @@
+// #define SUBMESH_DEBUGGING
+
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using System.Text;
+using Unity.Collections;
+using Unity.Collections.LowLevel.Unsafe;
+
+using UnityEngine;
+using UnityEngine.Profiling;
+using UnityEngine.Serialization;
+using UnityEngine.Android;
+
+#if UNITY_EDITOR
+using UnityEditor;
+#endif
+
+using Application = UnityEngine.Application;
+
+/**
+ * @file OvrAvatarEntity.cs
+ * This is the main file for a partial class.
+ * Other functionality is split out into the files below:
+ * - OvrAvatarEntity_Color.cs
+ * - OvrAvatarEntity_Debug.cs
+ * - OvrAvatarEntity_Loading.cs
+ * - OvrAvatarEntity_LOD.cs
+ * - OvrAvatarEntity_Rendering.cs
+ * - OvrAvatarEntity_Streaming.cs
+ */
+namespace Oculus.Avatar2
+{
+
+    /**
+     * Describes a single avatar with multiple levels of detail.
+     *
+     * An avatar entity encapsulates all of the assets associated with it.
+     * It provides access to the avatar's skeleton, meshes and materials
+     * and integrates input from tracking components to drive the
+     * avatar's motion. This base class provides defaults for all of these.
+     * To customize avatar behavior you must derive from OvrAvatarEntity.
+     * Then you can supply your application's preferences.
+     *
+     * # Loading an Avatar #
+     * An avatar may have many representations varying in complexity
+     * and fidelity. You can select among these capabilities
+     * by setting _creationInfo flags before calling [CreateEntity()](@ref OvrAvatarEntity.CreateEntity).
+     *
+     * You can load the avatar's assets from LoadUser to download the user's custom avatar.
+     * You can also load from [zip files](@ref LoadAssetsFromZipSource),
+     * [streaming assets](@ref LoadAssetsFromStreamingAssets] or
+     * [directly from memory](@ref LoadAssetsFromData) from within your subclass
+     * to provide application-specific avatar loading.
+     *
+     * The avatar's assets are loaded in the background so your application
+     * will not display the avatar immediately after load. The @ref IsPendingAvatar
+     * flag is set when loading starts and cleared when it has finished.
+     */
+    public partial class OvrAvatarEntity : MonoBehaviour
+    {
+        // If any variable is used across these files, it should be placed in this file
+
+        //:: Constants
+        private const string logScope = "entity";
+
+        protected const CAPI.ovrAvatar2EntityFeatures UPDATE_POSE_FEATURES =
+            CAPI.ovrAvatar2EntityFeatures.AnalyticIk |
+            CAPI.ovrAvatar2EntityFeatures.Animation;
+        protected const CAPI.ovrAvatar2EntityFeatures UPDATE_MOPRHS_FEATURES =
+            CAPI.ovrAvatar2EntityFeatures.Animation |
+            CAPI.ovrAvatar2EntityFeatures.UseDefaultFaceAnimations;
+
+        //:: Internal Structs & Classes
+        protected readonly struct SkeletonJoint : IComparable<SkeletonJoint>
+        {
+            public readonly string name;
+            public readonly Transform transform;
+            public readonly int parentIndex;
+            public readonly CAPI.ovrAvatar2NodeId nodeId;
+
+            public SkeletonJoint(string jointName, Transform tx, int parentIdx, CAPI.ovrAvatar2NodeId nodeIdentifier)
+            {
+                name = jointName;
+                transform = tx;
+                parentIndex = parentIdx;
+                nodeId = nodeIdentifier;
+            }
+
+            public SkeletonJoint(in SkeletonJoint oldJoint, string newName, int parentIdx)
+                : this(newName, oldJoint.transform, parentIdx, oldJoint.nodeId)
+            { }
+
+            public int CompareTo(SkeletonJoint other)
+            {
+                return other.parentIndex - parentIndex;
+            }
+        }
+
+        // TODO: This should just include LodCost?
+        protected sealed class PrimitiveRenderData : IDisposable
+        {
+            public PrimitiveRenderData(CAPI.ovrAvatar2NodeId mshNodeId
+                , CAPI.ovrAvatar2Id primId
+                , CAPI.ovrAvatar2PrimitiveRenderInstanceID renderInstId
+                , OvrAvatarRenderable ovrRenderable, OvrAvatarPrimitive ovrPrim)
+            {
+                meshNodeId = mshNodeId;
+                primitiveId = primId;
+                instanceId = renderInstId;
+                renderable = ovrRenderable;
+                skinnedRenderable = ovrRenderable as OvrAvatarSkinnedRenderable;
+                primitive = ovrPrim;
+            }
+
+            public readonly CAPI.ovrAvatar2NodeId meshNodeId;
+            public readonly CAPI.ovrAvatar2Id primitiveId;
+            public readonly CAPI.ovrAvatar2PrimitiveRenderInstanceID instanceId;
+            public readonly OvrAvatarRenderable renderable;
+            public readonly OvrAvatarSkinnedRenderable skinnedRenderable;
+            public readonly OvrAvatarPrimitive primitive;
+
+            public bool IsValid => instanceId != CAPI.ovrAvatar2PrimitiveRenderInstanceID.Invalid;
+
+            public void Dispose()
+            {
+                if (renderable != null) { renderable.Dispose(); }
+            }
+
+            public override string ToString()
+            {
+                return $"nodeId:{meshNodeId},primId:{primitiveId},instId:{instanceId},renderable:{renderable.name},prim:{primitive.name}";
+            }
+        }
+
+        //:: Public Properties
+
+        public bool HasJoints => SkeletonJointCount > 0;
+
+        // TODO: Remove and replace IsCreated and IsLoading with corresponding checks of LoadState?
+        public bool IsCreated => entityId != CAPI.ovrAvatar2EntityId.Invalid;
+
+        /// True if the model assets have been loaded, and are currently being applied to the avatar.
+        public bool IsApplyingModels { get; private set; }
+
+        /// True if the assets for an avatar are still loading.
+        public bool IsPendingAvatar
+        {
+            get => IsPendingCdnAvatar || IsPendingZipAvatar;
+        }
+
+        /// True if the avatar is still loading the default avatar.
+        public bool IsPendingDefaultModel { get; private set; }
+
+        /// True if the avatar is still loading assets from a ZIP file.
+        public bool IsPendingZipAvatar { get; private set; }
+
+        /// True if the avatar is still loading assets from a CDN.
+        public bool IsPendingCdnAvatar { get; private set; }
+
+        /// True if the avatar has any avatar loaded other than the default avatar.
+        public bool HasNonDefaultAvatar { get; private set; }
+
+        /// Number of joints in the avatar skeleton.
+        public int SkeletonJointCount => _skeleton.Length;
+
+        // TODO: what does "active" mean?
+        /// True if this entity is active.
+        public bool EntityActive
+        {
+            get => _lastActive;
+            set => _nextActive = value;
+        }
+
+        // External classes or overrides can add to this Action as needed.
+        public event Action<bool> OnCulled;
+
+        // The list of child GPU instances of the avatar
+        private readonly List<GPUInstancedAvatar> GPUInstances = new List<GPUInstancedAvatar>();
+
+        // Used to track fast loading of presets to ensure correct cleanup
+        private readonly List<string> fastLoadPresetPaths = new List<string>();
+
+        // Used to track when entity is loading full preset
+        private bool isLoadingFullPreset = false;
+
+        //:: Serialized Fields
+        /**
+         * Avatar creation configuration to use when creating this avatar.
+         * Specifies overall level of detail, body parts to display,
+         * rendering and animation characteristics and the avatar's point of view.
+         *
+         * If you are instantiating the same avatar asset more than once in the same
+         * scene, you must use the same *renderFilter* for both. Using different
+         * render filters on the same avatar asset is not supported.
+         *
+         * @see CAPI.ovrAvatar2EntityCreateInfo
+         * @see CAPI.ovrAvatar2EntityLODFlags
+         * @see CAPI.ovrAvatar2EntityManifestationFlags
+         * @see CAPI.ovrAvatar2EntityFeatures
+         * @see CAPI.ovrAvatar2EntityFilters
+         */
+        [Tooltip("Include the largest set of options that will be needed at runtime.")]
+        [SerializeField]
+        protected CAPI.ovrAvatar2EntityCreateInfo _creationInfo = new CAPI.ovrAvatar2EntityCreateInfo()
+        {
+            features = CAPI.ovrAvatar2EntityFeatures.Preset_Default,
+            renderFilters = new CAPI.ovrAvatar2EntityFilters
+            {
+                lodFlags = CAPI.ovrAvatar2EntityLODFlags.All,
+                manifestationFlags = CAPI.ovrAvatar2EntityManifestationFlags.Half,
+                viewFlags = CAPI.ovrAvatar2EntityViewFlags.All,
+                subMeshInclusionFlags = CAPI.ovrAvatar2EntitySubMeshInclusionFlags.All,
+                highQualityFlags = CAPI.ovrAvatar2EntityHighQualityFlags.None
+            }
+        };
+
+        [SerializeField]
+        private CAPI.ovrAvatar2EntityViewFlags _activeView = CAPI.ovrAvatar2EntityViewFlags.FirstPerson;
+        [SerializeField]
+        private CAPI.ovrAvatar2EntityManifestationFlags _activeManifestation = CAPI.ovrAvatar2EntityManifestationFlags.Half;
+
+        [Tooltip("These flags allow the original sub-meshes used to create the avatar to be enbled in the index buffer. This only works in Unity Editor. To use this for production, modify the CreationInfo.RenderFilters.SubMeshInclusionFlags above.")]
+        [SerializeField]
+        private CAPI.ovrAvatar2EntitySubMeshInclusionFlags _activeSubMeshes = CAPI.ovrAvatar2EntitySubMeshInclusionFlags.All;
+
+        [Tooltip("If new sub-mesh types are introduced after this version of the SDK, theis flag determines if they are visible by default. If excluding only one mesh it's best to heep this on. If including ony one mesh it's best to keep this off.")]
+        [SerializeField]
+        private bool _activeSubMeshesIncludeUntyped = true;
+
+        [SerializeField]
+        private CAPI.ovrAvatar2EntityHighQualityFlags _activeHighQuality = CAPI.ovrAvatar2EntityHighQualityFlags.None;
+
+        // Tracking
+        [Header("Tracking Input")]
+        [SerializeField]
+        private OvrAvatarBodyTrackingBehavior _bodyTracking;
+
+        [FormerlySerializedAs("_faceTrackingBehavior")]
+        [SerializeField]
+        private OvrAvatarFacePoseBehavior _facePoseBehavior;
+
+        [FormerlySerializedAs("_eyeTrackingBehavior")]
+        [SerializeField]
+        private OvrAvatarEyePoseBehavior _eyePoseBehavior;
+
+        [SerializeField]
+        private OvrAvatarLipSyncBehavior _lipSync;
+
+        [Header("Skeleton Output")]
+
+        [Tooltip("Joints which will always have their Unity.Transform updated")]
+        [SerializeField]
+        protected CAPI.ovrAvatar2JointType[] _criticalJointTypes = Array.Empty<CAPI.ovrAvatar2JointType>();
+
+        [Header("Experimental Features",order=30)]
+        [SerializeField]
+        [Tooltip("Enable experimental features on this Entity")]
+        private DefaultableBool _useExperimentalFeatures = DefaultableBool.Default;
+
+        // TODO: Decouple experimental systems from features once bugs are resolved
+        protected bool UseExperimentalFeatures => _useExperimentalFeatures.GetValue(OvrAvatarManager.Instance.UseExperimentalSystems);
+
+        private uint[] _unityUpdateJointIndices = Array.Empty<uint>(); // names with missing/inactive joints removed
+
+        //:: Protected Variables
+        protected internal virtual Transform _baseTransform => transform;
+
+        protected CAPI.ovrAvatar2EntityId entityId { get; private set; } = CAPI.ovrAvatar2EntityId.Invalid;
+
+        internal CAPI.ovrAvatar2EntityId internalEntityId => entityId;
+
+        protected UInt64 _userId = 0;
+
+        protected CAPI.ovrAvatar2EntityViewFlags ActiveView { get; set; }
+
+        //:: Private Variables
+
+        // Coroutine is started on OvrAvatarManager to prevent early stops from disabling the Monobehavior
+        private Coroutine _loadingRoutineBacking;
+        private Coroutine _loadingRoutine
+        {
+            get { return _loadingRoutineBacking; }
+            set
+            {
+                if (_loadingRoutineBacking != null)
+                {
+                    if (value != null)
+                    {
+                        OvrAvatarLog.LogError("More than one loading function running simultaneously!", logScope, this);
+                    }
+                    StopCoroutine(_loadingRoutineBacking);
+                }
+
+                _loadingRoutineBacking = value;
+            }
+        }
+
+        // Mappings
+        protected PrimitiveRenderData[][] _visiblePrimitiveRenderers = Array.Empty<PrimitiveRenderData[]>();
+
+        // TODO: This likely has no utility anymore? It's a mirror of _meshNodes w/ a different key
+        protected readonly Dictionary<CAPI.ovrAvatar2PrimitiveRenderInstanceID, PrimitiveRenderData[]> _primitiveRenderables
+            = new Dictionary<CAPI.ovrAvatar2PrimitiveRenderInstanceID, PrimitiveRenderData[]>();
+
+        // Render Data
+        private SkeletonJoint[] _skeleton = Array.Empty<SkeletonJoint>();
+
+        private readonly Dictionary<CAPI.ovrAvatar2NodeId, uint> _nodeToIndex = new Dictionary<CAPI.ovrAvatar2NodeId, uint>();
+        private readonly Dictionary<CAPI.ovrAvatar2JointType, CAPI.ovrAvatar2NodeId> _jointTypeToNodeId = new Dictionary<CAPI.ovrAvatar2JointType, CAPI.ovrAvatar2NodeId>();
+
+        private bool _lastActive = true;
+        private bool _nextActive = true;
+
+        // Suppress "never used" warning in non-editor builds
+#pragma warning disable 0169
+        // Suppress "never assigned to" warning
+#pragma warning disable 0649
+        [System.Serializable]
+        private struct DebugDrawing
+        {
+            public bool drawTrackingPose;
+            public bool drawBoneNames;
+            public bool drawSkelHierarchy;
+            public bool drawSkelHierarchyInGame;
+            public bool drawSkinTransformsInGame;
+            public bool drawCriticalJoints;
+            public Color skeletonColor;
+        }
+
+        [Header("Debug")]
+
+        [SerializeField]
+        private DebugDrawing _debugDrawing = new DebugDrawing
+        { skeletonColor = Color.red };
+#pragma warning restore 0649
+#pragma warning restore 0169
+
+        public bool TrackingPoseValid
+        {
+            get
+            {
+                if (IsCreated)
+                {
+                    var result = CAPI.ovrAvatar2Tracking_GetPoseValid(entityId, out var isValid);
+                    if (result == CAPI.ovrAvatar2Result.Success)
+                    {
+                        return isValid;
+                    }
+                    OvrAvatarLog.LogError($"ovrAvatar2Tracking_GetPoseValid failed with {result}");
+                    return false;
+                }
+                return false;
+            }
+        }
+
+        private MaterialPropertyBlock _gpuInstanceMaterialProperties = null;
+
+        /////////////////////////////////////////////////
+        //:: Core Unity Functions
+
+        #region Core Unity Functions
+
+        protected virtual void Awake()
+        {
+            CreateEntity();
+
+#if UNITY_EDITOR
+#if UNITY_2019_3_OR_NEWER
+            SceneView.duringSceneGui += OnSceneGUI;
+#else
+            SceneView.onSceneGUIDelegate += OnSceneGUI;
+#endif
+#endif
+#if UNITY_EDITOR || DEVELOPMENT_BUILD
+            Camera.onPostRender += OnCameraPostRender;
+#endif
+            InitAvatarLOD();
+        }
+
+        public void UpdateLODInternal()
+        {
+            if (!isActiveAndEnabled || !IsCreated)
+            {
+                return;
+            }
+
+            Profiler.BeginSample("OvrAvatarEntity::UpdateLODInternal");
+
+            UpdateAvatarLODOverride();
+
+            SendImportanceAndCost();
+
+            Profiler.EndSample();
+        }
+
+        // Non-virtual update run before virtual method
+        internal void PreSDKUpdateInternal(bool active, ref NativeArray<CAPI.ovrAvatar2Transform> transforms, uint nextTransformIndex)
+        {
+            // State validation
+            // Validate sync of tracked state
+            OvrAvatarLog.AssertConstMessage(active == _lastActive
+                , "Inconsistent activity state", logScope, this);
+
+            // Apply C# active/inactive state to nativeSDK
+            if (_lastActive != _nextActive)
+            {
+                if (CAPI.ovrAvatar2Entity_SetActive(entityId, _nextActive)
+                    .EnsureSuccess("ovrAvatar2Entity_SetActive", logScope, this))
+                {
+                    _lastActive = _nextActive;
+                }
+            }
+
+            // If active, apply C# state changes to nativeSDK
+            unsafe
+            {
+                void* transformsPtr = NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(transforms);
+                UnsafeUtility.WriteArrayElement<CAPI.ovrAvatar2Transform>(transformsPtr, (int)nextTransformIndex, _baseTransform.ToWorldOvrTransform().ConvertSpace());
+            }
+        }
+
+        internal void PostSDKUpdateInternal(bool active)
+        {
+            // TODO: Notify other systems of state change?
+            _lastActive = _nextActive = active;
+        }
+
+        // This behaviour is manually updated at a specific time during OvrAvatarManager::Update()
+        // to prevent issues with Unity script update ordering
+        internal void UpdateInternal(float dT)
+        {
+            if (!isActiveAndEnabled || !IsCreated) { return; }
+
+            Profiler.BeginSample("OvrAvatarEntity::UpdateInternal");
+
+            if (IsApplyingModels || IsPendingAvatar)
+            {
+                Profiler.BeginSample("OvrAvatarEntity::UpdateLoadingStatus");
+                // TODO: What other errors should we be checking for?
+                var result = entityStatus;
+
+                // Occurs when spec download fails
+                if (result == CAPI.ovrAvatar2Result.DataNotAvailable
+                // Occurs when failing to parse glb asset
+                    || result == CAPI.ovrAvatar2Result.InvalidData
+                // Occurs when CDN download fails
+                    || result == CAPI.ovrAvatar2Result.Unknown)
+                {
+#pragma warning disable 618
+                    LoadState = LoadingState.Failed;
+#pragma warning restore 618
+                    IsApplyingModels = false;
+                }
+                Profiler.EndSample();
+            }
+
+            // Skip active render state updates if the entity is not active
+            if (EntityActive)
+            {
+                Profiler.BeginSample("OvrAvatarEntity::EntityActiveRenderUpdate");
+                EntityActiveRenderUpdate(dT);
+                Profiler.EndSample();
+            }
+
+            // Only apply render updates if created and not loading
+            if (IsCreated)
+            {
+                Profiler.BeginSample("OvrAvatarEntity::PerFrameRenderUpdate");
+                PerFrameRenderUpdate(dT);
+                Profiler.EndSample();
+            }
+
+            if (!IsLocal && useRenderLods && !IsApplyingModels)
+            {
+                Profiler.BeginSample("OvrAvatarEntity::ComputeNetworkLod");
+                ComputeNetworkLod();
+                Profiler.EndSample();
+            }
+
+            // accessing obsolete members
+#pragma warning disable 618
+            if (_lastInvokedLoadState != LoadState)
+            {
+                _lastInvokedLoadState = LoadState;
+
+                LoadingStateChanged.Invoke(LoadState);
+                EntityLoadingStateChanged.Invoke(this);
+            }
+#pragma warning restore 618
+
+            Profiler.EndSample(); // "OvrAvatarEntity::UpdateInternal"
+        }
+
+        protected void OnDestroy()
+        {
+            bool shouldTearDown = IsCreated || CurrentState != AvatarState.None || _skeleton.Length > 0 ||
+                                  IsApplyingModels || _loadingRoutine != null || _jointMonitor != null ||
+                                  !(_avatarLOD is null);
+            if (shouldTearDown)
+            {
+                Teardown();
+            }
+            OnDestroyCalled();
+
+#if UNITY_EDITOR
+#if UNITY_2019_3_OR_NEWER
+            SceneView.duringSceneGui -= OnSceneGUI;
+#else
+            SceneView.onSceneGUIDelegate -= OnSceneGUI;
+#endif
+#endif
+#if UNITY_EDITOR || DEVELOPMENT_BUILD
+            Camera.onPostRender -= OnCameraPostRender;
+#endif
+        }
+
+        protected virtual void OnDestroyCalled() { }
+
+
+#if UNITY_EDITOR
+        protected virtual void OnValidate()
+        {
+            // Allows the tracking to up updated in the editor.
+            SetBodyTracking(_bodyTracking);
+
+            SetFacePoseProvider(_facePoseBehavior);
+            SetEyePoseProvider(_eyePoseBehavior);
+
+            SetLipSync(_lipSync);
+            if (IsCreated)
+            {
+                SetActiveManifestation(_activeManifestation);
+                SetActiveView(_activeView);
+                SetActiveSubMeshInclusion(_activeSubMeshes);
+                SetActiveHighQuality(_activeHighQuality);
+                Hidden = Hidden;
+            }
+        }
+#endif // UNITY_EDITOR
+
+        #endregion
+
+        /////////////////////////////////////////////////
+        //:: Protected Functions
+
+        #region Life Cycle
+
+        /**
+         * Initializes this OvrAvatarEntity instance.
+         * An avatar may have many representations varying in complexity
+         * and fidelity. These are described by @ref CAPI.ovrAvatar2EntityCreateInfo.
+         * The value of @ref OvrAvatarEntity._creationInfo specifies the
+         * avatar configuration to create this entity.
+         *
+         * You can customize entity creation and settings by extending this class and overriding
+         * [ConfigureCreationInfo](@ref OvrAvatarEntity.ConfigureCreationInfo) or
+         * [ConfigureEntity](@ref OvrAvatarEntity.ConfigureEntity)
+         * @see CAPI.ovrAvatar2EntityCreateInfo
+         * @see CAPI.ovrAvatar2EntityFeatures
+         * @see CAPI.ovrAvatar2EntityManifestationFlags
+         * @see CAPI.ovrAvatar2EntityViewFlags
+         */
+        protected void CreateEntity()
+        {
+            if (IsCreated)
+            {
+                OvrAvatarLog.LogWarning("Setup called on an entity that has already been set up.", logScope, this);
+                return;
+            }
+            if (_material == null)
+            {
+                _material = new OvrAvatarMaterial();
+            }
+
+            var createInfoOverride = ConfigureCreationInfo();
+            if (createInfoOverride.HasValue)
+            {
+                _creationInfo = createInfoOverride.Value;
+            }
+
+            if (UseExperimentalFeatures)
+            {
+                AddExperimentalFeatureFlags(ref _creationInfo.features);
+            }
+
+            IsPendingDefaultModel = HasAllFeatures(CAPI.ovrAvatar2EntityFeatures.UseDefaultModel);
+            ValidateSkinningType();
+            SetRequiredFeatures();
+
+            entityId = CreateNativeEntity(in _creationInfo);
+            if (entityId == CAPI.ovrAvatar2EntityId.Invalid)
+            {
+                OvrAvatarLog.LogError("Failed to create entity pointer.", logScope, this);
+                IsPendingDefaultModel = false;
+                return;
+            }
+
+            OvrAvatarManager.Instance.AddTrackedEntity(this);
+
+#pragma warning disable 618
+            LoadState = LoadingState.Created;
+#pragma warning restore 618
+
+            IsApplyingModels = false;
+            CurrentState = AvatarState.Created;
+            InvokeOnCreated();
+
+            SetActiveView(_activeView);
+            SetActiveManifestation(_activeManifestation);
+            SetActiveSubMeshInclusion(_activeSubMeshes);
+            SetActiveHighQuality(_activeHighQuality);
+
+            if (IsLocal)
+            {
+                ConfigureLocalAvatarSettings();
+            }
+            else
+            {
+                SetStreamingPlayback(true);
+            }
+
+            OvrAvatarLog.LogDebug($"Entity {entityId} created as a {(IsLocal ? "local" : "remote")} avatar", logScope, this);
+
+            SetBodyTracking(_bodyTracking);
+            SetFacePoseProvider(_facePoseBehavior);
+            SetEyePoseProvider(_eyePoseBehavior);
+            SetLipSync(_lipSync);
+
+            ConfigureEntity();
+
+            // For right now, only GPU skinning supports motion smoothing
+            if (UseGpuSkinning && UseMotionSmoothingRenderer)
+            {
+                var entityAnimator = new EntityAnimatorMotionSmoothing(this);
+
+                _entityAnimator = entityAnimator;
+                _interpolationValueProvider = entityAnimator;
+            }
+            else
+            {
+                _entityAnimator = new EntityAnimatorDefault(this);
+            }
+
+            if (UseGpuSkinning)
+            {
+                if (UseMotionSmoothingRenderer)
+                {
+                    _jointMonitor = OvrAvatarManager.Instance.UseCriticalJointJobs
+                        ? new OvrAvatarEntitySmoothingJointJobMonitor(this, _interpolationValueProvider)
+                        : new OvrAvatarEntitySmoothingJointMonitor(this, _interpolationValueProvider);
+                }
+                else
+                {
+                    _jointMonitor = new OvrAvatarEntityJointMonitor(this);
+                }
+            }
+            else
+            {
+                // Only GpuSkinning supports MotionSmoothing, if we didn't/can't use that - indicate that it is off
+                MotionSmoothingSettings = MotionSmoothingOptions.FORCE_OFF;
+            }
+        }
+
+        /**
+         * To entity settings, extend this class and override ConfigureEntity
+         *
+         * @code
+         * class MyAvatar : public OvrAvatarEntity
+         * {
+         *     protected override void ConfigureEntity()
+         *     {
+         *
+         *         // ex: set to third person
+         *         SetActiveView(...);
+         *         SetActiveManifestation(...);
+         *         SetActiveSubMeshInclusion(...);
+         *         SetBodyTracking(...);
+         *         SetLipSync(...);
+         *     }
+         * @endcode
+         *
+        **/
+        protected virtual void ConfigureEntity()
+        {
+            OvrAvatarLog.LogDebug("Base ConfigureEntity Invoked", logScope, this);
+        }
+
+        /**
+         * To configure creation info on an entity, extend this class and override ConfigureCreationInfo
+         *
+         * @code
+         * class MyAvatar : public OvrAvatarEntity
+         * {
+         *     protected override void ConfigureCreationInfo()
+         *     {
+         *        return new CAPI.ovrAvatar2EntityCreateInfo
+         *           {
+         *               features = CAPI.ovrAvatar2EntityFeatures.Preset_Default,
+         *               renderFilters = new CAPI.ovrAvatar2EntityFilters
+         *               {
+         *                   lodFlags = CAPI.ovrAvatar2EntityLODFlags.All,
+         *                   manifestationFlags = CAPI.ovrAvatar2EntityManifestationFlags.Half,
+         *                   viewFlags = CAPI.ovrAvatar2EntityViewFlags.All,
+         *                   subMeshInclusionFlags = CAPI.ovrAvatar2EntitySubMeshInclusionFlags.All
+         *               }
+         *           };
+         *      }
+         * };
+         *
+         *  It is necessary to set all of the members of
+         *  @ref ovrAvatar2EntityCreateInfo. The default values are unspecified.
+         * @endcode
+         *
+        **/
+        protected virtual CAPI.ovrAvatar2EntityCreateInfo? ConfigureCreationInfo()
+        {
+            OvrAvatarLog.LogDebug("Base ConfigureCreationInfo Invoked", logScope, this);
+            return null;
+        }
+
+        protected static void AddExperimentalFeatureFlags(ref CAPI.ovrAvatar2EntityFeatures featureFlags)
+        {
+            const CAPI.ovrAvatar2EntityFeatures reservedBit = (CAPI.ovrAvatar2EntityFeatures)1;
+            featureFlags |= ~(CAPI.ovrAvatar2EntityFeatures.All | reservedBit);
+        }
+
+        public class GPUInstancedAvatar : MonoBehaviour
+        {
+            public Transform parentTransform = null;
+            // TODO: this can be passed to the class as a configuration parameter
+            private readonly Matrix4x4 _reflectionMatrix = new Matrix4x4(
+                new Vector4(-1.0f, 0.0f, 0.0f, 0.0f),
+                new Vector4(0.0f, 1.0f, 0.0f, 0.0f),
+                new Vector4(0.0f, 0.0f, 1.0f, 0.0f),
+                new Vector4(0.0f, 0.0f, 0.0f, 1.0f));
+
+            public void SetTransform(Vector3 position, Quaternion rotation)
+            {
+                transform.localPosition = position;
+
+                // copy scale and rotation from the parent
+                transform.localScale = parentTransform.localScale;
+                transform.rotation = parentTransform.rotation;
+
+                transform.rotation *= rotation;
+            }
+
+            public Matrix4x4 GetTransform()
+            {
+                return _reflectionMatrix * transform.localToWorldMatrix;
+            }
+        }
+
+        public GameObject CreateGPUInstance()
+        {
+            // Will be used later during rendering
+            if (_gpuInstanceMaterialProperties == null)
+            {
+                _gpuInstanceMaterialProperties = new MaterialPropertyBlock();
+            }
+
+            SetActiveView(CAPI.ovrAvatar2EntityViewFlags.ThirdPerson);
+            var instance = new GameObject($"{name}_gpu_instance_{GPUInstances.Count}");
+            var gpuInstancedAvatar = instance.AddComponent<GPUInstancedAvatar>();
+            GPUInstances.Add(gpuInstancedAvatar);
+            return instance;
+        }
+
+        public bool DestroyGPUInstance(GameObject instance)
+        {
+            OvrAvatarLog.Assert(instance != null, logScope, this);
+            var gpuInstancedAvatar = instance.GetComponent<GPUInstancedAvatar>();
+            if (gpuInstancedAvatar == null)
+            {
+                OvrAvatarLog.LogError("Attempted to destroy GPUInstance which does not have a GPUInstancedAvatar component", logScope, instance);
+                return false;
+            }
+            return DestroyGPUInstance(gpuInstancedAvatar);
+        }
+
+        public bool DestroyGPUInstance(GPUInstancedAvatar instance)
+        {
+            OvrAvatarLog.Assert(instance != null, logScope, this);
+            if (!GPUInstances.Remove(instance))
+            {
+                OvrAvatarLog.LogError($"Passed instance {instance} doesn't belong to current OvrAvatarEntity", logScope, instance);
+                return false;
+            }
+
+            GPUInstancedAvatar.Destroy(instance);
+            return true;
+        }
+
+        private OvrAvatarSkinnedRenderable findActiveRenderable()
+        {
+            OvrAvatarSkinnedRenderable activeRenderable = null;
+            foreach (var keyval in _skinnedRenderables)
+            {
+                OvrAvatarSkinnedRenderable renderable = keyval.Value;
+                if (renderable.isActiveAndEnabled)
+                {
+                    activeRenderable = renderable;
+                    break;
+                }
+            }
+
+            return activeRenderable;
+        }
+
+        private void UpdateGPUInstances()
+        {
+            if (GPUInstances.Count == 0)
+            {
+                return;
+            }
+
+            Profiler.BeginSample("OvrAvatarEntity::UpdateGPUInstances");
+
+            var renderable = findActiveRenderable();
+            if (renderable == null) { return; }
+
+            renderable.GetRenderParameters(out var mesh, out var material, out var transform, _gpuInstanceMaterialProperties);
+            if (mesh == null || material == null || transform == null)
+            {
+                return;
+            }
+
+            foreach (var instance in GPUInstances)
+            {
+                // TODO: this loop can be replaced with calling DrawMeshInstanced for batched GPU instancing
+                // but as of 2/16/2022 this doesn't seem to work with the material we're using, perhaps
+                // something is making it incompatible with batched instancing
+                // Update parent transform so rotation and scale can be copied
+                instance.parentTransform = transform;
+                Graphics.DrawMesh(mesh, instance.GetTransform(), material, 0, null, 0, _gpuInstanceMaterialProperties);
+            }
+
+            Profiler.EndSample();
+        }
+
+        private readonly List<OvrAvatarSkinnedRenderable> _perFrameRenderableCache = new List<OvrAvatarSkinnedRenderable>();
+        private void PerFrameRenderUpdate(float dT)
+        {
+            Debug.Assert(IsCreated);
+
+            // Find the visible skinned renderables to update. Note if the renderers all have valid animation data
+            _perFrameRenderableCache.Clear();
+            var activeAndEnabledRenderables = _perFrameRenderableCache;
+            bool doAllRenderersHaveValidAnimationData = true;
+            foreach (var primRenderables in _visiblePrimitiveRenderers)
+            {
+                foreach (var primRenderable in primRenderables)
+                {
+                    var skinnedRenderable = primRenderable.skinnedRenderable;
+                    if (skinnedRenderable is null || !skinnedRenderable.isActiveAndEnabled) { continue; }
+
+                    activeAndEnabledRenderables.Add(skinnedRenderable);
+
+                    if (doAllRenderersHaveValidAnimationData && !skinnedRenderable.IsAnimationDataCompletelyValid)
+                    {
+                        // Not valid animation data, thus all renderers' data is not valid
+                        doAllRenderersHaveValidAnimationData = false;
+                    }
+                }
+            }
+
+            Profiler.BeginSample("OvrAvatarEntity::UpdateAnimationTime");
+            _entityAnimator.UpdateAnimationTime(dT, doAllRenderersHaveValidAnimationData);
+            Profiler.EndSample();
+
+            Profiler.BeginSample("OvrAvatarEntity::JointMonitor::UpdateJoints");
+            _jointMonitor?.UpdateJoints(dT);
+            Profiler.EndSample();
+
+            BroadcastPerFrameRenderUpdateToVisibleRenderables(activeAndEnabledRenderables);
+            UpdateGPUInstances();
+        }
+
+        private void BroadcastPerFrameRenderUpdateToVisibleRenderables(in List<OvrAvatarSkinnedRenderable> visibleRenderables)
+        {
+            foreach (var skinnedRenderable in visibleRenderables)
+            {
+                skinnedRenderable.RenderFrameUpdate();
+            }
+        }
+
+        private void EntityActiveRenderUpdate(float dT)
+        {
+            Debug.Assert(EntityActive && IsCreated);
+
+            // Check that our pose is valid
+            if (!QueryEntityPose(out var entityPose, out var hierarchyVersion)) { return; }
+            OvrAvatarLog.Assert(hierarchyVersion != CAPI.ovrAvatar2HierarchyVersion.Invalid, logScope, this);
+
+            if (!QueryEntityRenderState(out var renderState)) { return; }
+            OvrAvatarLog.Assert(renderState.allNodesVersion != CAPI.ovrAvatar2EntityRenderStateVersion.Invalid, logScope, this);
+            OvrAvatarLog.Assert(renderState.visibleNodesVersion != CAPI.ovrAvatar2EntityRenderStateVersion.Invalid, logScope, this);
+            OvrAvatarLog.AssertConstMessage(hierarchyVersion == renderState.hierarchyVersion
+                , "hierarchyVersion mismatch", logScope, this);
+
+            // TODO: Checking this isn't really part of "RenderUpdate", move outside this method
+            if (_currentHierarchyVersion != hierarchyVersion)
+            {
+                // Verify an update to this version isn't already in progress
+                if (_targetHierarchyVersion != hierarchyVersion)
+                {
+                    // HACK: If a model is currently being applied, wait for it to finish to avoid race conditions
+                    if (IsApplyingModels)
+                    {
+                        // TODO: Cancel current skeleton update if one is in progress
+                        return;
+                    }
+                    // END-HACK: If a model is currently being applied, wait for it to finish to avoid race conditions
+                    // Refresh current Unity version to match the current runtime version
+                    QueueUpdateSkeleton(hierarchyVersion);
+                }
+
+                // We can not update our current render objects w/ the native runtime state (out of sync)
+                // Leave Unity in previous state (freeze render updates)
+                return;
+            }
+
+            // TODO: We shouldn't need to stall here, this matches "legacy" behavior more accurately though
+            if (_currentVisibleNodesVersion != renderState.visibleNodesVersion)
+            {
+                LoadSync_CheckForNewRenderables_Internal(in renderState, out var result);
+
+                if (result.newPrimitiveIds != null)
+                {
+                    // Something went wrong or we're loading primitives, block for primitive update
+                    QueueBuildPrimitives();
+
+                    // Leave previous state (freeze render updates)
+                    return;
+                }
+
+                // TODO: Build Renderables before primitives finish loading?
+                if (result.newRenderIndices != null)
+                {
+                    // Build new renderables synchronously and render this frame
+                    // TODO: Might be worth timeslicing?
+                    using (var newIdxArray = new NativeArray<UInt32>(result.newRenderIndices.Count
+                        , Allocator.Temp, NativeArrayOptions.UninitializedMemory))
+                    {
+                        newIdxArray.CopyFrom(result.newRenderIndices);
+
+                        LoadSync_BuildPrimitives_Internal(in renderState
+                            , newIdxArray, result.targetVisVersion);
+                    }
+                }
+
+                // Update visibility flags
+                UpdateVisibility(in renderState);
+            }
+
+            // Clean up nodes which are no longer in use
+            // TODO: This likely isn't the best timing for this?
+            UpdateAllNodes(in renderState);
+
+            Profiler.BeginSample("OvrAvatarEntity::OnActiveRender");
+            OnActiveRender(in renderState, in entityPose, hierarchyVersion, dT);
+            Profiler.EndSample();
+        }
+
+        private void QueueUpdateSkeleton(CAPI.ovrAvatar2HierarchyVersion hierarchyVersion)
+        {
+            _targetHierarchyVersion = hierarchyVersion;
+
+            OvrAvatarLog.Assert(!IsApplyingModels);
+            // Reset load state
+#pragma warning disable 618
+            LoadState = LoadingState.Loading;
+#pragma warning restore 618
+            IsApplyingModels = true;
+
+            OvrAvatarManager.Instance.QueueLoadAvatar(this, () =>
+            {
+                _loadingRoutine = OvrAvatarManager.Instance.StartCoroutine(LoadAsync_BuildSkeletonAndPrimitives());
+            });
+        }
+        private void QueueBuildPrimitives()
+        {
+            OvrAvatarLog.Assert(!IsApplyingModels);
+            // Reset load state
+#pragma warning disable 618
+            LoadState = LoadingState.Loading;
+#pragma warning restore 618
+            IsApplyingModels = true;
+
+            OvrAvatarManager.Instance.QueueLoadAvatar(this, () =>
+            {
+                _loadingRoutine = OvrAvatarManager.Instance.StartCoroutine(LoadAsync_BuildPrimitives());
+            });
+        }
+
+        // TODO*: Better naming when the lifecycle of the entity is fixed properly
+        private void OnActiveRender(in CAPI.ovrAvatar2EntityRenderState renderState
+            , in CAPI.ovrAvatar2Pose entityPose
+            , CAPI.ovrAvatar2HierarchyVersion hierVersion
+            , float dT)
+        {
+            OvrAvatarLog.AssertConstMessage(IsCreated
+                , "OnActiveRender while not created", logScope, this);
+
+            OvrAvatarLog.Assert(_currentHierarchyVersion == hierVersion, logScope, this);
+
+            _entityAnimator.AddNewAnimationFrame(
+                Time.time,
+                dT,
+                entityPose,
+                renderState);
+
+            // Call obsolete function
+#pragma warning disable 618
+            Profiler.BeginSample("OvrAvatarEntity::DidRender");
+            DidRender();
+            Profiler.EndSample();
+#pragma warning restore 618
+        }
+
+        [Obsolete("DidRender is obsolete.", false)]
+        protected virtual void DidRender() { }
+
+        /**
+         * Tear down this entity, stop all tracking and rendering
+         * and free associated memory.
+         * @see CreateEntity
+         */
+        public void Teardown()
+        {
+            System.Diagnostics.Debug.Assert(entityId != CAPI.ovrAvatar2EntityId.Invalid);
+
+            if (CurrentState != AvatarState.None)
+            {
+                InvokePreTeardown();
+            }
+
+            // If not, we are shutting down and will skip some steps
+            bool hasManagerInstance = OvrAvatarManager.hasInstance;
+
+            OvrAvatarLog.LogDebug($"Tearing down entity {entityId}", logScope, this);
+            if (_loadingRoutine != null)
+            {
+                StopCoroutine(_loadingRoutine);
+                _loadingRoutine = null;
+
+                if (hasManagerInstance)
+                {
+                    OvrAvatarManager.Instance.FinishedAvatarLoad();
+                }
+            }
+
+            if (hasManagerInstance)
+            {
+                OvrAvatarManager.Instance.RemoveLoadRequests(this);
+                if (IsApplyingModels)
+                {
+                    OvrAvatarManager.Instance.RemoveQueuedLoad(this);
+                }
+
+                if (IsCreated)
+                {
+                    if (!IsLocal)
+                    {
+                        SetStreamingPlayback(false);
+                    }
+
+                    OvrAvatarManager.Instance.RemoveTrackedEntity(this);
+                }
+            }
+
+            if (IsCreated)
+            {
+                var didDestroy = DestroyNativeEntity();
+                if (!didDestroy)
+                {
+                    OvrAvatarLog.LogWarning("Failed to destroy native entity", logScope, this);
+                }
+            }
+
+#pragma warning disable 618
+            LoadState = LoadingState.NotCreated;
+#pragma warning restore 618
+            IsApplyingModels = false;
+            CurrentState = AvatarState.None;
+
+            ResetLODRange();
+
+            if (!(_avatarLOD is null))
+            {
+                TeardownLodCullingPoints();
+                ShutdownAvatarLOD();
+                _avatarLOD = null;
+            }
+
+            // TODO: These will get destroyed w/ LODObjects - though being explicit would be nice
+            // This trips an error in Unity currently,
+            /* "Can't destroy Transform component of 'S0_L2_M1_V1_optimized_geom,0'.
+             * If you want to destroy the game object, please call 'Destroy' on the game object instead.
+             * Destroying the transform component is not allowed." */
+            //for (int i = 0; i < _primitiveRenderables.Length; ++i)
+            //{
+            //    ref readonly var primRend = ref _primitiveRenderables[i];
+            //    DestroyRenderable(in primRend);
+            //}
+            _visiblePrimitiveRenderers = Array.Empty<PrimitiveRenderData[]>();
+            _skinnedRenderables.Clear();
+            _meshNodes.Clear();
+
+            _jointMonitor?.Dispose();
+            _jointMonitor = null;
+
+            DestroySkeleton();
+
+            _currentVisibleNodesVersion = _targetVisibleNodesVersion = CAPI.ovrAvatar2EntityRenderStateVersion.Invalid;
+            _currentAllNodesVersion = _targetAllNodesVersion = CAPI.ovrAvatar2EntityRenderStateVersion.Invalid;
+            _currentHierarchyVersion = _targetHierarchyVersion = CAPI.ovrAvatar2HierarchyVersion.Invalid;
+        }
+
+        #endregion
+
+        #region Asset Loading Requests
+
+        /**
+         * Load avatar assets from a block of data.
+         * @param data         C++ pointer to asset data.
+         * @param size         byte size of asset data block.
+         * @param callbackName name of function to call after data has loaded.
+         * @see LoadAssetsFromZipSource
+         */
+        protected void LoadAssetsFromData(IntPtr data, UInt32 size, string callbackName)
+        {
+            if (!IsCreated)
+            {
+                OvrAvatarLog.LogError("Cannot load assets before entity has been created.", logScope, this);
+                return;
+            }
+
+            CAPI.ovrAvatar2Result result = CAPI.OvrAvatarEntity_LoadMemoryWithFilters(entityId, data, size, callbackName, _creationInfo.renderFilters, out var loadRequestId);
+            if (result.IsSuccess())
+            {
+                ClearFailedLoadState();
+                OvrAvatarManager.Instance.RegisterLoadRequest(this, loadRequestId);
+            }
+            else
+            {
+                OvrAvatarLog.LogError($"Failed to load asset from data of size {size}. {result}", logScope, this);
+            }
+        }
+
+        /**
+         * Load avatar assets from a block of data.
+         * @param data         byte array containing asset data.
+         * @param callbackName name of function to call after data has loaded
+         * @see LoadAssetsFromZipSource
+         */
+        protected void LoadAssetsFromData(byte[] data, string callbackName)
+        {
+            if (!IsCreated)
+            {
+                OvrAvatarLog.LogError("Cannot load assets before entity has been created.", logScope, this);
+                return;
+            }
+
+            GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
+            LoadAssetsFromData(handle.AddrOfPinnedObject(), (UInt32)data.Length, callbackName);
+            handle.Free();
+        }
+
+        #endregion
+
+        #region Getters/Setters
+
+        /**
+         * Gets the current avatar view flags (first person, third person).
+         * It is possible for multiple flags to be true at once.
+         * @returns CAPI.ovrAvatar2EntityViewFlags designating avatar viewpoint.
+         * @see CAPI.ovrAvatar2EntityViewFlags
+         * @see SetActiveView
+         */
+        protected CAPI.ovrAvatar2EntityViewFlags GetActiveView() => _activeView;
+
+        /**
+         * Selects the avatar viewpoint (first person, third person).
+         * @param CAPI.ovrAvatar2EntityViewFlags designating avatar viewpoint.
+         * @see CAPI.ovrAvatar2EntityViewFlags
+         * @see GetActiveView
+         */
+        protected void SetActiveView(CAPI.ovrAvatar2EntityViewFlags view)
+        {
+            var result = CAPI.ovrAvatar2Entity_SetViewFlags(entityId, _hidden ? CAPI.ovrAvatar2EntityViewFlags.None : view);
+            if (result.IsSuccess())
+            {
+                _activeView = view;
+            }
+            else
+            {
+                OvrAvatarLog.LogError($"SetViewFlags Failed: {result}");
+            }
+        }
+
+        /**
+         * Gets the body parts included for this avatar.
+         * It is possible for multiple manifestations to be active at once
+         * @returns CAPI.ovrAvatar2EntityManifestationFlags designating body parts.
+         * @see CAPI.ovrAvatar2EntityManifestationFlags
+         * @see SetActiveManifestation
+         */
+        protected CAPI.ovrAvatar2EntityManifestationFlags GetActiveManifestation() => _activeManifestation;
+
+        /**
+         * Selects the body parts to include for this avatar.
+         * Loading the changes will be triggered on the next Update
+         * @param CAPI.ovrAvatar2EntityManifestationFlags designating avatar body parts to include
+         * @see CAPI.ovrAvatar2EntityManifestationFlags
+         * @see GetActiveManifestation
+         */
+        protected void SetActiveManifestation(CAPI.ovrAvatar2EntityManifestationFlags manifestation)
+        {
+            if (IsCreated)
+            {
+                var result = CAPI.ovrAvatar2Entity_SetManifestationFlags(entityId, manifestation);
+                if (result.IsSuccess())
+                {
+                    _activeManifestation = manifestation;
+                }
+                else
+                {
+                    OvrAvatarLog.LogError($"SetManifestationFlags Failed: {result}");
+                }
+            }
+            else
+            {
+                _activeManifestation = manifestation;
+            }
+        }
+
+        protected void SetActiveSubMeshInclusion(CAPI.ovrAvatar2EntitySubMeshInclusionFlags subMeshInclusion)
+        {
+            var result = CAPI.ovrAvatar2Entity_SetSubMeshInclusionFlags(entityId, subMeshInclusion);
+            if (!result.EnsureSuccess("ovrAvatar2Entity_SetSubMeshInclusionFlags", logScope, this))
+            {
+                _activeSubMeshes = subMeshInclusion;
+
+                // TODO: change this to a class member with a mx number of meshes
+                List<UnityEngine.Rendering.SubMeshDescriptor> subMeshDescriptors = new List<UnityEngine.Rendering.SubMeshDescriptor>(64);
+                foreach (PrimitiveRenderData[] renderDatas in _visiblePrimitiveRenderers)
+                {
+                    foreach (PrimitiveRenderData renderData in renderDatas)
+                    {
+                        OvrAvatarRenderable renderable = renderData.renderable;
+                        MeshRenderer renderer = renderable.rendererComponent as MeshRenderer;
+                        if (renderer != null)
+                        {
+                            MeshFilter filter = renderer.GetComponent<MeshFilter>();
+                            Mesh mesh = filter.sharedMesh;
+#if SUBMESH_DEBUGGING
+                            OvrAvatarLog.LogInfo("BEFORE MeshInfo: " + mesh.triangles.Length + " triangles, " + mesh.subMeshCount + " submesh count", logScope);
+                            for (int i = 0; i < mesh.subMeshCount; i++) {
+                                UnityEngine.Rendering.SubMeshDescriptor desc = mesh.GetSubMesh(i);
+                                OvrAvatarLog.LogInfo("BEFORE SubMeshInfo[" + i + "]: " + desc.indexStart + ", " + desc.indexCount, logScope);
+                            }
+#endif
+                            int originalNumberIndices = renderable.originalNumberIndices;
+                            UInt16[] originalIndexBuffer = renderable.originalIndexBuffer;
+                            if (originalNumberIndices <= 0 && mesh.triangles.Length > 0)
+                            {
+                                renderable.originalNumberIndices = mesh.triangles.Length;
+                                renderable.originalIndexBuffer = Array.ConvertAll(mesh.triangles, item => (UInt16)item);
+
+                                originalNumberIndices = renderable.originalNumberIndices;
+                                originalIndexBuffer = renderable.originalIndexBuffer;
+                            }
+
+                            if (originalNumberIndices <= 0)
+                            {
+                                // we can't continue at this point, it may be indicative that the model is still loading/unloaded
+                            }
+                            else if ((subMeshInclusion & CAPI.ovrAvatar2EntitySubMeshInclusionFlags.All) == CAPI.ovrAvatar2EntitySubMeshInclusionFlags.All)
+                            {
+                                int unitySubMeshIndex = 0;
+                                UnityEngine.Rendering.SubMeshDescriptor desc = new UnityEngine.Rendering.SubMeshDescriptor(0, originalNumberIndices);
+                                mesh.SetIndexBufferParams(originalNumberIndices, UnityEngine.Rendering.IndexFormat.UInt16);
+                                mesh.SetIndexBufferData<UInt16>(originalIndexBuffer, 0, 0, originalNumberIndices);
+                                mesh.subMeshCount = 1;
+                                mesh.SetTriangles(originalIndexBuffer, unitySubMeshIndex);
+                                mesh.SetSubMesh(unitySubMeshIndex, desc);
+                            }
+                            else
+                            {
+                                // each primitive has it's own submeshes so do a for loop on the subMeshes
+                                uint avatarSdkSubMeshCount = 0;
+                                var countResult = CAPI.ovrAvatar2Primitive_GetSubMeshCount(renderData.primitiveId, out avatarSdkSubMeshCount);
+                                if (countResult.IsSuccess())
+                                {
+                                    unsafe
+                                    {
+                                        int totalSubMeshBufferSize = 0;
+
+                                        for (uint subMeshIndex = 0; subMeshIndex < avatarSdkSubMeshCount; subMeshIndex++)
+                                        {
+
+                                            CAPI.ovrAvatar2PrimitiveSubmesh subMesh;
+                                            var subMeshResult = CAPI.ovrAvatar2Primitive_GetSubMeshByIndex(renderData.primitiveId, subMeshIndex, out subMesh);
+                                            if (subMeshResult.IsSuccess())
+                                            {
+                                                CAPI.ovrAvatar2EntitySubMeshInclusionFlags localInclusionFlags = subMesh.inclusionFlags;
+                                                if (!(localInclusionFlags == CAPI.ovrAvatar2EntitySubMeshInclusionFlags.All && !_activeSubMeshesIncludeUntyped) && (localInclusionFlags & subMeshInclusion) != 0)
+                                                {
+                                                    UnityEngine.Rendering.SubMeshDescriptor desc = new UnityEngine.Rendering.SubMeshDescriptor((int)subMesh.indexStart, (int)subMesh.indexCount);
+                                                    subMeshDescriptors.Add(desc);
+                                                    totalSubMeshBufferSize += desc.indexCount;
+                                                }
+                                            }
+                                        }
+
+                                        mesh.SetIndexBufferParams(originalNumberIndices, UnityEngine.Rendering.IndexFormat.UInt16);
+                                        UInt16[] indexBufferCopy;
+                                        {
+                                            indexBufferCopy = new UInt16[totalSubMeshBufferSize];
+                                            // Workaround because Sub Mesh API is not working as planned...
+                                            int subMeshDestination = 0;
+                                            for (int unitySubMeshIndex = 0; unitySubMeshIndex < subMeshDescriptors.Count; unitySubMeshIndex++)
+                                            {
+                                                var desc = subMeshDescriptors[unitySubMeshIndex];
+                                                for (int indexNumber = desc.indexStart; indexNumber < desc.indexCount + desc.indexStart; indexNumber++)
+                                                {
+                                                    indexBufferCopy[subMeshDestination] = renderable.originalIndexBuffer[indexNumber];
+                                                    subMeshDestination++;
+                                                }
+                                            }
+                                        }
+                                        subMeshDescriptors.Clear();
+
+                                        mesh.subMeshCount = 1;
+                                        mesh.SetIndexBufferData<UInt16>(indexBufferCopy, 0, 0, totalSubMeshBufferSize);
+                                        mesh.SetTriangles(indexBufferCopy, 0);
+                                    }
+                                }
+                            }
+#if SUBMESH_DEBUGGING
+                            OvrAvatarLog.LogInfo("AFTER MeshInfo: " + mesh.triangles.Length + " triangles, " + mesh.subMeshCount + " submesh count", logScope);
+                            for (int i = 0; i < mesh.subMeshCount; i++)
+                            {
+                                UnityEngine.Rendering.SubMeshDescriptor desc = mesh.GetSubMesh(i);
+                                OvrAvatarLog.LogInfo("AFTER SubMeshInfo[" + i + "]: " + desc.indexStart + ", " + desc.indexCount, logScope);
+                            }
+#endif
+                        }
+                    }
+                }
+            }
+        }
+
+        /**
+         * Gets the current avatar high quality flags (normal map, hair map).
+         * It is possible for multiple flags to be true at once.
+         * @returns CAPI.ovrAvatar2EntityHighQualityFlags designating avatar choices.
+         * @see CAPI.ovrAvatar2EntityHighQualityFlags
+         * @see SetActiveHighQualityFlags
+         */
+        protected CAPI.ovrAvatar2EntityHighQualityFlags GetActiveHighQuality() => _activeHighQuality;
+
+        /**
+         * Selects the high quality flags (normal map, hair map).
+         * @param CAPI.ovrAvatar2EntityHighQualityFlags designating avatar viewpoint.
+         * @see CAPI.ovrAvatar2EntityViewFlags
+         * @see GetActiveHighQualityFlags
+         */
+        protected void SetActiveHighQuality(CAPI.ovrAvatar2EntityHighQualityFlags highQuality)
+        {
+            var result = CAPI.ovrAvatar2Entity_SetHighQualityFlags(entityId, highQuality);
+            if (result.IsSuccess())
+            {
+                _activeHighQuality = highQuality;
+
+                // TODO: Should we iterate over all the renderers here the way the submeshes do?
+                // TODO: change this to a class member with a mx number of meshes
+                List<UnityEngine.Rendering.SubMeshDescriptor> subMeshDescriptors = new List<UnityEngine.Rendering.SubMeshDescriptor>(64);
+                foreach (PrimitiveRenderData[] renderDatas in _visiblePrimitiveRenderers)
+                {
+                    foreach (PrimitiveRenderData renderData in renderDatas)
+                    {
+                        OvrAvatarRenderable renderable = renderData.renderable;
+                        MeshRenderer renderer = renderable.rendererComponent as MeshRenderer;
+                        if (renderer != null)
+                        {
+                            bool enableNormalMap = (_activeHighQuality & CAPI.ovrAvatar2EntityHighQualityFlags.NormalMaps) != 0;
+                            bool enablePropertyHairMap = (_activeHighQuality & CAPI.ovrAvatar2EntityHighQualityFlags.PropertyHairMap) != 0;
+                            if (enableNormalMap)
+                            {
+                                renderer.sharedMaterial.EnableKeyword("HAS_NORMAL_MAP_ON");
+                                renderer.sharedMaterial.SetFloat("HAS_NORMAL_MAP", 1.0f);
+                            }
+                            else
+                            {
+                                renderer.sharedMaterial.DisableKeyword("HAS_NORMAL_MAP_ON");
+                                renderer.sharedMaterial.SetFloat("HAS_NORMAL_MAP", 0.0f);
+                            }
+
+                            if (enablePropertyHairMap)
+                            {
+                                renderer.sharedMaterial.EnableKeyword("ENABLE_HAIR_ON");
+                                renderer.sharedMaterial.SetFloat("ENABLE_HAIR", 1.0f);
+                            }
+                            else
+                            {
+                                renderer.sharedMaterial.DisableKeyword("ENABLE_HAIR_ON");
+                                renderer.sharedMaterial.SetFloat("ENABLE_HAIR", 0.0f);
+                            }
+                        }
+                    }
+                }
+            }
+            else
+            {
+                OvrAvatarLog.LogError($"SetHighQualityFlags Failed: {result}");
+            }
+        }
+
+        #endregion
+
+        #region Misc
+        // TODO: Remove pose requirement
+        protected string GetNameForNode(CAPI.ovrAvatar2NodeId nodeId, in CAPI.ovrAvatar2Pose tempPose)
+        {
+            return CAPI.OvrAvatar2Entity_GetNodeName(entityId, nodeId);
+        }
+
+        protected CAPI.ovrAvatar2NodeId GetNodeForType(CAPI.ovrAvatar2JointType type)
+        {
+            if (!_jointTypeToNodeId.TryGetValue(type, out var nodeId))
+            {
+                return CAPI.ovrAvatar2NodeId.Invalid;
+            }
+            return nodeId;
+        }
+
+        protected uint GetIndexForNode(CAPI.ovrAvatar2NodeId nodeId)
+        {
+            if (!_nodeToIndex.TryGetValue(nodeId, out var idx))
+            {
+                OvrAvatarLog.LogError($"Unable to find index for nodeId '{nodeId}'", logScope, this);
+                idx = uint.MaxValue;
+            }
+            return idx;
+        }
+
+        protected SkeletonJoint GetSkeletonJoint(int index)
+        {
+            Debug.Assert(index >= 0,
+                $"Index {index} out of range for Skeleton joints. Joint count is {SkeletonJointCount}");
+
+            return GetSkeletonJoint((uint)index);
+        }
+
+        protected SkeletonJoint GetSkeletonJoint(uint index)
+        {
+            Debug.Assert(index < SkeletonJointCount,
+                $"Index {index} out of range for Skeleton joints. Joint count is {SkeletonJointCount}");
+
+            return _skeleton[index];
+        }
+
+        protected SkeletonJoint? GetSkeletonJoint(CAPI.ovrAvatar2JointType jointType)
+        {
+            var nodeId = GetNodeForType(jointType);
+            if (nodeId == CAPI.ovrAvatar2NodeId.Invalid) { return null; }
+
+            var idx = GetIndexForNode(nodeId);
+            return GetSkeletonJoint(idx);
+        }
+
+        protected Transform GetSkeletonTransformByType(CAPI.ovrAvatar2JointType jointType)
+        {
+            // If joint monitor is disabled due to using Unity Skinning,
+            // we can still provide the transform from the entity skeleton hierarchy
+            if (_jointMonitor != null && _jointMonitor.TryGetTransform(jointType, out var tx))
+            {
+                return tx;
+            }
+            else
+            {
+                return GetSkeletonJoint(jointType)?.transform;
+            }
+        }
+
+        // NOTE: This does NOT retrieve transforms for Critical Joints when Optimize Critical Joints is enabled
+        protected Transform GetSkeletonTxByIndex(int index)
+        {
+            OvrAvatarLog.Assert(index >= 0);
+            return GetSkeletonTxByIndex((uint)index);
+        }
+
+        // NOTE: This does NOT retrieve transforms for Critical Joints when Optimize Critical Joints is enabled
+        protected Transform GetSkeletonTxByIndex(uint index)
+        {
+            if (_jointMonitor != null)
+            {
+                OvrAvatarLog.LogWarning(
+                    "Optimize Critical Joints is enabled. GetSkeletonTxByIndex will always return null. Use GetSkeletonTransformByType instead",
+                    logScope, this);
+                return null;
+            }
+
+            OvrAvatarLog.Assert(index < _skeleton.Length);
+            var tx = _skeleton[index].transform;
+            OvrAvatarLog.Assert(tx != null, logScope, this);
+            return tx;
+        }
+
+        public bool GetMonitoredPositionAndOrientation(CAPI.ovrAvatar2JointType jointType, out Vector3 position
+            , out Quaternion orientation)
+        {
+            Debug.Assert(_jointMonitor != null);
+            if (_jointMonitor != null &&
+                _jointMonitor.TryGetPositionAndOrientation(jointType, out position, out orientation))
+            {
+                return true;
+            }
+            position = Vector3.zero;
+            orientation = Quaternion.identity;
+            return false;
+        }
+
+        /**
+         * Get the current focal point that the avatar is looking at.
+         * @returns 3D vector with gaze position.
+         */
+        public Vector3? GetGazePosition()
+        {
+            var gazePos = new CAPI.ovrAvatar2Vector3f();
+            var result = CAPI.ovrAvatar2Behavior_GetGazePos(entityId, ref gazePos);
+            if (result != CAPI.ovrAvatar2Result.Success)
+            {
+                return null;
+            }
+#if OVR_AVATAR_ENABLE_CLIENT_XFORM
+            return gazePos;
+#else
+            return gazePos.ConvertSpace();
+#endif
+        }
+
+        /**
+         * Determines if this avatar has one or more of the given features.
+         *
+         * The avatar features are specified when the avatar is created.
+         * @param features set of features to check for.
+         * @returns true if one of the specified features is set for this avatar.
+         * @see CAPI.ovrAvatar2EntityFeatures
+         * @see CreateEntity
+         * @see CAPI.ovrAvatar2EntityCreateInfo
+         */
+        public bool HasAnyFeatures(CAPI.ovrAvatar2EntityFeatures features)
+        {
+            return (_creationInfo.features & features) != 0;
+        }
+
+
+        /**
+         * Determines if this avatar has all of the given features.
+         *
+         * The avatar features are specified when the avatar is created.
+         * @param features set of features to check for.
+         * @returns true if all of the specified features are set for this avatar.
+         * @see CAPI.ovrAvatar2EntityFeatures
+         * @see CreateEntity
+         * @see CAPI.ovrAvatar2EntityCreateInfo
+         */
+        public bool HasAllFeatures(CAPI.ovrAvatar2EntityFeatures features)
+        {
+            return (_creationInfo.features & features) == features;
+        }
+
+        private bool ForceEnableFeatures(CAPI.ovrAvatar2EntityFeatures features)
+        {
+            var newFeatures = _creationInfo.features | features;
+            bool didAdd = newFeatures != _creationInfo.features;
+            if (didAdd)
+            {
+                OvrAvatarLog.LogVerbose($"Force enabling features {features & ~_creationInfo.features}", logScope, this);
+
+                _creationInfo.features = newFeatures;
+            }
+            return didAdd;
+        }
+
+        private bool ForceDisableFeatures(CAPI.ovrAvatar2EntityFeatures features)
+        {
+            var newFeatures = _creationInfo.features & ~features;
+            bool didRemove = newFeatures != _creationInfo.features;
+            if (didRemove)
+            {
+                OvrAvatarLog.LogVerbose($"Force disabling features {features & _creationInfo.features}", logScope, this);
+
+                _creationInfo.features = newFeatures;
+            }
+            return didRemove;
+        }
+
+        #endregion
+
+        /////////////////////////////////////////////////
+        //:: Private Functions
+
+        #region Private Helpers
+
+        private bool QueryEntityPose(out CAPI.ovrAvatar2Pose entityPose, out CAPI.ovrAvatar2HierarchyVersion poseVersion)
+        {
+            var result = CAPI.ovrAvatar2Entity_GetPose(entityId, out entityPose, out poseVersion);
+            if (!result.IsSuccess())
+            {
+                OvrAvatarLog.LogError($"Entity_GetPose {result}", logScope, this);
+                return false;
+            }
+
+            return true;
+        }
+
+        private bool QueryEntityRenderState(out CAPI.ovrAvatar2EntityRenderState renderState)
+        {
+            if (!HasAnyFeatures(CAPI.ovrAvatar2EntityFeatures.Rendering_Prims))
+            {
+                renderState = default;
+                return false;
+            }
+
+            var result = CAPI.ovrAvatar2Render_QueryRenderState(entityId, out renderState);
+            if (!result.IsSuccess())
+            {
+                OvrAvatarLog.LogError($"QueryRenderState Error: {result}", logScope, this);
+                return false;
+            }
+
+            return true;
+        }
+
+        private bool QueryPrimitiveRenderState(int index, out CAPI.ovrAvatar2PrimitiveRenderState renderState)
+        {
+            if (index < 0 || index >= primitiveRenderCount)
+            {
+                renderState = default;
+                OvrAvatarLog.LogError(
+                    $"IndexOutOfRange. Tried {index} when _primitiveRenderCount is {primitiveRenderCount}");
+                return false;
+            }
+
+            return QueryPrimitiveRenderState_Direct(index, out renderState);
+        }
+
+        // No range checking, used while building primitives
+        private bool QueryPrimitiveRenderState_Direct(int index, out CAPI.ovrAvatar2PrimitiveRenderState renderState)
+        {
+            if (index < 0)
+            {
+                OvrAvatarLog.LogError($"GetPrimitiveRenderStateByIndex Invalid Index: {index}", logScope, this);
+                renderState = default;
+                return false;
+            }
+            return QueryPrimitiveRenderState_Direct((uint)index, out renderState);
+        }
+
+        private bool QueryPrimitiveRenderState_Direct(uint index, out CAPI.ovrAvatar2PrimitiveRenderState renderState)
+        {
+            if (!HasAnyFeatures(CAPI.ovrAvatar2EntityFeatures.Rendering_Prims))
+            {
+                renderState = default;
+                return false;
+            }
+
+            var result = CAPI.ovrAvatar2Render_GetPrimitiveRenderStateByIndex(entityId, (UInt32)index, out renderState);
+            if (result != CAPI.ovrAvatar2Result.Success)
+            {
+                OvrAvatarLog.LogError($"GetPrimitiveRenderStateByIndex Error: {result} at index {index}", logScope, this);
+                return false;
+            }
+
+            return true;
+        }
+
+        #endregion
+
+        #region Tracking
+
+        /**
+         * Select the body tracker to use for this avatar.
+         *
+         * The input body tracker has a *TrackingContext* member
+         * which implements the interface between the Avatar SDK
+         * and the body tracking implementation.
+         *
+         * @param bodyTrackingBehavior body tracking implementation.
+         * @see OvrAvatarBodyTrackingBehavior
+         * @see OvrAvatarBodyTrackingContextBase
+         */
+        public void SetBodyTracking(OvrAvatarBodyTrackingBehavior bodyTrackingBehavior)
+        {
+            _bodyTracking = bodyTrackingBehavior;
+            if (IsCreated)
+            {
+                SetBodyTrackingContext(_bodyTracking?.TrackingContext);
+            }
+        }
+
+        /**
+         * Select the face pose to use for this avatar.
+         */
+        public void SetFacePoseProvider(OvrAvatarFacePoseBehavior facePoseBehavior)
+        {
+            _facePoseBehavior = facePoseBehavior;
+            if (IsCreated)
+            {
+                SetFacePoseProvider(_facePoseBehavior?.FacePoseProvider);
+            }
+        }
+
+        /**
+         * Select the eye pose to use for this avatar.
+         */
+        public void SetEyePoseProvider(OvrAvatarEyePoseBehavior eyePoseBehavior)
+        {
+            _eyePoseBehavior = eyePoseBehavior;
+            if (IsCreated)
+            {
+                SetEyePoseProvider(_eyePoseBehavior?.EyePoseProvider);
+            }
+        }
+
+        /**
+         * Select the lip sync behavior to use for this avatar.
+         *
+         * The input body tracker has a *LipSyncContext* member
+         * which implements the interface between the Avatar SDK
+         * and the lip tracking implementation.
+         *
+         * @param lipSyncBehavior lip sync implementation.
+         * @see OvrAvatarLipSyncBehavior
+         * @see OvrAvatarLipSyncContextBase
+         */
+        public void SetLipSync(OvrAvatarLipSyncBehavior lipSyncBehavior)
+        {
+            _lipSync = lipSyncBehavior;
+            if (IsCreated)
+            {
+                SetLipSyncContext(_lipSync != null ? _lipSync.LipSyncContext : null);
+            }
+        }
+
+        private void SetBodyTrackingContext(OvrAvatarBodyTrackingContextBase newContext)
+        {
+            // Special case to reduce overhead
+            if (newContext is IOvrAvatarNativeBodyTracking bodyTracking)
+            {
+                var nativeContext = bodyTracking.NativeDataContext;
+                CAPI.ovrAvatar2Tracking_SetBodyTrackingContextNative(entityId, in nativeContext)
+                    .EnsureSuccess("ovrAvatar2Tracking_SetBodyTrackingContextNative", logScope, this);
+            }
+            else
+            {
+                var dataContext = newContext?.DataContext ?? new CAPI.ovrAvatar2TrackingDataContext();
+                CAPI.ovrAvatar2Tracking_SetBodyTrackingContext(entityId, in dataContext)
+                    .EnsureSuccess("ovrAvatar2Tracking_SetBodyTrackingContext", logScope, this);
+            }
+        }
+
+        private void SetFacePoseProvider(OvrAvatarFacePoseProviderBase newProvider)
+        {
+            // Special case to reduce overhead
+            if (newProvider is IOvrAvatarNativeFacePose facePose)
+            {
+                var nativeProvider = facePose.NativeProvider;
+                CAPI.ovrAvatar2Input_SetFacePoseProviderNative(entityId, in nativeProvider)
+                    .EnsureSuccess("ovrAvatar2Input_SetFacePoseProviderNative", logScope, this);
+            }
+            else
+            {
+                var dataContext = newProvider?.Provider ?? new CAPI.ovrAvatar2FacePoseProvider();
+                CAPI.ovrAvatar2Input_SetFacePoseProvider(entityId, in dataContext)
+                    .EnsureSuccess("ovrAvatar2Input_SetFacePoseProvider", logScope, this);
+            }
+        }
+
+        private void SetEyePoseProvider(OvrAvatarEyePoseProviderBase newProvider)
+        {
+            // Special case to reduce overhead
+            if (newProvider is IOvrAvatarNativeEyePose eyePose)
+            {
+                var nativeProvider = eyePose.NativeProvider;
+                CAPI.ovrAvatar2Input_SetEyePoseProviderNative(entityId, in nativeProvider)
+                    .EnsureSuccess("ovrAvatar2Input_SetEyePoseProviderNative", logScope, this);
+            }
+            else
+            {
+                var dataContext = newProvider?.Context ?? new CAPI.ovrAvatar2EyePoseProvider();
+                CAPI.ovrAvatar2Input_SetEyePoseProvider(entityId, in dataContext)
+                    .EnsureSuccess("ovrAvatar2Input_SetEyePoseProvider", logScope, this);
+            }
+        }
+
+        private void SetLipSyncContext(OvrAvatarLipSyncContextBase newContext)
+        {
+            // Special case to reduce overhead
+            if (newContext is OvrAvatarVisemeContext visemeContext)
+            {
+                var ncb = visemeContext.NativeCallbacks;
+                CAPI.ovrAvatar2Tracking_SetLipSyncContextNative(entityId, in ncb)
+                    .EnsureSuccess("ovrAvatar2Tracking_SetLipSyncContextNative", logScope, this);
+            }
+            else
+            {
+                var dataContext = newContext?.DataContext ?? new CAPI.ovrAvatar2LipSyncContext();
+                CAPI.ovrAvatar2Tracking_SetLipSyncContext(entityId, in dataContext)
+                    .EnsureSuccess("ovrAvatar2Tracking_SetLipSyncContext", logScope, this);
+            }
+        }
+
+        #endregion
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..08565ec1494acc3de562ff5870a883fbd8acf74d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 14cc9f112881fe648b1af1feaa98396f
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntityJointMonitor.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntityJointMonitor.cs
new file mode 100644
index 0000000000000000000000000000000000000000..f1126a23381531f7394978e13a57bef1db79add7
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntityJointMonitor.cs
@@ -0,0 +1,344 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+using Oculus.Skinning.GpuSkinning;
+
+using UnityEngine;
+using Object = UnityEngine.Object;
+
+namespace Oculus.Avatar2
+{
+    internal interface IJointData
+    {
+        Transform JointTransform { get; }
+
+        bool TryGetPosAndOrientation(out Vector3 pos, out Quaternion quat);
+
+        void Dispose();
+    }
+
+    internal abstract class EntityJointMonitorBase<T> : IJointMonitor where T : IJointData
+    {
+        private readonly List<CAPI.ovrAvatar2JointType> _monitoredJoints = new List<CAPI.ovrAvatar2JointType>();
+
+        private readonly Dictionary<CAPI.ovrAvatar2JointType, T> _jointsToData =
+            new Dictionary<CAPI.ovrAvatar2JointType, T>();
+
+        private OvrAvatarEntity _entity;
+
+        protected abstract string LogScope { get; }
+
+        private bool TryGetJointData(CAPI.ovrAvatar2JointType jointType, out T jointData)
+        {
+            return _jointsToData.TryGetValue(jointType, out jointData);
+        }
+
+        public bool TryGetTransform(CAPI.ovrAvatar2JointType jointType, out Transform tx)
+        {
+            if (TryGetJointData(jointType, out var jointData) && IsJointDataValid(jointData))
+            {
+                tx = jointData.JointTransform;
+                return true;
+            }
+
+            tx = null;
+            return false;
+        }
+
+        public bool TryGetPositionAndOrientation(CAPI.ovrAvatar2JointType jointType, out Vector3 pos
+            , out Quaternion rot)
+        {
+            if (TryGetJointData(jointType, out var jointData) && jointData.TryGetPosAndOrientation(out pos, out rot))
+            {
+                return true;
+            }
+            pos = Vector3.zero;
+            rot = Quaternion.identity;
+            return false;
+        }
+
+        protected EntityJointMonitorBase(OvrAvatarEntity entity)
+        {
+            _entity = entity;
+        }
+
+        public void Dispose()
+        {
+            Dispose(true);
+        }
+
+        ~EntityJointMonitorBase()
+        {
+            Dispose(false);
+        }
+
+        protected virtual void Dispose(bool isDispose)
+        {
+            _monitoredJoints.Clear();
+
+            foreach (var jointData in _jointsToData.Values)
+            {
+                jointData.Dispose();
+            }
+            _jointsToData.Clear();
+
+            _entity = null;
+        }
+
+
+        private T AddMonitoredJoint(CAPI.ovrAvatar2JointType jointType)
+        {
+            OvrAvatarLog.Assert(
+                !_jointsToData.TryGetValue(jointType, out var prevJointData) || !IsJointDataValid(prevJointData),
+                LogScope,
+                _entity);
+
+            var newJointData = CreateNewJointData(jointType);
+
+            if (!_jointsToData.ContainsKey(jointType))
+            {
+                // Newly tracked joint
+                _jointsToData.Add(jointType, newJointData);
+            }
+            else
+            {
+                // Had previously tracked joint, but was cleared out (null data)
+                _jointsToData[jointType] = newJointData;
+            }
+
+            return newJointData;
+        }
+
+        void IJointMonitor.OnJointPosesUpdated(List<OvrAvatarJointPose> jointPoses)
+        {
+            int jointsUpdatedCount = 0;
+            foreach (var jointPose in jointPoses)
+            {
+                var jointType = jointPose.jointType;
+                T jointData;
+                if (_jointsToData.TryGetValue(jointType, out jointData))
+                {
+                    // Null data (can happen if
+                    // entity clears out joint that was previously monitored)
+                    if (!IsJointDataValid(jointData))
+                    {
+                        jointData = AddMonitoredJoint(jointType);
+                    }
+                }
+                else
+                {
+                    // OvrAvatarEntity added this joint - begin tracking it
+                    _monitoredJoints.Add(jointType);
+                    jointData = AddMonitoredJoint(jointType);
+                }
+
+                AddNewAnimationFrameForJoint(jointData, jointPose.objectSpacePosition, jointPose.objectSpaceOrientation);
+                ++jointsUpdatedCount;
+            }
+
+            if (_jointsToData.Count != jointsUpdatedCount)
+            {
+                // Not all transforms we have were updated, so at least one joint no longer exists on the entity
+                foreach (var jointType in _monitoredJoints)
+                {
+                    // Clear out previously monitored joints that the entity
+                    // no longer has
+                    if (!_entity.IsJointTypeLoaded(jointType))
+                    {
+                        if (_jointsToData.TryGetValue(jointType, out var jointData) && IsJointDataValid(jointData))
+                        {
+                            DisposeJointData(jointType, jointData);
+                            _jointsToData[jointType] = default(T);
+                        }
+                    }
+                }
+            }
+        }
+
+        protected Transform CreateNewTransform(CAPI.ovrAvatar2JointType jointType)
+        {
+            var go = new GameObject("Joint " + jointType);
+            var newTx = go.transform;
+
+            newTx.SetParent(_entity._baseTransform);
+            newTx.localScale = Vector3.one;
+
+            return newTx;
+        }
+
+        protected Dictionary<CAPI.ovrAvatar2JointType, T>.ValueCollection GetAllJointData()
+        {
+            return _jointsToData.Values;
+        }
+
+        private static bool IsJointDataValid(T jointData)
+        {
+            return !EqualityComparer<T>.Default.Equals(jointData, default(T));
+        }
+
+        protected abstract T CreateNewJointData(CAPI.ovrAvatar2JointType jointType);
+
+        protected virtual void DisposeJointData(CAPI.ovrAvatar2JointType jointType, T jointData)
+        {
+            jointData.Dispose();
+        }
+
+        protected abstract void AddNewAnimationFrameForJoint(
+            T jointData,
+            in Vector3 objectSpacePosition,
+            in Quaternion objectSpaceOrientation);
+
+        public abstract void UpdateJoints(float deltaTime);
+    } // end class
+
+    internal class TransformHolder : IJointData, IDisposable
+    {
+        public Transform JointTransform { get; }
+
+        public TransformHolder(Transform tx)
+        {
+            JointTransform = tx;
+        }
+
+        public bool TryGetPosAndOrientation(out Vector3 pos, out Quaternion quat)
+        {
+            if (JointTransform == null)
+            {
+                pos = Vector3.zero;
+                quat = Quaternion.identity;
+                return false;
+            }
+            pos = JointTransform.localPosition;
+            quat = JointTransform.localRotation;
+            return true;
+        }
+
+        public void Dispose()
+        {
+            Object.Destroy(JointTransform.gameObject);
+        }
+    }
+
+    internal class OvrAvatarEntityJointMonitor : EntityJointMonitorBase<TransformHolder>
+    {
+        public OvrAvatarEntityJointMonitor(OvrAvatarEntity entity) : base(entity)
+        {
+        }
+
+        protected override string LogScope => "jointMonitor";
+
+        protected override TransformHolder CreateNewJointData(CAPI.ovrAvatar2JointType jointType)
+        {
+            var newTransform = CreateNewTransform(jointType);
+            return new TransformHolder(newTransform);
+        }
+
+        protected override void AddNewAnimationFrameForJoint(
+            TransformHolder jointData,
+            in Vector3 objectSpacePosition,
+            in Quaternion objectSpaceOrientation)
+        {
+            // Animation frames just overwrite the transform directly
+            jointData.JointTransform.localPosition = objectSpacePosition;
+            jointData.JointTransform.localRotation = objectSpaceOrientation;
+        }
+
+        public override void UpdateJoints(float deltaTime)
+        {
+            // Intentionally empty
+        }
+    }
+
+    internal class InterpolatingJoint : IJointData
+    {
+        private Vector3 _objectSpacePosition0;
+        private Vector3 _objectSpacePosition1;
+
+        private Quaternion _objectSpaceOrientation0;
+        private Quaternion _objectSpaceOrientation1;
+
+        private float _lastInterpolationValue = 0.0f;
+
+        public InterpolatingJoint(Transform tx)
+        {
+            JointTransform = tx;
+        }
+
+        public Transform JointTransform { get; private set; }
+
+        public bool TryGetPosAndOrientation(out Vector3 pos, out Quaternion quat)
+        {
+            CalculateUpdate(_lastInterpolationValue, out pos, out quat);
+            return true;
+        }
+
+        public void Dispose()
+        {
+            if (JointTransform == null) { return; }
+
+            Object.Destroy(JointTransform.gameObject);
+            JointTransform = null;
+        }
+
+        public void AddNewAnimationFrame(in Vector3 objectSpacePosition, in Quaternion objectSpaceOrientation)
+        {
+            // Shift the "latest frame"'s data to the "earliest frame"'s data and then
+            // add in the new frame
+            _objectSpacePosition0 = _objectSpacePosition1;
+            _objectSpaceOrientation0 = _objectSpaceOrientation1;
+
+            _objectSpacePosition1 = objectSpacePosition;
+            _objectSpaceOrientation1 = objectSpaceOrientation;
+        }
+
+        public void CalculateUpdate(float interpolationValue, out Vector3 position, out Quaternion rotation)
+        {
+            position = Vector3.Lerp(_objectSpacePosition0, _objectSpacePosition1, interpolationValue);
+            rotation = Quaternion.Slerp(_objectSpaceOrientation0, _objectSpaceOrientation1, interpolationValue);
+        }
+
+        public void UpdateTransform(float interpolationValue)
+        {
+            _lastInterpolationValue = interpolationValue;
+
+            CalculateUpdate(interpolationValue, out var pos, out var rot);
+            JointTransform.localPosition = pos;
+            JointTransform.localRotation = rot;
+        }
+    }
+
+    internal class OvrAvatarEntitySmoothingJointMonitor : EntityJointMonitorBase<InterpolatingJoint>
+    {
+        protected readonly IInterpolationValueProvider _interpolationProvider;
+
+        public OvrAvatarEntitySmoothingJointMonitor(OvrAvatarEntity entity, IInterpolationValueProvider interpolationValueProvider) : base(entity)
+        {
+            _interpolationProvider = interpolationValueProvider;
+        }
+
+        protected override string LogScope => "SmoothingJointMonitor";
+
+        protected override InterpolatingJoint CreateNewJointData(CAPI.ovrAvatar2JointType jointType)
+        {
+            var newTransform = CreateNewTransform(jointType);
+            return new InterpolatingJoint(newTransform);
+        }
+
+        protected override void AddNewAnimationFrameForJoint(InterpolatingJoint jointData, in Vector3 objectSpacePosition, in Quaternion objectSpaceOrientation)
+        {
+            jointData.AddNewAnimationFrame(in objectSpacePosition, in objectSpaceOrientation);
+        }
+
+        public override void UpdateJoints(float deltaTime)
+        {
+            float interpolationValue = _interpolationProvider.GetRenderInterpolationValue();
+
+            // Update all joints
+            foreach (var joint in GetAllJointData())
+            {
+                joint.UpdateTransform(interpolationValue);
+            }
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntityJointMonitor.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntityJointMonitor.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..13f4e5a2def17fdae7712e553c3190cc89a50254
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntityJointMonitor.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: c960823da55e50445b4dda07080790bc
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntityJointMonitorJob.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntityJointMonitorJob.cs
new file mode 100644
index 0000000000000000000000000000000000000000..76c2d4023ee2f6b21e55c7ab4d12f691c6635eeb
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntityJointMonitorJob.cs
@@ -0,0 +1,198 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+using Unity.Collections;
+using Unity.Jobs;
+using UnityEngine.Jobs;
+using UnityEngine.Profiling;
+
+
+namespace Oculus.Avatar2
+{
+    internal class OvrAvatarEntitySmoothingJointJobMonitor : OvrAvatarEntitySmoothingJointMonitor
+    {
+        private const string logScope = "OvrAvatarEntitySmoothingJointJobMonitor";
+
+        public OvrAvatarEntitySmoothingJointJobMonitor(OvrAvatarEntity entity
+            , IInterpolationValueProvider interpolationValueProvider)
+            : base(entity, interpolationValueProvider)
+        {
+        }
+
+        /* Transforms which will be updated - used to schedule the transform job */
+        private TransformAccessArray _jobTransformAccess = default;
+        /* Sized to the number of transforms which will actually be updated, stores the pose data used by the job */
+        private NativeArray<JointPose> _jointJobNativeBuffer;
+
+        /* Used to quickly determine when the set of monitored joints changes */
+        private readonly HashSet<InterpolatingJoint> _activeJoints = new HashSet<InterpolatingJoint>();
+
+        /* Used as fast storage for the current set of monitored interpolating joints
+         - in order to update `jointJobNativeBuffer_` before scheduling the next transform job */
+        private InterpolatingJoint[] _jobJoints = Array.Empty<InterpolatingJoint>();
+
+        /* Only needed for safe shutdown */
+        private JobHandle _currentJob = default;
+
+        /* Track whether the monitored joints have changed since the last update */
+        private bool _monitoredJointsChanged = false;
+
+        protected override InterpolatingJoint CreateNewJointData(CAPI.ovrAvatar2JointType jointType)
+        {
+            var joint = base.CreateNewJointData(jointType);
+            // TODO: This shouldn't be necessary, any call to `CreateNewJointData` should indicate the joints changed
+            // For now this acts as a factor of safety
+            _monitoredJointsChanged |= _activeJoints.Add(joint);
+            return joint;
+        }
+
+        protected override void DisposeJointData(CAPI.ovrAvatar2JointType jointType, InterpolatingJoint jointData)
+        {
+            // TODO: This shouldn't be necessary, any call to `DisposeJointData` should indicate the joints changed
+            // For now this acts as a factor of safety
+            _monitoredJointsChanged |= _activeJoints.Remove(jointData);
+            base.DisposeJointData(jointType, jointData);
+        }
+
+        public override void UpdateJoints(float deltaTime)
+        {
+            var jointCount = _activeJoints.Count;
+
+            if (_monitoredJointsChanged)
+            {
+                Profiler.BeginSample("JointMonitorJob::RebuildBuffers");
+                // Size all buffers to `jointCount`
+                var newJobTransformAccess = new TransformAccessArray(jointCount);
+                var newJointJobNativeBuffer = new NativeArray<JointPose>(
+                    jointCount,
+                    Allocator.Persistent,
+                    NativeArrayOptions.UninitializedMemory);
+
+                if (jointCount != _jobJoints.Length)
+                {
+                    Array.Resize(ref _jobJoints, jointCount);
+                }
+
+                int regIdx = 0;
+                var jointData = GetAllJointData();
+                OvrAvatarLog.Assert(jointData.Count == jointCount, logScope);
+
+                // Populate new transformAccessArray and update current set of jobJoints
+                foreach (var registeredJoint in jointData)
+                {
+                    _jobJoints[regIdx++] = registeredJoint;
+                    // note: filling via index does not work
+                    newJobTransformAccess.Add(registeredJoint.JointTransform);
+                }
+
+                // Ensure current job is finished before disposing the arrays its using
+                _currentJob.Complete();
+
+                // Clean up old buffers
+                if(_jobTransformAccess.isCreated) { _jobTransformAccess.Dispose(); }
+                if(_jointJobNativeBuffer.IsCreated) { _jointJobNativeBuffer.Dispose(); }
+
+                // Swap in new ones
+                _jobTransformAccess = newJobTransformAccess;
+                _jointJobNativeBuffer = newJointJobNativeBuffer;
+
+                _monitoredJointsChanged = false;
+
+                Profiler.EndSample(); //"JointMonitorJob::RebuildBuffers"
+            }
+            else
+            {
+                // Must wait for previous job to complete before updating `_jointJobNativeBuffer`
+                _currentJob.Complete();
+            }
+
+            /* Early out if we have 0 joints to update, attempting to schedule a 0 length job will crash */
+            if (jointCount == 0) { return; }
+
+            // Update all jointPoses
+            Profiler.BeginSample("JointMonitorJob::UpdateJointPoses");
+            float interpolationValue = _interpolationProvider.GetRenderInterpolationValue();
+            int idx = 0;
+            foreach (var joint in _jobJoints)
+            {
+                // TODO: This could be moved into the IJob itself,
+                // but we need to guard against adding new frames while it runs
+                joint.CalculateUpdate(interpolationValue, out var pos, out var rot);
+                _jointJobNativeBuffer[idx++] = new JointPose(in pos, in rot);
+            }
+
+            Profiler.EndSample(); //"JointMonitorJob::UpdateJointPoses"
+
+            _currentJob = ScheduleUpdateTransformsJob(in _jointJobNativeBuffer, in _jobTransformAccess);
+        }
+
+        private static JobHandle ScheduleUpdateTransformsJob(in NativeArray<JointPose> joints, in TransformAccessArray transforms) {
+            Debug.Assert(joints.Length > 0);
+            Debug.Assert(joints.Length == transforms.length);
+
+            // TODO: Consider merging all updateTransforms jobs into one to kickoff in `SyncOutputComplete`
+            // * Would reduce scheduling overhead at the cost of less granularity (higher chance to block on mainthread, for longer)
+            var updateJob = new UpdateJointTransformsJob(joints);
+            return updateJob.Schedule(transforms);
+        }
+
+        protected override void Dispose(bool isDispose)
+        {
+            _currentJob.Complete();
+
+            if (_jobTransformAccess.isCreated)
+            {
+                _jobTransformAccess.Dispose();
+                _jobTransformAccess = default;
+            }
+
+            if (_jointJobNativeBuffer.IsCreated)
+            {
+                _jointJobNativeBuffer.Dispose();
+                _jointJobNativeBuffer = default;
+            }
+            _activeJoints.Clear();
+            _jobJoints = null;
+
+            base.Dispose(isDispose);
+        }
+
+        /* Job Structs */
+
+        private readonly struct JointPose
+        {
+            public readonly Vector3 Position;
+            public readonly Quaternion Rotation;
+
+            public JointPose(in Vector3 pos, in Quaternion rot)
+            {
+                this.Position = pos;
+                this.Rotation = rot;
+            }
+        }
+
+        private readonly struct UpdateJointTransformsJob : IJobParallelForTransform {
+            public UpdateJointTransformsJob(in NativeArray<JointPose> joints) {
+                _jointTransforms = joints;
+            }
+
+            [ReadOnly]
+            private readonly NativeArray<JointPose> _jointTransforms;
+
+            void IJobParallelForTransform.Execute(int index, TransformAccess txAccess) {
+                Profiler.BeginSample("UpdateJointTransformsJob::Execute");
+                // Avoid JointPose copy and NativeArray indexer
+                unsafe
+                {
+                    var jointPose = _jointTransforms.GetReadonlyPtr() + index;
+
+                    txAccess.localPosition = jointPose->Position;
+                    txAccess.localRotation = jointPose->Rotation;
+                }
+
+                Profiler.EndSample();
+            }
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntityJointMonitorJob.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntityJointMonitorJob.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..1a6cddce689cb16c4f5312245d4353d9022c9d2e
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntityJointMonitorJob.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 73c4b0981b7f53c429fcde13cd8c884c
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Animation.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Animation.cs
new file mode 100644
index 0000000000000000000000000000000000000000..a9b7de4a300d6e2b064320da3aaadfc2acb770a5
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Animation.cs
@@ -0,0 +1,456 @@
+using System;
+using System.Collections.Generic;
+
+using Unity.Collections;
+
+using UnityEngine;
+
+namespace Oculus.Avatar2
+{
+    public interface IInterpolationValueProvider
+    {
+        // Will return a value between 0.0 and 1.0 (inclusive)
+        float GetRenderInterpolationValue();
+    }
+
+    // Partial class intended to encapsulate "avatar animation" related functionality.
+    // Mainly related to "morph targets" and "skinning"
+    public partial class OvrAvatarEntity
+    {
+        private EntityAnimatorBase _entityAnimator;
+        private IInterpolationValueProvider _interpolationValueProvider;
+
+        #region Entity Animators
+
+        private abstract class EntityAnimatorBase
+        {
+            protected readonly OvrAvatarEntity Entity;
+
+            protected EntityAnimatorBase(OvrAvatarEntity entity)
+            {
+                Entity = entity;
+            }
+
+            private readonly List<PrimitiveRenderData> _addNewAnimationFrameCache = new List<PrimitiveRenderData>();
+            public virtual void AddNewAnimationFrame(
+                float timestamp,
+                float deltaTime,
+                in CAPI.ovrAvatar2Pose entityPose,
+                in CAPI.ovrAvatar2EntityRenderState renderState)
+            {
+                // If a remote avatar is playing back streaming packet, update pose and morph targets.
+                var isPlayingStream = !Entity.IsLocal && Entity.GetStreamingPlaybackState().HasValue;
+
+                bool skeletalAnimation = isPlayingStream || Entity.HasAnyFeatures(UPDATE_POSE_FEATURES);
+                bool morphAnimation = isPlayingStream || Entity.HasAnyFeatures(UPDATE_MOPRHS_FEATURES);
+
+                if (skeletalAnimation || morphAnimation)
+                {
+                    Entity.BroadcastAnimationFrameStart();
+
+                    // It's possible that the animation frame start call can complete an LOD transition
+                    // which may disable animation, so re-query the animation enable state
+                    // TODO: This call is not cheap
+                    // - we should have the broadcast method report if a rebuild is necessary
+                    _addNewAnimationFrameCache.Clear();
+                    Entity.AppendPrimitivesWithAnimationEnabled(null, _addNewAnimationFrameCache);
+                    if (skeletalAnimation)
+                    {
+                        Entity.SamplePose(in entityPose, in renderState, _addNewAnimationFrameCache);
+                    }
+
+                    if (morphAnimation)
+                    {
+                        Entity.SampleMorphTargets(_addNewAnimationFrameCache);
+                    }
+                }
+
+                Entity.MonitorJoints(in entityPose);
+            }
+
+            public abstract void UpdateAnimationTime(float deltaTime, bool isAllAnimationDataValid);
+        }
+
+        private sealed class EntityAnimatorMotionSmoothing : EntityAnimatorBase, IInterpolationValueProvider
+        {
+            // Currently using as a double buffering setup with only two frames, FrameA and FrameB
+            // No pending frames are stored, as new frames come in before previous render frames are finished,
+            // old frames are dropped
+            private static readonly int NUM_ANIMATION_FRAMES = 2;
+
+            private sealed class AnimationFrameInfo
+            {
+                public float Timestamp { get; private set; }
+
+                public bool IsValid { get; private set; }
+
+                public void UpdateValues(float time)
+                {
+                    Timestamp = time;
+                    IsValid = true;
+                }
+            }
+
+            // In this implementation, no pending frames are held, only  2 "animation frames"
+            // are held on to
+            private readonly AnimationFrameInfo[] _animationFrameInfo = new AnimationFrameInfo[NUM_ANIMATION_FRAMES];
+            private float _latestAnimationFrameTime;
+            private int _nextAnimationFrameIndex;
+
+            private bool _hasTwoValidAnimationFrames;
+
+            private float _interpolationValue;
+
+            private int EarliestAnimationFrameIndex => _nextAnimationFrameIndex;
+            private int LatestAnimationFrameIndex => 1 - _nextAnimationFrameIndex;
+
+            public EntityAnimatorMotionSmoothing(OvrAvatarEntity entity) : base(entity)
+            {
+                for (int i = 0; i < _animationFrameInfo.Length; i++)
+                {
+                    _animationFrameInfo[i] = new AnimationFrameInfo();
+                }
+            }
+
+            public float GetRenderInterpolationValue()
+            {
+                return _interpolationValue;
+            }
+
+            public override void AddNewAnimationFrame(
+                float timestamp,
+                float deltaTime,
+                in CAPI.ovrAvatar2Pose entityPose,
+                in CAPI.ovrAvatar2EntityRenderState renderState)
+            {
+                base.AddNewAnimationFrame(timestamp, deltaTime, entityPose, renderState);
+                AddNewAnimationFrameTime(timestamp, deltaTime);
+            }
+
+            public override void UpdateAnimationTime(float deltaTime, bool isAllAnimationDataValid)
+            {
+                CalculateInterpolationValue(deltaTime, isAllAnimationDataValid);
+            }
+
+            private void AddNewAnimationFrameTime(float timestamp, float deltaTime)
+            {
+                // In this implementation, there are no historical/pending frames on top of the "render frames"
+                // (the frames currently rendered/interpolated between).
+                // Note the time of the frame to be added
+                _animationFrameInfo[_nextAnimationFrameIndex].UpdateValues(timestamp);
+
+                // Advance/ping pong frame index
+                _nextAnimationFrameIndex =
+                    1 - _nextAnimationFrameIndex; // due to there only being 2 frames, this will ping pong
+
+                if (!_hasTwoValidAnimationFrames && _animationFrameInfo[1].IsValid)
+                {
+                    _hasTwoValidAnimationFrames = true;
+                }
+
+                if (_hasTwoValidAnimationFrames)
+                {
+                    var earliestFrame = _animationFrameInfo[EarliestAnimationFrameIndex];
+
+                    // Fast forward/rewind render frame time to be the earliest frame's timestamp minus the delta.
+                    // This has two effects:
+                    // 1) If the frame generation frequency changes to be faster (i.e. frames at 0, 1, 1.5),
+                    //    then this logic "fast forwards" the render time which may cause a jump in animation, but
+                    //    keeps the "interpolation window" (the time that fake animation data is generated) to
+                    //    be the smallest possible.
+                    // 2) If the frame generate frequency slows down (i.e. frames at 0, 0.5, 2), then this logic
+                    //    "rewinds" the render time which will cause the animation to not skip any of the animation
+                    //    window
+                    _latestAnimationFrameTime = earliestFrame.Timestamp - deltaTime;
+                }
+            }
+
+            private void CalculateInterpolationValue(float delta, bool isAllAnimationDataValid)
+            {
+                // Can only advance if there are 2 or more valid render frames
+                if (!_hasTwoValidAnimationFrames)
+                {
+                    _interpolationValue = 0.0f;
+                    return;
+                }
+
+                _latestAnimationFrameTime += delta;
+
+                // For "motion smoothing" any OvrAvatarSkinnedRenderable subclass that is going to be rendered,
+                // ideally, will be rendered with "completely valid render data". Unfortunately that might not be
+                // the case (at the moment, until LOD transitions happen differently).
+                // The "joint monitoring" however is done on a per entity
+                // basis (the renderables all share a common single skeleton).
+                // For both the joint monitor and the renderables to all have the same interpolation value,
+                // they will all pull from the same source/get passed the same value instead of calculating
+                // it themselves (which will also save computation).
+                // Given these facts, there needs to be some coupling so that the calculation of the interpolation
+                // value knows if all of the renderables being rendered in a given frame have complete valid data.
+
+                // If all renderables' data is completely valid, then interpolation value can be calculated as normal, otherwise, it will
+                // be clamped to 1.0
+                if (!isAllAnimationDataValid)
+                {
+                    // Not all skinned renderables have "completely valid animation data".
+                    // Return 1.0 so that the renderables + joints are rendering their
+                    // latest (and only guaranteed valid) animation frame
+                    _interpolationValue = 1.0f;
+                    return;
+                }
+
+                float t0 = _animationFrameInfo[EarliestAnimationFrameIndex].Timestamp;
+                float t1 = _animationFrameInfo[LatestAnimationFrameIndex].Timestamp;
+
+                // InverseLerp clamps to 0 to 1
+                _interpolationValue = Mathf.InverseLerp(t0, t1, _latestAnimationFrameTime);
+            }
+        }
+
+        private sealed class EntityAnimatorDefault : EntityAnimatorBase
+        {
+            public EntityAnimatorDefault(OvrAvatarEntity entity) : base(entity)
+            {
+            }
+
+            public override void UpdateAnimationTime(float deltaTime, bool isAllAnimationDataValid)
+            {
+                // Intentionally empty
+            }
+        }
+
+        #endregion
+
+        #region Runtime
+
+        // Loop over all skinned renderables, updating the "animation enabled" state and keeping track of which ones
+        // are animation enabled
+        private void AppendPrimitivesWithAnimationEnabled(
+            List<OvrAvatarSkinnedRenderable> skinnedOut, List<PrimitiveRenderData> renderDataOut)
+        {
+            foreach (var primRenderables in _visiblePrimitiveRenderers)
+            {
+                foreach (var primRenderable in primRenderables)
+                {
+                    var skinnedRenderable = primRenderable.skinnedRenderable;
+                    // TODO: Remove this expensive `GameObject.==` check
+                    if (skinnedRenderable == null || !skinnedRenderable.enabled) { continue; }
+
+                    if (skinnedRenderable.IsAnimationEnabled)
+                    {
+                        skinnedOut?.Add(skinnedRenderable);
+                        renderDataOut?.Add(primRenderable);
+                    }
+                }
+            }
+        }
+
+
+        private void SampleSkinningOrigin(in CAPI.ovrAvatar2PrimitiveRenderState primState,
+            out CAPI.ovrAvatar2Transform skinningOrigin)
+        {
+            skinningOrigin = primState.skinningOrigin;
+#if !OVR_AVATAR_ENABLE_CLIENT_XFORM
+
+            // HACK: Mirror rendering transforms to fixup coordinate system errors
+            skinningOrigin.scale.z *= -1f;
+            skinningOrigin = skinningOrigin.ConvertSpace();
+#endif
+        }
+
+        private readonly List<OvrAvatarSkinnedRenderable> _broadcastAnimationFrameStartCache
+            = new List<OvrAvatarSkinnedRenderable>();
+        private void BroadcastAnimationFrameStart()
+        {
+            // Due to the fact that calling AnimationFrameUpdate may trigger
+            // some LOD transitions which in turn, may alter the IsAnimationEnabled
+            // value of a OvrAvatarSkinnedRenderable, this function can't
+            // used any cached lists of "animation enabled" renderables and instead
+            // needs to check IsAnimationEnabled before calling AnimationFrameUpdate
+            // Loop over all skinned renderables, updating the "animation enabled" state and keeping track of which ones
+            // are animation enabled
+            _broadcastAnimationFrameStartCache.Clear();
+            AppendPrimitivesWithAnimationEnabled(_broadcastAnimationFrameStartCache, null);
+            foreach (var skinnedRenderable in _broadcastAnimationFrameStartCache)
+            {
+                try
+                {
+                    skinnedRenderable.AnimationFrameUpdate();
+                }
+                catch (Exception e)
+                {
+                    OvrAvatarLog.LogException("AnimationFrameUpdate", e, logScope, this);
+                }
+            }
+        }
+
+        private void SamplePrimitivesSkinningOrigin(in CAPI.ovrAvatar2EntityRenderState renderState)
+        {
+            if (!HasAnyFeatures(CAPI.ovrAvatar2EntityFeatures.Rendering_Prims))
+            {
+                return;
+            }
+
+            var renderStateIndices = new NativeArray<UInt32>((int)renderState.primitiveCount, Allocator.Temp, NativeArrayOptions.UninitializedMemory);
+            var renderStates = new NativeArray<CAPI.ovrAvatar2PrimitiveRenderState>((int)renderState.primitiveCount, Allocator.Temp, NativeArrayOptions.UninitializedMemory);
+            unchecked
+            {
+                for (uint i = 0; i < renderState.primitiveCount; ++i)
+                {
+                    renderStateIndices[(int)i] = i;
+                }
+            }
+
+            unsafe
+            {
+                var result = CAPI.ovrAvatar2Render_GetPrimitiveRenderStatesByIndex(entityId, renderStateIndices.GetPtr<UInt32>(), renderStates.GetPtr<CAPI.ovrAvatar2PrimitiveRenderState>(),
+                    renderState.primitiveCount);
+                if (result != CAPI.ovrAvatar2Result.Success)
+                {
+                    OvrAvatarLog.LogError($"GetPrimitiveRenderStatesByIndex Error: {result}", logScope, this);
+                    return;
+                }
+            }
+
+            for (uint i = 0; i < renderState.primitiveCount; ++i)
+            {
+                var primState = renderStates[(int)i];
+                SampleSkinningOrigin(in primState, out var skinningOrigin);
+
+                var primRenderables = _primitiveRenderables[primState.id];
+                foreach (var primRend in primRenderables)
+                {
+                    var skinnedRenderable = primRend.skinnedRenderable;
+                    if (skinnedRenderable is null)
+                    {
+                        // Non-skinned renderables just apply the transform
+                        var t = primRend.renderable.transform;
+                        t.ApplyOvrTransform(skinningOrigin);
+                    }
+                    else
+                    {
+                        // Otherwise call function on skinned renderable.
+                        // Why does this needs to be called for all renderables
+                        // but UpdateJointMatrices is only called on "visible renderers"?
+                        // It would make sense if they were updated together
+                        skinnedRenderable.UpdateSkinningOrigin(skinningOrigin);
+                    }
+                }
+            }
+        }
+
+        private void SamplePose(
+            in CAPI.ovrAvatar2Pose entityPose,
+            in CAPI.ovrAvatar2EntityRenderState renderState,
+            in List<PrimitiveRenderData> animatablePrimitives)
+        {
+            SamplePrimitivesSkinningOrigin(renderState);
+
+            OvrAvatarLog.AssertConstMessage(entityPose.jointCount == SkeletonJointCount
+                , "entity pose does not match skeleton.", logScope, this);
+
+            // Are all SkinnedRenderables able to update without using Unity.Transform?
+            bool needsFullTransformUpdate = false;
+            // TODO: This will result in redundant skinningMatrices query in UpdateJointMatrices
+            foreach (var primRenderable in animatablePrimitives)
+            {
+                var skinnedRenderable = primRenderable.skinnedRenderable;
+                Debug.Assert(skinnedRenderable != null);
+                Debug.Assert(skinnedRenderable.IsAnimationEnabled);
+
+                var primitive = primRenderable.primitive;
+                needsFullTransformUpdate |=
+                    !skinnedRenderable.UpdateJointMatrices(entityId, primitive, primRenderable.instanceId);
+            }
+
+            needsFullTransformUpdate |= (_debugDrawing.drawSkelHierarchy ||
+                                         _debugDrawing.drawSkelHierarchyInGame ||
+                                         _debugDrawing.drawSkinTransformsInGame);
+
+            // If JointMonitoring is enabled, it will update transforms
+            if (_jointMonitor == null)
+            {
+                if (needsFullTransformUpdate && _jointMonitor == null)
+                {
+                    for (uint i = 0; i < entityPose.jointCount; ++i)
+                    {
+                        UpdateSkeletonTransformAtIndex(in entityPose, i);
+                    }
+                }
+                else
+                {
+                    foreach (var skeletonIdx in _unityUpdateJointIndices)
+                    {
+                        UpdateSkeletonTransformAtIndex(in entityPose, skeletonIdx);
+                    }
+                }
+            }
+            else
+            {
+                Debug.Assert(_unityUpdateJointIndices.Length == 0);
+            }
+        }
+
+        private void UpdateSkeletonTransformAtIndex(in CAPI.ovrAvatar2Pose entityPose, uint skeletonIdx)
+        {
+            var jointUnityTx = GetSkeletonTxByIndex(skeletonIdx);
+
+            unsafe
+            {
+                CAPI.ovrAvatar2Transform* jointTransform = entityPose.localTransforms + skeletonIdx;
+                if ((*jointTransform).IsNan()) return;
+
+                var jointParentIndex = entityPose.GetParentIndex(skeletonIdx);
+#if OVR_AVATAR_ENABLE_CLIENT_XFORM
+                jointUnityTx.ApplyOvrTransform(jointTransform);
+#else
+                if (jointParentIndex != -1)
+                {
+                    jointUnityTx.ApplyOvrTransform(jointTransform);
+                }
+                else
+                {
+                    // HACK: Mirror rendering transforms across Z to fixup coordinate system errors
+                    // Copy provided transform, we should not modify the source array
+                    var flipScaleZ = *jointTransform;
+                    flipScaleZ.scale.z = -flipScaleZ.scale.z;
+                    jointUnityTx.ApplyOvrTransform(in flipScaleZ);
+                }
+#endif
+            }
+        }
+
+        private void SampleMorphTargets(in List<PrimitiveRenderData> animatablePrimitives)
+        {
+            foreach (var primRenderable in animatablePrimitives)
+            {
+                var skinnedRenderable = primRenderable.skinnedRenderable;
+                Debug.Assert(skinnedRenderable != null);
+                Debug.Assert(skinnedRenderable.IsAnimationEnabled);
+
+                var primitive = primRenderable.primitive;
+
+                if (primitive.morphTargetCount == 0) { continue; }
+
+                var instanceId = primRenderable.instanceId;
+                UInt32 morphTargetCount = primitive.morphTargetCount;
+                UInt32 bufferSize = sizeof(float) * morphTargetCount;
+                using var weightsBufferHandle = skinnedRenderable.CheckoutMorphTargetBuffer(morphTargetCount);
+                var result =
+                    CAPI.ovrAvatar2Render_GetMorphTargetWeights(entityId, instanceId, weightsBufferHandle.BufferPtr,
+                        bufferSize);
+                if (result.IsSuccess())
+                {
+                    skinnedRenderable.MorphTargetBufferUpdated(weightsBufferHandle);
+                }
+                else
+                {
+                    OvrAvatarLog.LogError(
+                        $"Error: GetMorphTargetWeights {result} for ID {primitive.assetId}, instance {primRenderable.instanceId}",
+                        logScope);
+                }
+            }
+        }
+
+        #endregion
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Animation.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Animation.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..4a9f3de4c71e23de4367aaae7f3257c894506a8e
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Animation.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 49919497ea5d8f94d93fbaf506a6bdf8
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Debug.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Debug.cs
new file mode 100644
index 0000000000000000000000000000000000000000..223410d128e63732eeb280f564eee4898f9d0e35
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Debug.cs
@@ -0,0 +1,357 @@
+using System;
+using System.Runtime.InteropServices;
+using UnityEngine;
+using System.Collections.Generic;
+#if UNITY_EDITOR
+using UnityEditor;
+#endif
+
+namespace Oculus.Avatar2
+{
+    public partial class OvrAvatarEntity
+    {
+        public string debugEntityRenderState()
+        {
+            if (QueryEntityRenderState(out var entityState))
+            {
+                return debugEntityRenderState(in entityState);
+            }
+            return string.Empty;
+        }
+        public string debugEntityRenderState(in CAPI.ovrAvatar2EntityRenderState entityState)
+        {
+            string builder = "";
+            string sep = "";
+            for (uint idx = 0; idx < entityState.primitiveCount; idx++)
+            {
+                builder += sep + debugPrimitiveRenderStateString(idx);
+                sep = ",\n";
+            }
+            return builder;
+        }
+
+        public string debugPrimitiveRenderStateString(uint primIdx)
+        {
+            if (QueryPrimitiveRenderState_Direct(primIdx, out var renderState))
+            {
+                return $"<{primIdx}-{debugPrimitiveRenderStateString(in renderState)}>";
+            }
+            return string.Empty;
+        }
+        public string debugPrimitiveRenderStateString(in CAPI.ovrAvatar2PrimitiveRenderState renderState)
+        {
+            return $"id:{renderState.id},primId:{renderState.primitiveId},nodeId:{renderState.meshNodeId}";
+        }
+
+        public string debugPrimitiveRenderFlagsString(uint primIdx)
+        {
+            if (QueryPrimitiveRenderState_Direct(primIdx, out var renderState))
+            {
+                return $"<{primIdx}-{debugPrimitiveRenderFlagsString(renderState.primitiveId)}>";
+            }
+            return string.Empty;
+        }
+        public string debugPrimitiveRenderFlagsString(CAPI.ovrAvatar2Id primitiveId)
+        {
+            if (CAPI.ovrAvatar2Asset_GetViewFlags(primitiveId, out var viewFlags)
+                .EnsureSuccess("ovrAvatar2Asset_GetViewFlags") &&
+                CAPI.ovrAvatar2Asset_GetLodFlags(primitiveId, out var lodFlags)
+                .EnsureSuccess("ovrAvatar2Asset_GetLodFlags"))
+            {
+                return $"view:{viewFlags},lod:{lodFlags}";
+            }
+            return string.Empty;
+        }
+
+        public Dictionary<CAPI.ovrAvatar2JointType, string> debugJointNamesForTypes()
+        {
+            // TODO: Remove pose dependency when GetNameForNode is added
+            if (!QueryEntityPose(out var pose, out var hierVer)) { return null; }
+
+            int jointTypeCount = (int)CAPI.ovrAvatar2JointType.Count;
+            var buildDict = new Dictionary<CAPI.ovrAvatar2JointType, string>(jointTypeCount);
+            var allJointTypes = new CAPI.ovrAvatar2JointType[jointTypeCount];
+            for (int idx = 0; idx < allJointTypes.Length; ++idx)
+            {
+                allJointTypes[idx] = (CAPI.ovrAvatar2JointType)idx;
+            }
+
+            var allJointNames = CAPI.OvrAvatar2Entity_QueryJointTypeNodes(entityId, allJointTypes, this);
+
+            for (int idx = 0; idx < jointTypeCount; ++idx)
+            {
+                buildDict.Add(allJointTypes[idx], GetNameForNode(allJointNames[idx], in pose));
+            }
+            return buildDict;
+        }
+
+        // TODO: Create generic LoadUri method which StressReloadingAvatar may use instead
+        protected internal bool StressReloading_LoadUri(string uri)
+        {
+            var result = CAPI.OvrAvatarEntity_LoadUri(entityId, uri, out var loadRequestId);
+            if (result != CAPI.ovrAvatar2Result.Success)
+            {
+                OvrAvatarLog.LogWarning($"LoadUri failed: {result}");
+                return false;
+            }
+            OvrAvatarManager.Instance.RegisterLoadRequest(this, loadRequestId);
+
+            return true;
+        }
+
+#if UNITY_EDITOR || DEVELOPMENT_BUILD
+        #region Game Debug Drawing
+        private static Material _debugLineMat = null;
+        private Material DebugLineMat
+        {
+            get
+            {
+                if (_debugLineMat == null)
+                {
+                    _debugLineMat = new Material(Shader.Find("Hidden/Internal-Colored"));
+                    _debugLineMat.SetInt("_ZTest", 0);
+                }
+                return _debugLineMat;
+            }
+        }
+
+        // OnPostRender only works when the Component is a child of the Camera
+        private void OnCameraPostRender(Camera cam)
+        {
+            if (_debugDrawing.drawSkelHierarchyInGame)
+            {
+                GameDebugDrawSkelHierarchyInGame();
+            }
+
+            if (_debugDrawing.drawSkinTransformsInGame)
+            {
+                foreach (var meshNodeKVP in _meshNodes)
+                {
+                    foreach (var prim in meshNodeKVP.Value)
+                    {
+                        GameDebugDrawSkinTransforms(prim, 0.005f);
+                    }
+                }
+            }
+        }
+
+        private void GameDebugDrawSkelHierarchyInGame()
+        {
+            DebugLineMat.SetPass(0);
+            GL.Begin(GL.LINES);
+            GL.Color(_debugDrawing.skeletonColor);
+            for (int i = 0; i < SkeletonJointCount; ++i)
+            {
+                SkeletonJoint joint = GetSkeletonJoint(i);
+                if (joint.transform == null) { continue; }
+
+                if (joint.parentIndex >= 0)
+                {
+                    SkeletonJoint parentJoint = GetSkeletonJoint(joint.parentIndex);
+                    if (parentJoint.transform == null) { continue; }
+
+                    GL.Vertex(parentJoint.transform.position);
+                    GL.Vertex(joint.transform.position);
+                }
+            }
+            GL.End();
+        }
+
+        private void GameDebugDrawSkinTransforms(PrimitiveRenderData prd, float locatorScale)
+        {
+            var scaleNegZ = Matrix4x4.Scale(new Vector3(1.0f, 1.0f, -1.0f));
+            bool usingJointMonitor = _jointMonitor != null;
+            CAPI.ovrAvatar2Pose entityPose = default;
+            if (usingJointMonitor)
+            {
+                if (QueryEntityPose(out entityPose, out var throwaway))
+                {
+                    unsafe { usingJointMonitor &= entityPose.objectTransforms != null; }
+                }
+                else
+                {
+                    usingJointMonitor = false;
+                }
+            }
+
+            unsafe
+            {
+                var skinTransformPtr = stackalloc Matrix4x4[prd.primitive.joints.Length];
+                var skinTransformSize = sizeof(Matrix4x4) * prd.primitive.joints.Length;
+                if (CAPI.ovrAvatar2Render_GetSkinTransforms(entityId, prd.instanceId, new IntPtr(skinTransformPtr), (UInt32)skinTransformSize, false).IsSuccess())
+                {
+                    DebugLineMat.SetPass(0);
+                    GL.Begin(GL.LINES);
+
+                    for (int iJoint = 0; iJoint < prd.primitive.joints.Length; ++iJoint)
+                    {
+                        var poseIndex = prd.primitive.joints[iJoint];
+                        var skinTransform = skinTransformPtr[iJoint];
+                        var bindPose = prd.primitive.mesh.bindposes[iJoint];
+                        var worldPos = (transform.worldToLocalMatrix * scaleNegZ * skinTransform * bindPose.inverse).MultiplyPoint3x4(Vector3.zero);
+
+                        // skin transform position
+                        GL.Color(Color.red);
+                        GL.Vertex(worldPos);
+                        GL.Vertex(worldPos + Vector3.right * locatorScale);
+
+                        GL.Color(Color.green);
+                        GL.Vertex(worldPos);
+                        GL.Vertex(worldPos + Vector3.up * locatorScale);
+
+                        GL.Color(Color.blue);
+                        GL.Vertex(worldPos);
+                        GL.Vertex(worldPos + Vector3.forward * locatorScale);
+
+                        Vector3 jointPos = default;
+
+                        if (usingJointMonitor)
+                        {
+                            CAPI.ovrAvatar2Transform tx;
+                            unsafe { tx = entityPose.objectTransforms[poseIndex].ConvertSpace(); }
+                            jointPos = !tx.IsNan() ? (Vector3)tx.position : worldPos;
+                        }
+                        else
+                        {
+                            SkeletonJoint joint = GetSkeletonJoint(poseIndex);
+                            jointPos = joint.transform != null ? jointPos = joint.transform.position : worldPos;
+                        }
+
+                        // line from pose to skin transform
+                        GL.Color(Color.magenta);
+                        GL.Vertex(worldPos);
+                        GL.Vertex(jointPos);
+                    }
+
+                    GL.End();
+                }
+            }
+        }
+        #endregion
+#endif
+
+#if UNITY_EDITOR
+        #region Editor Debug Drawing
+        private void OnSceneGUI(SceneView sceneView)
+        {
+            if (!IsCreated) { return; }
+
+            if (_debugDrawing.drawTrackingPose)
+            {
+                if (CAPI.ovrAvatar2Tracking_GetPose(entityId, out var pose) == CAPI.ovrAvatar2Result.Success)
+                {
+                    EditorDebugDrawPose(in pose);
+                }
+            }
+
+            if (_debugDrawing.drawSkelHierarchy)
+            {
+                EditorDebugDrawSkelHierarchy();
+            }
+        }
+
+        private const float CRITICAL_JOINT_RADIUS = 0.125f;
+        void OnDrawGizmosSelected()
+        {
+            if (_skeleton == null) { return; }
+
+            if (_debugDrawing.drawCriticalJoints)
+            {
+                foreach (var jointPose in _monitoredJointPoses)
+                {
+                    var skelJointTx = GetSkeletonTransformByType(jointPose.jointType);
+                    if (skelJointTx)
+                    {
+                        Gizmos.matrix = skelJointTx.localToWorldMatrix;
+                        Gizmos.color = Color.red;
+                        Gizmos.DrawWireSphere(Vector3.zero, CRITICAL_JOINT_RADIUS);
+                        Gizmos.color = Color.blue;
+                        Gizmos.DrawRay(Vector3.zero, Vector3.forward * CRITICAL_JOINT_RADIUS);
+                        Gizmos.color = Color.green;
+                        Gizmos.DrawRay(Vector3.zero, Vector3.up * CRITICAL_JOINT_RADIUS);
+                    }
+                }
+            }
+        }
+
+        private void EditorDebugDrawPose(in CAPI.ovrAvatar2Pose pose)
+        {
+            uint count = pose.jointCount;
+            Color color = Color.blue;
+
+            // Draw the skeleton in the entity space
+            Handles.matrix = transform.localToWorldMatrix;
+
+            for (int i = 0; i < count; ++i)
+            {
+                unsafe
+                {
+                    CAPI.ovrAvatar2Transform* jointTransform = pose.objectTransforms + i;
+                    EditorDebugDrawOvrTransform(*jointTransform, 0.03f);
+
+                    var jointParentIndex = pose.GetParentIndex(i);
+                    if (jointParentIndex >= 0)
+                    {
+                        CAPI.ovrAvatar2Transform* parentTransform = pose.objectTransforms + jointParentIndex;
+                        Handles.color = color;
+                        Handles.DrawLine(jointTransform->ConvertSpace().position, parentTransform->ConvertSpace().position);
+                    }
+                }
+            }
+
+            // Reset to world space
+            Handles.matrix = Matrix4x4.identity;
+        }
+
+        private void EditorDebugDrawSkelHierarchy()
+        {
+            Color color = _debugDrawing.skeletonColor;
+            for (int i = 0; i < SkeletonJointCount; ++i)
+            {
+                SkeletonJoint joint = GetSkeletonJoint(i);
+                if (joint.transform == null) { continue; }
+
+                EditorDebugDrawTransform(joint.transform, 0.03f);
+                if (_debugDrawing.drawBoneNames)
+                {
+                    Handles.color = color;
+                    Handles.Label(joint.transform.position, joint.name);
+                }
+
+                if (joint.parentIndex >= 0)
+                {
+                    SkeletonJoint parentJoint = GetSkeletonJoint(joint.parentIndex);
+                    if (parentJoint.transform == null) { continue; }
+
+                    Handles.color = color;
+                    Handles.DrawLine(joint.transform.position, parentJoint.transform.position);
+                }
+            }
+        }
+
+        private void EditorDebugDrawOvrTransform(CAPI.ovrAvatar2Transform transformToDraw, float scale)
+        {
+            transformToDraw = transformToDraw.ConvertSpace();
+            Vector3 p = transformToDraw.position;
+            Quaternion orientation = transformToDraw.orientation;
+            Handles.color = Color.red;
+            Handles.DrawLine(p, p + (orientation * Vector3.right * scale));
+            Handles.color = Color.green;
+            Handles.DrawLine(p, p + (orientation * Vector3.up * scale));
+            Handles.color = Color.blue;
+            Handles.DrawLine(p, p + (orientation * Vector3.forward * scale));
+        }
+
+        private void EditorDebugDrawTransform(Transform transformToDraw, float scale)
+        {
+            Handles.color = Color.red;
+            Handles.DrawLine(transformToDraw.position, transformToDraw.position + (transformToDraw.right * scale));
+            Handles.color = Color.green;
+            Handles.DrawLine(transformToDraw.position, transformToDraw.position + (transformToDraw.up * scale));
+            Handles.color = Color.blue;
+            Handles.DrawLine(transformToDraw.position, transformToDraw.position + (transformToDraw.forward * scale));
+        }
+        #endregion
+#endif // UNITY_EDITOR
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Debug.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Debug.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..0e29884bab1edc1744bd376dacf891967e39abd7
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Debug.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 796a094df910dbc4e94e8f32379dd367
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_HandPoses.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_HandPoses.cs
new file mode 100644
index 0000000000000000000000000000000000000000..48e54caef63d8e258e3bfdcd204613fdaa024815
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_HandPoses.cs
@@ -0,0 +1,77 @@
+using System;
+
+using UnityEngine;
+
+namespace Oculus.Avatar2
+{
+    public partial class OvrAvatarEntity : MonoBehaviour
+    {
+        internal bool SetCustomWristOffset(CAPI.ovrAvatar2Side side, in CAPI.ovrAvatar2Transform offset)
+        {
+            return CAPI.OvrAvatar2_SetCustomWristOffset(entityId, side, in offset, this);
+        }
+
+        internal bool SetCustomHandSkeleton(CAPI.ovrAvatar2Side side, in CAPI.ovrAvatar2TrackingBodySkeleton cSkel)
+        {
+            return CAPI.OvrAvatar2_SetCustomHandSkeleton(entityId, side, in cSkel, this);
+        }
+
+        internal bool SetCustomHandPose(CAPI.ovrAvatar2Side side, in CAPI.ovrAvatar2TrackingBodyPose cPose)
+        {
+            return CAPI.OvrAvatar2_SetCustomHandPose(entityId, side, in cPose, this);
+        }
+
+        internal bool ClearCustomHandPose(CAPI.ovrAvatar2Side side)
+        {
+            return CAPI.OvrAvatar2_ClearCustomHandPose(entityId, side, this);
+        }
+    }
+
+    public partial class CAPI
+    {
+        private const string handPoseScope = "handPose";
+
+        internal static bool
+        OvrAvatar2_SetCustomWristOffset(
+            ovrAvatar2EntityId entityId,
+            ovrAvatar2Side side,
+            in ovrAvatar2Transform offset,
+            OvrAvatarEntity context)
+        {
+            return ovrAvatar2_SetCustomWristOffset(entityId, side, in offset)
+                .EnsureSuccess("ovrAvatar2_SetCustomWristOffset", handPoseScope, context);
+        }
+
+        internal static bool
+            OvrAvatar2_SetCustomHandSkeleton(
+                ovrAvatar2EntityId entityId,
+                ovrAvatar2Side side,
+                in ovrAvatar2TrackingBodySkeleton skeleton,
+                OvrAvatarEntity context)
+        {
+            return ovrAvatar2_SetCustomHandSkeleton(entityId, side, in skeleton)
+                .EnsureSuccess("ovrAvatar2_SetCustomHandSkeleton", handPoseScope, context);
+        }
+
+        internal static bool
+            OvrAvatar2_SetCustomHandPose(
+                ovrAvatar2EntityId entityId,
+                ovrAvatar2Side side,
+                in ovrAvatar2TrackingBodyPose pose,
+                OvrAvatarEntity context)
+        {
+            return ovrAvatar2_SetCustomHandPose(entityId, side, in pose)
+                .EnsureSuccess("ovrAvatar2_SetCustomHandPose", handPoseScope, context);
+        }
+
+
+
+        internal static bool OvrAvatar2_ClearCustomHandPose(ovrAvatar2EntityId entityId,
+                ovrAvatar2Side side,
+                OvrAvatarEntity context)
+        {
+            return ovrAvatar2_ClearCustomHandPose(entityId, side)
+                .EnsureSuccess("ovrAvatar2_ClearCustomHandPose", handPoseScope, context);
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_HandPoses.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_HandPoses.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..09ec0aaa769975de08405493f4e878497ec49095
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_HandPoses.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: b826f43fea59354489f2a2701d01e9d3
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_JointMonitoring.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_JointMonitoring.cs
new file mode 100644
index 0000000000000000000000000000000000000000..d1cf8d37ed3bfe5d65d8ead4cae390202fc7d91f
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_JointMonitoring.cs
@@ -0,0 +1,119 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace Oculus.Avatar2
+{
+    internal interface IJointMonitor : IDisposable
+    {
+        void OnJointPosesUpdated(List<OvrAvatarJointPose> jointPoses);
+
+        // TODO: Remove from interface when API is finalized and component can be accessed directly
+        bool TryGetTransform(CAPI.ovrAvatar2JointType jointType, out Transform tx);
+
+        // TODO: Remove from interface when API is finalized and component can be accessed directly
+        bool TryGetPositionAndOrientation(CAPI.ovrAvatar2JointType jointType, out Vector3 pos, out Quaternion rot);
+
+        void UpdateJoints(float deltaTime);
+    }
+
+    internal readonly struct OvrAvatarJointPose
+    {
+        public readonly CAPI.ovrAvatar2JointType jointType;
+        public readonly uint jointIndex;
+        public readonly Vector3 objectSpacePosition;
+        public readonly Quaternion objectSpaceOrientation;
+
+        internal OvrAvatarJointPose(CAPI.ovrAvatar2JointType jointType, uint jointIndex)
+        {
+            this.jointType = jointType;
+            this.jointIndex = jointIndex;
+            this.objectSpacePosition = Vector3.zero;
+            this.objectSpaceOrientation = Quaternion.identity;
+        }
+
+        internal OvrAvatarJointPose(in OvrAvatarJointPose pose, in CAPI.ovrAvatar2Transform tx)
+        {
+            this.jointType = pose.jointType;
+            this.jointIndex = pose.jointIndex;
+            this.objectSpacePosition = tx.position;
+            this.objectSpaceOrientation = tx.orientation;
+        }
+    }
+
+    public partial class OvrAvatarEntity : MonoBehaviour
+    {
+        private readonly HashSet<CAPI.ovrAvatar2JointType> _monitoredJointTypes =
+            new HashSet<CAPI.ovrAvatar2JointType>();
+        private readonly List<OvrAvatarJointPose> _monitoredJointPoses =
+            new List<OvrAvatarJointPose>();
+
+        private IJointMonitor _jointMonitor = null;
+
+        internal bool IsJointTypeLoaded(CAPI.ovrAvatar2JointType jointType)
+        {
+            return GetNodeForType(jointType) != CAPI.ovrAvatar2NodeId.Invalid;
+        }
+
+        internal bool AddMonitoredJoint(CAPI.ovrAvatar2JointType jointType)
+        {
+            if (_jointMonitor != null && !_monitoredJointTypes.Contains(jointType))
+            {
+                _monitoredJointTypes.Add(jointType);
+
+                var nodeId = GetNodeForType(jointType);
+                if (nodeId != CAPI.ovrAvatar2NodeId.Invalid)
+                {
+                    var index = _nodeToIndex[nodeId];
+                    _monitoredJointPoses.Add(new OvrAvatarJointPose(jointType, index));
+                }
+
+                return true;
+            }
+
+            return false;
+        }
+
+        internal bool RemoveMonitoredJoint(CAPI.ovrAvatar2JointType jointType)
+        {
+            if (_jointMonitor != null && _monitoredJointTypes.Contains(jointType))
+            {
+                _monitoredJointTypes.Remove(jointType);
+                _monitoredJointPoses.RemoveAll(pose => pose.jointType == jointType);
+                return true;
+            }
+
+            return false;
+        }
+
+        private void MonitorJoints(in CAPI.ovrAvatar2Pose entityPose)
+        {
+            // Only do the work if someone cares about it
+            if (_jointMonitor == null || _monitoredJointPoses.Count == 0) { return; }
+
+            bool validObjectTransforms;
+            unsafe { validObjectTransforms = entityPose.objectTransforms != null; }
+
+            if (validObjectTransforms)
+            {
+                for(int i = 0; i < _monitoredJointPoses.Count; ++i)
+                {
+                    var jointIndex = _monitoredJointPoses[i].jointIndex;
+                    CAPI.ovrAvatar2Transform tx;
+                    unsafe { tx = entityPose.objectTransforms[jointIndex].ConvertSpace(); }
+
+                    // Use root transform values instead
+                    // BUG: Native sdk shouldn't be giving NaN values in the first place
+                    if (tx.IsNan()) {
+                        tx = (CAPI.ovrAvatar2Transform)_baseTransform;
+                    }
+
+                    // Update transform
+                    _monitoredJointPoses[i] = new OvrAvatarJointPose(_monitoredJointPoses[i], in tx);
+                }
+            }
+
+            _jointMonitor.OnJointPosesUpdated(_monitoredJointPoses);
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_JointMonitoring.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_JointMonitoring.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..f1a36c11cd9df25e54464a5ef8895fe51ca86e23
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_JointMonitoring.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 4ffecea81eeb429438a0b96532c0f1a5
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_LOD.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_LOD.cs
new file mode 100644
index 0000000000000000000000000000000000000000..be71e7a2d74baacca0c0bf0c39680db54fff86a4
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_LOD.cs
@@ -0,0 +1,352 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+
+using UnityEngine;
+
+using Debug = UnityEngine.Debug;
+
+namespace Oculus.Avatar2
+{
+    public partial class OvrAvatarEntity : MonoBehaviour
+    {
+        [Header("LOD")]
+        private AvatarLOD _avatarLOD;
+
+        private readonly LodData[] _visibleLodData = new LodData[CAPI.ovrAvatar2EntityLODFlagsCount];
+
+        /// Number of LODs loaded for this avatar.
+        private int lodObjectCount { get; set; } = 0;
+
+        /// Index of lowest quality level of detail loaded for this avatar.
+        public int LowestQualityLODIndex { get; private set; } = -1;
+
+        /// Index of highest quality level of detail loaded for this avatar.
+        public int HighestQualityLODIndex { get; private set; } = -1;
+
+        /// Provides vertex and triangle counts for each level of detail.
+        public IReadOnlyList<AvatarLODCostData> CopyVisibleLODCostData()
+        {
+            var lodCosts = new AvatarLODCostData[CAPI.ovrAvatar2EntityLODFlagsCount];
+            var allLodCost = _visibleAllLodData.IsValid ? _visibleAllLodData.totalCost : default;
+            for (int idx = 0; idx < _visibleLodData.Length; idx++)
+            {
+                ref readonly var lodObj = ref _visibleLodData[idx];
+                lodCosts[idx] = AvatarLODCostData.Sum(in allLodCost, in lodObj.totalCost);
+            }
+            return lodCosts;
+        }
+
+        // TODO: This is a silly method to have - IReadOnlyDictionary maybe?
+        private Dictionary<int, LodData> CopyVisibleLODData()
+        {
+            var lodDict = new Dictionary<int, LodData>(lodObjectCount);
+            for (int idx = 0; idx < _visibleLodData.Length; idx++)
+            {
+                ref var lodObj = ref _visibleLodData[idx];
+                if (lodObj.HasInstances)
+                {
+                    lodDict.Add(idx, lodObj);
+                }
+            }
+            return lodDict;
+        }
+
+        /// Per-avatar level of detail information.
+        public AvatarLOD AvatarLOD
+        {
+            get
+            {
+                if (_avatarLOD == null)
+                {
+                    _avatarLOD = gameObject.GetOrAddComponent<AvatarLOD>();
+
+                    // In some edge cases `GetOrAddComponent` can return null
+                    if (_avatarLOD != null)
+                    {
+                        _avatarLOD.Entity = this;
+                    }
+                }
+
+                return _avatarLOD;
+            }
+        }
+
+        // TODO: Setup LOD control via these properties, suppressing unused warnings for now
+#pragma warning disable 0414
+        // Intended LOD to render
+        // TODO: Have LOD system drive this value
+        private readonly uint _targetLodIndex = 0;
+        // LOD currently being rendered
+        // TODO: Drive this from render state + `ovrAvatar2Entity_[Get/Set]LodFlags`
+        private readonly int _currentLodIndex = -1;
+#pragma warning restore 0414
+
+        private LodData _visibleAllLodData;
+
+        // High level container for a given singular LOD, may combine multiple primitive instances
+        public struct LodData
+        {
+            internal LodData(GameObject gob)
+            {
+                gameObject = gob;
+                transform = gob.transform;
+
+                instances = new HashSet<OvrAvatarRenderable>();
+                totalCost = default;
+            }
+
+            public bool IsValid => gameObject != null;
+            public bool HasInstances => instances != null && instances.Count > 0;
+
+            // TODO: Refactor AvatarLOD and remove
+            public int vertexCount => (int) totalCost.meshVertexCount;
+            public int triangleCount => (int) totalCost.renderTriangleCount;
+
+            // TODO: Remove gameObject and transform fields - manage these internally
+            public readonly GameObject gameObject;
+            public readonly Transform transform;
+
+            // Discrete renderables, may be parented to various gameObjects
+            private readonly HashSet<OvrAvatarRenderable> instances;
+
+            public AvatarLODCostData totalCost;
+
+            internal void AddInstance(OvrAvatarRenderable newInstance)
+            {
+                if (instances.Add(newInstance))
+                {
+                    totalCost = AvatarLODCostData.Sum(in totalCost, in newInstance.CostData);
+                }
+            }
+            internal bool RemoveInstance(OvrAvatarRenderable oldInstance)
+            {
+                bool didRemove = instances.Remove(oldInstance);
+                if (didRemove)
+                {
+                    totalCost = AvatarLODCostData.Subtract(in totalCost, in oldInstance.CostData);
+                }
+                OvrAvatarLog.Assert(didRemove, logScope);
+                return didRemove;
+            }
+            internal void Clear()
+            {
+                instances.Clear();
+                totalCost = default;
+            }
+        }
+
+        private void InitAvatarLOD()
+        {
+            AvatarLOD.CulledChangedEvent += OnCullChangedEvent;  // Access to the public AvatarLod causes the component to be GetOrAdded (see above)
+        }
+
+        internal void UpdateAvatarLODOverride()    // internal so it can be called from the LOD Manager, which runs at a slower framerate
+        {
+            if (_avatarLOD != null)
+            {
+                _avatarLOD.UpdateOverride();
+            }
+        }
+
+        private  void ShutdownAvatarLOD()
+        {
+            if (_avatarLOD != null) // check the private instance to avoid creating a new one on the spot
+            {
+                _avatarLOD.CulledChangedEvent -= OnCullChangedEvent;
+
+                Destroy(_avatarLOD);
+                _avatarLOD = null;
+            }
+        }
+
+        protected virtual void ComputeImportanceAndCost(out float importance, out UInt32 cost)
+        {
+            var avatarLod = AvatarLOD;
+            var avatarLevel = avatarLod.Level;
+            if (0 <= avatarLevel)
+            {
+                importance = avatarLod.updateImportance;
+                cost = avatarLod.UpdateCost;
+            }
+            else
+            {
+                importance = 0f;
+                cost = 0;
+            }
+        }
+
+        internal void SendImportanceAndCost()    // internal so it can be called from the LOD Manager, which runs at a slower framerate
+        {
+            ComputeImportanceAndCost(out float importance, out UInt32 cost);
+
+            // set importance for next frame
+            CAPI.ovrAvatar2Importance_SetImportanceAndCost(entityId, importance, cost);
+        }
+
+        [Conditional("UNITY_DEVELOPMENT")]
+        [Conditional("UNITY_EDITOR")]
+        internal void TrackUpdateAge()
+        {
+#if UNITY_EDITOR || UNITY_DEVELOPMENT
+            var avatarLod = AvatarLOD;
+            // Track of the last update time for debug tools
+            if (EntityActive)
+            {
+                avatarLod.previousUpdateAgeWindowSeconds = avatarLod.lastUpdateAgeSeconds + Time.deltaTime;
+                avatarLod.lastUpdateAgeSeconds = 0;
+            }
+            else
+            {
+                avatarLod.lastUpdateAgeSeconds += Time.deltaTime;
+            }
+#endif // UNITY_EDITOR || UNITY_DEVELOPMENT
+        }
+
+        protected virtual void OnCullChangedEvent(bool culled)
+        {
+            OnCulled?.Invoke(culled);
+
+#if AVATAR_CULLING_DEBUG
+            // Use to easily debug but do not check in enabled, its way too verbose and allocates string:
+            OvrAvatarLog.LogInfo("Caught culling event for Avatar " + AvatarLOD.name + (culled ? "":" NOT") + " CULLED", logScope, this);
+#endif
+        }
+
+        private void SetupLodGroups()
+        {
+            bool setupLodGroups = false;
+
+            var avatarLod = AvatarLOD;
+            if (lodObjectCount > 1)
+            {
+                // TODO: Update avatarLOD to take array, didn't want to change *too* many classes all at once
+                var lodDict = CopyVisibleLODData();
+
+                // Don't add if effective lodCount is <= 1
+                // TODO: It seems like this should be handled by `AvatarLOD`?
+                setupLodGroups = lodDict.Count > 1;
+                if (setupLodGroups)
+                {
+                    avatarLod.AddLODGameObjectGroupBySdkRenderers(lodDict);
+                }
+            }
+
+            if (!setupLodGroups)
+            {
+                avatarLod.ClearLODGameObjects();
+            }
+            avatarLod.AddLODActionGroup(gameObject, UpdateAvatarLodColor, 5);
+        }
+
+        private void ResetLodCullingPoints()
+        {
+            if (_avatarLOD != null)
+            {
+                _avatarLOD.Reset();
+            }
+        }
+
+        private void SetupLodCullingPoints()
+        {
+            // TODO: This seems like mostly logic which should live in AvatarLODManager?
+            // populate the centerXform and the extraXforms for culling
+            if (HasJoints)
+            {
+                var avatarLod = AvatarLOD;
+                var lodManager = AvatarLODManager.Instance;
+
+                var skelJoint = GetSkeletonTransformByType(lodManager.JointTypeToCenterOn);
+                bool hasSkelJoint = skelJoint != null;
+                if(!hasSkelJoint) {
+                    OvrAvatarLog.LogError($"SkeletonJoint not found for center joint {lodManager.JointTypeToCenterOn}", logScope, this);
+                }
+
+                avatarLod.centerXform = hasSkelJoint ? skelJoint : _baseTransform;
+
+                avatarLod.extraXforms.Clear();
+
+                foreach (var jointType in lodManager.jointTypesToCullOnArray)
+                {
+                    var cullJoint = GetSkeletonTransformByType(jointType);
+                    if (cullJoint)
+                    {
+                        avatarLod.extraXforms.Add(cullJoint);
+                    }
+                    else
+                    {
+                        OvrAvatarLog.LogError($"Unable to find cullJoint for jointType {jointType}", logScope, this);
+                    }
+                }
+            }
+            else
+            {
+                // If there are no skeletal joints, reset AvatarLOD to default settings
+                TeardownLodCullingPoints();
+            }
+        }
+
+        private void TeardownLodCullingPoints()
+        {
+            if (_avatarLOD != null)
+            {
+                // reset JointToCenterOn
+                _avatarLOD.centerXform = _baseTransform;
+
+                // reset extraXforms
+                _avatarLOD.extraXforms.Clear();
+            }
+        }
+
+        // Used for tracking Entity's valid LOD range
+        private void ResetLODRange()
+        {
+            LowestQualityLODIndex = HighestQualityLODIndex = -1;
+        }
+        private void ExpandLODRange(uint lod)
+        {
+            // TODO: Initial values of -1/-1 aren't super clean
+            if (LowestQualityLODIndex < lod) { LowestQualityLODIndex = (int) lod; }
+            if (HighestQualityLODIndex < 0 || HighestQualityLODIndex > lod) { HighestQualityLODIndex = (int) lod; }
+        }
+        private void RefreshLODRange()
+        {
+            ResetLODRange();
+            for (uint lodIdx = 0; lodIdx < _visibleLodData.Length; ++lodIdx)
+            {
+                if (_visibleLodData[lodIdx].HasInstances)
+                {
+                    ExpandLODRange(lodIdx);
+                }
+            }
+        }
+
+        // Perform runtime configuration when `IsLocal==true`
+        private void ConfigureLocalAvatarSettings()
+        {
+            OvrAvatarLog.Assert(IsLocal, logScope, this);
+
+            // If we are local, update AvatarLODManager to assign `firstPersonAvatarLod`
+            // NOTE: `CAPI.ovrAvatar2EntityViewFlags.FirstPerson` is not actually required to use this property
+            // "firstPerson" refers to whether there is a camera being used to render from this avatar's perspective
+            // TODO: This needs to be improved to support "possessing" multiple avatars :/
+            var lodManager = AvatarLODManager.Instance;
+            if (lodManager == null) { return; }
+
+            var childCamera = GetComponentInChildren<Camera>();
+            // Check if we have a camera
+            if (childCamera == null) { return; }
+
+            // Confirm that it is the same camera LODManager is using
+            var lodCamera = lodManager.CurrentCamera;
+            if (!(lodCamera is null) && lodCamera != childCamera) { return; }
+
+            lodManager.firstPersonAvatarLod = AvatarLOD;
+            // No need to motion smooth, as we will skin every frame
+            MotionSmoothingSettings = MotionSmoothingOptions.FORCE_OFF;
+
+            OvrAvatarLog.LogDebug(
+                "Disabled motion smoothing per AvatarLODManager config", logScope, this);
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_LOD.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_LOD.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..a922f9b4768ad46a81a1e56e11bae4c6b3e93d2a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_LOD.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 3a4c63ee602f040468f282f676ebb190
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Lifecycle.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Lifecycle.cs
new file mode 100644
index 0000000000000000000000000000000000000000..4f31921dcf44f7c7549ad13c1c1d6e184de0ecbf
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Lifecycle.cs
@@ -0,0 +1,69 @@
+using System;
+
+namespace Oculus.Avatar2
+{
+    public partial class OvrAvatarEntity : UnityEngine.MonoBehaviour
+    {
+        private CAPI.ovrAvatar2EntityId CreateNativeEntity(in CAPI.ovrAvatar2EntityCreateInfo info)
+        {
+            if (!info.IsValid)
+            {
+                OvrAvatarLog.LogWarning("Attempted to create entity with invalid info", logScope, this);
+                return CAPI.ovrAvatar2EntityId.Invalid;
+            }
+            if (!CAPI.OvrAvatar2Entity_Create(in info, this, out var entityId))
+            {
+                OvrAvatarLog.LogError($"Failed to create entity on gameObject:`{name}`", logScope, this);
+                return CAPI.ovrAvatar2EntityId.Invalid;
+            }
+            return entityId;
+        }
+
+        private bool DestroyNativeEntity()
+        {
+            if (entityId == CAPI.ovrAvatar2EntityId.Invalid)
+            {
+                OvrAvatarLog.LogWarning("Attempted to destroy entity with invalid ID", logScope, this);
+                return false;
+            }
+            if (!CAPI.OvrAvatar2Entity_Destroy(entityId, this))
+            {
+                OvrAvatarLog.LogError($"Failed to destroy entity on gameObject:`{name}`", logScope, this);
+                return false;
+            }
+            OvrAvatarLog.LogVerbose("Successfully destroyed native entity", logScope, this);
+            entityId = CAPI.ovrAvatar2EntityId.Invalid;
+            return true;
+        }
+    }
+
+    public partial class CAPI
+    {
+        private const string lifeCycleScope = "lifecycle";
+
+        /// Create an entity, allocating memory
+        /// \param info - configuration for new entity
+        /// \param context - OvrAvatarEntity instance which will own this native entity
+        /// \param newEntityId - native entityId of new entity, or `Invalid` if errors detected
+        /// \return result code
+        ///
+        internal static bool OvrAvatar2Entity_Create(in ovrAvatar2EntityCreateInfo info, OvrAvatarEntity context
+            , out ovrAvatar2EntityId newEntityId)
+        {
+            OvrAvatarLog.Assert(info.IsValid, lifeCycleScope, context);
+            return ovrAvatar2Entity_Create(in info, out newEntityId)
+                .EnsureSuccess("ovrAvatar2Entity_Create", lifeCycleScope, context);
+        }
+
+        /// Destroy an entity, releasing all related memory
+        /// \param entity to destroy
+        /// \return result code
+        ///
+        internal static bool OvrAvatar2Entity_Destroy(ovrAvatar2EntityId entityId, OvrAvatarEntity context)
+        {
+            OvrAvatarLog.Assert(entityId != ovrAvatar2EntityId.Invalid, lifeCycleScope, context);
+            return ovrAvatar2Entity_Destroy(entityId)
+                .EnsureSuccess("ovrAvatar2Entity_Destroy", lifeCycleScope, context);
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Lifecycle.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Lifecycle.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..ab40be742872a20d6ac593d38bd0740bdc24678a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Lifecycle.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 97675a1a7f6ceb7448bef6f86cb798f4
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Loading.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Loading.cs
new file mode 100644
index 0000000000000000000000000000000000000000..3e00ab37718ed719e5ee4280d5adef6949af0dcd
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Loading.cs
@@ -0,0 +1,1716 @@
+// TODO: Delete when multi LOD primitives align correctly when rendered
+#define OVR_AVATAR_HIDE_CONTROLLER_PRIMITIVES
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Threading.Tasks;
+
+using Unity.Collections;
+
+using UnityEngine;
+using UnityEngine.Events;
+using UnityEngine.Profiling;
+
+namespace Oculus.Avatar2
+{
+    public partial class OvrAvatarEntity : MonoBehaviour
+    {
+        /////////////////////////////////////////////////
+        //:: Deprecated LoadingState APIs
+
+        #region Deprecated LoadingState APIs
+
+        public enum LoadingState
+        {
+            Failed = -1,
+            NotCreated = 0,
+            Created,
+            Loading,
+            Success,
+        }
+
+        [Obsolete("Deprecated, please refer to documentation")]
+        public LoadingState LoadState { get; protected set; }
+        private LoadingState _lastInvokedLoadState = LoadingState.NotCreated;
+
+        [Serializable]
+        [Obsolete("Deprecated, please refer to documentation")]
+        public class LoadingStateEvent : UnityEvent<LoadingState> { }
+
+        [Serializable]
+        [Obsolete("Deprecated, please refer to documentation")]
+        public class EntityLoadingStateEvent : UnityEvent<OvrAvatarEntity> { }
+
+        [Header("Events (Deprecated)")]
+        [Obsolete("Deprecated, please refer to documentation")]
+        public LoadingStateEvent LoadingStateChanged = new LoadingStateEvent();
+        [Obsolete("Deprecated, please refer to documentation")]
+        public EntityLoadingStateEvent EntityLoadingStateChanged = new EntityLoadingStateEvent();
+
+        #endregion
+
+        /////////////////////////////////////////////////
+        //:: Public API
+
+        #region Public APIs
+
+        ///
+        /// Represents what is currently loaded & ready to be used/accessed on this Avatar Entity
+        /// Order is guaranteed:
+        ///   None -> Created -> Skeleton -> DefaultAvatar (if enabled) -> UserAvatar
+        ///
+        /// If a load step fails, the sequence will not advance
+        ///
+        /// If the user avatar is destroyed, it calls "PreTeardown" and before returning to state None
+        ///
+        public enum AvatarState
+        {
+            /// Initial state - Not yet created
+            None = 0,
+
+            /// Native Avatar Entity has been created
+            Created,
+
+            //------
+            // Enum values after this are guaranteed to have a skeleton loaded
+            // ----
+
+            /// Skeleton has loaded, but no model. Streaming/Animation APIs available, attachments, etc.
+            Skeleton,
+
+            /// (Optional) Default Avatar is loaded and renderable
+            DefaultAvatar,
+
+            /// (Optional) Fast Load Avatar is loaded and renderable
+            FastLoad,
+
+            /// A non-default (CDN or preset) avatar is loaded and renderable
+            UserAvatar
+        }
+
+        public AvatarState CurrentState { get; protected set; } = AvatarState.None;
+
+        [Serializable]
+        public class AvatarStateEvent : UnityEvent<OvrAvatarEntity> { }
+
+        [Serializable]
+        public class AvatarLoadFailedEvent : UnityEvent<OvrAvatarEntity, CAPI.ovrAvatar2LoadRequestInfo> { }
+
+        [Header("Events")]
+        /// Called when native avatar is created, after entering state AvatarState.Created
+        public AvatarStateEvent OnCreatedEvent = new AvatarStateEvent();
+
+        /// Called when skeleton is loaded, after entering state AvatarState.Skeleton
+        public AvatarStateEvent OnSkeletonLoadedEvent = new AvatarStateEvent();
+
+        /// Called when Default Avatar is loaded, after entering state AvatarState.DefaultAvatar
+        public AvatarStateEvent OnDefaultAvatarLoadedEvent = new AvatarStateEvent();
+
+        /// Called when Fast Load Avatar is loaded, after entering state AvatarState.FastLoad
+        public AvatarStateEvent OnFastLoadAvatarLoadedEvent = new AvatarStateEvent();
+
+        /// Called the first time a User Avatar is loaded, after entering state AvatarState.UserAvatar
+        public AvatarStateEvent OnUserAvatarLoadedEvent = new AvatarStateEvent();
+
+        /// Called at the start of Teardown() before reverting to state AvatarState.None
+        public AvatarStateEvent PreTeardownEvent = new AvatarStateEvent();
+
+        /// Called when a load fails
+        public AvatarLoadFailedEvent OnLoadFailedEvent = new AvatarLoadFailedEvent();
+
+        #endregion
+
+        /////////////////////////////////////////////////
+        //:: Private/Protected State
+
+        #region Private/Protected State
+
+        // TODO: This can probably be consolidated w/ `_primitiveRenderables`
+        private readonly Dictionary<CAPI.ovrAvatar2NodeId, PrimitiveRenderData[]> _meshNodes
+            = new Dictionary<CAPI.ovrAvatar2NodeId, PrimitiveRenderData[]>();
+
+        private uint primitiveRenderCount => (uint)_visiblePrimitiveRenderers.Length;
+        // Used to quickly detect when render state has changed
+        private CAPI.ovrAvatar2HierarchyVersion _currentHierarchyVersion
+            = CAPI.ovrAvatar2HierarchyVersion.Invalid;
+        private CAPI.ovrAvatar2EntityRenderStateVersion _currentAllNodesVersion
+            = CAPI.ovrAvatar2EntityRenderStateVersion.Invalid;
+        private CAPI.ovrAvatar2EntityRenderStateVersion _currentVisibleNodesVersion
+            = CAPI.ovrAvatar2EntityRenderStateVersion.Invalid;
+
+        private CAPI.ovrAvatar2HierarchyVersion _targetHierarchyVersion
+            = CAPI.ovrAvatar2HierarchyVersion.Invalid;
+        private CAPI.ovrAvatar2EntityRenderStateVersion _targetAllNodesVersion
+            = CAPI.ovrAvatar2EntityRenderStateVersion.Invalid;
+        private CAPI.ovrAvatar2EntityRenderStateVersion _targetVisibleNodesVersion
+            = CAPI.ovrAvatar2EntityRenderStateVersion.Invalid;
+
+        protected bool IsUnitySynced => IsUnityHierarchySynced
+            && AreUnityNodesSynced
+            && IsUnityVisibiltySynced;
+
+        protected bool IsUnityHierarchySynced => _currentHierarchyVersion == _targetHierarchyVersion;
+        protected bool AreUnityNodesSynced => _currentAllNodesVersion == _targetAllNodesVersion;
+        protected bool IsUnityVisibiltySynced => _currentVisibleNodesVersion == _targetVisibleNodesVersion;
+
+        protected CAPI.ovrAvatar2Result entityStatus => CAPI.ovrAvatar2Entity_GetStatus(entityId);
+
+        // Hold original load settings, need after the fast load has finished, to issue the full load
+        private CAPI.ovrAvatar2EntityLODFlags _lodFilters;
+        private string[] _assetPaths;
+        private CAPI.ovrAvatar2EntityFilters _loadFilters;
+
+        #endregion
+
+        /////////////////////////////////////////////////
+        //:: Protected Virtual Methods
+
+        #region Subclass extensions
+
+        /// Called when native avatar is created, after entering state AvatarState.Created
+        protected virtual void OnCreated() { }
+
+        /// Called when skeleton is loaded, after entering state AvatarState.Skeleton
+        protected virtual void OnSkeletonLoaded() { }
+
+        /// Called when Default Avatar is loaded, after entering state AvatarState.DefaultAvatar
+        protected virtual void OnDefaultAvatarLoaded() { }
+
+        /// Called when FastLoad Avatar is loaded, after entering state AvatarState.FastLoad
+        protected virtual void OnFastLoadAvatarLoaded() { }
+
+        /// Called the first time a User Avatar is loaded, after entering state AvatarState.UserAvatar
+        protected virtual void OnUserAvatarLoaded() { }
+
+        /// Called at the start of Teardown() before reverting to state AvatarState.None
+        protected virtual void PreTeardown() { }
+
+        /// Called when a load operation fails. See LoadRequestInfo for reason
+        protected virtual void OnLoadFailed(CAPI.ovrAvatar2LoadRequestInfo loadRequest) { }
+
+        ///
+        /// Called on any LoadRequest state change for this entity. Overriding this is not recommended because the
+        /// behavior may not always be intuitive; For example, "Success" on a LoadRequest only means that the
+        /// assets have been loaded in native code, it does not mean that any Unity game objects have been created/updated.
+        /// Most users should prefer the AvatarState callbacks such as "OnUserAvatarLoaded".
+        protected virtual void OnLoadRequestStateChanged(CAPI.ovrAvatar2LoadRequestInfo loadRequest)
+        {
+            if (loadRequest.state == CAPI.ovrAvatar2LoadRequestState.Failed)
+            {
+                OvrAvatarLog.LogInfo($"[{entityId}] OnLoadFailed requestId={loadRequest.id} ({loadRequest.failedReason.ToString()})", logScope, this);
+                OnLoadFailed(loadRequest);
+                OnLoadFailedEvent?.Invoke(this, loadRequest);
+            }
+        }
+
+        /// Called when a new OvrAvatarRenderable is created during the asset loading process
+        protected virtual void OnRenderableCreated(OvrAvatarRenderable newRenderable)
+        {
+            OvrAvatarLog.LogVerbose($"Created new renderable - {newRenderable.name}", logScope, this);
+
+#if OVR_AVATAR_HIDE_CONTROLLER_PRIMITIVES
+            // HACK: hide controllers by meshName (quest + rift)
+            if (newRenderable.name.Contains("controller") || newRenderable.name.Contains("touch"))
+            {
+                newRenderable.IsHidden = true;
+            }
+#endif
+        }
+
+        #endregion // Subclass extensions
+
+        /////////////////////////////////////////////////
+        //:: Private Functions
+
+        #region Asset Loading
+        public Task<OvrAvatarManager.HasAvatarChangedRequestResultCode> HasAvatarChangedAsync()
+        {
+            return OvrAvatarManager.Instance.SendHasAvatarChangedRequestAsync(entityId);
+        }
+
+        private bool ShouldFastLoad()
+        {
+            return OvrAvatarManager.Instance.UseFastLoadAvatar && CurrentState < AvatarState.FastLoad;
+        }
+
+        /* Load local user's CDN asset with filters specified in `creationInfo.renderFilters` */
+        protected void LoadUser()
+        {
+            LoadUserWithFilters(in _creationInfo.renderFilters);
+        }
+
+
+        protected void LoadUserWithFilters(in CAPI.ovrAvatar2EntityFilters filters)
+        {
+            if (!OvrAvatarEntitlement.AccessTokenIsValid())
+            {
+                OvrAvatarLog.LogError($"Cannot LoadUser until a valid Access Token is set.", logScope, this);
+                return;
+            }
+
+            if (_userId == 0)
+            {
+                OvrAvatarLog.LogError("Cannot LoadUser until User Id is set.", logScope, this);
+                return;
+            }
+
+            if (ShouldFastLoad())
+            {
+                _loadFilters = filters;
+            }
+
+            CAPI.ovrAvatar2Result result;
+            CAPI.ovrAvatar2LoadRequestId loadRequestId;
+            if (ShouldFastLoad())
+            {
+                result = CAPI.OvrAvatarEntity_LoadUserWithFiltersFast(entityId, _userId, in filters, out loadRequestId);
+            }
+            else
+            {
+                result = CAPI.OvrAvatarEntity_LoadUserWithFilters(entityId, _userId, in filters, out loadRequestId);
+            }
+
+            if (result == CAPI.ovrAvatar2Result.Pending)
+            {
+                OvrAvatarLog.LogDebug($"Loaded user ID {_userId} onto entity {entityId}", logScope, this);
+                ClearFailedLoadState();
+                IsPendingCdnAvatar = true;
+                OvrAvatarManager.Instance.RegisterLoadRequest(this, loadRequestId);
+            }
+            else
+            {
+                OvrAvatarLog.LogError($"LoadUser Failed: {result}", logScope, this);
+                IsPendingCdnAvatar = false;
+                // TODO: This is good in theory, but it conflicts w/ rendering logic
+#pragma warning disable 618
+                LoadState = LoadingState.Failed;
+#pragma warning restore 618
+            }
+        }
+
+        // TODO: Rename? This is also how you load assets from cdn if it can't find it from a zip source
+        /**
+         * Load avatar assets from a Zip file from Unity streaming assets.
+         * This function loads all levels of detail.
+         * @param string[]   array of strings containing asset directories to search.
+         * @see LoadAssetsFromData
+         * @see LoadAssetsFromStreamingAssets
+         */
+        protected bool LoadAssetsFromZipSource(string[] assetPaths)
+        {
+            return LoadAssetsFromZipSource(assetPaths, _creationInfo.renderFilters.lodFlags);
+        }
+
+        /**
+         * Load avatar assets from a Zip file from Unity streaming assets.
+         * @param string[]   array of strings containing asset directories to search.
+         * @param lodFilter  level of detail(s) to load.
+         * @see LoadAssetsFromData
+         * @see LoadAssetsFromStreamingAssets
+         * @see CAPI.ovrAvatar2EntityLODFlags
+         */
+        protected bool LoadAssetsFromZipSource(string[] assetPaths, CAPI.ovrAvatar2EntityLODFlags lodFilter)
+        {
+            if (!IsCreated)
+            {
+                OvrAvatarLog.LogError("Cannot load assets before entity has been created.", logScope, this);
+                return false;
+            }
+
+            var loadFilters = _creationInfo.renderFilters;
+            loadFilters.lodFlags = lodFilter;
+
+            if (ShouldFastLoad())
+            {
+                _lodFilters = lodFilter;
+                _assetPaths = assetPaths;
+            }
+            else
+            {
+                isLoadingFullPreset = true;
+            }
+
+            bool didLoadZipAsset = false;
+
+            if (assetPaths != null)
+            {
+                foreach (var path in assetPaths)
+                {
+                    CAPI.ovrAvatar2Result result;
+                    CAPI.ovrAvatar2LoadRequestId loadRequestId;
+                    if (ShouldFastLoad())
+                    {
+                        var fastpath = path.Replace(OvrAvatarManager.Instance.GetPlatformGLBPostfix(true).ToLower(), OvrAvatarManager.Instance.GetFastLoadGLBPostfix(true).ToLower());
+                        fastpath = $"zip://{fastpath}";
+                        result = CAPI.OvrAvatarEntity_LoadUriWithFiltersFast(entityId, fastpath, loadFilters, out loadRequestId);
+                        fastLoadPresetPaths.Add(fastpath);
+                    }
+                    else
+                    {
+                        result = CAPI.OvrAvatarEntity_LoadUriWithFilters(entityId, $"zip://{path}", loadFilters, out loadRequestId);
+                    }
+                    if (result.IsSuccess())
+                    {
+                        didLoadZipAsset = true;
+                        OvrAvatarManager.Instance.RegisterLoadRequest(this, loadRequestId);
+                    }
+                    else
+                    {
+                        OvrAvatarLog.LogError($"Failed to load asset. {result} at path: {path}", logScope, this);
+                    }
+                }
+                OvrAvatarLog.Assert(didLoadZipAsset);
+            }
+
+            IsPendingZipAvatar = didLoadZipAsset;
+            if (didLoadZipAsset)
+            {
+                ClearFailedLoadState();
+            }
+
+            return didLoadZipAsset;
+        }
+
+        /**
+        * Load avatar assets from Unity streaming assets.
+        * @param string[]   array of strings containing asset directories
+        *                   to search relative to *Application.streamingAssetsPath*.
+        * @see LoadAssetsFromData
+        * @see LoadAssetsFromZipSource
+        */
+        protected void LoadAssetsFromStreamingAssets(string[] assetPaths)
+        {
+            if (!IsCreated)
+            {
+                OvrAvatarLog.LogError("Cannot load assets before entity has been created.", logScope, this);
+                return;
+            }
+
+            string prefix = OvrAvatarManager.IsAndroidStandalone ? $"apk://" : $"file://{Application.streamingAssetsPath}/";
+            foreach (var path in assetPaths)
+            {
+                CAPI.ovrAvatar2Result result = CAPI.OvrAvatarEntity_LoadUriWithFilters(entityId, prefix + path, _creationInfo.renderFilters, out var loadRequestId);
+                if (result.IsSuccess())
+                {
+                    ClearFailedLoadState();
+                    OvrAvatarManager.Instance.RegisterLoadRequest(this, loadRequestId);
+                }
+                else
+                {
+                    OvrAvatarLog.LogError(
+                        $"Failed to load asset from streaming assets. {result} at path: {prefix + path}"
+                        , logScope, this);
+                }
+            }
+        }
+
+        private IEnumerator LoadAsync_BuildSkeletonAndPrimitives()
+        {
+            Debug.Assert(IsApplyingModels);
+            OvrAvatarLog.LogVerbose($"Beginning LoadAsync_BuildSkeletonAndPrimitives", logScope, this);
+
+            bool builtSkeleton = false;
+            do
+            {
+                if (QueryEntityPose(out var entityPose, out var hierarchyVersion))
+                {
+                    LoadSync_BuildSkeleton(in entityPose, hierarchyVersion);
+                    builtSkeleton = true;
+                }
+                else
+                {
+                    OvrAvatarLog.LogError("Failed to query entity pose for building skeleton!", logScope, this);
+                }
+            } while (!builtSkeleton);
+
+            OvrAvatarLog.Assert(CurrentState != AvatarState.None);
+            if (CurrentState == AvatarState.Created && SkeletonJointCount > 0)
+            {
+                CurrentState = AvatarState.Skeleton;
+                InvokeOnSkeletonLoaded();
+            }
+
+            yield return LoadAsync_BuildPrimitives();
+        }
+
+        private IEnumerator LoadAsync_BuildPrimitives()
+        {
+            yield return LoadAsyncCoroutine_BuildPrimitives_Internal();
+
+            LoadAsync_Finalize_Internal();
+        }
+
+        // TODO: Determine if this is worth slicing
+        private void LoadSync_BuildSkeleton(in CAPI.ovrAvatar2Pose entityPose, CAPI.ovrAvatar2HierarchyVersion hierVersion)
+        {
+            _targetHierarchyVersion = hierVersion;
+
+            OvrAvatarLog.LogVerbose($"Starting BuildSkeleton", logScope, this);
+
+            var previousSkeleton = _skeleton;
+            var previousNodeToIndex = _nodeToIndex;
+
+            var jointCount = entityPose.jointCount;
+            var newSkeleton = new SkeletonJoint[jointCount];
+
+            var buildNewNodeToIndex = new Dictionary<CAPI.ovrAvatar2NodeId, uint>();
+
+            // Only create transforms if we aren't using Joint Monitoring
+            bool createTransforms = _jointMonitor == null;
+
+            // Build hierarchy array
+            for (uint i = 0; i < jointCount; ++i)
+            {
+                var nodeId = entityPose.GetNodeIdAtIndex(i);
+
+                var jointName = GetNameForNode(nodeId, in entityPose);
+                if (string.IsNullOrWhiteSpace(jointName)) { jointName = "joint" + i; }
+
+                if (nodeId == CAPI.ovrAvatar2NodeId.Invalid)
+                {
+                    // Skip this index
+                    OvrAvatarLog.LogError($"Invalid nodeId for {jointName}", logScope, this);
+                    continue;
+                }
+
+                buildNewNodeToIndex.Add(nodeId, i);
+
+                var jointParentIndex = entityPose.GetParentIndex(i);
+
+                if (previousNodeToIndex.TryGetValue(nodeId, out var previousIndex))
+                {
+                    // Existing SkeletonJoint
+                    ref readonly var previousJoint = ref previousSkeleton[previousIndex];
+                    OvrAvatarLog.Assert(previousJoint.nodeId == nodeId);
+
+                    // TODO: Does jointParentIndex change?
+                    newSkeleton[i] = new SkeletonJoint(in previousJoint, jointName, jointParentIndex);
+                }
+                else
+                {
+                    // New SkeletonJoint
+                    var jointObject = createTransforms ? (new GameObject(jointName)).transform : null;
+
+                    newSkeleton[i] = new SkeletonJoint(jointName
+                            , jointObject
+                            , jointParentIndex
+                            , nodeId
+                        );
+                }
+            }
+
+            if (createTransforms)
+            {
+                ReparentSkeletonJoints(newSkeleton, _baseTransform, in entityPose);
+            }
+
+            // Destroy unused SkeletonJoints
+            List<SkeletonJoint> jointsToDestroy = null;
+            foreach (var nodeToIndex in previousNodeToIndex)
+            {
+                if (!buildNewNodeToIndex.ContainsKey(nodeToIndex.Key))
+                {
+                    var oldJoint = previousSkeleton[nodeToIndex.Value];
+                    if (jointsToDestroy == null)
+                    {
+                        // Don't prealloc capacity below 4 (C# default) - in addition, 0 or negative values will throw an exception
+                        int capacity = Mathf.Max(4, previousNodeToIndex.Count - buildNewNodeToIndex.Count);
+                        jointsToDestroy = new List<SkeletonJoint>(capacity);
+                    }
+                    jointsToDestroy.Add(oldJoint);
+                }
+            }
+
+            if (jointsToDestroy != null)
+            {
+                jointsToDestroy.Sort();
+                foreach (var joint in jointsToDestroy)
+                {
+                    DestroyJoint(in joint);
+                }
+                jointsToDestroy.Clear();
+            }
+
+            _skeleton = newSkeleton;
+
+            // Clear previous state
+            _nodeToIndex.CopyFrom(buildNewNodeToIndex);
+            _jointTypeToNodeId.Clear();
+
+            if (jointCount != 0)
+            {
+                // Map each Joint Type to a Node ID. If there is no corresponding node, the dictionary holds a value of Invalid
+                const int allJointCount = (int)CAPI.ovrAvatar2JointType.Count;
+                using var allJointTypes
+                    = new NativeArray<CAPI.ovrAvatar2JointType>(allJointCount
+                        , Allocator.Temp, NativeArrayOptions.UninitializedMemory);
+                unsafe
+                {
+                    CAPI.ovrAvatar2JointType* typesData = allJointTypes.GetPtr();
+                    for (int i = 0; i < allJointCount; ++i)
+                    {
+                        typesData[i] = (CAPI.ovrAvatar2JointType)i;
+                    }
+
+                    using var allJointNodes
+                        = CAPI.OvrAvatar2Entity_QueryJointTypeNodes_NativeArray(entityId, in allJointTypes, this);
+                    if (allJointTypes.Length != allJointNodes.Length)
+                    {
+                        OvrAvatarLog.LogError(
+                            $"allJointTypes.Length ({allJointTypes.Length}) != allJointNodes.Length ({allJointTypes.Length})"
+                            , logScope, this);
+                    }
+                    else
+                    {
+                        CAPI.ovrAvatar2NodeId* nodeData = allJointNodes.array.GetPtr();
+                        for (int i = 0; i < allJointNodes.Length; ++i)
+                        {
+                            _jointTypeToNodeId.Add(typesData[i], nodeData[i]);
+                        }
+                    }
+                }
+            }
+
+            // Don't build critical joints if there are no joints or if using Joint Monitor
+            if (_jointMonitor != null)
+            {
+                UpdateMonitoredJoints();
+                MonitorJoints(in entityPose); // Update monitored joints to their correct positions
+                _unityUpdateJointIndices = Array.Empty<uint>();
+            }
+            else if (jointCount != 0)
+            {
+                var updateJointArray = UpdateCriticalJoints();
+                // finally sort and store all unity update joints into our member array of indices
+                Array.Sort(updateJointArray);
+                _unityUpdateJointIndices = updateJointArray;
+            }
+            else
+            {
+                _unityUpdateJointIndices = Array.Empty<uint>();
+            }
+
+            SetupLodCullingPoints();
+
+            _currentHierarchyVersion = hierVersion;
+        }
+
+        private void UpdateMonitoredJoints()
+        {
+            _monitoredJointTypes.Clear();
+            _monitoredJointPoses.Clear();
+
+            foreach (var jointType in _criticalJointTypes)
+            {
+                AddMonitoredJoint(jointType);
+            }
+
+            var centerJoint = AvatarLODManager.Instance.JointTypeToCenterOn;
+            if (centerJoint != CAPI.ovrAvatar2JointType.Invalid)
+            {
+                AddMonitoredJoint(centerJoint);
+            }
+
+            var jointTypes = AvatarLODManager.Instance.JointTypesToCullOn;
+            var jointTypesCount = jointTypes.Count;
+            for (int jointIdx = 0; jointIdx < jointTypesCount; ++jointIdx)
+            {
+                AddMonitoredJoint(jointTypes[jointIdx]);
+            }
+        }
+
+        private uint[] UpdateCriticalJoints()
+        {
+            // In the following loops, fill the array `criticalJointIndices` wih any joint indices for joints
+            // that needed to be updated on the Unity object hierarchy for important world space functions or
+            // app logic reference. These will then be copied into `_unityUpdateJointIndices`
+            HashSet<uint> criticalJointIndices = new HashSet<uint>();
+
+            var critJointTypeSet = new HashSet<CAPI.ovrAvatar2JointType>(_criticalJointTypes);
+
+            if (AvatarLODManager.hasInstance)
+            {
+                var centerJoint = AvatarLODManager.Instance.JointTypeToCenterOn;
+                if (centerJoint != CAPI.ovrAvatar2JointType.Invalid)
+                {
+                    critJointTypeSet.Add(centerJoint);
+                }
+
+                var jointTypes = AvatarLODManager.Instance.JointTypesToCullOn;
+                var jointTypesCount = jointTypes.Count;
+                for (int jointIdx = 0; jointIdx < jointTypesCount; ++jointIdx)
+                {
+                    critJointTypeSet.Add(jointTypes[jointIdx]);
+                }
+            }
+
+            if (critJointTypeSet.Count > 0)
+            {
+                foreach (var jointType in critJointTypeSet)
+                {
+                    var nodeId = GetNodeForType(jointType);
+                    if (nodeId == CAPI.ovrAvatar2NodeId.Invalid) continue;
+
+                    var critIdx = GetIndexForNode(nodeId);
+                    criticalJointIndices.Add(critIdx);
+                }
+            }
+
+            if (criticalJointIndices.Count > 0)
+            {
+                // Ensure all parent joint transforms are updated as well
+                // TODO: Proxy criticalJoints directly w/out intermediate hierarchy
+                foreach (var skelIdx in criticalJointIndices.ToArray())
+                {
+                    int parentIndex = _skeleton[skelIdx].parentIndex;
+                    while (parentIndex >= 0)
+                    {
+                        criticalJointIndices.Add((uint)parentIndex);
+                        parentIndex = _skeleton[parentIndex].parentIndex;
+                    }
+                }
+            }
+
+            return criticalJointIndices.ToArray();
+        }
+
+        // TODO: Could save a lot of struct copying by just passing the Transform directly
+        private void DestroyJoint(in SkeletonJoint joint)
+        {
+            var jointTx = joint.transform;
+            if (jointTx)
+            {
+                OvrAvatarLog.Assert(jointTx.childCount == 0);
+                GameObject.Destroy(jointTx.gameObject);
+            }
+        }
+
+        private static void _SetupInitialJointTransform(in CAPI.ovrAvatar2Pose entityPose, in SkeletonJoint joint, uint txIdx)
+        {
+            unsafe
+            {
+                CAPI.ovrAvatar2Transform* jointTransform = entityPose.localTransforms + txIdx;
+#if OVR_AVATAR_ENABLE_CLIENT_XFORM
+                joint.transform.ApplyOvrTransform(jointTransform);
+#else
+                // HACK: Mirror rendering transforms across X to fixup coordinate system errors
+                if (joint.parentIndex == -1)
+                {
+                    var flipCopy = *jointTransform;
+                    flipCopy.scale.z = -flipCopy.scale.z;
+                    joint.transform.ApplyOvrTransform(in flipCopy);
+                }
+                else
+                {
+                    joint.transform.ApplyOvrTransform(jointTransform);
+                }
+#endif
+            }
+        }
+
+        private OvrTime.SliceStep WaitForLoad(ref CheckPrimitivesResult result, int iPrimitive)
+        {
+            if (!QueryPrimitiveRenderState_Direct(iPrimitive, out var primState))
+            {
+                OvrAvatarLog.LogError($"Failed to query primitiveIndex {iPrimitive}", logScope, this);
+
+#pragma warning disable 618
+                LoadState = LoadingState.Failed;
+#pragma warning restore 618
+                return OvrTime.SliceStep.Cancel;
+            }
+
+            // Check if we already have a renderable for this instance - thus primitive must be loaded
+            if (_meshNodes.TryGetValue(primState.meshNodeId, out var primRenderDatas))
+            {
+                // TODO: Would be nice to setup our maps to make this check very concise
+                // |-> loop is unnecessary if all primitives for a given node and always rendered together? That seems possible?
+                foreach (var primRenderData in primRenderDatas)
+                {
+                    if (primRenderData.primitiveId == primState.primitiveId)
+                    {
+                        // This primitive is already loaded onto this node
+                        return OvrTime.SliceStep.Continue;
+                    }
+                }
+            }
+
+            if (!OvrAvatarManager.GetOvrAvatarAsset(primState.primitiveId, out OvrAvatarPrimitive primitive) || primitive == null)
+            {
+                // TODO: This case should be completely impossible now
+                // - primitives should be stood be stubbed out before any building begins
+                OvrAvatarLog.LogError($"Failed to find asset primitiveId {primState.primitiveId}", logScope, this);
+
+#pragma warning disable 618
+                LoadState = LoadingState.Failed;
+#pragma warning restore 618
+                return OvrTime.SliceStep.Cancel;
+            }
+            if (primitive.isCancelled)
+            {
+                OvrAvatarLog.LogError($"primitiveId {primState.primitiveId} was cancelled", logScope, this);
+
+#pragma warning disable 618
+                LoadState = LoadingState.Failed;
+#pragma warning restore 618
+                return OvrTime.SliceStep.Cancel;
+            }
+
+            // Wait for primitive to load
+            if (!primitive.isLoaded)
+            {
+                // This primitiveID must load before proceeding
+                if (result.newPrimitiveIds == null) { result.newPrimitiveIds = new HashSet<CAPI.ovrAvatar2Id>(); }
+                result.newPrimitiveIds.Add(primState.primitiveId);
+                return OvrTime.SliceStep.Delay;
+            }
+
+            // Mark new instance for instantiation
+            if (result.newRenderIndices == null) { result.newRenderIndices = new HashSet<UInt32>(); }
+            result.newRenderIndices.Add((uint)iPrimitive);
+
+            // Load complete, continue to next index
+            return OvrTime.SliceStep.Continue;
+        }
+
+        private bool BuildNewPrimitiveRenderables(in NativeArray<UInt32> newRenderIndices)
+        {
+            bool builtAll = true;
+            foreach (var newRenderIndex in newRenderIndices)
+            {
+                var newRenderable = BuildPrimitiveRenderable(newRenderIndex);
+                if (newRenderable != null)
+                {
+                    // TODO: This could use a better data structure, remove `_primitiveRenderables` first
+                    if (!_meshNodes.TryGetValue(newRenderable.meshNodeId, out var existingRenderData))
+                    {
+                        var newRenderDatas = new PrimitiveRenderData[] { newRenderable };
+
+                        _meshNodes.Add(newRenderable.meshNodeId, newRenderDatas);
+                        _primitiveRenderables.Add(newRenderable.instanceId, newRenderDatas);
+                    }
+                    else
+                    {
+                        var lastElement = existingRenderData.Length;
+                        Array.Resize(ref existingRenderData, lastElement + 1);
+                        existingRenderData[lastElement] = newRenderable;
+
+                        _meshNodes[newRenderable.meshNodeId] = existingRenderData;
+                        _primitiveRenderables[newRenderable.instanceId] = existingRenderData;
+                    }
+
+                    InitializeRenderable(newRenderable.renderable);
+                }
+                else
+                {
+                    builtAll = false;
+                    OvrAvatarLog.LogError("Failed to build primitive renderable", logScope, this);
+                }
+            }
+            return builtAll;
+        }
+
+        private PrimitiveRenderData BuildPrimitiveRenderable(uint iPrimitive)
+        {
+            if (!QueryPrimitiveRenderState_Direct(iPrimitive, out var primState))
+            {
+                OvrAvatarLog.LogError($"Failed to query primitiveIndex {iPrimitive}", logScope, this);
+#pragma warning disable 618
+                LoadState = LoadingState.Failed;
+#pragma warning restore 618
+                return null;
+            }
+
+            // nodeId for this renderer, used by visibility control
+            var meshNodeId = primState.meshNodeId;
+            if (meshNodeId == CAPI.ovrAvatar2NodeId.Invalid)
+            {
+                OvrAvatarLog.LogError($"Invalid meshNodeId {iPrimitive}", logScope, this);
+#pragma warning disable 618
+                LoadState = LoadingState.Failed;
+#pragma warning restore 618
+                return null;
+            }
+
+            if (!OvrAvatarManager.GetOvrAvatarAsset(primState.primitiveId, out OvrAvatarPrimitive primitive) || primitive == null)
+            {
+                // TODO: This case should be completely impossible now
+                // - primitives should be stood be stubbed out before any building begins
+                OvrAvatarLog.LogError($"Failed to find asset primitiveId {primState.primitiveId}", logScope, this);
+#pragma warning disable 618
+                LoadState = LoadingState.Failed;
+#pragma warning restore 618
+                return null;
+            }
+
+            OvrAvatarLog.Assert(primitive.isLoaded);
+            OvrAvatarLog.Assert(!primitive.isCancelled);
+
+            OvrAvatarRenderable renderable = null;
+            if (primitive.joints.Length == 0)
+            {
+                // TODO: T76240496 - Verify unskinned meshes still behave correctly
+                renderable = CreateRenderable(primitive);
+            }
+            else
+            {
+                // Build mapping
+                Transform[] joints = new Transform[primState.pose.jointCount];
+
+                bool validJoints = true;
+                for (int iJoint = 0; iJoint < primitive.joints.Length; ++iJoint)
+                {
+                    // Get the primitive's joint index and name
+                    int poseIndex = primitive.joints[iJoint];
+                    var jointNodeId = primState.pose.GetNodeIdAtIndex(poseIndex);
+
+                    // Find the corresponding joint in the entity pose
+                    // TODO: This should no longer be necessary
+                    if (!_nodeToIndex.TryGetValue(jointNodeId, out uint entityIndex))
+                    {
+                        string jointName = GetNameForNode(jointNodeId, in primState.pose);
+                        OvrAvatarLog.LogError(
+                            $"Could not map primitive {iPrimitive} to the entity pose. " +
+                            $"Joint {iJoint} {jointName} not found", logScope, this);
+
+                        validJoints = false;
+                        break;
+                        // TODO(T80085915) Bring this back once controllers can be successfully rendered in Unity.
+                        //LoadState = LoadingState.Failed;
+                        //_loadingRoutine = null;
+                        //yield break;
+                    }
+
+                    joints[iJoint] = _skeleton[entityIndex].transform;
+                }
+
+                if (validJoints)
+                {
+                    renderable = CreateRenderable(primitive);
+                    var skinnedRenderable = renderable as OvrAvatarSkinnedRenderable;
+
+                    if (skinnedRenderable != null)
+                    {
+                        skinnedRenderable.ApplySkeleton(joints);
+                        _skinnedRenderables.Add(primitive, skinnedRenderable);
+                    }
+                }
+            }
+
+            if (!(renderable is null))
+            {
+                SampleSkinningOrigin(in primState, out var skinningOrigin);
+                renderable.transform.ApplyOvrTransform(in skinningOrigin);
+            }
+
+            return new PrimitiveRenderData
+            (
+                meshNodeId, primitive.assetId, primState.id, renderable, primitive
+            );
+        }
+
+        private struct CheckPrimitivesResult
+        {
+            public HashSet<CAPI.ovrAvatar2Id> newPrimitiveIds;
+            public HashSet<uint> newRenderIndices;
+
+            public CAPI.ovrAvatar2EntityRenderStateVersion targetAllNodesVersion;
+            public CAPI.ovrAvatar2EntityRenderStateVersion targetVisVersion;
+
+            public CheckPrimitivesResult(CAPI.ovrAvatar2EntityRenderStateVersion allNodesVersion,
+                CAPI.ovrAvatar2EntityRenderStateVersion visVersion)
+            {
+                newPrimitiveIds = null;
+                newRenderIndices = null;
+
+                targetAllNodesVersion = allNodesVersion;
+                targetVisVersion = visVersion;
+            }
+        }
+
+        private void LoadSync_CheckForNewRenderables_Internal(in CAPI.ovrAvatar2EntityRenderState entityRenderState, out CheckPrimitivesResult result)
+        {
+            _targetVisibleNodesVersion = entityRenderState.visibleNodesVersion;
+
+            result = default;
+            result.targetAllNodesVersion = entityRenderState.allNodesVersion;
+            result.targetVisVersion = entityRenderState.visibleNodesVersion;
+
+            // TODO: Should use instanceId
+            for (int iPrimitive = 0; iPrimitive < entityRenderState.primitiveCount; ++iPrimitive)
+            {
+                // If we must wait for load, then we have new primitives
+                // TODO: Perform a more succinct check
+                WaitForLoad(ref result, iPrimitive);
+            }
+        }
+
+        // TODO: Convert to TimeSliced method
+        private IEnumerator LoadAsyncCoroutine_BuildPrimitives_Internal()
+        {
+            // TODO: A timeout seems logical, but we've never had one so far :X
+            // |-> In theory, once we finish waiting for loading building should never fail
+            do
+            {
+                OvrTime.SliceStep step = LoadSync_CheckPrimitivesLoaded_Internal(out var result);
+                if (step != OvrTime.SliceStep.Continue)
+                {
+                    // Unrecoverable loading error - abort
+                    if (step == OvrTime.SliceStep.Cancel) { yield break; }
+                    // Try again next frame
+                    yield return null;
+                    // - from the top!
+                    continue;
+                }
+
+                OvrAvatarLog.Assert(result.targetAllNodesVersion != CAPI.ovrAvatar2EntityRenderStateVersion.Invalid);
+                OvrAvatarLog.Assert(result.targetVisVersion != CAPI.ovrAvatar2EntityRenderStateVersion.Invalid);
+
+                if (result.newRenderIndices != null && !LoadAsync_BuildPrimitives_Internal(in result))
+                {
+                    // Retry next frame
+                    yield return null;
+                    // - from the top!
+                    continue;
+                }
+
+                // Finish
+                break;
+            }
+            while (true);
+        }
+
+        private OvrTime.SliceStep LoadSync_CheckPrimitivesLoaded_Internal(out CheckPrimitivesResult result)
+        {
+            if (!QueryEntityRenderState(out var entityRenderState))
+            {
+                result = default;
+
+                OvrAvatarLog.LogError("Unable to query entity render state", logScope, this);
+                return OvrTime.SliceStep.Cancel;
+            }
+
+            result = new CheckPrimitivesResult(entityRenderState.allNodesVersion, entityRenderState.visibleNodesVersion);
+
+            // TODO: Should use instanceId
+            for (int iPrimitive = 0; iPrimitive < entityRenderState.primitiveCount; ++iPrimitive)
+            {
+                var step = WaitForLoad(ref result, iPrimitive);
+                if (step != OvrTime.SliceStep.Continue) { return step; }
+            }
+            return OvrTime.SliceStep.Continue;
+        }
+
+        /* Run from Coroutine - needs to query renderState, will retry on failure */
+        private bool LoadAsync_BuildPrimitives_Internal(in CheckPrimitivesResult result)
+        {
+            if (QueryEntityRenderState(out var renderState))
+            {
+                using (var newRenderArray = new NativeArray<uint>(result.newRenderIndices.Count
+                    , Allocator.Temp, NativeArrayOptions.UninitializedMemory))
+                {
+                    newRenderArray.CopyFrom(result.newRenderIndices);
+                    var builtAll = LoadSync_BuildPrimitives_Internal(in renderState, in newRenderArray, result.targetVisVersion);
+                    if (builtAll)
+                    {
+                        // Update visibility flags
+                        UpdateVisibility(in renderState);
+                    }
+                    return builtAll;
+                }
+            }
+            return false;
+        }
+
+        private bool LoadSync_BuildPrimitives_Internal(in CAPI.ovrAvatar2EntityRenderState renderState
+            , in NativeArray<UInt32> newRenderIndices
+            , CAPI.ovrAvatar2EntityRenderStateVersion targetVisibleVersion)
+        {
+            OvrAvatarLog.LogVerbose($"Starting BuildPrimitives", logScope, this);
+
+            bool builtAll = BuildNewPrimitiveRenderables(newRenderIndices);
+            OvrAvatarLog.AssertConstMessage(builtAll
+                , "Unable to update allNodesVersion", logScope, this);
+
+            return builtAll;
+        }
+
+        private void CheckLoadedAssets()
+        {
+            bool hasDefaultModel = false;
+            bool hasUserModel = false;
+            var wasPendingZipAvatar = IsPendingZipAvatar;
+            var wasPendingCdnAvatar = IsPendingCdnAvatar;
+
+            using (var loadedAssetTypes = CAPI.OvrAvatar2Entity_GetLoadedAssetTypes_NativeArray(entityId))
+            {
+                foreach (var assetType in loadedAssetTypes)
+                {
+                    switch (assetType)
+                    {
+                        case CAPI.ovrAvatar2EntityAssetType.SystemDefaultModel:
+                            hasDefaultModel = true;
+
+                            IsPendingDefaultModel = false;
+                            break;
+
+                        case CAPI.ovrAvatar2EntityAssetType.SystemOther:
+                            // Controllers and other misc, not currently tracked from Unity
+                            break;
+
+                        case CAPI.ovrAvatar2EntityAssetType.Other:
+                            hasUserModel = true;
+
+                            HasNonDefaultAvatar = true;
+                            IsPendingCdnAvatar = false;
+                            IsPendingZipAvatar = false;
+                            break;
+                    }
+                }
+            }
+
+            if (isLoadingFullPreset)
+            {
+                foreach (var fastLoadPreset in fastLoadPresetPaths)
+                {
+                    bool success = CAPI.OvrAvatar2Entity_UnloadUri(entityId, fastLoadPreset);
+                    OvrAvatarLog.AssertConstMessage(success
+                        , "Failed to unload fastload profile", logScope, this);
+                }
+                fastLoadPresetPaths.Clear();
+                isLoadingFullPreset = false;
+            }
+
+            if (hasDefaultModel || hasUserModel)
+            {
+                // Skeleton should always load before a user model or default model
+                OvrAvatarLog.Assert(SkeletonJointCount > 0);
+
+                // If Skeleton is loaded, the avatar should be in at least the "Skeleton" state
+                OvrAvatarLog.Assert(CurrentState >= AvatarState.Skeleton);
+            }
+
+            if (CurrentState == AvatarState.Skeleton && hasDefaultModel)
+            {
+                CurrentState = AvatarState.DefaultAvatar;
+                InvokeOnDefaultAvatarLoaded();
+            }
+
+            if (hasDefaultModel && hasUserModel)
+            {
+                CAPI.OvrAvatar2Entity_UnloadDefaultModel(entityId);
+            }
+
+            if (ShouldFastLoad() && CurrentState < AvatarState.FastLoad && hasUserModel)
+            {
+                CurrentState = AvatarState.FastLoad;
+                if (wasPendingZipAvatar)
+                {
+                    LoadAssetsFromZipSource(_assetPaths, _lodFilters);
+                }
+                else if (wasPendingCdnAvatar)
+                {
+                    CAPI.OvrAvatarEntity_LoadUserWithFilters(entityId, _userId, _loadFilters, out var loadRequestId);
+                }
+                InvokeOnFastLoadAvatarLoaded();
+            }
+            else if (CurrentState < AvatarState.UserAvatar && hasUserModel)
+            {
+                CurrentState = AvatarState.UserAvatar;
+                InvokeOnUserAvatarLoaded();
+            }
+        }
+
+        private void LoadAsync_Finalize_Internal()
+        {
+            CheckLoadedAssets();
+
+            LoadAsync_Finalize();
+
+#pragma warning disable 618
+            if (LoadState != LoadingState.Failed && CurrentState == AvatarState.UserAvatar)
+            {
+                LoadState = LoadingState.Success;
+            }
+#pragma warning restore 618
+
+            IsApplyingModels = false;
+
+            // Null out the backing explicitly to avoid cancelling this coroutine
+            _loadingRoutineBacking = null;
+
+            // Allow OvrAvatarManager to start the next queued load
+            OvrAvatarManager.Instance.FinishedAvatarLoad();
+        }
+
+        // Override hook for additional work once primitives are loaded, but before loading is marked complete
+        protected virtual void LoadAsync_Finalize() { }
+
+        private static void ReparentSkeletonJoints(SkeletonJoint[] newSkel
+            , Transform baseTransform, in CAPI.ovrAvatar2Pose entityPose)
+        {
+            // Reparent transforms into hierarchy
+            unsafe
+            {
+                uint jointCount = entityPose.jointCount;
+                for (uint i = 0; i < jointCount; ++i)
+                {
+                    ref readonly SkeletonJoint joint = ref newSkel[i];
+
+                    Transform parentTransform =
+                        joint.parentIndex < 0 ? baseTransform : newSkel[joint.parentIndex].transform;
+
+                    joint.transform.SetParent(parentTransform, false);
+
+                    _SetupInitialJointTransform(in entityPose, in joint, i);
+                }
+            }
+        }
+
+        private void UpdateVisibility(in CAPI.ovrAvatar2EntityRenderState renderState)
+        {
+            unsafe
+            {
+                if (renderState.visibleNodesVersion != _currentVisibleNodesVersion)
+                {
+                    _targetVisibleNodesVersion = renderState.visibleNodesVersion;
+
+                    Array.Resize(ref _visiblePrimitiveRenderers, (int)renderState.visibleMeshNodesCount);
+
+                    // TODO: These aren't *both* necessary but there are two pieces of logic twisted together here currently
+                    uint visibleIndex = 0;
+                    uint subsequentVisibleIdx = 0;
+                    var nextVisibleNodeId = _CheckNextVisibleNodeId(in renderState, ref subsequentVisibleIdx);
+
+                    for (int allNodeIdx = 0; allNodeIdx < renderState.allMeshNodesCount; allNodeIdx++)
+                    {
+                        var node = renderState.allMeshNodes[allNodeIdx];
+
+                        bool isVisible = node == nextVisibleNodeId;
+                        if (_meshNodes.TryGetValue(node, out var nodeRenderData))
+                        {
+                            // TODO: Setup a callback? This would be useful state change info for apps
+                            // TODO: Sum up the cost for the node?
+                            foreach (var primRenderable in nodeRenderData)
+                            {
+                                var renderable = primRenderable.renderable;
+                                if (renderable.Visible != isVisible)
+                                {
+                                    if (renderable.Visible)
+                                    {
+                                        RemoveVisibleLodCost(in primRenderable);
+                                    }
+                                    else
+                                    {
+                                        AddVisibleLodCost(in primRenderable);
+                                    }
+
+                                    renderable.Visible = isVisible;
+                                }
+                            }
+                        }
+
+                        if (isVisible)
+                        {
+                            nextVisibleNodeId = _CheckNextVisibleNodeId(in renderState, ref subsequentVisibleIdx);
+
+                            // TODO: We could gather this list more efficiently while we're checking renderState
+                            // This location covers our weird untrimmed edge cases though
+                            _visiblePrimitiveRenderers[visibleIndex++] = nodeRenderData;
+                            if (nodeRenderData == null)
+                            {
+                                OvrAvatarLog.LogError($"Missing visible meshNode with id {node}", logScope, this);
+                            }
+                        }
+                    }
+
+                    RefreshLODRange();
+
+                    SetupLodGroups();
+
+                    _currentVisibleNodesVersion = renderState.visibleNodesVersion;
+                }
+            }
+        }
+
+        private static CAPI.ovrAvatar2NodeId _CheckNextVisibleNodeId(in CAPI.ovrAvatar2EntityRenderState renderState, ref uint nextIndex)
+        {
+            if (nextIndex < renderState.visibleMeshNodesCount)
+            {
+                return renderState.GetVisibleMeshNodeAtIdx(nextIndex++);
+            }
+            return CAPI.ovrAvatar2NodeId.Invalid;
+        }
+
+        private void UpdateAllNodes(in CAPI.ovrAvatar2EntityRenderState renderState)
+        {
+            if (_currentAllNodesVersion != renderState.allNodesVersion)
+            {
+                _targetAllNodesVersion = renderState.allNodesVersion;
+
+                // If `allNodes` has changed, some may have been removed
+                // New nodes will have been built during the visibility update
+
+                // Avoid allocs if there's nothing to clean up
+                if (_meshNodes.Count > 0)
+                {
+                    // TODO: Not ideal to be checking the nodes we just added for removal :/
+
+                    // TODO: Avoid allocs, this is a fairly rare operation though
+                    var allNodeHash = new HashSet<CAPI.ovrAvatar2NodeId>();
+                    var disposeList = new List<CAPI.ovrAvatar2NodeId>();
+
+                    for (uint allIdx = 0; allIdx < renderState.allMeshNodesCount; ++allIdx)
+                    {
+                        allNodeHash.Add(renderState.GetAllMeshNodeAtIdx(allIdx));
+                    }
+
+                    foreach (var meshNodeKVP in _meshNodes)
+                    {
+                        var nodeId = meshNodeKVP.Key;
+                        if (!allNodeHash.Contains(nodeId))
+                        {
+                            disposeList.Add(nodeId);
+                        }
+                    }
+                    foreach (var disposePrimData in disposeList)
+                    {
+                        RemoveNodeRenderables(disposePrimData);
+                    }
+                }
+
+                _currentAllNodesVersion = renderState.allNodesVersion;
+            }
+        }
+
+        private void DestroySkeleton()
+        {
+            ResetLODRange();
+            ResetLodCullingPoints();
+
+            for (int i = 0; i < _skeleton.Length; ++i)
+            {
+                var skelTx = _skeleton[i].transform;
+                if (skelTx != null)
+                {
+                    var skelGob = skelTx.gameObject;
+                    skelGob.SetActive(false);
+                    GameObject.Destroy(skelGob);
+                }
+            }
+
+            for (int idx = 0; idx < _visibleLodData.Length; ++idx)
+            {
+                ref var lodObject = ref _visibleLodData[idx];
+                if (lodObject.IsValid)
+                {
+                    DestroyLODObject(ref lodObject);
+                }
+            }
+            OvrAvatarLog.Assert(lodObjectCount == 0);
+            lodObjectCount = 0;
+
+            _skeleton = Array.Empty<SkeletonJoint>();
+
+            foreach (var renderables in _primitiveRenderables)
+            {
+                foreach (var renderableData in renderables.Value)
+                {
+                    renderableData?.Dispose();
+                }
+            }
+            _primitiveRenderables.Clear();
+
+            _jointTypeToNodeId.Clear();
+            _nodeToIndex.Clear();
+
+            _unityUpdateJointIndices = Array.Empty<uint>();
+        }
+
+        // TODO: This whole method should move into _Rendering
+        private OvrAvatarRenderable CreateRenderable(OvrAvatarPrimitive primitive)
+        {
+            GameObject primitiveObject = new GameObject(primitive.name, typeof(MeshFilter));
+
+            var renderable = AddRenderableComponent(primitiveObject, primitive);
+
+            // LODs
+            var parent = _baseTransform;
+            if (primitive.lodFlags != 0)
+            {
+                // TODO: Remove this special case for `CAPI.ovrAvatar2EntityLODFlags.All`
+                if (primitive.lodFlags == CAPI.ovrAvatar2EntityLODFlags.All)
+                {
+                    // Currently this is covering controller meshes
+                    if (!_visibleAllLodData.IsValid)
+                    {
+                        // TODO: Coverage value is unused... probably the wrong structure?
+                        var allLodGo = new GameObject($"AllLOD");
+                        var allLodTx = allLodGo.transform;
+
+                        allLodTx.SetParent(_baseTransform, false);
+
+                        _visibleAllLodData = new LodData(allLodGo);
+                    }
+
+                    // TODO: Update the lod range... but only if there are no other primitives :/
+                    parent = _visibleAllLodData.transform;
+                }
+                else
+                {
+                    var withoutLeastFlag = primitive.lodFlags & (primitive.lodFlags - 1);
+
+                    if (withoutLeastFlag == 0)
+                    {
+                        // Primitive is for only one LOD
+
+                        uint onlyBit = (uint)(withoutLeastFlag ^ primitive.lodFlags);
+                        uint lodIndex = (uint)Mathf.Log(onlyBit, 2);
+
+                        // Primitive represents a specific LOD
+                        Debug.Assert((1 << (int)lodIndex) == onlyBit);
+
+                        ref var lodData = ref _visibleLodData[lodIndex];
+
+                        // First renderable at this LOD level
+                        if (!lodData.IsValid)
+                        {
+                            GameObject go = new GameObject($"LOD{lodIndex}");
+                            var goTX = go.transform;
+
+                            goTX.SetParent(_baseTransform, false);
+
+                            lodData = new LodData(go);
+
+                            lodObjectCount++;
+                        }
+
+                        parent = lodData.transform;
+                    }
+                    else
+                    {
+                        OvrAvatarLog.LogError("Multi LOD Primitives are not currently supported", logScope, this);
+                        // Primitive is used for multiple LODs
+
+                        // TODO: This needs to handle any partial set of flags (not just All)
+                        /* Primitive covers multiple LODs
+                        for (uint idx = CAPI.ovrAvatar2EntityLODFlagsCount - 1; idx >= 0; --idx)
+                        {
+                            ref var lodData = ref _visibleLodData[idx];
+
+                            var lodFlag = (CAPI.ovrAvatar2EntityLODFlags)(1 << (int)idx);
+                            if ((lodFlag & primitive.lodFlags) != 0)
+                            {
+                                // TODO: Should expand LOD range - but this doesn't *quite* work correctly w/ the current setup
+                                // ExpandLODRange(idx);
+                                // TODO: Add to cost of each covered LOD to ensure accurate scheduling
+                                // - we need to potentially create the LOD entry from here? Refactor?
+                                // - We wouldn't have the right coverage value to initialize with :/
+                            }
+                        } */
+                    }
+                }
+            }
+
+            primitiveObject.transform.SetParent(parent, false);
+
+            return renderable;
+        }
+
+        private void AddVisibleLodCost(in PrimitiveRenderData renderData)
+        {
+            OvrAvatarLog.Assert(renderData.renderable);
+
+            // Increase the LodCost for affected lod levels.
+            {
+                if (renderData.primitive.lodFlags == CAPI.ovrAvatar2EntityLODFlags.All)
+                {
+                    _visibleAllLodData.AddInstance(renderData.renderable);
+                }
+                else
+                {
+                    // TODO: Update all LODs for renderData
+                    /*
+                    for (uint idx = 0; idx < CAPI.ovrAvatar2EntityLODFlagsCount; ++idx)
+                    {
+                        if ((lodFlag & primitive.lodFlags) != 0)
+                    /*/
+                    var primHighestQualityLODIndex = renderData.primitive.HighestQualityLODIndex;
+                    OvrAvatarLog.Assert(primHighestQualityLODIndex >= 0);
+                    if (primHighestQualityLODIndex >= 0)
+                    {
+                        var idx = (uint)primHighestQualityLODIndex;
+                        //*/
+                        {
+                            ref var lodObject = ref _visibleLodData[idx];
+
+                            lodObject.AddInstance(renderData.renderable);
+                        }
+                    }
+                }
+            }
+        }
+
+        private void RemoveVisibleLodCost(in PrimitiveRenderData renderData)
+        {
+            OvrAvatarLog.Assert(renderData.renderable);
+            // Reduce the LodCost for affected lod levels.
+            {
+                if (renderData.primitive.lodFlags == CAPI.ovrAvatar2EntityLODFlags.All)
+                {
+                    var didRemove = _visibleAllLodData.RemoveInstance(renderData.renderable);
+                    OvrAvatarLog.Assert(didRemove, logScope, this);
+                }
+                else
+                {
+                    // TODO: Update all LODs for renderData
+                    /*
+                    for (uint idx = 0; idx < CAPI.ovrAvatar2EntityLODFlagsCount; ++idx)
+                    {
+                        if ((lodFlag & primitive.lodFlags) != 0)
+                    /*/
+                    var primHighestQualityLODIndex = renderData.primitive.HighestQualityLODIndex;
+                    OvrAvatarLog.Assert(primHighestQualityLODIndex >= 0);
+                    if (primHighestQualityLODIndex >= 0)
+                    {
+                        var idx = (uint)primHighestQualityLODIndex;
+                        //*/
+                        {
+                            ref var lodObject = ref _visibleLodData[idx];
+
+                            bool didRemove = lodObject.RemoveInstance(renderData.renderable);
+                            OvrAvatarLog.Assert(didRemove);
+                        }
+                    }
+                }
+            }
+        }
+
+        private void RemoveNodeRenderables(CAPI.ovrAvatar2NodeId meshNodeId)
+        {
+            OvrAvatarLog.Assert(_meshNodes.ContainsKey(meshNodeId), logScope, this);
+            if (_meshNodes.TryGetValue(meshNodeId, out var primRenderDatas))
+            {
+                bool didRemove = _meshNodes.Remove(meshNodeId);
+                OvrAvatarLog.Assert(didRemove, logScope, this);
+
+                foreach (var renderData in primRenderDatas)
+                {
+                    var primitive = renderData.primitive;
+                    _skinnedRenderables.Remove(primitive);
+                    _primitiveRenderables.Remove(renderData.instanceId);
+
+                    // If visible reduce the vertex count for the lod level.
+                    if (renderData.renderable.Visible)
+                    {
+                        RemoveVisibleLodCost(in renderData);
+                    }
+
+                    DestroyRenderable(in renderData);
+                }
+            }
+        }
+
+        private void DestroyRenderable(in PrimitiveRenderData renderData)
+        {
+            // Detach the game object from the hierarchy so its components will not be found.
+            var renderable = renderData.renderable;
+            if (renderable != null)
+            {
+                renderable.enabled = false;
+
+                var gob = renderable.gameObject;
+                gob.SetActive(false);
+                gob.transform.SetParent(null, false);
+                GameObject.Destroy(gob);
+            }
+        }
+
+        private void DestroyLODObject(ref LodData lodObject)
+        {
+            OvrAvatarLog.Assert(lodObject.IsValid);
+
+            lodObjectCount--;
+            Destroy(lodObject.gameObject);
+            lodObject = default;
+        }
+
+        private void SetRequiredFeatures()
+        {
+            if (UseGpuSkinning)
+            {
+                if (ForceEnableFeatures(CAPI.ovrAvatar2EntityFeatures.Rendering_ObjectSpaceTransforms))
+                {
+                    OvrAvatarLog.LogWarning("Rendering_ObjectSpaceTransforms force enabled due to GPU Skinning - consider enabling in prefab `_creationInfo.features`",
+                        logScope, this);
+                }
+            }
+
+            if (HasAllFeatures(CAPI.ovrAvatar2EntityFeatures.Rendering_Prims))
+            {
+                if (ForceEnableFeatures(CAPI.ovrAvatar2EntityFeatures.Rendering_SkinningMatrices))
+                {
+                    OvrAvatarLog.LogWarning(
+                        @"Rendering_SkinningMatrices force enabled due to Rendering_Prims being enabled.
+Needed for bounding box calculation. Consider enabling in the prefab `_creationInfo.features`", logScope, this);
+                }
+            }
+        }
+
+        private void ClearFailedLoadState()
+        {
+#pragma warning disable 618
+            if (LoadState == LoadingState.Failed)
+            {
+                LoadState = LoadingState.Created;
+            }
+#pragma warning restore 618
+        }
+
+        #endregion
+
+        #region User Callback Invocation
+
+        private void InvokeOnCreated()
+        {
+            OvrAvatarLog.LogInfo($"[{entityId}] OnCreated", logScope, this);
+
+            Profiler.BeginSample("OvrAvatarEntity::OnCreated Callbacks");
+            try
+            {
+                OnCreated();
+                OnCreatedEvent?.Invoke(this);
+            }
+            catch (Exception e)
+            {
+                OvrAvatarLog.LogException("OnCreated user callback", e, logScope, this);
+            }
+            finally
+            {
+                Profiler.EndSample();
+            }
+        }
+
+        private void InvokeOnSkeletonLoaded()
+        {
+            OvrAvatarLog.LogInfo($"[{entityId}] OnSkeletonLoaded", logScope, this);
+
+            Profiler.BeginSample("OvrAvatarEntity::OnSkeletonLoaded Callbacks");
+            try
+            {
+                OnSkeletonLoaded();
+                OnSkeletonLoadedEvent?.Invoke(this);
+            }
+            catch (Exception e)
+            {
+                OvrAvatarLog.LogException("OnSkeletonLoaded user callback", e, logScope, this);
+            }
+            finally
+            {
+                Profiler.EndSample();
+            }
+        }
+
+        private void InvokeOnDefaultAvatarLoaded()
+        {
+            OvrAvatarLog.LogInfo($"[{entityId}] OnDefaultAvatarLoaded", logScope, this);
+
+            Profiler.BeginSample("OvrAvatarEntity::OnDefaultAvatarLoaded Callbacks");
+            try
+            {
+                OnDefaultAvatarLoaded();
+                OnDefaultAvatarLoadedEvent?.Invoke(this);
+            }
+            catch (Exception e)
+            {
+                OvrAvatarLog.LogException("OnDefaultAvatarLoaded user callback", e, logScope, this);
+            }
+            finally
+            {
+                Profiler.EndSample();
+            }
+        }
+
+        private void InvokeOnFastLoadAvatarLoaded()
+        {
+            OvrAvatarLog.LogInfo($"[{entityId}] OnFastLoadAvatarLoaded", logScope, this);
+
+            Profiler.BeginSample("OvrAvatarEntity::OnFastLoadAvatarLoaded Callbacks");
+            try
+            {
+                OnFastLoadAvatarLoaded();
+                OnFastLoadAvatarLoadedEvent?.Invoke(this);
+            }
+            catch (Exception e)
+            {
+                OvrAvatarLog.LogException("OnFastLoadAvatarLoaded user callback", e, logScope, this);
+            }
+            finally
+            {
+                Profiler.EndSample();
+            }
+        }
+
+        private void InvokeOnUserAvatarLoaded()
+        {
+            OvrAvatarLog.LogInfo($"[{entityId}] OnUserAvatarLoaded", logScope, this);
+
+            Profiler.BeginSample("OvrAvatarEntity::OnUserAvatarLoaded Callbacks");
+            try
+            {
+                OnUserAvatarLoaded();
+                OnUserAvatarLoadedEvent?.Invoke(this);
+            }
+            catch (Exception e)
+            {
+                OvrAvatarLog.LogException("OnUserAvatarLoaded user callback", e, logScope, this);
+            }
+            finally
+            {
+                Profiler.EndSample();
+            }
+        }
+
+        private void InvokePreTeardown()
+        {
+            OvrAvatarLog.LogInfo($"[{entityId}] PreTeardown");
+
+            Profiler.BeginSample("OvrAvatarEntity::PreTeardown Callbacks");
+            try
+            {
+                // Order is intentionally reversed from the others - For teardown typically you'd want external systems to
+                // clean up before OvrAvatarEntity starts tearing down.
+                PreTeardownEvent?.Invoke(this);
+                PreTeardown();
+            }
+            catch (Exception e)
+            {
+                OvrAvatarLog.LogException("PreTeardown user callback", e, logScope, this);
+            }
+            finally
+            {
+                Profiler.EndSample();
+            }
+        }
+
+        internal void InvokeOnLoadRequestStateChanged(CAPI.ovrAvatar2LoadRequestInfo loadRequestInfo)
+        {
+            Profiler.BeginSample("OvrAvatarEntity::OnLoadRequestStateChanged Callbacks");
+            try
+            {
+                OnLoadRequestStateChanged(loadRequestInfo);
+            }
+            catch (Exception e)
+            {
+                OvrAvatarLog.LogException("OnLoadRequestStateChanged user callback", e, logScope, this);
+            }
+            finally
+            {
+                Profiler.EndSample();
+            }
+        }
+
+        #endregion
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Loading.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Loading.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..70ed2d186ae259443a988d77bf457fbce20233ff
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Loading.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: e26c995f75d37704cbc75bb4373c541e
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Manifestation.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Manifestation.cs
new file mode 100644
index 0000000000000000000000000000000000000000..8b506f0c93fb71b9f6278ecd88b3f4f5b595f5a2
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Manifestation.cs
@@ -0,0 +1,28 @@
+using System;
+
+namespace Oculus.Avatar2
+{
+    public partial class OvrAvatarEntity
+    {
+        // TODO: Should probably be private/internal?
+        public bool GetAvailableManifestationFlags(out UInt32 manifestationFlags)
+        {
+            return CAPI.ovrAvatar2Entity_GetAvailableManifestationFlags(entityId, out manifestationFlags)
+                .EnsureSuccess("ovrAvatar2Entity_GetAvailableManifestationFlags", logScope, this);
+        }
+
+        // TODO: Should probably be private/internal?
+        public bool GetManifestationFlags(out CAPI.ovrAvatar2EntityManifestationFlags manifestationFlags)
+        {
+            return CAPI.ovrAvatar2Entity_GetManifestationFlags(entityId, out manifestationFlags)
+                .EnsureSuccess("ovrAvatar2Entity_GetAvailableManifestationFlags", logScope, this);
+        }
+
+        // TODO: Should probably be private/internal?
+        public bool SetManifestationFlags(CAPI.ovrAvatar2EntityManifestationFlags manifestation)
+        {
+            return CAPI.ovrAvatar2Entity_SetManifestationFlags(entityId, manifestation)
+                .EnsureSuccess("ovrAvatar2Entity_SetManifestationFlags", logScope, this);
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Manifestation.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Manifestation.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..eab5a9e0e5d35dd8754d7d787dab6adca811bd69
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Manifestation.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 9465f27285ab20040ab91097096bd268
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Material.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Material.cs
new file mode 100644
index 0000000000000000000000000000000000000000..f45a7dee886ceee429b1d317e4a4f51bbc0682c9
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Material.cs
@@ -0,0 +1,146 @@
+using System;
+using UnityEngine;
+
+namespace Oculus.Avatar2
+{
+
+    public partial class OvrAvatarEntity : MonoBehaviour
+    {
+        private static readonly int DEBUG_TINT_ID = Shader.PropertyToID("_DebugTint");
+
+        private OvrAvatarMaterial _material = null;
+
+        [Obsolete("Deprecated - no longer necessary")]
+        public void InitializeMaterialPropertyBlock()
+        {
+        }
+
+        public OvrAvatarMaterial Material
+        {
+            get { return _material; }
+        }
+
+        /**
+         * Enables or disables a shader keyword for this avatar.
+         * The changes are immediately applied to all its renderables.
+         */
+        [Obsolete("Use OvrAvatarMaterial instead", false)]
+        public void SetMaterialKeyword(string keyword, bool enable)
+        {
+            // remember keyword for future renderables
+            _material.SetKeyword(keyword, enable);
+            foreach (var meshNodeKVP in _meshNodes)
+            {
+                foreach (var primRenderable in meshNodeKVP.Value)
+                {
+                    var renderable = primRenderable.renderable;
+                    if (!renderable) { continue; }
+                    renderable.SetMaterialKeyword(keyword, enable);
+                }
+            }
+        }
+        /**
+         * Changes the shader used by this avatar.
+         * The changes are immediately applied to all its renderables.
+         */
+        [Obsolete("Use OvrAvatarMaterial instead", false)]
+        public void SetMaterialShader(Shader shader)
+        {
+            // remember shader for future renderables
+            _material.SetShader(shader);
+            foreach (var meshNodeKVP in _meshNodes)
+            {
+                foreach (var primRenderable in meshNodeKVP.Value)
+                {
+                    var renderable = primRenderable.renderable;
+                    if (!renderable) { continue; }
+                    renderable.SetShader(shader);
+                }
+            }
+        }
+
+        /**
+         * Changes one or more material properties via a callback.
+         * The changes are immediately applied to all its renderables.
+         */
+        [Obsolete("Use OvrAvatarMaterial instead", false)]
+        public void SetMaterialProperties(Action<OvrAvatarMaterial> callback)
+        {
+            callback(_material);
+            ApplyMaterial();
+        }
+
+        /**
+         * Changes one or more material properties via a callback.
+         * The changes are immediately applied to all its renderables.
+         */
+        [Obsolete("Use OvrAvatarMaterial instead", false)]
+        public void SetMaterialProperties<TParam>(Action<OvrAvatarMaterial, TParam> callback, TParam userData)
+        {
+            callback(_material, userData);
+            ApplyMaterial();
+        }
+
+        /**
+         * Applies the shader, keywords and material properties from
+         * OvrAvatarEntity.material to all the renderables associated
+         * with this avatar.
+         */
+        public void ApplyMaterial()
+        {
+            foreach (var meshNodeKVP in _meshNodes)
+            {
+                foreach (var primRenderable in meshNodeKVP.Value)
+                {
+                    var renderable = primRenderable.renderable;
+                    if (!renderable) { continue; }
+                    _material.Apply(renderable);
+                }
+            }
+        }
+
+        public void SetSharedMaterialProperties(Action<UnityEngine.Material> callback)
+        {
+            // TODO: This will not cover all future renderables
+            // Each primitive has its own property block so callback has to be called once per primitive
+            // TODO: Check if there's a way around this
+
+            foreach (var meshNodeKVP in _meshNodes)
+            {
+                foreach (var primRenderable in meshNodeKVP.Value)
+                {
+                    var renderable = primRenderable.renderable;
+                    if (!renderable) { continue; }
+                    var rend = renderable.rendererComponent;
+                    callback(rend.sharedMaterial);
+                }
+            }
+        }
+
+        private void UpdateAvatarLodColor()
+        {
+            if (AvatarLOD.Level > -1 && AvatarLODManager.Instance.debug.displayLODColors)
+            {
+                _material.SetKeyword("DEBUG_TINT", true);
+                _material.SetColor(DEBUG_TINT_ID, AvatarLODManager.LOD_COLORS[AvatarLOD.overrideLOD ? AvatarLOD.overrideLevel : AvatarLOD.Level]);
+            }
+            else
+            {
+                _material.SetKeyword("DEBUG_TINT", true);
+                _material.SetColor(DEBUG_TINT_ID, Color.white);
+            }
+            ApplyMaterial();
+        }
+
+        /***
+         * Applies the current material state (keywords, shader, properties)
+         * to the given renderable. This function should be called whenever a
+         * new renderable is added.
+         */
+        internal void ConfigureRenderableMaterial(OvrAvatarRenderable renderable)
+        {
+            if (!renderable) { return; }
+            _material.Apply(renderable);
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Material.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Material.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..182570a667a72fce0d78852aeb5c3cc24ca860b7
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Material.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 36c2daf253839d84883e1aeb575c3416
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Rendering.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Rendering.cs
new file mode 100644
index 0000000000000000000000000000000000000000..b2bdca44409deda266cb296fded251f03c811e1f
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Rendering.cs
@@ -0,0 +1,217 @@
+//#define OVR_AVATAR_AUTO_DISABLE_SKINNING_MATRICES_WITH_UNITYSMR
+
+using Oculus.Skinning;
+using Oculus.Skinning.GpuSkinning;
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace Oculus.Avatar2
+{
+    public partial class OvrAvatarEntity : MonoBehaviour
+    {
+        // TODO: Move more rendering logic here
+
+        public bool isStaticMesh => SkinningType == SkinningConfig.NONE;
+        private bool UseGpuSkinning => SkinningType == SkinningConfig.OVR_UNITY_GPU_FULL;
+        private bool UseGpuMorphTargets => SkinningType == SkinningConfig.OVR_UNITY_GPU_FULL;
+
+        private bool UseMotionSmoothingRenderer => MotionSmoothingSettings == MotionSmoothingOptions.USE_CONFIG_SETTING ? GpuSkinningConfiguration.Instance.MotionSmoothing : MotionSmoothingSettings == MotionSmoothingOptions.FORCE_ON;
+
+        private Transform _probeAnchor = null;
+
+        /////////////////////////////////////////////////
+        //:: Private Functions
+
+        #region Config
+
+        public enum SkinningConfig
+        {
+            DEFAULT,
+            NONE,
+
+            UNITY,
+
+            OVR_UNITY_GPU_FULL,
+
+            // TODO: Turn into flags, need to genericize EntityFeatureDrawer
+
+            // OVR_UNITY_GPU_JOINTS_ONLY,
+            // OVR_UNITY_GPU_MORPH_TARGETS,
+
+            // OVR_UNITY_CPU,
+
+            // OVR_NATIVE_CPU,
+            // OVR_NATIVE_GPU
+        }
+
+        [SerializeField]
+        private SkinningConfig SkinningType = SkinningConfig.DEFAULT;
+
+        private enum MotionSmoothingOptions
+        {
+            USE_CONFIG_SETTING,
+            FORCE_ON,
+            FORCE_OFF,
+        }
+
+        [Tooltip("Enable/disable motion smoothing for an individual OvrAvatarEntity. By default uses the setting specified in the GpuSkinningConfiguration.")]
+        [SerializeField]
+        private MotionSmoothingOptions MotionSmoothingSettings = MotionSmoothingOptions.USE_CONFIG_SETTING;
+
+        [SerializeField]
+        private bool _hidden = false;
+
+        private bool UseAppSwRenderer => GpuSkinningConfiguration.Instance.SupportApplicationSpaceWarp;
+
+        // TODO: This should be keyed by primitiveId instead of instance?
+        private readonly Dictionary<OvrAvatarPrimitive, OvrAvatarSkinnedRenderable> _skinnedRenderables =
+            new Dictionary<OvrAvatarPrimitive, OvrAvatarSkinnedRenderable>();
+
+        public bool Hidden
+        {
+            get => _hidden;
+            set
+            {
+                _hidden = value;
+                SetActiveView(GetActiveView());
+            }
+        }
+
+        // Called by CreateRenderable, setup skinning type based on primitive and configuration
+        private OvrAvatarRenderable AddRenderableComponent(GameObject primitiveObject, OvrAvatarPrimitive primitive)
+        {
+            bool hasSkinningData = primitive.joints.Length > 0;
+            if (!hasSkinningData)
+            {
+                SkinningType = SkinningConfig.NONE;
+            }
+
+            var renderable = AddRenderableComponent(primitiveObject);
+
+            if (!(_probeAnchor is null))
+            {
+                renderable.rendererComponent.probeAnchor = _probeAnchor;
+            }
+
+            renderable.ApplyMeshPrimitive(primitive);
+
+            return renderable;
+        }
+
+        private OvrAvatarRenderable AddRenderableComponent(GameObject primitiveObject)
+        {
+            switch (SkinningType)
+            {
+                case SkinningConfig.DEFAULT:
+                case SkinningConfig.UNITY:
+                    return primitiveObject.AddComponent<OvrAvatarUnitySkinnedRenderable>();
+
+                case SkinningConfig.OVR_UNITY_GPU_FULL:
+                    if (!UseMotionSmoothingRenderer)
+                    {
+                        if (!UseAppSwRenderer)
+                        {
+                            return primitiveObject.AddComponent<OvrAvatarGpuSkinnedRenderable>();
+                        }
+
+                        return primitiveObject.AddComponent<OvrAvatarGpuSkinnedMvRenderable>();
+                    }
+                    else
+                    {
+                        if (!UseAppSwRenderer)
+                        {
+                            var renderable = primitiveObject.AddComponent<OvrAvatarGpuInterpolatedSkinnedRenderable>();
+                            renderable.InterpolationValueProvider = _interpolationValueProvider;
+
+                            return renderable;
+                        }
+                        else
+                        {
+                            var renderable = primitiveObject.AddComponent<OvrAvatarGpuInterpolatedSkinnedMvRenderable>();
+                            renderable.InterpolationValueProvider = _interpolationValueProvider;
+
+                            return renderable;
+                        }
+                    }
+                case SkinningConfig.NONE:
+                    return primitiveObject.AddComponent<OvrAvatarRenderable>();
+
+                default:
+                    throw new ArgumentException($"Invalid SkinningType: {(int)SkinningType}");
+
+            }
+        }
+
+
+        private void ValidateSkinningType()
+        {
+            GpuSkinningConfiguration.HandleDefaultConfig(ref SkinningType);
+
+            if (SkinningType == SkinningConfig.OVR_UNITY_GPU_FULL && !OvrAvatarManager.Instance.OvrGPUSkinnerSupported)
+            {
+#if UNITY_ANDROID && !UNITY_2019_3_OR_NEWER
+                OvrAvatarLog.LogError("OvrGpuSkinning is unavailable on Android before Unity 2019.3", logScope, this);
+#else
+                if (!OvrAvatarManager.Instance.gpuSkinningShaderLevelSupported)
+                {
+                    OvrAvatarLog.LogInfo("OvrGpuSkinning unsupported on this hardware, attempting fallback to UnitySMR", logScope, this);
+                }
+                else
+                {
+                    OvrAvatarLog.LogWarning("OvrGpuSkinning unsupported, attempting fallback to UnitySMR", logScope, this);
+                }
+#endif   // !UNITY_ANDROID || UNITY_2019_3_OR_NEWER
+
+                SkinningType = SkinningConfig.UNITY;
+            }
+
+            if (SkinningType == SkinningConfig.UNITY && !OvrAvatarManager.Instance.UnitySMRSupported)
+            {
+                if (!OvrAvatarManager.Instance.OvrGPUSkinnerSupported)
+                {
+                    OvrAvatarLog.LogError("UnitySMR unsupported with no fallback", logScope, this);
+
+                    SkinningType = SkinningConfig.NONE;
+                }
+                else
+                {
+                    OvrAvatarLog.LogWarning("UnitySMR unsupported, falling back to OvrGPU", logScope, this);
+
+                    SkinningType = SkinningConfig.OVR_UNITY_GPU_FULL;
+                }
+            }
+
+            // Intentional SkinningConfig.NONE config should log no warnings/errors
+        }
+
+        #endregion
+
+        public void SetProbeAnchor(Transform anchor)
+        {
+            // TODO: Confirm this catches renderables added later
+            _probeAnchor = anchor;
+
+            foreach (var meshNodeKVP in _meshNodes)
+            {
+                foreach (var primRenderData in meshNodeKVP.Value)
+                {
+                    var renderable = primRenderData.renderable;
+                    if (!renderable) { continue; }
+                    var rend = renderable.rendererComponent;
+                    rend.probeAnchor = anchor;
+                }
+            }
+        }
+
+        /**
+         * Configures the material for this renderable
+         * with the last known material state.
+         */
+        private void InitializeRenderable(OvrAvatarRenderable renderable)
+        {
+            ConfigureRenderableMaterial(renderable);
+            OnRenderableCreated(renderable);
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Rendering.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Rendering.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..30149adad4da615cfd187057a42dd2dc15532908
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Rendering.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 86366b210e0d3b14bb75b1ca6bb2d6eb
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Streaming.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Streaming.cs
new file mode 100644
index 0000000000000000000000000000000000000000..5823db924519cebb7bd9c2243f0e9c2c70c05d86
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Streaming.cs
@@ -0,0 +1,407 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+
+using Unity.Collections;
+using Unity.Collections.LowLevel.Unsafe;
+
+using UnityEngine;
+
+namespace Oculus.Avatar2
+{
+    public partial class OvrAvatarEntity : MonoBehaviour
+    {
+        public enum StreamLOD
+        {
+            Full = CAPI.ovrAvatar2StreamLOD.Full,
+            High = CAPI.ovrAvatar2StreamLOD.High,
+            Medium = CAPI.ovrAvatar2StreamLOD.Medium,
+            Low = CAPI.ovrAvatar2StreamLOD.Low
+        }
+
+        public const StreamLOD StreamLODFirst = StreamLOD.Full;
+        public const StreamLOD StreamLODLast = StreamLOD.Low;
+        public const int StreamLODCount = (StreamLOD.Low - StreamLOD.Full) + 1;
+
+        // Public Properties
+        public bool IsLocal => _isLocal;
+        public StreamLOD activeStreamLod => (StreamLOD)_activeStreamLod;
+
+        [System.Obsolete("Use GetLastByteSizeForStreamLod or GetLastByteSizeForLodIndex instead")]
+        public IReadOnlyCollection<long> StreamLodBytes => _lastStreamLodByteSize;
+
+        public long GetLastByteSizeForStreamLod(StreamLOD lod) => GetLastByteSizeForLodIndex((int)lod);
+        public long GetLastByteSizeForLodIndex(int lodIndex) => _lastStreamLodByteSize[lodIndex];
+
+        // When true, links the network streaming fidelity to the rendering Lod groups
+        [HideInInspector]
+        public bool useRenderLods = true;
+
+        // Serialized Variables
+        [Header("Networking")]
+        [SerializeField]
+        private bool _isLocal = true;
+
+        // Private Variables
+        private CAPI.ovrAvatar2StreamLOD _activeStreamLod = CAPI.ovrAvatar2StreamLOD.Low;
+        private long[] _lastStreamLodByteSize = new long[StreamLODCount];
+
+        #region Public Streaming Functions
+
+        public bool RecordStart()
+        {
+            return CAPI.ovrAvatar2Streaming_RecordStart(entityId)
+                .EnsureSuccess("ovrAvatar2Streaming_RecordStart", logScope, this);
+        }
+
+        public bool RecordStop()
+        {
+            return CAPI.ovrAvatar2Streaming_RecordStop(entityId)
+                .EnsureSuccess("ovrAvatar2Streaming_RecordStop", logScope, this);
+        }
+
+        // TODO: Should probably be internal?
+        public bool GetRecordingSize(CAPI.ovrAvatar2StreamLOD lod, out UInt64 bytes)
+        {
+            return CAPI.ovrAvatar2Streaming_GetRecordingSize(entityId, lod, out bytes)
+                .EnsureSuccess("ovrAvatar2Streaming_GetRecordingSize", logScope, this);
+        }
+
+        public void SetIsLocal(bool newValue)
+        {
+            if (IsLocal == newValue) return;
+
+            _isLocal = newValue;
+            if (IsCreated)
+            {
+                SetStreamingPlayback(!IsLocal);
+            }
+        }
+
+        // I'm not sure what the sideeffects might be of recording snapshots that go unused?
+        // At the very least, it doesn't seem like the most performant option.
+        internal UInt64 GetRecordingSize(StreamLOD lod)
+        {
+            var lastSize = GetLastByteSizeForStreamLod(lod);
+            if (lastSize > 0)
+            {
+                return (UInt64)lastSize;
+            }
+            if (TryRecordSnapshot(lod, out var bytes))
+            {
+                return bytes;
+            }
+            return 0;
+        }
+
+        public void SetPlaybackTimeDelay(float value)
+        {
+            var result = CAPI.ovrAvatar2Streaming_SetPlaybackTimeDelay(entityId, value);
+            result.LogAssert("ovrAvatar2Streaming_SetPlaybackTimeDelay", logScope, this);
+        }
+
+        // Local Avatar
+        public byte[] RecordStreamData(StreamLOD lod)
+        {
+            if (!TryRecordSnapshot(lod, out var bytes))
+            {
+                return null;
+            }
+
+            var lodToUse = (CAPI.ovrAvatar2StreamLOD)lod;
+            using (var data = new NativeArray<byte>((int)bytes, Allocator.Temp, NativeArrayOptions.UninitializedMemory))
+            {
+                bool success;
+                unsafe
+                {
+                    UInt64 bufferSize = bytes;
+                    success = CAPI.OvrAvatar2Streaming_SerializeRecording(entityId, lodToUse, data.GetPtr(), ref bufferSize);
+                }
+                return success ? data.ToArray() : null;
+            }
+        }
+
+        // Caller owns lifetime of dataBuffer
+        [Obsolete("Using fixed size buffers leads to unexpected networking failures. Will be removed in near future. Use RecordStreamData_AutoBuffer to pass buffer by reference, so SDK can resize if needed.")]
+        public UInt32 RecordStreamData(StreamLOD lod, in NativeArray<byte> dataBuffer)
+        {
+            IntPtr dataBufferPtr;
+            unsafe { dataBufferPtr = (IntPtr)dataBuffer.GetUnsafePtr(); }
+            return RecordStreamData(lod, dataBufferPtr, dataBuffer.GetBufferSize());
+        }
+
+        // If data fits w/in the provided buffer, it is used
+        // - otherwise buffer is disposed and a new one created to fit requested LOD
+        public UInt32 RecordStreamData_AutoBuffer(StreamLOD lod, ref NativeArray<byte> dataBuffer)
+        {
+            // TODO: When `RecordStreamData` removed, rename `RecordStreamData_AutoBuffer` to `RecordStreamData`
+            if (!TryRecordSnapshot(lod, out var bytes))
+            {
+                return 0;
+            }
+
+            int bufferSize = dataBuffer.Length;
+            if ((UInt32)bytes > bufferSize)
+            {
+                if (dataBuffer.IsCreated) { dataBuffer.Dispose(); }
+
+                // TODO: Use allocator type of provided `dataBuffer`?
+                dataBuffer = new NativeArray<byte>((int)bytes, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
+            }
+
+            var lodToUse = (CAPI.ovrAvatar2StreamLOD)lod;
+            bool success;
+            unsafe
+            {
+                UInt64 dataBufferSize = dataBuffer.GetBufferSize();
+                success = CAPI.OvrAvatar2Streaming_SerializeRecording(entityId, lodToUse, dataBuffer.GetPtr(), ref dataBufferSize);
+            }
+            if (!success)
+            {
+                dataBuffer.Dispose();
+                dataBuffer = default;
+                return 0;
+            }
+
+            return (UInt32)bytes;
+        }
+
+        // If data fits w/in the provided buffer, it is used
+        // - otherwise buffer is resized
+        public UInt32 RecordStreamData_AutoBuffer(StreamLOD lod, ref byte[] dataBuffer)
+        {
+            // TODO: When `RecordStreamData` removed, rename `RecordStreamData_AutoBuffer` to `RecordStreamData`
+            if (!TryRecordSnapshot(lod, out var bytes))
+            {
+                return 0;
+            }
+
+            int bufferSize = dataBuffer.Length;
+            if ((UInt32)bytes > bufferSize)
+            {
+                Array.Resize(ref dataBuffer, (int)bytes);
+            }
+
+
+            bool success;
+            unsafe
+            {
+                fixed (byte* dataBufferPtr = dataBuffer)
+                {
+                    var lodToUse = (CAPI.ovrAvatar2StreamLOD)lod;
+                    var bufferSizeAndBytesWritten = (UInt64)dataBuffer.LongLength;
+                    success = CAPI.OvrAvatar2Streaming_SerializeRecording(entityId, lodToUse, dataBufferPtr, ref bufferSizeAndBytesWritten);
+                }
+            }
+
+            return success ? (UInt32)bytes : 0;
+        }
+
+        [Obsolete("Prefer RecordStreamData_AutoBuffer variants")]
+        public UInt32 RecordStreamData(StreamLOD lod, IntPtr buffer, UInt32 bufferSize)
+        {
+            if (!TryRecordSnapshot(lod, out var bytes))
+            {
+                return 0;
+            }
+
+            if ((UInt32)bytes > bufferSize)
+            {
+                OvrAvatarLog.LogError($"StreamData buffer is too small. Size was {bufferSize} but needed to be {(UInt32)bytes}");
+                return 0;
+            }
+
+            var lodToUse = (CAPI.ovrAvatar2StreamLOD)lod;
+            bool success;
+            unsafe
+            {
+                byte* bufferPtr = (byte*)buffer.ToPointer();
+                UInt64 bufferSizeAndBytesWritten = bufferSize;
+                success = CAPI.OvrAvatar2Streaming_SerializeRecording(
+                    entityId, lodToUse, bufferPtr, ref bufferSizeAndBytesWritten);
+            }
+            return success ? (UInt32)bytes : 0;
+        }
+
+        // Remote Avatar
+        public void ForceStreamLod(StreamLOD newLod)
+        {
+            useRenderLods = false;
+            _activeStreamLod = (CAPI.ovrAvatar2StreamLOD)newLod;
+        }
+
+        public bool ApplyStreamData(byte[] data)
+        {
+            if (!_VerifyCanApplyStreaming()) { return false; }
+
+            unsafe
+            {
+                fixed (byte* dataPtr = data)
+                {
+                    return _ExecuteApplyStreamData(dataPtr, (UInt32)data.Length);
+                }
+            }
+        }
+        public bool ApplyStreamData(in NativeArray<byte> array)
+        {
+            OvrAvatarLog.AssertConstMessage(array.IsCreated, "NativeArray is not created");
+            if (array.GetBufferSize() <= 0)
+            {
+                OvrAvatarLog.LogError("Invalid buffer size parameter", logScope, this);
+                return false;
+            }
+
+            if (!_VerifyCanApplyStreaming()) { return false; }
+
+            unsafe
+            {
+                return _ExecuteApplyStreamData(array.GetPtr(), array.GetBufferSize());
+            }
+        }
+        public bool ApplyStreamData(in NativeSlice<byte> slice)
+        {
+            if (slice.Length <= 0)
+            {
+                OvrAvatarLog.LogError("NativeSlice has size 0", logScope, this);
+                return false;
+            }
+
+            if (!_VerifyCanApplyStreaming()) { return false; }
+
+            unsafe
+            {
+                return _ExecuteApplyStreamData(slice.GetPtr(), (UInt32)(slice.Length * sizeof(byte)));
+            }
+        }
+
+        [Obsolete("Prefer ApplyStreamData overrides with explicit container types")]
+        public bool ApplyStreamData(IntPtr data, UInt32 size)
+        {
+            if (!_VerifyCanApplyStreaming()) { return false; }
+
+            unsafe
+            {
+                return _ExecuteApplyStreamData((byte*)data, size);
+            }
+        }
+
+        private bool _VerifyCanApplyStreaming()
+        {
+            if (!IsCreated) { return false; }
+            // TODO: This is not entirely sufficient, could have joints but the wrong skeleton :/
+            if (!HasJoints) { return false; }
+            if (IsLocal)
+            {
+                OvrAvatarLog.LogWarning(
+                    $"Tried to receive network data on a local avatar. Use SetIsLocal first.", logScope, this);
+                return false;
+            }
+            return true;
+        }
+
+        private unsafe bool _ExecuteApplyStreamData(byte* dataPtr, UInt32 size)
+        {
+            OvrAvatarLog.Assert(dataPtr != null);
+            OvrAvatarLog.Assert(size > 0);
+
+            var result = CAPI.OvrAvatar2Streaming_DeserializeRecording(entityId, dataPtr, size, this);
+            if (!result)
+            {
+                OvrAvatarLog.LogWarning("Failed to apply stream data", logScope, this);
+            }
+            return result;
+        }
+
+        public CAPI.ovrAvatar2StreamingPlaybackState? GetStreamingPlaybackState()
+        {
+            var result = CAPI.ovrAvatar2Streaming_GetPlaybackState(entityId, out var playbackState);
+            switch (result)
+            {
+                case CAPI.ovrAvatar2Result.Success:
+                    return playbackState;
+                case CAPI.ovrAvatar2Result.NotFound:
+                    return null;
+                default:
+
+                    result.LogAssert("ovrAvatar2Streaming_GetPlaybackState", logScope, this);
+                    return null;
+            }
+        }
+
+        #endregion Public Streaming Functions
+
+        protected bool SetStreamingPlayback(bool shouldStart)
+        {
+            CAPI.ovrAvatar2Result result;
+            if (shouldStart)
+            {
+                result = CAPI.ovrAvatar2Streaming_PlaybackStart(entityId);
+                result.LogAssert("ovrAvatar2Streaming_PlaybackStart", logScope, this);
+            }
+            else
+            {
+                result = CAPI.ovrAvatar2Streaming_PlaybackStop(entityId);
+                result.LogAssert("ovrAvatar2Streaming_PlaybackStop", logScope, this);
+            }
+
+            return result.IsSuccess();
+        }
+
+        protected void ComputeNetworkLod()
+        {
+            var newLod = CAPI.ovrAvatar2StreamLOD.Low;
+
+            var lodLevel = AvatarLOD.overrideLOD ? AvatarLOD.overrideLevel : AvatarLOD.wantedLevel;
+            if (lodLevel != -1)
+            {
+                if (lodLevel < 1)
+                {
+                    newLod = CAPI.ovrAvatar2StreamLOD.High;
+                }
+                else if (lodLevel < 2)
+                {
+                    newLod = CAPI.ovrAvatar2StreamLOD.Medium;
+                }
+            }
+
+            _activeStreamLod = newLod;
+        }
+
+        private bool TryRecordSnapshot(StreamLOD lod, out UInt64 bytes)
+        {
+            if (!IsCreated)
+            {
+                OvrAvatarLog.LogError("Cannot record stream data until entity is created", logScope, this);
+                bytes = 0;
+                return false;
+            }
+
+            if (!HasJoints)
+            {
+                OvrAvatarLog.LogError("Cannot record stream data until entity has loaded a skeleton", logScope, this);
+                bytes = 0;
+                return false;
+            }
+
+            var result = CAPI.ovrAvatar2Streaming_RecordSnapshot(entityId);
+            if (!result.EnsureSuccess("ovrAvatar2Streaming_RecordSnapshot", logScope, this))
+            {
+                bytes = 0;
+                return false;
+            }
+
+            var lodToUse = (CAPI.ovrAvatar2StreamLOD)lod;
+            result = CAPI.ovrAvatar2Streaming_GetRecordingSize(entityId, lodToUse, out bytes);
+            if (!result.EnsureSuccess("ovrAvatar2Streaming_GetRecordingSize", logScope, this))
+            {
+                // TODO: Is there any necessary "cleanup" after `ovrAvatar2Streaming_RecordSnapshot` was unsuccesful?
+                bytes = 0;
+                return false;
+            }
+
+            _lastStreamLodByteSize[(int)lod] = (long)(bytes);
+
+            return true;
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Streaming.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Streaming.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..f5a7b2ef954018e0e65eb2ef8cd6b16a63b0f7a0
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEntity_Streaming.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 0851fb9f16defc94db2b8a41ac73887f
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarEyePoseBehavior.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEyePoseBehavior.cs
new file mode 100644
index 0000000000000000000000000000000000000000..2b9ac6d60872f8d3525cf623e7f7c090d1f3d702
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEyePoseBehavior.cs
@@ -0,0 +1,13 @@
+using Oculus.Avatar2;
+using UnityEngine;
+
+namespace Oculus.Avatar2
+{
+    /// <summary>
+    /// MonoBehavior which holds a eye pose context so it can be referenced in the inspector
+    /// </summary>
+    public abstract class OvrAvatarEyePoseBehavior : MonoBehaviour
+    {
+        public abstract OvrAvatarEyePoseProviderBase EyePoseProvider { get; }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarEyePoseBehavior.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEyePoseBehavior.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..5658e1a650301ed4414cbd838b1f7a5edd365a3c
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEyePoseBehavior.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: befbe97888fac624986e8a211d1e086a
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarEyePoseProviderBase.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEyePoseProviderBase.cs
new file mode 100644
index 0000000000000000000000000000000000000000..d98aae985a69e0c5629e5fb8b581b71fd5cad5be
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEyePoseProviderBase.cs
@@ -0,0 +1,51 @@
+using System;
+using AOT;
+
+namespace Oculus.Avatar2
+{
+    /// <summary>
+    /// Base class for C# code to supply eye pose data for avatar entities
+    /// </summary>
+    public abstract class OvrAvatarEyePoseProviderBase : OvrAvatarCallbackContextBase
+    {
+        private readonly OvrAvatarEyesPose _eyePose = new OvrAvatarEyesPose();
+
+        internal CAPI.ovrAvatar2EyePoseProvider Context { get; }
+
+        protected OvrAvatarEyePoseProviderBase()
+        {
+            var context = new CAPI.ovrAvatar2EyePoseProvider
+            {
+                provider = new IntPtr(id),
+                eyePoseCallback = EyePoseCallback
+            };
+            Context = context;
+        }
+
+        protected abstract bool GetEyePose(OvrAvatarEyesPose eyePose);
+
+        [MonoPInvokeCallback(typeof(CAPI.EyePoseCallback))]
+        private static bool EyePoseCallback(out CAPI.ovrAvatar2EyesPose eyePose, IntPtr userContext)
+        {
+            try
+            {
+                var provider = GetInstance<OvrAvatarEyePoseProviderBase>(userContext);
+                if (provider != null)
+                {
+                    if (provider.GetEyePose(provider._eyePose))
+                    {
+                        eyePose = provider._eyePose.ToNative();
+                        return true;
+                    }
+                }
+            }
+            catch (Exception e)
+            {
+                OvrAvatarLog.LogError(e.ToString());
+            }
+
+            eyePose = default;
+            return false;
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarEyePoseProviderBase.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEyePoseProviderBase.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..19c1ea84a17ab07b026138e90581e5e11c466df3
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEyePoseProviderBase.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a9b0b7bcd8fd5f742bf97a919edcd705
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarEyeTrackingBehaviorOvrPlugin.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEyeTrackingBehaviorOvrPlugin.cs
new file mode 100644
index 0000000000000000000000000000000000000000..b24c68eedd1cf42c3987b96af28b145e111dec99
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEyeTrackingBehaviorOvrPlugin.cs
@@ -0,0 +1,41 @@
+using Oculus.Avatar2;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace Oculus.Avatar2
+{
+    /// <summary>
+    /// EyePose behavior that enables eye tracking through OVRPlugin.
+    /// </summary>
+    public class OvrAvatarEyeTrackingBehaviorOvrPlugin : OvrAvatarEyePoseBehavior
+    {
+        private OvrAvatarEyePoseProviderBase _eyePoseProvider;
+
+        public override OvrAvatarEyePoseProviderBase EyePoseProvider
+        {
+            get
+            {
+                InitializeEyePoseProvider();
+
+                return _eyePoseProvider;
+            }
+        }
+
+        private void InitializeEyePoseProvider()
+        {
+            if (_eyePoseProvider == null && OvrAvatarManager.Instance != null)
+            {
+                OvrAvatarManager.Instance.RequestEyeTrackingPermission();
+                if (OvrAvatarManager.Instance.OvrPluginEyePoseProvider != null)
+                {
+                    OvrAvatarLog.LogInfo("Eye tracking service available");
+                    _eyePoseProvider = OvrAvatarManager.Instance.OvrPluginEyePoseProvider;
+                }
+                else
+                {
+                    OvrAvatarLog.LogWarning("Eye tracking service unavailable");
+                }
+            }
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarEyeTrackingBehaviorOvrPlugin.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEyeTrackingBehaviorOvrPlugin.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..4440bc129a7382f18f1395406198f889549abdc4
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEyeTrackingBehaviorOvrPlugin.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 488cfafa131933046a924063017e8372
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarEyeTrackingState.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEyeTrackingState.cs
new file mode 100644
index 0000000000000000000000000000000000000000..ed9b129d495e1c4a057a3f96f844712b9427432c
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEyeTrackingState.cs
@@ -0,0 +1,30 @@
+using System;
+using UnityEngine;
+
+namespace Oculus.Avatar2
+{
+    /// <summary>
+    /// Data needed to drive eye tracking of an avatar.
+    /// </summary>
+    public sealed class OvrAvatarEyesPose
+    {
+        public CAPI.ovrAvatar2EyePose leftEye;
+        public CAPI.ovrAvatar2EyePose rightEye;
+
+        #region Native Conversions
+        internal CAPI.ovrAvatar2EyesPose ToNative()
+        {
+            var native = new CAPI.ovrAvatar2EyesPose();
+            native.leftEye = leftEye;
+            native.rightEye = rightEye;
+            return native;
+        }
+
+        internal void FromNative(in CAPI.ovrAvatar2EyesPose native)
+        {
+            leftEye = native.leftEye;
+            rightEye = native.rightEye;
+        }
+        #endregion
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarEyeTrackingState.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEyeTrackingState.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..d1c9ef2974505302d6fa60373fab7ba7dedf2383
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarEyeTrackingState.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 8bef4423d1a381d4180ed9508e0791a5
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarFacePose.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarFacePose.cs
new file mode 100644
index 0000000000000000000000000000000000000000..88e79ed7b71d78c9270084e738eabc5957d27863
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarFacePose.cs
@@ -0,0 +1,53 @@
+using System;
+
+namespace Oculus.Avatar2
+{
+    /// <summary>
+    /// Data needed to drive face tracking of an avatar.
+    /// </summary>
+    public sealed class OvrAvatarFacePose
+    {
+        public readonly float[] expressionWeights = new float[(int)CAPI.ovrAvatar2FaceExpression.Count];
+        public readonly float[] expressionConfidence = new float[(int)CAPI.ovrAvatar2FaceExpression.Count];
+        public Int64 sampleTimeNS;
+
+        internal static CAPI.ovrAvatar2FacePose GenerateEmptyNativePose()
+        {
+            var native = new CAPI.ovrAvatar2FacePose();
+            native.expressionWeights = new float[(int)CAPI.ovrAvatar2FaceExpression.Count];
+            native.expressionConfidence = new float[(int)CAPI.ovrAvatar2FaceExpression.Count];
+            return native;
+        }
+
+        #region Native Conversions
+        internal CAPI.ovrAvatar2FacePose ToNative()
+        {
+            CAPI.ovrAvatar2FacePose native = GenerateEmptyNativePose();
+            for (var i = 0; i < expressionWeights.Length; i++)
+            {
+                native.expressionWeights[i] = expressionWeights[i];
+            }
+
+            for (var i = 0; i < expressionConfidence.Length; i++)
+            {
+                native.expressionConfidence[i] = expressionConfidence[i];
+            }
+
+            return native;
+        }
+
+        internal void FromNative(in CAPI.ovrAvatar2FacePose native)
+        {
+            for (var i = 0; i < expressionWeights.Length; i++)
+            {
+                expressionWeights[i] = native.expressionWeights[i];
+            }
+
+            for (var i = 0; i < expressionConfidence.Length; i++)
+            {
+                expressionConfidence[i] = native.expressionConfidence[i];
+            }
+        }
+        #endregion
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarFacePose.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarFacePose.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..01ae2fb028916b113b6845f04090678b03fef095
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarFacePose.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a4d52f7b7b29d564a950e8701fc62325
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarFacePoseBehavior.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarFacePoseBehavior.cs
new file mode 100644
index 0000000000000000000000000000000000000000..7510f55a7c960ba3b8e28586e0208537249c12a4
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarFacePoseBehavior.cs
@@ -0,0 +1,13 @@
+using Oculus.Avatar2;
+using UnityEngine;
+
+namespace Oculus.Avatar2
+{
+    /// <summary>
+    /// MonoBehavior which holds a face tracking context so it can be referenced in the inspector
+    /// </summary>
+    public abstract class OvrAvatarFacePoseBehavior : MonoBehaviour
+    {
+        public abstract OvrAvatarFacePoseProviderBase FacePoseProvider { get; }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarFacePoseBehavior.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarFacePoseBehavior.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..5af34b634ee987333ce65cf82603c4cbb3437aa5
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarFacePoseBehavior.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: f31e359a8969b8d479c81c20efd5491b
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarFacePoseProviderBase.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarFacePoseProviderBase.cs
new file mode 100644
index 0000000000000000000000000000000000000000..32cf780d1d0d9f5e3d62cc978fdd48a2cc105fd8
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarFacePoseProviderBase.cs
@@ -0,0 +1,53 @@
+using System;
+using AOT;
+
+namespace Oculus.Avatar2
+{
+    /// <summary>
+    /// Base class for C# code to supply face pose data for avatar entities
+    /// </summary>
+    public abstract class OvrAvatarFacePoseProviderBase : OvrAvatarCallbackContextBase
+    {
+        private readonly OvrAvatarFacePose _facePose = new OvrAvatarFacePose();
+        internal CAPI.ovrAvatar2FacePoseProvider Provider { get; }
+
+        protected OvrAvatarFacePoseProviderBase()
+        {
+            var provider = new CAPI.ovrAvatar2FacePoseProvider
+            {
+                provider = new IntPtr(id),
+                facePoseCallback = FacePoseCallback
+            };
+            Provider = provider;
+        }
+
+        protected abstract bool GetFacePose(OvrAvatarFacePose facePose);
+
+        [MonoPInvokeCallback(typeof(CAPI.FacePoseCallback))]
+        private static bool FacePoseCallback(out CAPI.ovrAvatar2FacePose facePose, IntPtr userContext)
+        {
+            try
+            {
+                var provider = GetInstance<OvrAvatarFacePoseProviderBase>(userContext);
+                if (provider != null)
+                {
+                    if (provider.GetFacePose(provider._facePose))
+                    {
+                        facePose = provider._facePose.ToNative();
+                        return true;
+                    }
+
+                    facePose = OvrAvatarFacePose.GenerateEmptyNativePose();
+                    return false;
+                }
+            }
+            catch (Exception e)
+            {
+                OvrAvatarLog.LogError(e.ToString());
+            }
+
+            facePose = default;
+            return false;
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarFacePoseProviderBase.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarFacePoseProviderBase.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..0f11cb8205dcb81a2ab01ef6045bdcab32f2dd05
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarFacePoseProviderBase.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 4e1714ead6faca44ab8aa9d5f2cd5478
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarFaceTrackingBehaviorOvrPlugin.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarFaceTrackingBehaviorOvrPlugin.cs
new file mode 100644
index 0000000000000000000000000000000000000000..f55b06fa6bf6c69e9214cab2baec9f67dd712a35
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarFaceTrackingBehaviorOvrPlugin.cs
@@ -0,0 +1,42 @@
+using Oculus.Avatar2;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.Android;
+
+namespace Oculus.Avatar2
+{
+    /// <summary>
+    /// FaceTracking behavior that enables face tracking through OVRPlugin.
+    /// </summary>
+    public class OvrAvatarFaceTrackingBehaviorOvrPlugin : OvrAvatarFacePoseBehavior
+    {
+        private OvrAvatarFacePoseProviderBase _facePoseProvider;
+
+        public override OvrAvatarFacePoseProviderBase FacePoseProvider
+        {
+            get
+            {
+                InitializeFacePoseProvider();
+
+                return _facePoseProvider;
+            }
+        }
+
+        private void InitializeFacePoseProvider()
+        {
+            if (_facePoseProvider == null && OvrAvatarManager.Instance != null)
+            {
+                OvrAvatarManager.Instance.RequestFaceTrackingPermission();
+                if (OvrAvatarManager.Instance.OvrPluginFacePoseProvider != null)
+                {
+                    OvrAvatarLog.LogInfo("Face tracking service available");
+                    _facePoseProvider = OvrAvatarManager.Instance.OvrPluginFacePoseProvider;
+                }
+                else
+                {
+                    OvrAvatarLog.LogWarning("Face tracking service unavailable");
+                }
+            }
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarFaceTrackingBehaviorOvrPlugin.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarFaceTrackingBehaviorOvrPlugin.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..247a77f3ef8107d250c88378c8970fabaceb8ec6
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarFaceTrackingBehaviorOvrPlugin.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 0e0330898740b1e4ca2071e639e5ecbd
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarGazeTarget.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarGazeTarget.cs
new file mode 100644
index 0000000000000000000000000000000000000000..39a3051b8beb01339d96ad44c36c37c4db18a03c
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarGazeTarget.cs
@@ -0,0 +1,133 @@
+using UnityEngine;
+
+/// @file OvrAvatarGazeTarget.cs
+
+namespace Oculus.Avatar2
+{
+    ///
+    /// Designates something the avatar can look at.
+    /// When this component is enabled, it is added to the
+    /// gaze target manager (in the avatar manager) which
+    /// will provide its position to the avatar behavior
+    /// engine every frame to calculate where the avatar
+    /// should look.
+    ///
+    /// The example below shows how to make a gaze target for the avatar's left hand.
+    /// @code
+    /// OvrAvatarEntity entity;
+    /// Transform jointTransform = entity.GetSkeletonTransform(CAPI.ovrAvatar2JointType.LeftHandIndexProximal);
+    /// GameObject gazeTargetObj = new GameObject("lookatlefthand");
+    /// OvrAvatarGazeTarget gazeTarget = gazeTargetObj.AddComponent<OvrAvatarGazeTarget>();
+    /// gazeTarget.TargetType = CAPI.ovrAvatar2GazeTargetType.AvatarHand;
+    /// gazeTargetObj.transform.SetParent(jointTransform, false);
+    /// @endcode
+    ///
+    /// @see OvrAvatarGazeTargetManager
+    /// @see OvrAvatarManager.GazeTargetManager
+    ///
+    public class OvrAvatarGazeTarget : MonoBehaviour
+    {
+        private static CAPI.ovrAvatar2Id nextId;
+
+        [SerializeField]
+        private CAPI.ovrAvatar2GazeTargetType _targetType = CAPI.ovrAvatar2GazeTargetType.Object;
+
+        private bool _targetCreated;
+        private Transform _transform;
+
+        internal bool Dirty => _transform.hasChanged;
+
+        internal CAPI.ovrAvatar2GazeTarget Target { get; private set; }
+        internal CAPI.ovrAvatar2Vector3f NativePosition => ((CAPI.ovrAvatar2Vector3f)Position).ConvertSpace();
+
+        /// Unique identifier for gaze target.
+        public CAPI.ovrAvatar2Id Id => Target.id;
+
+        /// Current position of gaze target.
+        public Vector3 Position => _transform.position;
+
+        /// Type of gaze target (avatar head, hand, moving or static object).
+        public CAPI.ovrAvatar2GazeTargetType TargetType
+        {
+            get => _targetType;
+            set
+            {
+                if (value != Target.type)
+                {
+                    _targetType = value;
+                    OnTypeChanged();
+                }
+            }
+        }
+
+        #region Unity Events
+
+        protected virtual void Awake()
+        {
+            _transform = transform;
+            Target = new CAPI.ovrAvatar2GazeTarget
+            {
+                id = nextId++,
+                type = _targetType
+            };
+        }
+
+        protected virtual void OnEnable()
+        {
+            CreateTarget();
+        }
+
+        protected virtual void OnDisable()
+        {
+            DestroyTarget();
+        }
+
+        protected virtual void OnValidate()
+        {
+            // Trigger type changes.
+            TargetType = _targetType;
+        }
+
+        #endregion
+
+        #region Private Methods
+
+        private void DestroyTarget()
+        {
+            // This object may be destroyed after the manager.
+            var mgr = OvrAvatarManager.Instance;
+            if (mgr)
+            {
+                mgr.GazeTargetManager.RemoveTarget(this);
+            }
+            _targetCreated = false;
+        }
+
+        private void CreateTarget()
+        {
+            var newTarget = Target;
+            newTarget.type = _targetType;
+            newTarget.worldPosition = NativePosition;
+            Target = newTarget;
+            _targetCreated = OvrAvatarManager.Instance.GazeTargetManager.AddTarget(this);
+        }
+
+        private void OnTypeChanged()
+        {
+            // Targets have to be recreated when changing type
+            if (_targetCreated)
+            {
+                DestroyTarget();
+                CreateTarget();
+            }
+        }
+
+
+        #endregion
+
+        internal void MarkClean()
+        {
+            _transform.hasChanged = false;
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarGazeTarget.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarGazeTarget.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..3ca25a3bb6fef8d909cb8b4d1d8314075e5165d0
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarGazeTarget.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 2ca1c3c8e8ea8844689c9e255d729f0e
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 100
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarGazeTargetManager.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarGazeTargetManager.cs
new file mode 100644
index 0000000000000000000000000000000000000000..431f0ef3b63bfd2064f989c78d6b41092579fc06
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarGazeTargetManager.cs
@@ -0,0 +1,119 @@
+using System;
+using System.Collections.Generic;
+
+/// @file OvrAvatarGazeTargetManager.cs
+
+namespace Oculus.Avatar2
+{
+    ///
+    /// Handles the set of gaze targets the avatar can look at.
+    /// Typically this is another avatars head, avatar hands
+    /// or an object in the scene. This class collects the
+    /// positions of each of these targets every frame to
+    /// calculate what the avatar should look at.
+    ///
+    /// A gaze target is added to this set when an instance
+    /// of @ref OvrAvatarGazeTarget is created. Typically this
+    /// is when the @ref OvrAvatarGazeTarget component is enabled.
+    /// The gaze target is removed from the set when the
+    /// component is disabled.
+    ///
+    /// @see OvrAvatarGazeTarget
+    /// @see OvrAvatarEntity.GetGazePosition
+    ///
+    public class OvrAvatarGazeTargetManager
+    {
+        private readonly List<OvrAvatarGazeTarget> _gazeTargets = new List<OvrAvatarGazeTarget>();
+        private CAPI.ovrAvatar2GazeTarget[] _targetsToUpdate;
+
+        internal bool AddTarget(OvrAvatarGazeTarget target)
+        {
+            var created = CreateGazeTarget(target.Target);
+            if (created)
+            {
+                _gazeTargets.Add(target);
+            }
+
+            return created;
+        }
+
+        internal void RemoveTarget(OvrAvatarGazeTarget target)
+        {
+            var index = _gazeTargets.IndexOf(target);
+            if (index >= 0)
+            {
+                var lastIndex = _gazeTargets.Count - 1;
+                _gazeTargets[index] = _gazeTargets[lastIndex];
+                _gazeTargets.RemoveAt(lastIndex);
+            }
+
+            DestroyGazeTarget(target.Target);
+        }
+
+        internal void Update()
+        {
+            if (_targetsToUpdate == null || _targetsToUpdate.Length < _gazeTargets.Capacity)
+            {
+                _targetsToUpdate = new CAPI.ovrAvatar2GazeTarget[_gazeTargets.Capacity];
+            }
+
+            var updateCount = 0;
+            foreach (var target in _gazeTargets)
+            {
+                if (target.Dirty)
+                {
+                    target.MarkClean();
+                    _targetsToUpdate[updateCount] = target.Target;
+                    _targetsToUpdate[updateCount].worldPosition =
+                       target.NativePosition;
+                    updateCount++;
+                }
+            }
+
+            if (updateCount > 0)
+            {
+                unsafe
+                {
+                    fixed (CAPI.ovrAvatar2GazeTarget* targets = _targetsToUpdate)
+                    {
+                        var result = CAPI.ovrAvatar2Behavior_UpdateGazeTargetPositions(targets, updateCount);
+                        if (result != CAPI.ovrAvatar2Result.Success)
+                        {
+                            OvrAvatarLog.LogWarning("Could not update gazeTargets");
+                        }
+                    }
+                }
+            }
+        }
+
+        private static bool CreateGazeTarget(CAPI.ovrAvatar2GazeTarget target)
+        {
+            unsafe
+            {
+                CAPI.ovrAvatar2GazeTarget* pTarget = &target;
+                var result = CAPI.ovrAvatar2Behavior_CreateGazeTargets((IntPtr)pTarget, 1);
+                if (result != CAPI.ovrAvatar2Result.Success)
+                {
+                    OvrAvatarLog.LogWarning("Could not create gaze target");
+                }
+
+                return result == CAPI.ovrAvatar2Result.Success;
+            }
+        }
+
+        private static bool DestroyGazeTarget(CAPI.ovrAvatar2GazeTarget target)
+        {
+            unsafe
+            {
+                CAPI.ovrAvatar2GazeTarget* pTarget = &target;
+                var result = CAPI.ovrAvatar2Behavior_DestroyGazeTargets((IntPtr)pTarget, 1);
+                if (result != CAPI.ovrAvatar2Result.Success)
+                {
+                    OvrAvatarLog.LogWarning("Could not destroy gaze target");
+                }
+
+                return result == CAPI.ovrAvatar2Result.Success;
+            }
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarGazeTargetManager.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarGazeTargetManager.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..343a5d7d0d962df52adc42233444a0f48313278a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarGazeTargetManager.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 2c6a496cd8d36de439fff21335f7622f
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarInputControlDelegate.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarInputControlDelegate.cs
new file mode 100644
index 0000000000000000000000000000000000000000..f2bf261e308cf240c931378fe5e8631629abe9c5
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarInputControlDelegate.cs
@@ -0,0 +1,20 @@
+namespace Oculus.Avatar2
+{
+    /**
+     * Base class for setting input controls on an avatar entity.
+     */
+    public abstract class OvrAvatarInputControlDelegate : IOvrAvatarInputControlDelegate
+    {
+        public abstract bool GetInputControlState(out OvrAvatarInputControlState inputControlState);
+
+        /**
+         * Gets the controller type.
+         * @returns which type of controller being used (Rift, Touch, etc._)
+         * @see CAPI.ovrAvatar2ControllerType
+         */
+        protected virtual CAPI.ovrAvatar2ControllerType GetControllerType()
+        {
+            return OvrAvatarManager.Instance.ControllerType;
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarInputControlDelegate.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarInputControlDelegate.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..7ed1c696e839ceef64b82e1e90468c66d3485ee2
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarInputControlDelegate.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: c2ffd924ce38ea546b0d4a281cc8fd31
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarInputControlState.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarInputControlState.cs
new file mode 100644
index 0000000000000000000000000000000000000000..3052b26c2adab3d5b205e83d7cc926473c8dd58d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarInputControlState.cs
@@ -0,0 +1,65 @@
+using System;
+
+namespace Oculus.Avatar2
+{
+    public struct OvrAvatarControllerState
+    {
+        public CAPI.ovrAvatar2Button buttonMask;
+        public CAPI.ovrAvatar2Touch touchMask;
+        public float joystickX;
+        public float joystickY;
+        public float indexTrigger;
+        public float handTrigger;
+        public bool isActive;
+        public bool isVisible;
+    }
+
+    public struct OvrAvatarInputControlState
+    {
+        public CAPI.ovrAvatar2ControllerType type;
+        public OvrAvatarControllerState leftControllerState;
+        public OvrAvatarControllerState rightControllerState;
+
+        #region Native Conversions
+        private CAPI.ovrAvatar2ControllerState ToNative (in OvrAvatarControllerState controller)
+        {
+            return new CAPI.ovrAvatar2ControllerState
+            {
+                buttonMask = controller.buttonMask,
+                touchMask = controller.touchMask,
+                joystickX = controller.joystickX,
+                joystickY = controller.joystickY,
+                indexTrigger = controller.indexTrigger,
+                handTrigger = controller.handTrigger,
+            };
+        }
+
+        internal CAPI.ovrAvatar2InputControlState ToNative()
+        {
+            return new CAPI.ovrAvatar2InputControlState
+            {
+                type = type,
+                leftControllerState = ToNative(leftControllerState),
+                rightControllerState = ToNative(rightControllerState),
+            };
+        }
+
+        private void FromNative (in CAPI.ovrAvatar2ControllerState nativeController, ref OvrAvatarControllerState controller)
+        {
+            controller.buttonMask = nativeController.buttonMask;
+            controller.touchMask = nativeController.touchMask;
+            controller.joystickX = nativeController.joystickX;
+            controller.joystickY = nativeController.joystickY;
+            controller.indexTrigger = nativeController.indexTrigger;
+            controller.handTrigger = nativeController.handTrigger;
+        }
+
+        internal void FromNative(ref CAPI.ovrAvatar2InputControlState native)
+        {
+            type = native.type;
+            FromNative(native.leftControllerState, ref leftControllerState);
+            FromNative(native.rightControllerState, ref rightControllerState);
+        }
+        #endregion
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarInputControlState.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarInputControlState.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..c972e4f1acc0ccdce4e701dcfcf15042c74acc5d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarInputControlState.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 899e61386dca25343938b4083519ffff
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarInputManager.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarInputManager.cs
new file mode 100644
index 0000000000000000000000000000000000000000..78463f1003cca60c3814f2986f9e1b872f1a6366
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarInputManager.cs
@@ -0,0 +1,121 @@
+using UnityEngine;
+
+/// @file OvrAvatarInputManager.cs
+
+namespace Oculus.Avatar2
+{
+    /**
+     * Base class for setting tracking input on an avatar entity.
+     * Allows you to select which components to use for tracking in
+     * the Unity Editor.
+     * @see OvrAvatarBodyTrackingBehavior
+     */
+    public abstract class OvrAvatarInputManager : OvrAvatarBodyTrackingBehavior
+    {
+        private OvrAvatarBodyTrackingContext _bodyTracking = null;
+
+        /**
+         * The current body tracking implementation.
+         * @see OvrAvatarBodyTrackingContext
+         */
+        public OvrAvatarBodyTrackingContext BodyTracking
+        {
+            private set => _bodyTracking = value;
+            get
+            {
+                InitializeTracking();
+                return _bodyTracking;
+            }
+        }
+
+        [SerializeField, Tooltip("This adds one frame of latency to the body solver. In exchange saves some main thread CPU time")]
+        private bool _useAsyncBodySolver = false;
+
+        private OvrAvatarBodyTrackingContextBase _trackingContext;
+
+        /**
+         * The current body tracking implementation.
+         * Gets the body tracking information from sensors and applies it to the skeleton.
+         * Accessing the body tracking context also initializes body tracking.
+         * @see OvrAvatarBodyTrackingContextBase
+         */
+        public override OvrAvatarBodyTrackingContextBase TrackingContext
+        {
+            get
+            {
+                InitializeTracking();
+
+                return _trackingContext;
+            }
+        }
+
+        protected void InitializeTracking()
+        {
+            if (_trackingContext == null && OvrAvatarManager.initialized)
+            {
+                if (_bodyTracking == null)
+                {
+                    _bodyTracking = OvrAvatarBodyTrackingContext.Create(_useAsyncBodySolver);
+                }
+                _trackingContext = _bodyTracking;
+            }
+        }
+
+        protected void OnDestroy()
+        {
+            OnDestroyCalled();
+
+            BodyTracking?.Dispose();
+            BodyTracking = null;
+        }
+
+        /**
+         * Called from *OnDestroy* to provide a way
+         * for subclasses to intercept destruction and
+         * do their own cleanup.
+         * The default implementation does nothing.
+         */
+        protected virtual void OnDestroyCalled()
+        {
+        }
+
+        /**
+         * Returns the position and orientation of the headset
+         * and controllers in T-Pose.
+         * Useful for initial calibration or testing.
+         * @see CAPI.ovrAvatar2InputTransforms
+         */
+        protected unsafe OvrAvatarInputTrackingState GetTPose()
+        {
+            var transforms = new OvrAvatarInputTrackingState
+            {
+                headsetActive = true,
+                leftControllerActive = true,
+                rightControllerActive = true,
+                leftControllerVisible = true,
+                rightControllerVisible = true,
+                headset =
+                {
+                    position = new CAPI.ovrAvatar2Vector3f() {x = 0, y = 1.6169891f, z = 0},
+                    orientation = new CAPI.ovrAvatar2Quatf() {x = 0, y = 0, z = 0, w = -1f},
+                    scale = new CAPI.ovrAvatar2Vector3f() {x = 1f, y = 1f, z = 1f}
+                },
+                leftController =
+                {
+                    position = new CAPI.ovrAvatar2Vector3f() {x = -0.79f, y = 1.37f, z = 0},
+                    orientation =
+                        new CAPI.ovrAvatar2Quatf() {x = 0, y = -0.70710678118f, z = 0, w = -0.70710678118f},
+                    scale = new CAPI.ovrAvatar2Vector3f() {x = 1f, y = 1f, z = 1f}
+                },
+                rightController =
+                {
+                    position = new CAPI.ovrAvatar2Vector3f() {x = 0.79f, y = 1.37f, z = 0},
+                    orientation = new CAPI.ovrAvatar2Quatf() {x = 0, y = 0.70710678118f, z = 0, w = -0.70710678118f},
+                    scale = new CAPI.ovrAvatar2Vector3f() {x = 1f, y = 1f, z = 1f}
+                }
+            };
+
+            return transforms;
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarInputManager.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarInputManager.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..de1190c5e27a5108a914a437dee3a45af3df4017
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarInputManager.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: afb01ff29d420994cad34f671d9b73de
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarInputTrackingContextBase.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarInputTrackingContextBase.cs
new file mode 100644
index 0000000000000000000000000000000000000000..60aa2eaaa150a482fec4060b517844e0f8144926
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarInputTrackingContextBase.cs
@@ -0,0 +1,47 @@
+using AOT;
+using System;
+
+namespace Oculus.Avatar2
+{
+    public abstract class OvrAvatarInputTrackingContextBase : OvrAvatarCallbackContextBase
+    {
+        private OvrAvatarInputTrackingState _inputTrackingState = new OvrAvatarInputTrackingState();
+        internal CAPI.ovrAvatar2InputTrackingContext Context { get; }
+
+        protected OvrAvatarInputTrackingContextBase()
+        {
+            var context = new CAPI.ovrAvatar2InputTrackingContext
+            {
+                context = new IntPtr(id),
+                inputTrackingCallback = InputTrackingCallback,
+            };
+            Context = context;
+        }
+
+        protected abstract bool GetInputTrackingState(out OvrAvatarInputTrackingState inputTrackingState);
+
+        [MonoPInvokeCallback(typeof(CAPI.InputTrackingCallback))]
+        private static bool InputTrackingCallback(out CAPI.ovrAvatar2InputTrackingState inputTrackingState, IntPtr userContext)
+        {
+            try
+            {
+                var context = GetInstance<OvrAvatarInputTrackingContextBase>(userContext);
+                if (context != null)
+                {
+                    if (context.GetInputTrackingState(out context._inputTrackingState))
+                    {
+                        inputTrackingState = context._inputTrackingState.ToNative();
+                        return true;
+                    }
+                }
+            }
+            catch (Exception e)
+            {
+                OvrAvatarLog.LogError(e.ToString());
+            }
+
+            inputTrackingState = default;
+            return false;
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarInputTrackingContextBase.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarInputTrackingContextBase.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..deb115d8438b63ab7df1d4fbf3ecfd9976f3b1c6
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarInputTrackingContextBase.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 19c5a3ff9ccdf714289d18c6996a5c6d
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarInputTrackingDelegate.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarInputTrackingDelegate.cs
new file mode 100644
index 0000000000000000000000000000000000000000..b410bc49c6d5444e68d1ccb3a33d74c74af2e7be
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarInputTrackingDelegate.cs
@@ -0,0 +1,146 @@
+using UnityEngine;
+
+namespace Oculus.Avatar2
+{
+    /**
+     * Base class for setting input tracking on an avatar entity.
+     */
+    public abstract class OvrAvatarInputTrackingDelegate : IOvrAvatarInputTrackingDelegate
+    {
+        // TODO: These should likely be reduced, but we need better data to do so unilaterally. Apps can use whatever values they please.
+        // NOTE: 15% longer than 1/2 Manute Bol's armspan, this is still past the distance where retargeting goes insane
+        public const float DEFAULT_CLAMP_HEAD_TO_HAND_DISTANCE = 1.5f;
+        // NOTE: Basically arbitrary but larger than clamp distance
+        public const float DEFAULT_DISABLE_HEAD_TO_HAND_DISTANCE = 1.75f;
+
+        // Squared thresholds for comparsion w/out Sqrt
+        private const float DEFAULT_CLAMP_DIST_SQUARED = DEFAULT_CLAMP_HEAD_TO_HAND_DISTANCE * DEFAULT_CLAMP_HEAD_TO_HAND_DISTANCE;
+        private const float DEFAULT_DISABLE_DIST_SQUARED = DEFAULT_DISABLE_HEAD_TO_HAND_DISTANCE * DEFAULT_DISABLE_HEAD_TO_HAND_DISTANCE;
+
+        // get the raw unfiltered, unconverted input transforms
+        public abstract bool GetRawInputTrackingState(out OvrAvatarInputTrackingState inputTrackingState);
+
+        public bool GetInputTrackingState(out OvrAvatarInputTrackingState inputTrackingState)
+        {
+            if (!GetRawInputTrackingState(out inputTrackingState))
+            {
+                return false;
+            }
+
+            ConvertTransform(ref inputTrackingState.headset);
+            ConvertTransform(ref inputTrackingState.leftController);
+            ConvertTransform(ref inputTrackingState.rightController);
+
+            FilterInput(ref inputTrackingState);
+
+            return true;
+        }
+
+        private static void ConvertTransform(ref CAPI.ovrAvatar2Transform transform)
+        {
+            // Convert from Unity coordinate space to the SDK coordinate space
+            transform = transform.ConvertSpace();
+        }
+
+        /**
+         * Selects how controller tracking is filtered.
+         * You can specify the mimimum distance between hands and head,
+         * and what to do about distant or inactive controllers.
+         * Subclasses can override to change clamp/disable distances, or simply skip this filtering.
+         * @param inputState    which controllers to affect.
+         * @param inputTransforms has the current controller position & orientation on entry,
+         *                        gets the updated position & orientation on exit.
+         * @see CAPI.ovrAvatar2InputState
+         * @see CAPI.ovrAvatar2InputTransforms
+         */
+        protected virtual void FilterInput(ref OvrAvatarInputTrackingState inputTracking)
+        {
+            ClampHandPositions(ref inputTracking, out var handDistances, DEFAULT_CLAMP_DIST_SQUARED);
+            DisableDistantControllers(ref inputTracking, in handDistances, DEFAULT_DISABLE_DIST_SQUARED);
+            HideInactiveControllers(ref inputTracking);
+        }
+
+        /**
+         * @class InputHandDistances
+         * Contains hand distances squared for each controller.
+         */
+        protected readonly struct InputHandDistances
+        {
+            public InputHandDistances(float lSquared, float rSquared) { leftSquared = lSquared; rightSquared = rSquared; }
+
+            public readonly float leftSquared;
+            public readonly float rightSquared;
+        }
+
+        /**
+         * Clamp hand distance from head to be within a specified distance.
+         * @param tracking             input position & orientation of each controller.
+         * @param distances            gets the squared distance of each hand on output.
+         * @param clampDistanceSquared maximum distance between hands and head squared.
+         * @see FilterInput
+         * @see CAPI.ovrAvatar2InputTransforms
+         * @see InputHandDistances
+         */
+        protected static void ClampHandPositions(ref OvrAvatarInputTrackingState tracking, out InputHandDistances distances, float clampDistanceSquared)
+        {
+            ref readonly var hmdPos = ref tracking.headset.position;
+            ref var leftHandPos = ref tracking.leftController.position;
+            ref var rightHandPos = ref tracking.rightController.position;
+
+            var leftHandDist = ClampHand(in hmdPos, ref leftHandPos, clampDistanceSquared);
+            var rightHandDist = ClampHand(in hmdPos, ref rightHandPos, clampDistanceSquared);
+
+            distances = new InputHandDistances(leftHandDist, rightHandDist);
+        }
+
+        /**
+         * Disable controllers further than the specified distance.
+         * @param inputControl           which controllers to affect.
+         * @param handDistances          squared distance of controller.
+         * @param disableDistanceSquared distance beyond which controller is disabled squared.
+         * @see FilterInput
+         * @see CAPI.ovrAvatar2InputState
+         * @see InputHandDistances
+         */
+        protected static void DisableDistantControllers(ref OvrAvatarInputTrackingState inputTracking, in InputHandDistances handDistances, float disableDistanceSquared)
+        {
+            DisableDistantController(ref inputTracking.leftControllerActive, handDistances.leftSquared, disableDistanceSquared);
+            DisableDistantController(ref inputTracking.rightControllerActive, handDistances.rightSquared, disableDistanceSquared);
+        }
+
+        protected static float ClampHand(in CAPI.ovrAvatar2Vector3f hmdPos, ref CAPI.ovrAvatar2Vector3f handPos, float clampDistanceSquared)
+        {
+            OvrAvatarLog.Assert(clampDistanceSquared >= 0.0f);
+
+            var hmdToHand = handPos - hmdPos;
+            float hmdToHandLenSquared = hmdToHand.LengthSquared;
+            if (hmdToHandLenSquared > clampDistanceSquared)
+            {
+                handPos = hmdPos + (hmdToHand * Mathf.Sqrt(clampDistanceSquared / hmdToHandLenSquared));
+            }
+            return hmdToHandLenSquared;
+        }
+
+        protected static void DisableDistantController(ref bool controllerActive, float distanceSquared, float disableThresholdSquared)
+        {
+            OvrAvatarLog.Assert(disableThresholdSquared >= 0.0f);
+
+            if (distanceSquared > disableThresholdSquared)
+            {
+                controllerActive = false;
+            }
+        }
+
+        /**
+         * Hide rendering of inactive controllers.
+         * @param inputControl   which controllers to affect.
+         * @see FilterInput
+         * @see CAPI.ovrAvatar2InputState
+         */
+        protected static void HideInactiveControllers(ref OvrAvatarInputTrackingState inputTracking)
+        {
+            inputTracking.leftControllerVisible &= inputTracking.leftControllerActive;
+            inputTracking.rightControllerVisible &= inputTracking.rightControllerActive;
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarInputTrackingDelegate.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarInputTrackingDelegate.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..beaa5248d07aaab6010d95524a32c219e0d90354
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarInputTrackingDelegate.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: f97ce58e40f77a84684e7d888e082552
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarInputTrackingState.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarInputTrackingState.cs
new file mode 100644
index 0000000000000000000000000000000000000000..4be712b7079e36a0efe606f6dc60b32de310321d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarInputTrackingState.cs
@@ -0,0 +1,45 @@
+using System;
+
+namespace Oculus.Avatar2
+{
+    public struct OvrAvatarInputTrackingState
+    {
+        public bool headsetActive;
+        public bool leftControllerActive;
+        public bool rightControllerActive;
+        public bool leftControllerVisible;
+        public bool rightControllerVisible;
+        public CAPI.ovrAvatar2Transform headset;
+        public CAPI.ovrAvatar2Transform leftController;
+        public CAPI.ovrAvatar2Transform rightController;
+
+        #region Native Conversions
+        internal CAPI.ovrAvatar2InputTrackingState ToNative()
+        {
+            return new CAPI.ovrAvatar2InputTrackingState
+            {
+                headsetActive = headsetActive,
+                leftControllerActive = leftControllerActive,
+                rightControllerActive = rightControllerActive,
+                leftControllerVisible = leftControllerVisible,
+                rightControllerVisible = rightControllerVisible,
+                headset = headset,
+                leftController = leftController,
+                rightController = rightController,
+            };
+        }
+
+        internal void FromNative(ref CAPI.ovrAvatar2InputTrackingState native)
+        {
+            headsetActive = native.headsetActive;
+            leftControllerActive = native.leftControllerActive;
+            rightControllerActive = native.rightControllerActive;
+            leftControllerVisible = native.leftControllerVisible;
+            rightControllerVisible = native.rightControllerVisible;
+            headset = native.headset;
+            leftController = native.leftController;
+            rightController = native.rightController;
+        }
+        #endregion
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarInputTrackingState.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarInputTrackingState.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..efe7e7cb3c33a75c0b5b266ff905458afc694397
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarInputTrackingState.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 4ffae6fd4af1edb4192126b38721c534
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarLipSyncBehavior.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarLipSyncBehavior.cs
new file mode 100644
index 0000000000000000000000000000000000000000..27b6f5092bb326d9e576cb8f815d475f86cb0563
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarLipSyncBehavior.cs
@@ -0,0 +1,19 @@
+using UnityEngine;
+/// @file OvrAvatarLipSyncBehavior.cs
+
+namespace Oculus.Avatar2
+{
+    /**
+     * MonoBehavior which holds a lip sync context so it can be referenced in the inspector.
+     * @see OvrAvatarLipSyncContextBase
+     */
+    public abstract class OvrAvatarLipSyncBehavior : MonoBehaviour
+    {
+        /**
+         * Get the lip sync implementation.
+         * Subclasses must implement a getter for this property.
+         * @see OvrAvatarLipSyncContextBase
+         */
+        public abstract OvrAvatarLipSyncContextBase LipSyncContext { get; }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarLipSyncBehavior.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarLipSyncBehavior.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..765a7643a929188a3fcf9d03f166491155f6f713
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarLipSyncBehavior.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: d6dc82537219e284e8c17f11381d63e0
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarLipSyncContext.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarLipSyncContext.cs
new file mode 100644
index 0000000000000000000000000000000000000000..6f28d5d9171ea16069d6add49092e5982c3f181d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarLipSyncContext.cs
@@ -0,0 +1,211 @@
+using System;
+using UnityEngine;
+using UnityEngine.Serialization;
+
+/**
+ * @file OvrAvatarLipSyncContext.cs
+ */
+
+namespace Oculus.Avatar2
+{
+
+    /// Indicates the audio source to drive lip sync.
+    public enum LipSyncAudioSourceType
+    {
+        /// Audio forwarding is disabled
+        None,
+
+        /// Audio is captured from the audio source on this game object
+        AudioSource,
+
+        /// @ref ProcessAudioSamples must be called manually to forward audio
+        Manual,
+    }
+
+    public class OvrAvatarLipSyncContext : OvrAvatarLipSyncBehavior
+    {
+        private const string logScope = "lipSync";
+        private const float bufferSizeRatio = 0.4f;
+
+        [Header("Audio Settings")]
+        [SerializeField]
+        protected LipSyncAudioSourceType _audioSourceType = LipSyncAudioSourceType.AudioSource;
+
+        /**
+         * Enables or disables audio capture.
+         * If set to true, no audio will play back to the speakers.
+         */
+        [Tooltip("If captured, no audio will play back to the speakers.")]
+        [FormerlySerializedAs("_captureAudio")]
+        public bool CaptureAudio = true;
+
+        [SerializeField]
+        private CAPI.ovrAvatar2LipSyncMode _mode = CAPI.ovrAvatar2LipSyncMode.Original;
+
+        [Range(0.0f, 100.0f)]
+        [SerializeField]
+        private int _smoothing;
+
+        [SerializeField]
+        private int _audioSampleRate = 48000;
+
+        protected OvrAvatarVisemeContext _visemeContext;
+
+        /**
+         * Controls the rate at which audio is sampled.
+         */
+        public int AudioSampleRate
+        {
+            get { return _audioSampleRate; }
+            set
+            {
+                if (value != _audioSampleRate)
+                {
+                    _audioSampleRate = value;
+                    _visemeContext?.SetSampleRate((UInt32)_audioSampleRate, (UInt32)(_audioSampleRate * bufferSizeRatio));
+                }
+            }
+        }
+
+        /**
+         * Establishes the method used for lip sync.
+         * @see CAPI.ovrAvatar2LipSyncMode
+         */
+        public CAPI.ovrAvatar2LipSyncMode Mode
+        {
+            get => _mode;
+            set
+            {
+                if (value != _mode)
+                {
+                    _mode = value;
+                    _visemeContext?.SetMode(_mode);
+                }
+            }
+        }
+
+        public override OvrAvatarLipSyncContextBase LipSyncContext
+        {
+            get
+            {
+                CreateVisemeContext();
+
+                return _visemeContext;
+            }
+        }
+
+        // Thread-safe check of this.enabled
+        protected bool _active;
+
+        // Core Unity Functions
+
+        private void Start()
+        {
+            SetAudioSourceType(_audioSourceType);
+        }
+
+        protected virtual void OnAudioFilterRead(float[] data, int channels)
+        {
+            if (_audioSourceType == LipSyncAudioSourceType.AudioSource)
+            {
+                ProcessAudioSamples(data, channels);
+            }
+        }
+
+        private void OnEnable()
+        {
+            _active = true;
+            CreateVisemeContext();
+        }
+
+        private void OnDisable()
+        {
+            _active = false;
+        }
+
+        private void OnDestroy()
+        {
+            _visemeContext?.Dispose();
+            _visemeContext = null;
+        }
+
+        private void OnValidate()
+        {
+            SetSmoothing(_smoothing);
+        }
+
+        // Public Functions
+
+        public void SetSmoothing(int smoothing)
+        {
+            _smoothing = Math.Max(Math.Min(smoothing, 100), 0);
+            _visemeContext?.SetSmoothing(_smoothing);
+        }
+
+        public void SetAudioSourceType(LipSyncAudioSourceType newType)
+        {
+            _audioSourceType = newType;
+
+            OvrAvatarLog.LogDebug($"Setting audio source type to {_audioSourceType}", logScope);
+
+            switch (_audioSourceType)
+            {
+                case LipSyncAudioSourceType.None:
+                    enabled = false;
+                    break;
+                case LipSyncAudioSourceType.AudioSource:
+                    enabled = true;
+                    break;
+                case LipSyncAudioSourceType.Manual:
+                    enabled = true;
+                    break;
+                default:
+                    break;
+            }
+        }
+
+        public virtual void ProcessAudioSamples(float[] data, int channels)
+        {
+            if (!_active || !OvrAvatarManager.initialized) return;
+
+            _visemeContext?.FeedAudio(data, channels);
+
+            if (CaptureAudio)
+            {
+                // Prevent other mixers or output from also using this audio input
+                Array.Clear(data, 0, data.Length);
+            }
+        }
+
+        public virtual void ProcessAudioSamples(short[] data, int channels)
+        {
+            if (!_active || !OvrAvatarManager.initialized) return;
+
+            _visemeContext?.FeedAudio(data, channels);
+
+            if (CaptureAudio)
+            {
+                // Prevent other mixers or output from also using this audio input
+                Array.Clear(data, 0, data.Length);
+            }
+        }
+
+        #region Private Methods
+
+        private void CreateVisemeContext()
+        {
+            if (_visemeContext == null && OvrAvatarManager.initialized)
+            {
+                _visemeContext = new OvrAvatarVisemeContext(new CAPI.ovrAvatar2LipSyncProviderConfig
+                {
+                    mode = _mode,
+                    audioBufferSize = (UInt32)(_audioSampleRate * bufferSizeRatio),
+                    audioSampleRate = (UInt32)_audioSampleRate
+                });
+                SetSmoothing(_smoothing);
+            }
+        }
+
+        #endregion
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarLipSyncContext.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarLipSyncContext.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..df6a3f3f0f84c9a66da32ce4c2b4eb318d1d03aa
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarLipSyncContext.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a509dbf839e0e4044a7affab5b5a3e55
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarLipSyncContextBase.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarLipSyncContextBase.cs
new file mode 100644
index 0000000000000000000000000000000000000000..3f4a637d7966a898196a542dd6da83147e043b11
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarLipSyncContextBase.cs
@@ -0,0 +1,71 @@
+using AOT;
+using System;
+
+/// @file OvrAvatarLipSyncContextBase.cs
+
+namespace Oculus.Avatar2
+{
+    /**
+     * Base class for C# code to drive lipsync data for avatar entites.
+     * @see OvrAvatarEntity.SetLipSync
+     */
+    public abstract class OvrAvatarLipSyncContextBase : OvrAvatarCallbackContextBase
+    {
+        // Cache the managed representation to reduce GC allocations
+        private readonly OvrAvatarLipSyncState _lipsyncState = new OvrAvatarLipSyncState();
+        internal CAPI.ovrAvatar2LipSyncContext DataContext { get; }
+
+        protected OvrAvatarLipSyncContextBase()
+        {
+            var dataContext = new CAPI.ovrAvatar2LipSyncContext();
+            dataContext.context = new IntPtr(id);
+            dataContext.lipSyncCallback = LipSyncCallback;
+
+            DataContext = dataContext;
+        }
+
+        /**
+         * Gets the lip sync state from the native lipsync implementation.
+         * The lip sync state contains the weights for the visemes to
+         * make the lip expression.
+         * Lip sync implementations must override this function to
+         * convert the native lip sync state into a form usable by Unity.
+         * @param lipsyncState  where to store the generated viseme weights.
+         * @see OvrAvatarLipSyncState
+         */
+        protected abstract bool GetLipSyncState(OvrAvatarLipSyncState lipsyncState);
+
+        [MonoPInvokeCallback(typeof(CAPI.LipSyncCallback))]
+        private static bool LipSyncCallback(out CAPI.ovrAvatar2LipSyncState lipsyncState, IntPtr userContext)
+        {
+            try
+            {
+                var context = GetInstance<OvrAvatarLipSyncContextBase>(userContext);
+                if (context != null)
+                {
+                    if (context.GetLipSyncState(context._lipsyncState))
+                    {
+                        lipsyncState = context._lipsyncState.ToNative();
+                        return true;
+                    }
+                }
+            }
+            catch (Exception e)
+            {
+                OvrAvatarLog.LogError(e.ToString());
+            }
+
+            lipsyncState = new CAPI.ovrAvatar2LipSyncState();
+            return false;
+        }
+
+        public OvrAvatarLipSyncState DebugQueryLipSyncState()
+        {
+            if (GetLipSyncState(_lipsyncState))
+            {
+                return _lipsyncState;
+            }
+            return null;
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarLipSyncContextBase.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarLipSyncContextBase.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..d87dae5c62ab2980730ca85c23815cdc953f5bba
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarLipSyncContextBase.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 45b2c010508c451193a93e90aa2c7bca
+timeCreated: 1605837516
\ No newline at end of file
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarLipSyncState.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarLipSyncState.cs
new file mode 100644
index 0000000000000000000000000000000000000000..646209fa4e952f412672a31871fc0d4bba6fa7d2
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarLipSyncState.cs
@@ -0,0 +1,65 @@
+/// @file OvrAvatarLipSyncState.cs
+
+namespace Oculus.Avatar2
+{
+    /**
+     * Collects the viseme weights for avatar lip motion and
+     * converts to and from C# and C++ native versions.
+     * @see ovrAvatar2Viseme
+     */
+    public sealed class OvrAvatarLipSyncState
+    {
+        /**
+         * How hard the avatar is laughing???
+         */
+        public float laughterScore;
+
+        /**
+         * Array of viseme weights.
+         * @see CAPI.ovrAvatar2Viseme
+         */
+        public readonly float[] visemes = new float[(int)CAPI.ovrAvatar2Viseme.Count];
+
+        #region Native Conversions
+        /**
+         * Creates a new native lip tracking state from this C# instance.
+         * @see CAPI.ovrAvatar2LipSyncState
+         * @see FromNative
+         */
+        internal CAPI.ovrAvatar2LipSyncState ToNative()
+        {
+            var native = new CAPI.ovrAvatar2LipSyncState
+            {
+                laughterScore = laughterScore,
+            };
+            unsafe
+            {
+                for (var i = 0; i < visemes.Length; i++)
+                {
+                    native.visemes[i] = visemes[i];
+                }
+            }
+
+            return native;
+        }
+
+        /**
+         * Copies the given native lip tracking state to this C# instance.
+         * @see CAPI.ovrAvatar2LipSyncState
+         * @see ToNative
+         */
+        internal void FromNative(ref CAPI.ovrAvatar2LipSyncState native)
+        {
+            unsafe
+            {
+                for (var i = 0; i < visemes.Length; i++)
+                {
+                    visemes[i] = native.visemes[i];
+                }
+            }
+
+            laughterScore = native.laughterScore;
+        }
+        #endregion
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarLipSyncState.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarLipSyncState.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..8e2d92dece6122d9a57b758c2e07076c26ef655a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarLipSyncState.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 1e89612f4a8642c8a7cc848f326347fc
+timeCreated: 1606784389
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarLog.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarLog.cs
new file mode 100644
index 0000000000000000000000000000000000000000..42b9a4f2f492e93f6fb0d4810cee81618773c396
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarLog.cs
@@ -0,0 +1,303 @@
+// If logs enabled, always enable special case handling for now
+#define OVRAVATAR_HANDLE_SPECIAL_CASE
+
+// Assert throw is only active when every mechanism for asserts is enabled
+// By default, all of these conditions fail
+#if OVRAVATAR_ASSERT_ENABLE_CONDITIONAL && OVRAVATAR_ASSERT_FORCE_ENABLE && UNITY_ASSERTIONS
+#define OVRAVATAR_ASSERT_THROW
+#endif
+
+using System;
+using UnityEngine;
+
+using Conditional = System.Diagnostics.ConditionalAttribute;
+
+namespace Oculus.Avatar2
+{
+
+    public static class OvrAvatarLog
+    {
+        public enum ELogLevel
+        {
+            Verbose,
+            Debug,
+            Info,
+            Warn,
+            Error
+        }
+
+        public static ELogLevel logLevel = ELogLevel.Info;
+
+        public delegate void LogDelegate(ELogLevel level, string scope, string msg);
+
+        public static event LogDelegate CustomLogger;
+
+        public const bool enabled =
+#if !OVRAVATAR_LOG_ENABLE_CONDITIONAL || OVRAVATAR_LOG_FORCE_ENABLE
+            true;
+#else
+            false;
+#endif
+
+        // When Logging Conditional is enabled, logs will default to off
+        // - this enables them when conditionals are active
+        public const string OVRAVATAR_LOG_FORCE_ENABLE = "OVRAVATAR_LOG_FORCE_ENABLE";
+        // Analagous to `OVRAVATAR_LOG_FORCE_ENABLE` but for asserts
+        public const string OVRAVATAR_ASSERT_FORCE_ENABLE = "OVRAVATAR_ASSERT_FORCE_ENABLE";
+
+        internal static ELogLevel GetLogLevel(CAPI.ovrAvatar2LogLevel priority)
+        {
+            switch (priority)
+            {
+                case CAPI.ovrAvatar2LogLevel.Unknown:
+                    return ELogLevel.Info;
+                case CAPI.ovrAvatar2LogLevel.Default:
+                    return ELogLevel.Info;
+                case CAPI.ovrAvatar2LogLevel.Verbose:
+                    return ELogLevel.Verbose;
+                case CAPI.ovrAvatar2LogLevel.Debug:
+                    return ELogLevel.Debug;
+                case CAPI.ovrAvatar2LogLevel.Info:
+                    return ELogLevel.Info;
+                case CAPI.ovrAvatar2LogLevel.Warn:
+                    return ELogLevel.Warn;
+                case CAPI.ovrAvatar2LogLevel.Error:
+                    return ELogLevel.Error;
+                case CAPI.ovrAvatar2LogLevel.Fatal:
+                    return ELogLevel.Error;
+                case CAPI.ovrAvatar2LogLevel.Silent:
+                    return ELogLevel.Verbose;
+                default:
+                    throw new ArgumentOutOfRangeException(nameof(priority), priority, null);
+            }
+        }
+
+        [AOT.MonoPInvokeCallback(typeof(CAPI.LoggingDelegate))]
+        internal static void LogCallBack(CAPI.ovrAvatar2LogLevel priority, string msg, IntPtr context)
+        {
+            if (priority == CAPI.ovrAvatar2LogLevel.Silent) return;
+            bool specialCase = false;
+            HandleSpecialCaseLogs(msg, ref specialCase);
+            if (!specialCase)
+            {
+                Log(GetLogLevel(priority), msg, "native");
+            }
+        }
+
+        private static string GetScopePrefix(string scope)
+        {
+            return String.IsNullOrEmpty(scope) ? "[ovrAvatar2]" : $"[ovrAvatar2 {scope}]";
+        }
+
+#if OVRAVATAR_LOG_ENABLE_CONDITIONAL
+        [Conditional(OVRAVATAR_LOG_FORCE_ENABLE)]
+#endif
+        public static void Log(ELogLevel level, string msg, string scope = "", UnityEngine.Object context = null)
+        {
+            if (CustomLogger != null)
+            {
+                CustomLogger(level, scope, msg);
+            }
+            else if (level >= logLevel)
+            {
+                string prefix = GetScopePrefix(scope);
+                if (level >= ELogLevel.Error)
+                {
+                    Debug.LogError($"{prefix} {msg}", context);
+                }
+                else if (level >= ELogLevel.Warn)
+                {
+                    Debug.LogWarning($"{prefix} {msg}", context);
+                }
+                else if (level >= ELogLevel.Info)
+                {
+                    Debug.Log($"{prefix} {msg}", context);
+                }
+                else if (level >= ELogLevel.Debug)
+                {
+                    Debug.Log($"{prefix}[Debug] {msg}", context);
+                }
+                else
+                {
+                    Debug.Log($"{prefix}[Verbose] {msg}", context);
+                }
+            }
+        }
+
+        [Conditional(OVRAVATAR_LOG_FORCE_ENABLE)]
+#if !OVRAVATAR_LOG_ENABLE_CONDITIONAL
+         [Conditional("DEVELOPMENT_BUILD")
+         , Conditional("UNITY_EDITOR")]
+#endif
+        public static void LogVerbose(string msg, string scope = "", UnityEngine.Object context = null)
+        {
+            Log(ELogLevel.Verbose, msg, scope, context);
+        }
+
+#if OVRAVATAR_LOG_ENABLE_CONDITIONAL
+        [Conditional(OVRAVATAR_LOG_FORCE_ENABLE)]
+#endif
+        public static void LogDebug(string msg, string scope = "", UnityEngine.Object context = null)
+        {
+            Log(ELogLevel.Debug, msg, scope, context);
+        }
+
+#if OVRAVATAR_LOG_ENABLE_CONDITIONAL
+        [Conditional(OVRAVATAR_LOG_FORCE_ENABLE)]
+#endif
+        public static void LogInfo(string msg, string scope = "", UnityEngine.Object context = null)
+        {
+            Log(ELogLevel.Info, msg, scope, context);
+        }
+
+#if OVRAVATAR_LOG_ENABLE_CONDITIONAL
+        [Conditional(OVRAVATAR_LOG_FORCE_ENABLE)]
+#endif
+        public static void LogWarning(string msg, string scope = "", UnityEngine.Object context = null)
+        {
+            Log(ELogLevel.Warn, msg, scope, context);
+        }
+
+#if OVRAVATAR_LOG_ENABLE_CONDITIONAL
+        [Conditional(OVRAVATAR_LOG_FORCE_ENABLE)]
+#endif
+        public static void LogError(string msg, string scope = "", UnityEngine.Object context = null)
+        {
+            Log(ELogLevel.Error, msg, scope, context);
+        }
+
+#if OVRAVATAR_LOG_ENABLE_CONDITIONAL
+        [Conditional(OVRAVATAR_LOG_FORCE_ENABLE)]
+#endif
+        public static void LogException(string operationName, Exception exception, string scope = "", UnityEngine.Object context = null)
+        {
+            LogError($"Exception during {operationName} - {exception}", scope, context);
+        }
+
+        public const string DEFAULT_ASSERT_MESSAGE = "condition false";
+        public const string DEFAULT_ASSERT_SCOPE = "OvrAssert";
+#if OVRAVATAR_LOG_ENABLE_CONDITIONAL
+        [Conditional(OVRAVATAR_LOG_FORCE_ENABLE)]
+#endif
+        public static void LogAssert(string message = DEFAULT_ASSERT_MESSAGE, string scope = DEFAULT_ASSERT_SCOPE, UnityEngine.Object context = null)
+        {
+            LogError($"Assertion failed: {message ?? DEFAULT_ASSERT_MESSAGE}", scope ?? DEFAULT_ASSERT_SCOPE, context);
+            Debug.Assert(false, $"ASSERT FAILED - [ovrAvatar2 {scope}] - {message}", context);
+
+            // Do not enable OVRAVATAR_ASSERT_THROW unless you want failure (ie: testing)
+            // - This will lead to catastrophic failure in many places in some applications
+            // -- In theory, those are the real logical failures.
+#if OVRAVATAR_ASSERT_THROW
+            throw new InvalidOperationException(message);
+#endif
+        }
+
+#if OVRAVATAR_ASSERT_ENABLE_CONDITIONAL || !UNITY_ASSERTIONS
+        [Conditional(OVRAVATAR_ASSERT_FORCE_ENABLE)]
+#endif
+        internal static void Assert(bool condition, string scope = DEFAULT_ASSERT_SCOPE, UnityEngine.Object context = null)
+        {
+            AssertConstMessage(condition, "condition false", scope, context);
+        }
+
+#if OVRAVATAR_ASSERT_ENABLE_CONDITIONAL || !UNITY_ASSERTIONS
+        [Conditional(OVRAVATAR_ASSERT_FORCE_ENABLE)]
+#endif
+        internal static void AssertConstMessage(bool condition, string message = null, string scope = DEFAULT_ASSERT_SCOPE, UnityEngine.Object context = null)
+        {
+            if (!condition)
+            {
+                LogAssert(message, scope, context);
+            }
+        }
+
+        public delegate string AssertStaticMessageBuilder();
+#if OVRAVATAR_ASSERT_ENABLE_CONDITIONAL || !UNITY_ASSERTIONS
+        [Conditional(OVRAVATAR_ASSERT_FORCE_ENABLE)]
+#endif
+        internal static void AssertStaticBuilder(bool condition, AssertStaticMessageBuilder builder, string scope = DEFAULT_ASSERT_SCOPE, UnityEngine.Object context = null)
+        {
+            if (!condition)
+            {
+                LogAssert(builder(), scope, context);
+            }
+        }
+
+        public delegate string AssertMessageBuilder<T>(in T param);
+#if OVRAVATAR_ASSERT_ENABLE_CONDITIONAL || !UNITY_ASSERTIONS
+        [Conditional(OVRAVATAR_ASSERT_FORCE_ENABLE)]
+#endif
+        internal static void AssertParam<T>(bool condition, in T buildParams, AssertMessageBuilder<T> builder
+            , string scope = DEFAULT_ASSERT_SCOPE, UnityEngine.Object context = null)
+        {
+            // Should be a static method, otherwise wrap in conditional to avoid alloc
+            Debug.Assert(builder.Target == null, "Instanced builder passed to AssertParam");
+            if (!condition)
+            {
+                LogAssert(builder(in buildParams), scope, context);
+            }
+        }
+
+        public delegate string AssertMessageBuilder<T0, T1>(in T0 param0, in T1 param1);
+#if OVRAVATAR_ASSERT_ENABLE_CONDITIONAL || !UNITY_ASSERTIONS
+        [Conditional(OVRAVATAR_ASSERT_FORCE_ENABLE)]
+#endif
+        internal static void AssertTwoParams<T0, T1>(bool condition, in T0 buildParam0, in T1 buildParam1, AssertMessageBuilder<T0, T1> builder
+            , string scope = DEFAULT_ASSERT_SCOPE, UnityEngine.Object context = null)
+        {
+            // Should be a static method, otherwise wrap in conditional to avoid alloc
+            Debug.Assert(builder.Target == null, "Instanced builder passed to AssertTwoParams");
+            if (!condition)
+            {
+                LogAssert(builder(in buildParam0, in buildParam1), scope, context);
+            }
+        }
+
+        public delegate string AssertLessThanMessageBuilder<T>(in T lhs, in T rhs);
+#if OVRAVATAR_ASSERT_ENABLE_CONDITIONAL || !UNITY_ASSERTIONS
+        [Conditional(OVRAVATAR_ASSERT_FORCE_ENABLE)]
+#endif
+        internal static void AssertLessThan(int lesser, int greater, AssertLessThanMessageBuilder<int> builder
+            , string scope = DEFAULT_ASSERT_SCOPE, UnityEngine.Object context = null)
+        {
+            // Should be a static method, otherwise wrap in conditional to avoid alloc
+            Debug.Assert(builder.Target == null);
+            if (lesser >= greater)
+            {
+                LogAssert(builder(in lesser, in greater), scope, context);
+            }
+        }
+
+        // HACK: TODO: Remove everything below here before release
+        // Down-ranking specific native logs that often spam
+
+        private const string _bodyApiMsg =
+            "failed 'ovrBody_GetPose(context_, pose)' with 'An unknown error has occurred'(65537)";
+
+        private const string gltfAttributeMsg =
+            "gltfmeshprimitiveitem::Skipping vertex attribute in glTF mesh primitive:";
+
+        private const string _ovrPluginInitMsg =
+            "tracking::OVRPlugin not initialized";
+
+
+        [Conditional("OVRAVATAR_HANDLE_SPECIAL_CASE")]
+        private static void HandleSpecialCaseLogs(string message, ref bool handled)
+        {
+            handled = false;
+
+            if (message.Contains(gltfAttributeMsg) || message.Contains(_ovrPluginInitMsg))
+            {
+                LogVerbose(message, "native");
+                handled = true;
+            }
+            else if (message.Contains(_bodyApiMsg))
+            {
+                LogVerbose(
+                    "tracking::Tracking failed 'ovrBody_GetPose(context_, pose)' with a bad input pose. Reusing pose from last frame",
+                    "native");
+                handled = true;
+            }
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarLog.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarLog.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..89b57174d85d94bdf0fe37ce143802825010f11b
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarLog.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 5ce604562247b5c478cd2295b17a0ffa
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarManager.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarManager.cs
new file mode 100644
index 0000000000000000000000000000000000000000..5b6fd79ee73977e94e58a48e54a4439a0f2ffced
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarManager.cs
@@ -0,0 +1,1570 @@
+#if USING_XR_MANAGEMENT && USING_XR_SDK_OCULUS && !OVRPLUGIN_UNSUPPORTED_PLATFORM
+#define USING_XR_SDK
+#endif
+
+using AOT;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Threading.Tasks;
+using UnityEngine;
+using Application = UnityEngine.Application;
+using Unity.Jobs;
+using UnityEngine.Profiling;
+using Debug = UnityEngine.Debug;
+using Unity.Collections;
+using static Oculus.Avatar2.CAPI;
+using Unity.Collections.LowLevel.Unsafe;
+
+
+
+#if UNITY_EDITOR
+using System.Runtime.CompilerServices;
+
+[assembly: InternalsVisibleTo("Oculus.AvatarSDK2.Editor")]
+[assembly: InternalsVisibleTo("AvatarSDK.PlayModeTests")]
+#endif
+
+/// @file OvrAvatarManager.cs
+
+/**
+ * @namespace Oculus
+ * Facebook Virtual Reality SDK for Unity.
+ */
+/**
+ * @namespace Oculus.Avatar2
+ * Facebook Avatar SDK for Unity.
+ * Enables loading and manipulating Facebook avatars
+ * in a Unity application.
+ */
+namespace Oculus.Avatar2
+{
+    /**
+     * @class OvrAvatarManager
+     * Configures global options that affect all avatars like
+     * tracking, skinning, asset sources and resource limits.
+     *
+     * There is only one avatar manager per Unity project.
+     * It can be updated in the Unity Editor before play
+     * and instantiated in prefabs with specific options.
+     * - Control system resource usage like how many
+     *   avatars loading concurrently, maximum network bandwidth,
+     *   how many concurrent loads are permissible
+     *   and dynamic streaming constraints.
+     * - Specify the locations of assets to preload.
+     *   for each platform (Quest, Quest2, Rift).
+     * - Select shader and material options.
+     * - Choose skinning and tracking behaviours.
+     * @see OvrAvatarEntity
+     */
+    [DefaultExecutionOrder(-1)]
+    public partial class OvrAvatarManager : OvrSingletonBehaviour<OvrAvatarManager>
+    {
+        public const bool IsAndroidStandalone =
+#if UNITY_ANDROID && !UNITY_EDITOR
+            true;
+#else
+            false;
+#endif
+
+        // Default setting for UseExperimentalSystems
+        private const bool ExperimentalSystemsDefaultValue = false;
+
+        public CAPI.ovrAvatar2Platform Platform { get; private set; } = CAPI.ovrAvatar2Platform.Invalid;
+        public CAPI.ovrAvatar2ControllerType ControllerType { get; private set; } = CAPI.ovrAvatar2ControllerType.Invalid;
+
+        /**
+         * Return codes for @ref UserHasAvatarAsync query.
+         */
+        public enum HasAvatarRequestResultCode
+        {
+            UnknownError = 0,
+            BadParameter = 1,
+            SendFailed = 2,
+            RequestFailed = 3,
+            RequestCancelled = 4,
+
+            HasNoAvatar = 8,
+            HasAvatar = 9,
+        }
+
+        /**
+         * Return codes for @ref UserHasAvataChangedAsync query.
+         */
+        public enum HasAvatarChangedRequestResultCode
+        {
+            UnknownError = 0,
+            BadParameter = 1,
+            SendFailed = 2,
+            RequestFailed = 3,
+            RequestCancelled = 4,
+
+            AvatarHasNotChanged = 8,
+            AvatarHasChanged = 9,
+        }
+
+        /**
+         * Encapsulates a mesh from an avatar primitive.
+         * @see OnAvatarMeshLoaded
+         */
+        public struct MeshData
+        {
+            public MeshData(
+                string name,
+                int[] indices,
+                Vector3[] positions,
+                Vector3[] normals,
+                Color32[] colors,
+                Vector2[] texcoords,
+                Vector4[] tangents,
+                BoneWeight[] boneweights,
+                Matrix4x4[] bindposes)
+            {
+                Name = name;
+                Indices = indices;
+                Positions = positions;
+                Normals = normals;
+                Colors = colors;
+                TexCoords = texcoords;
+                Tangents = tangents;
+                BindPoses = bindposes;
+                BoneWeights = boneweights;
+            }
+            public readonly string Name;
+            public readonly int[] Indices;
+            public readonly Vector3[] Positions;
+            public readonly Vector3[] Normals;
+            public readonly Color32[] Colors;
+            public readonly Vector2[] TexCoords;
+            public readonly Vector4[] Tangents;
+            public readonly BoneWeight[] BoneWeights;
+            public readonly Matrix4x4[] BindPoses;
+        };
+
+        private const string logScope = "manager";
+        public static bool initialized { get; private set; }
+
+        [Header("Loading")]
+
+        /**
+         * Maximum number of avatars allowed to load concurrently on the main thread.
+         * The default is 64.
+         */
+        [Tooltip("Number of avatars allowed to load concurrently on main thread")]
+        public int MaxConcurrentAvatarsLoading = 64;
+
+        /**
+         * Number of resources allowed to load concurrently on background threads.
+         * Roughly equal to number of cores used. Defaults to 2.
+         */
+        [Tooltip(@"Number of resources allowed to load concurrently on background threads
+Roughly equal to number of cores used")]
+        public int MaxConcurrentResourcesLoading = 2;
+
+        [Tooltip(@"Minimum amount of work to run per frame (in milliseconds) while sliced work remains")]
+        [SerializeField]
+        [Range(2, 12)]
+        private int _minSliceWorkPerFrameMS = 3;
+
+        /**
+         * Minimum amount of work to run per frame (in milliseconds) while sliced work remains.
+         * This serialized value is set when OvrAvatarManager is initialized but can diverge
+         * from the current setting. Negative values will skip runtime initialization of this
+         * budget and use compile time default.
+         */
+        public UInt16 MinSliceWorkPerFrameMS
+        {
+            get => OvrTime.minWorkPerFrameMS;
+            set => OvrTime.minWorkPerFrameMS = value;
+        }
+
+        private readonly Dictionary<OvrAvatarEntity, Action> _loadActions = new Dictionary<OvrAvatarEntity, Action>();
+        private readonly Queue<OvrAvatarEntity> _loadQueue = new Queue<OvrAvatarEntity>();
+
+        private readonly Queue<OvrAvatarResourceLoader> _resourceLoadQueue = new Queue<OvrAvatarResourceLoader>();
+
+        private int _numAvatarsLoading = 0;
+        private int _numResourcesLoading = 0;
+
+        [Header("Network")]
+
+        [Tooltip("Maximum concurrent network requests")]
+        [SerializeField]
+        [Range(1, 64)]
+        private Int64 _maxRequests = 16;
+
+        [Tooltip("-1 for no limit")]
+        [SerializeField]
+        private Int64 _maxSendBytesPerSecond = -1;
+
+        [Tooltip("-1 for no limit")]
+        [SerializeField]
+        private Int64 _maxReceiveBytesPerSecond = -1;
+
+        [Tooltip("Number of milliseconds to wait for avatar specification request to complete before failing, 0 for no limit, -1 to use default" +
+                 "\n avatar specification request are small, and aren't expected to take more than a second typically.")]
+        [SerializeField]
+        private Int32 _specificationTimeoutMs = -1;
+
+        [Tooltip("Number of milliseconds to wait for asset request to complete before failing, 0 for no limit, -1 to use default" +
+                 "\n asset request can take many seconds, normally set to no limit and rely on low bandwidth timeout")]
+        [SerializeField]
+        private Int32 _assetTimeoutMs = -1;
+
+        [Tooltip("Number of seconds below lowBandwidthBytesPerSecond to trigger timeout, 0 for no bandwidth timeout, -1 to use default")]
+        [SerializeField]
+        private Int32 _assetLowBandwidthTimeoutSeconds = -1;
+
+        [Tooltip("Bytes per second threshold for low bandwidth timeout, -1 to use default")]
+        [SerializeField]
+        private Int32 _assetLowBandwidthBytesPerSecond = -1;
+
+        /**
+        * A list of zip files to be loaded upon initializing.
+        * Filepaths must be relative to the *StreamingAssets* folder, postfix and file extension will be auto applied.
+        */
+        [Header("Preset Zip Assets")]
+        [Tooltip("A list of zip files to be loaded upon initializing." +
+                 "\n Filepaths must be relative to the StreamingAssets folder. zipPostfix will be automatically applied.")]
+
+        [SerializeField]
+        private List<string> _preloadZipFiles = new List<string>();
+
+        [Tooltip("A list of platform independent zip files to be loaded upon initializing." +
+                 "\n Filepaths must be relative to the StreamingAssets folder, postfix will not be applied.")]
+        [SerializeField]
+        private List<string> _preloadUniversalZipFiles = new List<string>();
+
+        [Tooltip("Platform postfix for avatar .zip files inside Streaming Assets, and for the .glb files themselves inside the zip (in lowercase).  E.g. 'PresetAvatars_Rift.zip', '0_rift.glb'.")]
+        [SerializeField]
+        private string zipPostfixDefault = "Rift";
+
+        [Tooltip("Platform postfix for avatar .zip files inside Streaming Assets, and for the .glb files themselves inside the zip (in lowercase).  E.g. 'PresetAvatars_Quest.zip', '0_quest.glb'.")]
+        [SerializeField]
+        private string zipPostfixAndroid = "Quest";
+
+        [Tooltip("Platform postfix for avatar .zip files inside Streaming Assets, and for the .glb files themselves inside the zip (in lowercase).  E.g. 'PresetAvatars_Quest.zip', '0_quest.glb'." +
+                 "\n\nNote, Quest and Quest 2 currently share the same set of presets to reduce APK size.")]
+        [SerializeField]
+        private string zipPostfixQuest2 = "Quest";
+
+        [Tooltip("FastLoad postfix for avatar .zip files inside Streaming Assets, and for the .glb files themselves inside the zip (in lowercase).  E.g. 'PresetAvatars_Fastload.zip', '0_fastload.glb'.")]
+        [SerializeField]
+        private string zipPostfixFastLoad = "Fastload";
+
+        [Tooltip("Extension of avatar glb files inside preset .zip files")]
+        [SerializeField]
+        private string zipFileExtension = ".glb";
+
+
+        [Header("Streaming Assets")]
+        [Tooltip("Platform postfix used when loading glb files directly out of Streaming Assets (not in a .zip)" +
+                 "\n\nThe full filename will be: (path)_(platform)(quality)(version)(extension), e.g. customavatar_riftv04.glb.zst")]
+        [SerializeField]
+        private string streamingAssetPostfixDefault = "rift";
+
+        [Tooltip("Platform postfix used when loading glb files directly out of Streaming Assets (not in a .zip)" +
+                 "\n\nThe full filename will be: (path)_(platform)(quality)(version)(extension), e.g. customavatar_quest1v04.glb.zst")]
+        [SerializeField]
+        private string streamingAssetPostfixAndroid = "quest1";
+
+        [Tooltip("Platform postfix used when loading glb files directly out of Streaming Assets (not in a .zip)" +
+                 "\n\nThe full filename will be: (path)_(platform)(quality)(version)(extension), e.g. customavatar_quest2v04.glb.zst")]
+        [SerializeField]
+        private string streamingAssetPostfixQuest2 = "quest2";
+
+        [Tooltip("Fast Load postfix used when loading glb files directly out of Streaming Assets (not in a .zip)" +
+                 "\n\nThe full filename will be: (path)_(platform)(quality)(version)(extension), e.g. customavatar_fastloadv04.glb.zst")]
+        [SerializeField]
+        private string streamingAssetPostfixFastLoad = "fastload";
+
+        [Tooltip("Version suffix (padded to 2 digits) when loading glb files directly out of Streaming Assets (not in a .zip)")]
+        [SerializeField]
+        private int assetVersionDefault = 4;
+
+        [Tooltip("Version suffix (padded to 2 digits) when loading glb files directly out of Streaming Assets (not in a .zip)")]
+        [SerializeField]
+        private int assetVersionAndroid = 4;
+
+        [Tooltip("Version suffix (padded to 2 digits) when loading glb files directly out of Streaming Assets (not in a .zip)")]
+        [SerializeField]
+        private int assetVersionQuest2 = 4;
+
+        [Tooltip("File extension used when loading glb files directly out of Streaming Assets (not in a .zip)")]
+        [SerializeField]
+        private string streamingAssetFileExtension = ".glb.zst";
+
+        [Header("Required Assets")]
+        [Tooltip("Where to find OvrAvatar2Assets.zip inside StreamingAssets")]
+        [SerializeField]
+        private string ovrAvatar2AssetFolder = "Oculus";
+
+        [Header("Customization")]
+
+        [Tooltip("Color of the default avatar model rendered by ovrAvatar2EntityFeatures.UseDefaultModel")]
+        [SerializeField]
+        private Color _defaultModelColor = new Color(CAPI.DefaultAvatarColorRed, CAPI.DefaultAvatarColorGreen, CAPI.DefaultAvatarColorBlue);
+
+        [Header("Shaders")]
+        /// Shader manager to use for all avatars.
+        [SerializeField]
+        public OvrAvatarShaderManagerBase ShaderManager;
+
+        [Header("Experimental")]
+        [Tooltip("Update critical joints using Unity Transform jobs, may help reduce main thread CPU usage")]
+        [SerializeField]
+        private bool _useCriticalJointJobs = false;
+        [Tooltip("Connect the avatar runtime to the Avatar Monitor pane in Unity to show avatar SDK data")]
+        [SerializeField]
+        private bool _enableDevTools = false;
+
+        [Tooltip(@"Initially load a lower quality, but significantly faster to load version of the avatar. This will be used until the full quality avatar is loaded.")]
+        [SerializeField]
+        public bool UseFastLoadAvatar = false;
+
+        [Tooltip(@"Enable experimental systems.")]
+        [SerializeField]
+        private DefaultableBool _useExperimentalSystems = DefaultableBool.Default;
+        
+        [Header("Debug")]
+        [SerializeField]
+        private CAPI.ovrAvatar2LogLevel _ovrLogLevel = CAPI.ovrAvatar2LogLevel.Verbose;
+
+        private readonly List<OvrAvatarEntity> _entityUpdateOrder = new List<OvrAvatarEntity>(16);
+        private OvrAvatarEntity[] _entityUpdateArray = Array.Empty<OvrAvatarEntity>();
+        private bool _entityUpdateArrayChanged = false;
+
+        private readonly Dictionary<CAPI.ovrAvatar2Id, OvrAvatarAssetBase> _assetMap = new Dictionary<CAPI.ovrAvatar2Id, OvrAvatarAssetBase>();
+        private readonly Dictionary<CAPI.ovrAvatar2Id, OvrAvatarResourceLoader> _resourcesByID = new Dictionary<CAPI.ovrAvatar2Id, OvrAvatarResourceLoader>();
+
+        /// Desired level for console logging.
+        public CAPI.ovrAvatar2LogLevel ovrLogLevel => _ovrLogLevel;
+
+        /// Effective setting for activating experimental systems, accounting for runtime config and compile time defaults
+        public bool UseExperimentalSystems => _useExperimentalSystems.GetValue(ExperimentalSystemsDefaultValue);
+        
+        public void SetLogLevel(CAPI.ovrAvatar2LogLevel logLevel)
+        {
+            if (_ovrLogLevel != logLevel)
+            {
+                _ovrLogLevel = logLevel;
+                OvrAvatarLog.logLevel = OvrAvatarLog.GetLogLevel(logLevel);
+            }
+        }
+
+        /// Maximum number of concurrent network requests.
+        /// The value -1 indicates no limit.
+        public Int64 MaxRequests => _maxRequests;
+
+        /// Maximum number of bytes to send per second.
+        /// The value -1 indicates no limit.
+        public Int64 MaxSendBytesPerSecond => _maxSendBytesPerSecond;
+
+        public Int64 MaxReceiveBytesPerSecond => _maxReceiveBytesPerSecond;
+
+        ///  Whether critical joints update using Unity Transform jobs, which may help reduce main thread CPU usage
+        public bool UseCriticalJointJobs => _useCriticalJointJobs;
+
+        // Whether to activate the arbiter client sending data
+        public bool EnableDevTools => _enableDevTools;
+
+        ///
+        public IOvrAvatarHandTrackingDelegate DefaultHandTrackingDelegate { get; private set; }
+
+        ///
+        public IOvrAvatarInputTrackingDelegate DefaultInputTrackingDelegate { get; private set; }
+
+        ///
+        public IOvrAvatarInputControlDelegate DefaultInputControlDelegate { get; private set; }
+
+        /// Selects the GPU skinning controller to use for all avatars.
+        public OvrAvatarGpuSkinningController GpuSkinningController { get; private set; }
+
+        /// Selects the gaze target manager to use for all avatars.
+        public OvrAvatarGazeTargetManager GazeTargetManager { get; private set; }
+
+
+        /// Selects the face tracker to use for all avatars.
+        internal OvrAvatarFacePoseProviderBase OvrPluginFacePoseProvider { get; private set; }
+
+        /// Selects the eye tracker to use for all avatars.
+        internal OvrAvatarEyePoseProviderBase OvrPluginEyePoseProvider { get; private set; }
+
+        private delegate void RequestDelegate(CAPI.ovrAvatar2Result result, IntPtr userContext);
+
+        private readonly Dictionary<CAPI.ovrAvatar2RequestId, RequestDelegate> _activeRequests =
+            new Dictionary<CAPI.ovrAvatar2RequestId, RequestDelegate>();
+
+        #region Lifecycle
+
+        protected override void Initialize()
+        {
+            Debug.Assert(!initialized);
+
+            // Can't query in static initializer under 2019.4
+            // - "UnityException: GetGraphicsShaderLevel is not allowed to be called from a MonoBehaviour constructor
+            // (or instance field initializer), call it in Awake or Start instead. Called from MonoBehaviour 'OvrAvatarManager'."
+            _shaderLevelSupport = SystemInfo.graphicsShaderLevel;
+
+            ValidateSupportedSkinners();
+
+            if (!OvrTime.HasLimitedBudget)
+            {
+                if (_minSliceWorkPerFrameMS <= UInt16.MaxValue)
+                {
+                    OvrTime.minWorkPerFrameMS = (UInt16)_minSliceWorkPerFrameMS;
+                }
+                else
+                {
+                    OvrTime.ResetInitialBudget();
+                }
+            }
+
+            OvrAvatarLog.logLevel = OvrAvatarLog.GetLogLevel(_ovrLogLevel);
+
+            // Used by nativeSDK telemetry to identify source app
+            var clientName = $"{Application.companyName}.{Application.productName}";
+            var clientAppVersionString = $"{Application.version}+{Application.unityVersion}";
+
+            var platform = GetPlatform();
+            OvrAvatarLog.LogInfo($"OvrAvatarManager initializing for app {clientName}::{clientAppVersionString} on platform '{CAPI.ovrAvatar2PlatformToString(platform)}'"
+                , logScope, this);
+
+            var initInfo = CAPI.OvrAvatar_DefaultInitInfo(clientAppVersionString, platform);
+            {
+                initInfo.flags |= CAPI.ovrAvatar2InitializeFlags.EnableSkinningOrigin;
+                initInfo.loggingLevel = _ovrLogLevel;
+                initInfo.loggingCallback = OvrAvatarLog.LogCallBack;
+                initInfo.loggingContext = IntPtr.Zero;
+                initInfo.requestCallback = RequestCallback;
+                initInfo.resourceLoadCallback = ResourceCallback;
+                initInfo.resourceLoadContext = IntPtr.Zero;
+                initInfo.fallbackPathToOvrAvatar2AssetsZip = GetAssetPathForFile(ovrAvatar2AssetFolder, true);
+                initInfo.numWorkerThreads = 1;
+                initInfo.fileOpenCallback = null;
+                initInfo.fileReadCallback = null;
+                initInfo.fileCloseCallback = null;
+                initInfo.fileReaderContext = IntPtr.Zero;
+                initInfo.maxNetworkRequests = _maxRequests;
+                initInfo.maxNetworkSendBytesPerSecond = _maxSendBytesPerSecond;
+                initInfo.maxNetworkReceiveBytesPerSecond = _maxReceiveBytesPerSecond;
+                initInfo.defaultModelColor.x = _defaultModelColor.r;
+                initInfo.defaultModelColor.y = _defaultModelColor.g;
+                initInfo.defaultModelColor.z = _defaultModelColor.b;
+                initInfo.clientName = clientName;
+                // Transform from SDK space (-Z forward) to Unity space (+Z forward)
+#if OVR_AVATAR_ENABLE_CLIENT_XFORM
+                initInfo.clientSpaceRightAxis = UnityEngine.Vector3.right;
+                initInfo.clientSpaceUpAxis = UnityEngine.Vector3.up;
+                initInfo.clientSpaceForwardAxis = UnityEngine.Vector3.forward;
+#else
+                initInfo.clientSpaceRightAxis = UnityEngine.Vector3.right;
+                initInfo.clientSpaceUpAxis = UnityEngine.Vector3.up;
+                initInfo.clientSpaceForwardAxis = -UnityEngine.Vector3.forward;
+#endif
+            }
+
+            if (UseExperimentalSystems
+            )
+            {
+                // All bits used by application during initialization (0-3)
+                const ovrAvatar2InitializeFlags appFlags
+                    = (ovrAvatar2InitializeFlags)(((int)ovrAvatar2InitializeFlags.Last << 1) - 1);
+                // Set of non-debug bits (0-29)
+                const ovrAvatar2InitializeFlags allSystemFlags = (ovrAvatar2InitializeFlags)((1 << 29) - 1);
+
+                // Specifics bits to set for experimental features (4-28)
+                const ovrAvatar2InitializeFlags experimentalMask = allSystemFlags & ~appFlags;
+                initInfo.flags |= experimentalMask;
+            }
+
+#if USING_XR_SDK
+            OvrAvatarLog.LogInfo($"Attempting to initialize ovrplugintracking lib", logScope, this);
+            if (OvrPluginTracking.Initialize(initInfo.loggingCallback, initInfo.loggingContext))
+            {
+
+                OvrPluginFacePoseProvider = OvrPluginTracking.CreateFaceTrackingContext();
+                if (OvrPluginFacePoseProvider != null)
+                {
+                    OvrAvatarLog.LogInfo("Created ovrplugintracking face tracking context", logScope, this);
+                }
+                else
+                {
+                    OvrAvatarLog.LogInfo("Failed to created ovrplugintracking face tracking context", logScope, this);
+                }
+
+                OvrPluginEyePoseProvider = OvrPluginTracking.CreateEyeTrackingContext();
+                if (OvrPluginEyePoseProvider != null)
+                {
+                    OvrAvatarLog.LogInfo("Created ovrplugintracking eye tracking context", logScope, this);
+                }
+                else
+                {
+                    OvrAvatarLog.LogInfo("Failed to created ovrplugintracking eye tracking context", logScope, this);
+                }
+
+                DefaultHandTrackingDelegate = OvrPluginTracking.CreateHandTrackingDelegate();
+                OvrAvatarLog.LogInfo(DefaultHandTrackingDelegate != null
+                    ? "Created ovrplugintracking hand tracking delegate"
+                    : "Failed to created ovrplugintracking hand tracking delegate", logScope, this);
+            }
+            else
+            {
+                OvrAvatarLog.LogInfo("Failed to initialize  ovrplugintracking lib", logScope, this);
+            }
+#endif
+
+            // This is actually used even w/out GPUSkinning as part of skinning mode selection
+            // NOTE: That is rather ugly, make that not the case or atleast rename
+            GpuSkinningConfiguration.Instantiate();
+            GpuSkinningConfiguration.Instance.ValidateFallbackSkinner(UnitySkinnerSupported, OvrGPUSkinnerSupported);
+
+            // initialize the GPU Skinning dll
+            bool gpuSkinningInitSuccess = false;
+            if (OvrGPUSkinnerSupported || OvrComputeSkinnerSupported)
+            {
+                try
+                {
+                    gpuSkinningInitSuccess = CAPI.OvrGpuSkinning_Initialize();
+                }
+                catch (DllNotFoundException linkExcpt)
+                {
+                    OvrAvatarLog.LogError($"DllNotFound, likely UnsatisfiedLinkError: {linkExcpt.Message}", logScope, this);
+                }
+                catch (Exception unknownExcpt)
+                {
+                    OvrAvatarLog.LogError($"Exception initializing gpuskinning: {unknownExcpt.Message}", logScope, this);
+                }
+
+                if (gpuSkinningInitSuccess)
+                {
+                    OvrAvatarLog.LogDebug("Initializing GPUSkinning Singletons");
+                    GpuSkinningController = new OvrAvatarGpuSkinningController();
+                }
+                else
+                {
+                    OvrAvatarLog.LogError("ovrGpuSkinning_Initialize failed", logScope, this);
+                }
+            }
+
+            bool ovrAvatar2Init = false;
+            try
+            {
+                ovrAvatar2Init = CAPI.OvrAvatar_Initialize(in initInfo);
+            }
+            catch (Exception e)
+            {
+                OvrAvatarLog.LogError($"Exception calling ovrAvatar2_Initialize: {e}", logScope, this);
+            }
+            initialized = ovrAvatar2Init;
+            if (!initialized)
+            {
+                OvrAvatarLog.LogError("ovrAvatar2_Initialize Failed", logScope, this);
+                return;
+            }
+            Platform = platform;
+            ControllerType = GetControllerType();
+
+            // Enable the tools link so we send data to the Avatar Resource Monitor window
+            // TODO: fix root cause of this check causing a crash
+            //if (OvrAvatarManager.Instance.EnableDevTools)
+            {
+                if (CAPI.ovrAvatar2_EnableDevToolsLink() != CAPI.ovrAvatar2Result.Success)
+                {
+                    OvrAvatarLog.LogWarning("Failed to enable dev tools link", logScope, this);
+                }
+            }
+
+            OvrAvatarLog.LogVerbose($"libovravatar2 initialized with version: {CAPI.OvrAvatar_GetVersionString()}", logScope, this);
+
+            AvatarLODManager.Instantiate();
+
+            // TODO: This was done in SampleManager after initialization. What is the effect?
+            CAPI.OvrAvatar2_Update();
+
+            //Preload zip files
+            foreach (var filePath in _preloadZipFiles)
+            {
+                AddZipSource(filePath);
+            }
+            foreach (var uniFilePath in _preloadUniversalZipFiles)
+            {
+                AddUniversalZipSource(uniFilePath);
+            }
+
+            GazeTargetManager = new OvrAvatarGazeTargetManager();
+
+            if (ShaderManager == null)
+            {
+                OvrAvatarLog.LogWarning("OvrAvatarShaderManager needs to be specified. Otherwise materials cannot be created. Falling back to default manager.", logScope, this);
+                ShaderManager = gameObject.AddComponent<OvrAvatarShaderManagerSingle>();
+            }
+
+            UpdateNetworkSetting(ref CAPI.SpecificationNetworkSettings.timeoutMS, _specificationTimeoutMs);
+            UpdateNetworkSetting(ref CAPI.AssetNetworkSettings.timeoutMS, _assetTimeoutMs);
+            UpdateNetworkSetting(ref CAPI.AssetNetworkSettings.lowSpeedTimeSeconds, _assetLowBandwidthTimeoutSeconds);
+            UpdateNetworkSetting(ref CAPI.AssetNetworkSettings.lowSpeedLimitBytesPerSecond, _assetLowBandwidthBytesPerSecond);
+
+            OvrAvatarLog.LogInfo($"OvrAvatarManager initialized app with target version: {CAPI.TargetAvatarLibVersionString}", logScope, this);
+        }
+        private static void UpdateNetworkSetting(ref UInt32 sdkSetting, Int32 serializedValue)
+        {
+            if (serializedValue >= 0)
+            {
+                sdkSetting = (uint)serializedValue;
+            }
+        }
+
+        protected virtual void Update()
+        {
+            UpdateInternal(Time.deltaTime);
+        }
+
+        // Used when updating avatarSDK from a mechanism other than UnityEvent.Update
+        public void Step(float deltaSeconds)
+        {
+            UpdateInternal(deltaSeconds);
+        }
+
+        private bool GetActives(in NativeArray<ovrAvatar2EntityId> entityIds, ref NativeArray<bool> actives)
+        {
+            Debug.Assert(entityIds.Length == actives.Length);
+            unsafe
+            {
+                return CAPI.ovrAvatar2Entity_GetActives((ovrAvatar2EntityId*)entityIds.GetUnsafePtr(), (bool*)actives.GetUnsafePtr(), (uint)entityIds.Length)
+                        .EnsureSuccess("ovrAvatar2Entity_GetActives", logScope, this);
+            }
+        }
+
+        private void UpdateInternal(float deltaSeconds)
+        {
+            if (!initialized) { return; }
+            if (ShaderManager == null)
+            {
+                OvrAvatarLog.LogDebug("OvrAvatarShaderManager needs to be specified", logScope, this);
+                return;
+            }
+            else if (!ShaderManager.Initialized)
+            {
+                OvrAvatarLog.LogDebug("Waiting for OvrAvatarShaderManager to finish initialization", logScope, this);
+                return;
+            }
+
+            Profiler.BeginSample("OvrTime.InternalUpdate");
+            OvrTime.InternalUpdate();
+            Profiler.EndSample();
+
+            Profiler.BeginSample("GazeTargetManager.Update");
+            GazeTargetManager.Update();
+            Profiler.EndSample();
+
+            AvatarLODManager.Instance.UpdateInternal();
+
+            if (_entityUpdateArrayChanged)
+            {
+                Profiler.BeginSample("OvrAvatarManager.entityUpdateArrayChanged");
+                _entityUpdateArrayChanged = false;
+
+                var updateCount = _entityUpdateOrder.Count;
+                if (_entityUpdateArray.Length != updateCount)
+                {
+                    Array.Resize(ref _entityUpdateArray, updateCount);
+                }
+                _entityUpdateOrder.CopyTo(_entityUpdateArray);
+                Profiler.EndSample();
+            }
+
+            var entityIds = new NativeArray<ovrAvatar2EntityId>(_entityUpdateArray.Length, Allocator.Temp, NativeArrayOptions.UninitializedMemory);
+            var actives = new NativeArray<bool>(_entityUpdateArray.Length, Allocator.Temp, NativeArrayOptions.UninitializedMemory);
+            unsafe
+            {
+                var ids = (ovrAvatar2EntityId*)entityIds.GetUnsafePtr();
+                for (int i = 0; i < _entityUpdateArray.Length; i++)
+                {
+                    ids[i] = _entityUpdateArray[i].internalEntityId;
+                }
+            }
+
+            Profiler.BeginSample("OvrAvatarManager.PreSDKUpdates");
+            var transforms = new NativeArray<CAPI.ovrAvatar2Transform>(_entityUpdateArray.Length, Allocator.Temp, NativeArrayOptions.UninitializedMemory);
+            uint nextTransformIndex = 0;
+            if (GetActives(entityIds, ref actives))
+            {
+                for (int i = 0; i < _entityUpdateArray.Length; i++)
+                {
+                    _entityUpdateArray[i].PreSDKUpdateInternal(actives[i], ref transforms, nextTransformIndex++);
+                }
+                unsafe
+                {
+                    CAPI.ovrAvatar2Entity_SetRoots(entityIds.GetPtr<ovrAvatar2EntityId>(), transforms.GetPtr<CAPI.ovrAvatar2Transform>(), nextTransformIndex);
+                }
+            }
+            Profiler.EndSample();
+
+            Profiler.BeginSample("CAPI.OvrAvatar2_Update");
+            CAPI.OvrAvatar2_Update(deltaSeconds);
+            Profiler.EndSample(); // "CAPI.OvrAvatar2_Update"
+
+            Profiler.BeginSample("OvrAvatarManager.PostSDKUpdates");
+            // DOD to OOP mismatch makes for pain, going to have to read and then write 64 bytes
+            // for every single avatar, just to set one bit's worth of info :(
+            if (GetActives(entityIds, ref actives))
+            {
+                for (int i = 0; i < _entityUpdateArray.Length; i++)
+                {
+                    _entityUpdateArray[i].PostSDKUpdateInternal(actives[i]);
+                }
+            }
+            Profiler.EndSample();
+
+            Profiler.BeginSample("OvrAvatarManager.UpdateLoadRequests");
+            if (_loadRequestsChanged)
+            {
+                // copy to avoid issues with modifying during iteration
+                Array.Resize(ref _loadRequestsArray, _loadRequests.Count);
+                _loadRequests.CopyTo(_loadRequestsArray);
+                _loadRequestsChanged = false;
+            }
+            foreach (var loadRequest in _loadRequestsArray)
+            {
+                if (loadRequest.Update())
+                {
+                    _loadRequests.Remove(loadRequest);
+                    _loadRequestsChanged = true;
+                }
+            }
+            Profiler.EndSample();
+
+            Profiler.BeginSample("OvrAvatarManager.UpdateInternals");
+            GpuSkinningController?.StartFrame();
+            foreach (var trackedEntity in _entityUpdateArray)
+            {
+                trackedEntity.UpdateInternal(deltaSeconds);
+            }
+            GpuSkinningController?.EndFrame();
+            Profiler.EndSample();
+
+            if (UseCriticalJointJobs)
+            {
+                // Ensure transform jobs are eligible to be assigned to worker threads
+                JobHandle.ScheduleBatchedJobs();
+            }
+
+            GpuSkinningController?.UpdateInternal();
+            Permission_Update();
+        }
+
+        protected override void Shutdown()
+        {
+            // MUST be set before calling ovrAvatar2_Shutdown, in case any sdk work is being done on another thread
+            initialized = false;
+
+            foreach (var entity in _entityUpdateOrder)
+            {
+                entity.Teardown();
+            }
+            _entityUpdateOrder.Clear();
+
+            var assets = new OvrAvatarAssetBase[_assetMap.Count];
+            _assetMap.Values.CopyTo(assets, 0);
+            foreach (var asset in assets)
+            {
+                asset.Dispose();
+            }
+
+            if (GpuSkinningController != null)
+            {
+                GpuSkinningController.Dispose();
+            }
+
+            foreach (var resource in _resourcesByID)
+            {
+                resource.Value.Dispose();
+            }
+            _resourcesByID.Clear();
+
+            OvrAvatarCallbackContextBase.DisposeAll();
+
+            OvrAvatarLog.LogInfo("SystemDispose", logScope, this);
+            bool shutdownSuccess = CAPI.OvrAvatar_Shutdown();
+            if (!shutdownSuccess)
+            {
+                OvrAvatarLog.LogError($"Failed OvrAvatar_Shutdown", logScope, this);
+            }
+
+            OvrPluginTracking.Shutdown();
+
+            if (OvrGPUSkinnerSupported || OvrComputeSkinnerSupported)
+            {
+                var gpuSkinningShutdownResult = CAPI.OvrGpuSkinning_Shutdown();
+                if (!gpuSkinningShutdownResult)
+                {
+                    OvrAvatarLog.LogError($"Failed to shutdown OvrGpuSkinning", logScope, this);
+                }
+            }
+
+            _entityUpdateArray = Array.Empty<OvrAvatarEntity>();
+            _assetMap.Clear();
+
+            _ShutdownSingleton<GpuSkinningConfiguration>(GpuSkinningConfiguration.Instance);
+
+            _ShutdownSingleton<AvatarLODManager>(AvatarLODManager.Instance);
+
+            GpuSkinningController = null;
+
+#if UNITY_EDITOR || UNITY_STANDALONE_WIN
+#if !DISABLE_IPC_CONNECTOR
+            if (IpcOafConnector.hasInstance)
+            {
+                IpcOafConnector.Instance.Destroy();
+            }
+#endif
+#endif
+
+            OvrTime.CancelAll();
+        }
+
+        #endregion
+
+        #region Public Functions
+
+        internal void AddTrackedEntity(OvrAvatarEntity entity)
+        {
+            if (!_entityUpdateOrder.Contains(entity))
+            {
+                _entityUpdateOrder.Add(entity);
+                _entityUpdateArrayChanged = true;
+            }
+        }
+
+        internal void RemoveTrackedEntity(OvrAvatarEntity entity)
+        {
+            if (_entityUpdateOrder.Contains(entity))
+            {
+                _entityUpdateOrder.Remove(entity);
+                _entityUpdateArrayChanged = true;
+            }
+        }
+
+        /**
+         * Load an additional asset ZIP file into memory.
+         * @param file  path to a zip file or a directory
+         *              (relative to *StreamingAssets* directory).
+         * A platform-specific postfix is added to the name of
+         * the ZIP file. This lets you select different assets
+         * for different platforms from within the Unity Editor.
+         */
+        public void AddZipSource(string file)
+        {
+            string platformPostfix = GetPlatformPostfix(true);
+            string fastPostfix = GetFastLoadPostfix(true);
+
+            string filePath;
+            if (platformPostfix.Length > 0)
+            {
+                // Remove extension so we can insert postfix
+                filePath = Path.ChangeExtension(file, null);
+                filePath += $"_{platformPostfix}.zip";
+            }
+            else
+            {
+                // Otherwise, simply ensure the filename ends with ".zip"
+                filePath = Path.ChangeExtension(file, "zip");
+            }
+            filePath = GetAssetPathForFile(filePath);
+            AddRawZipSource(filePath);
+
+            string fastPath = Path.ChangeExtension(file, null);
+            fastPath += $"_{fastPostfix}.zip";
+            fastPath = GetAssetPathForFile(fastPath);
+            AddRawZipSource(fastPath);
+        }
+
+        /**
+         * Load an additional asset ZIP file into memory.
+         * @param file  path to a zip file or a directory
+         *              (relative to *StreamingAssets* directory).
+         * The platform-specific prefix is *not* added.
+         * This ZIP file will be available on all platforms.
+         */
+        public void AddUniversalZipSource(string file)
+        {
+            file = Path.ChangeExtension(file, "zip");
+            file = GetAssetPathForFile(file);
+            AddRawZipSource(file);
+        }
+
+        private void AddRawZipSource(string filePath)
+        {
+            CAPI.ovrAvatar2Result result = CAPI.ovrAvatar2_AddZipSourceFile(filePath);
+            if (result != CAPI.ovrAvatar2Result.Success)
+            {
+                OvrAvatarLog.LogError($"Error AddZipSourceFile {result}. Path was {filePath}", logScope, this);
+            }
+            else
+            {
+                OvrAvatarLog.LogDebug($"Added zip source {filePath}", logScope, this);
+            }
+        }
+
+        /**
+         * Asynchronous request to determine whether a specific user has an avatar.
+         * @returns return code indicating the result of the request.
+         * @ returns @ ref HasAvatarRequestResultCode - one of:
+         *           UnknownError, BadParameter, SendFailed, RequestFailed,
+         *           HasNoAvatar or HasAvatar
+         * @see HasAvatarRequestResultCode
+         */
+        public async Task<HasAvatarRequestResultCode> UserHasAvatarAsync(UInt64 userId)
+        {
+            if (userId == 0)
+            {
+                OvrAvatarLog.LogError("UserHasAvatarAsync failed: userId must not be 0", logScope, this);
+                return HasAvatarRequestResultCode.BadParameter;
+            }
+
+            if (!OvrAvatarEntitlement.AccessTokenIsValid())
+            {
+                OvrAvatarLog.LogError("UserHasAvatarAsync failed: no valid access token", logScope, this);
+                return HasAvatarRequestResultCode.BadParameter;
+            }
+
+            // Queue request
+            bool requestSent = CAPI.OvrAvatar_HasAvatar(userId, out var requestId, IntPtr.Zero);
+            if (!requestSent)
+            {
+                OvrAvatarLog.LogError($"ovrAvatar2_HasAvatar failed to send request", logScope, this);
+                return HasAvatarRequestResultCode.SendFailed;
+            }
+
+            // Setup request handler
+            var completionSource = _RegisterBoolRequestHandler(requestId);
+
+            // Await request completion
+            await completionSource.Task;
+
+            var requestResult = completionSource.Task.Result;
+            bool requestSuccess = requestResult.requestResult.IsSuccess();
+            if (!requestSuccess)
+            {
+                OvrAvatarLog.LogError($"UserHasAvatarAsync completed the request but the result was {requestResult.requestResult}", logScope, this);
+                return HasAvatarRequestResultCode.RequestFailed;
+            }
+
+            if (!requestResult.resultBool.HasValue)
+            {
+                OvrAvatarLog.LogError($"ovrAvatar2_HasAvatar encountered an unexpected error", logScope, this);
+                return HasAvatarRequestResultCode.UnknownError;
+            }
+
+            if (!requestResult.resultBool.Value)
+            {
+                OvrAvatarLog.LogWarning($"ovrAvatar2_HasAvatar user has no saved avatar", logScope, this);
+                return HasAvatarRequestResultCode.HasNoAvatar;
+            }
+
+            return HasAvatarRequestResultCode.HasAvatar;
+        }
+
+        internal async Task<HasAvatarChangedRequestResultCode> SendHasAvatarChangedRequestAsync(CAPI.ovrAvatar2EntityId entityId)
+        {
+            // Queue request
+            bool requestSent = CAPI.OvrAvatar2_HasAvatarChanged(entityId, out var requestId, IntPtr.Zero);
+            if (!requestSent)
+            {
+                OvrAvatarLog.LogError($"ovrAvatar2_HasAvatar failed to send request", logScope, this);
+                return HasAvatarChangedRequestResultCode.SendFailed;
+            }
+
+            // Setup request handler
+            var completionSource = _RegisterBoolRequestHandler(requestId);
+
+            // Await request completion
+            await completionSource.Task;
+
+            var requestResult = completionSource.Task.Result;
+            bool requestSuccess = requestResult.requestResult.IsSuccess();
+            if (!requestSuccess)
+            {
+                OvrAvatarLog.LogError($"ovrAvatar2_HasAvatarChanged completed the request but the result was {requestResult.requestResult}", logScope, this);
+                return HasAvatarChangedRequestResultCode.RequestFailed;
+            }
+
+            // ReSharper disable once InvertIf
+            if (!requestResult.resultBool.HasValue)
+            {
+                OvrAvatarLog.LogError($"ovrAvatar2_HasAvatarChanged encountered an unexpected error", logScope, this);
+                return HasAvatarChangedRequestResultCode.UnknownError;
+            }
+
+            return requestResult.resultBool.Value
+                ? HasAvatarChangedRequestResultCode.AvatarHasChanged
+                : HasAvatarChangedRequestResultCode.AvatarHasNotChanged;
+        }
+
+        // TODO: Make static
+        private TaskCompletionSource<AvatarRequestBoolResults> _RegisterBoolRequestHandler(CAPI.ovrAvatar2RequestId requestId)
+        {
+            var completionSource = new TaskCompletionSource<AvatarRequestBoolResults>();
+            _activeRequests.Add(requestId,
+                (avatarResult, context) =>
+                {
+                    bool? resultBool = null;
+                    if (avatarResult.IsSuccess())
+                    {
+                        if (CAPI.OvrAvatar_GetRequestBool(requestId, out var requestBool))
+                        {
+                            resultBool = requestBool;
+                        }
+                    }
+
+                    completionSource.SetResult(new AvatarRequestBoolResults(avatarResult, resultBool));
+                });
+            return completionSource;
+        }
+
+        // Updates maxNetworkSendBytesPerSecond and maxNetworkReceiveBytesPerSecond
+
+        /**
+         * Updates network bandwidth settings.
+         * @param newMaxSendBytesPerSecond    Maximum number of bytes to send per second.
+         * @param newMaxReceiveBytesPerSecond Maximum number of bytes to receive per second.
+         * @param newMaxRequests              Maximum number of concurrent requests.
+         * These can also be configured from within the Unity Editor.
+         * @see MaxReceiveBytesPerSecond
+         * @see MaxSendBytesPerSecond
+         * @see MaxRequests
+         */
+        public void UpdateNetworkSettings(Int64 newMaxSendBytesPerSecond, Int64 newMaxReceiveBytesPerSecond, Int64 newMaxRequests)
+        {
+            var result = CAPI.ovrAvatar2_UpdateNetworkSettings(newMaxSendBytesPerSecond, newMaxReceiveBytesPerSecond, newMaxRequests);
+            if (result.IsSuccess())
+            {
+                _maxSendBytesPerSecond = newMaxSendBytesPerSecond;
+                _maxReceiveBytesPerSecond = newMaxReceiveBytesPerSecond;
+                _maxRequests = newMaxRequests;
+            }
+            else
+            {
+                OvrAvatarLog.LogError($"ovrAvatar2_UpdateNetworkSettings failed with result {result}");
+            }
+        }
+
+        /**
+         * Query network statistics for all avatars.
+         * @param downloadTotalBytes    Gets total bytes downloaded.
+         * @param downloadSpeed         Gets download speed (bytes /sec).
+         * @param totalRequests         Gets total number of requests.
+         * @param activeRequests        Get number of active requests.
+         */
+        public (UInt64 downloadTotalBytes, UInt64 downloadSpeed, UInt64 totalRequests, UInt64 activeRequests) QueryNetworkStats()
+        {
+            bool statsSuccess =
+                CAPI.OvrAvatar2_QueryNetworkStats(out var stats);
+
+            if (statsSuccess)
+            {
+                return (stats.downloadTotalBytes, stats.downloadSpeed, stats.totalRequests, stats.activeRequests);
+            }
+            else
+            {
+                OvrAvatarLog.LogError("ovrAvatar2_QueryNetworkStats failed");
+                return default;
+            }
+        }
+
+        public string GetPlatformGLBPostfix(bool isFromZip)
+        {
+            return GetPlatformPostfix(isFromZip).ToLower();
+        }
+
+        public string GetFastLoadGLBPostfix(bool isFromZip)
+        {
+            return isFromZip ? zipPostfixFastLoad : streamingAssetPostfixFastLoad;
+        }
+
+        public string GetPlatformGLBVersion(bool highQuality, bool isFromZip)
+        {
+            if (isFromZip)
+            {
+                return String.Empty;
+            }
+            int assetVersion = 0;
+            switch (Platform)
+            {
+                case CAPI.ovrAvatar2Platform.PC:
+                    assetVersion = assetVersionDefault;
+                    break;
+                case CAPI.ovrAvatar2Platform.Quest:
+                    assetVersion = assetVersionAndroid;
+                    break;
+                case CAPI.ovrAvatar2Platform.Quest2:
+                    assetVersion = assetVersionQuest2;
+                    break;
+                case CAPI.ovrAvatar2Platform.QuestPro:
+                    assetVersion = assetVersionQuest2;
+                    break;
+                default:
+                    OvrAvatarLog.LogError($"Error unknown platform for version number, using default. Platform was {Platform}.", logScope, this);
+                    assetVersion = assetVersionDefault;
+                    break;
+            }
+
+            return "_" + (highQuality ? "h" : "v") + assetVersion.ToString("00");
+        }
+
+        public string GetPlatformGLBExtension(bool isFromZip)
+        {
+            return isFromZip ? zipFileExtension : streamingAssetFileExtension;
+        }
+
+        private static CAPI.ovrAvatar2Platform GetPlatform()
+        {
+            return IsAndroidStandalone ? GetAndroidStandalonePlatform() : CAPI.ovrAvatar2Platform.PC;
+        }
+
+        private CAPI.ovrAvatar2ControllerType GetControllerType()
+        {
+            OvrAvatarLog.Assert(
+                CAPI.ovrAvatar2Platform.First <= Platform && Platform <= CAPI.ovrAvatar2Platform.Last
+                , logScope, this);
+
+            switch (Platform)
+            {
+                case CAPI.ovrAvatar2Platform.PC:
+                    return CAPI.ovrAvatar2ControllerType.Rift;
+                case CAPI.ovrAvatar2Platform.Quest:
+                    return CAPI.ovrAvatar2ControllerType.Touch;
+                case CAPI.ovrAvatar2Platform.Quest2:
+                    return CAPI.ovrAvatar2ControllerType.Quest2;
+                case CAPI.ovrAvatar2Platform.QuestPro:
+                    return CAPI.ovrAvatar2ControllerType.QuestPro;
+
+
+                case CAPI.ovrAvatar2Platform.Num:
+                case CAPI.ovrAvatar2Platform.Invalid:
+                default:
+                    OvrAvatarLog.LogError($"Unable to find controller type for current platform {Platform}", logScope, this);
+                    break;
+            }
+            return CAPI.ovrAvatar2ControllerType.Invalid;
+        }
+
+        #endregion
+
+        #region Private Functions
+
+        private string GetPlatformPostfix(bool isFromZip)
+        {
+            switch (Platform)
+            {
+                case CAPI.ovrAvatar2Platform.PC:
+                    return isFromZip ? zipPostfixDefault : streamingAssetPostfixDefault;
+                case CAPI.ovrAvatar2Platform.Quest:
+                    return isFromZip ? zipPostfixAndroid : streamingAssetPostfixAndroid;
+                case CAPI.ovrAvatar2Platform.Quest2:
+                    return isFromZip ? zipPostfixQuest2 : streamingAssetPostfixQuest2;
+                case CAPI.ovrAvatar2Platform.QuestPro:
+                    return isFromZip ? zipPostfixQuest2 : streamingAssetPostfixQuest2;
+                default:
+                    OvrAvatarLog.LogError($"Error unknown platform for prefix, using default. Platform was {Platform}.", logScope, this);
+                    return IsAndroidStandalone ? (isFromZip ? zipPostfixAndroid : streamingAssetPostfixAndroid) : (isFromZip ? zipPostfixDefault : streamingAssetPostfixDefault);
+            }
+        }
+
+        private string GetFastLoadPostfix(bool isFromZip)
+        {
+            return isFromZip ? zipPostfixFastLoad : streamingAssetPostfixFastLoad;
+        }
+
+
+        private string GetAssetPathForFile(string file, bool suppressNonExistentWarning = false)
+        {
+            if (Application.isEditor)
+            {
+                var path = Path.Combine(Application.dataPath, "Oculus", "Avatar2", "StreamingAssets", file);
+                if (!File.Exists(path))
+                {
+                    if (!suppressNonExistentWarning)
+                    {
+                        OvrAvatarLog.LogWarning("Asset doesn't exist: " + path, logScope);
+                    }
+                }
+                return path;
+            }
+
+            // ReSharper disable once ConditionIsAlwaysTrueOrFalse
+            return IsAndroidStandalone ? file : Path.Combine(Application.streamingAssetsPath, file);
+        }
+
+        private static CAPI.ovrAvatar2Platform GetAndroidStandalonePlatform()
+            => GetAndroidStandalonePlatform(SystemInfo.deviceName);
+
+
+        internal const string NonQuestDeviceLogText = "Identified non-Quest platform";
+        internal const string RecognizedQuestDeviceLogText =
+            "Identified Quest platform!";
+        internal const string UnrecognizedQuestDeviceWarningText =
+            "Unrecognized Quest platform, treating as QuestPro. AvatarSDK is likely out of date!";
+        internal static CAPI.ovrAvatar2Platform GetAndroidStandalonePlatform(string deviceName)
+        {
+            deviceName = deviceName.Trim();
+
+            var isQuestDevice = deviceName.IndexOf("Quest", StringComparison.OrdinalIgnoreCase) >= 0;
+            if (!isQuestDevice)
+            {
+                OvrAvatarLog.LogInfo(NonQuestDeviceLogText);
+                // Default to Quest1 settings for unrecognized Android headsets
+                OvrAvatarLog.LogVerbose("Reporting as Quest device", logScope);
+                return CAPI.ovrAvatar2Platform.Quest;
+            }
+
+            var foundType = CAPI.ovrAvatar2Platform.Invalid;
+
+            // Check explicitly for Quest1
+            bool isQuest1 = deviceName.EndsWith("Quest", StringComparison.OrdinalIgnoreCase);
+            if (isQuest1) { foundType = CAPI.ovrAvatar2Platform.Quest; }
+
+
+            if (foundType == CAPI.ovrAvatar2Platform.Invalid)
+            {
+                bool isQuest2 = deviceName.EndsWith("2")
+                                && (deviceName.EndsWith("Quest 2", StringComparison.OrdinalIgnoreCase)
+                                    || deviceName.EndsWith("Quest2", StringComparison.OrdinalIgnoreCase));
+                if (isQuest2) { foundType =  CAPI.ovrAvatar2Platform.Quest2; }
+            }
+
+            if (foundType == CAPI.ovrAvatar2Platform.Invalid)
+            {
+                bool isQuestPro = deviceName.EndsWith("Pro")
+                                  && (deviceName.EndsWith("Quest Pro", StringComparison.OrdinalIgnoreCase)
+                                      || deviceName.EndsWith("QuestPro", StringComparison.OrdinalIgnoreCase));
+                if (isQuestPro) { foundType =  CAPI.ovrAvatar2Platform.QuestPro; }
+            }
+
+            if (foundType == CAPI.ovrAvatar2Platform.Invalid)
+            {
+                // Default to QuestPro settings for any Quest device which isn't Quest1 or Quest2
+                OvrAvatarLog.LogWarning(UnrecognizedQuestDeviceWarningText, logScope);
+                return CAPI.ovrAvatar2Platform.QuestPro;
+            }
+
+            OvrAvatarLog.LogInfo(RecognizedQuestDeviceLogText, logScope);
+            OvrAvatarLog.LogVerbose($"Identified Quest platform {foundType}", logScope);
+            return foundType;
+        }
+
+        #endregion
+
+        #region SDK Callbacks
+
+        /**
+         * Event that provides the original untransformed avatar mesh data before skinning deformations are applied.
+         * Since each mesh is only loaded once, this function will be called for every level of detail
+         * of an avatar even if that avatar is loaded more than once.
+         * If OnAvatarMeshLoaded has no listeners, the Unity mesh vertices are loaded into the GPU and discarded.
+         * Otherwise, the mesh is provided as an argument to the listener function.
+         * The application is responsible for memory management of this mesh data and removing added listener delegates.
+         * @param mgr   OvrAvatarManager instance which invoked the event
+         * @param prim  OvrAvatarPrimitive describing the avatar SDK mesh
+         * @param mesh  MeshData structure containing the data for the mesh.
+         *              Not all of these fields will be present. If GPU skinning is used,
+         *              the BindPoses field will be null - this data is only present
+         *              for Unity skinning. Depending on avatar configuration,
+         *              Tangents and Colors might also be omitted.
+         */
+        public delegate void AvatarMeshLoadHandler(OvrAvatarManager mgr, OvrAvatarPrimitive prim, MeshData mesh);
+        public event AvatarMeshLoadHandler OnAvatarMeshLoaded;
+
+        internal bool HasMeshLoadListener => OnAvatarMeshLoaded != null;
+
+        internal void InvokeMeshLoadEvent(OvrAvatarPrimitive prim, MeshData destMesh)
+        {
+            Debug.Assert(HasMeshLoadListener);
+            OnAvatarMeshLoaded?.Invoke(this, prim, destMesh);
+        }
+
+        [MonoPInvokeCallback(typeof(CAPI.ResourceDelegate))]
+        public static void ResourceCallback(in CAPI.ovrAvatar2Asset_Resource resource, IntPtr context)
+        {
+            try
+            {
+                if (!hasInstance) { return; }
+
+                OvrAvatarResourceLoader.ResourceCallbackHandler(
+                    in resource,
+                    Instance._resourcesByID,
+                    out var queueLoad);
+
+                if (queueLoad != null)
+                {
+                    Instance.QueueResourceLoad(queueLoad);
+                }
+            }
+            catch (Exception e)
+            {
+                // Catch all exceptions to prevent C# exceptions from propagating to C++ catch handlers
+                try
+                {
+                    OvrAvatarLog.LogError($"{e.Message}\n{e.StackTrace}", logScope, OvrAvatarManager.Instance);
+                }
+                catch
+                {
+                    // ignored
+                }
+            }
+        }
+
+        [MonoPInvokeCallback(typeof(CAPI.RequestDelegate))]
+        public static void RequestCallback(CAPI.ovrAvatar2RequestId requestId, CAPI.ovrAvatar2Result result, IntPtr userContext)
+        {
+            try
+            {
+                if (!hasInstance) { return; }
+
+                if (Instance._activeRequests.TryGetValue(requestId, out var callback))
+                {
+                    callback.Invoke(result, userContext);
+                    Instance._activeRequests.Remove(requestId);
+                }
+                else
+                {
+                    OvrAvatarLog.LogError($"Unhandled request with ID {(Int32)requestId} and result {result}", logScope);
+                }
+            }
+            catch (Exception e)
+            {
+                // Catch all exceptions to prevent C# exceptions from propagating to C++ catch handlers
+                try
+                {
+                    OvrAvatarLog.LogError($"{e.Message}\n{e.StackTrace}", logScope);
+                }
+                catch
+                {
+                    // ignored
+                }
+            }
+        }
+
+        #endregion
+
+        #region Asset Loading
+
+        internal static bool IsOvrAvatarAssetLoaded(CAPI.ovrAvatar2Id assetId)
+        {
+            return OvrAvatarManager.Instance._assetMap.ContainsKey(assetId);
+        }
+
+        internal static bool GetOvrAvatarAsset<T>(CAPI.ovrAvatar2Id assetId, out T asset) where T : OvrAvatarAssetBase
+        {
+            if (Instance._assetMap.TryGetValue(assetId, out var foundAsset))
+            {
+                asset = foundAsset as T;
+                if (asset != null)
+                {
+                    return true;
+                }
+
+                OvrAvatarLog.LogError($"Found asset {assetId}, but it is not of type {typeof(T)}", logScope);
+            }
+
+            asset = null;
+            return false;
+        }
+
+        internal static void AddAsset(OvrAvatarAssetBase newAsset)
+        {
+            if (!Instance._assetMap.ContainsKey(newAsset.assetId))
+            {
+                Instance._assetMap.Add(newAsset.assetId, newAsset);
+            }
+            else
+            {
+                OvrAvatarLog.LogError($"Tried to add asset {newAsset.assetId}, but there is already a tracked asset with that ID!", logScope);
+            }
+        }
+
+        internal static void RemoveAsset(OvrAvatarAssetBase oldAsset)
+        {
+            Debug.Assert(!oldAsset.isLoaded);
+            if (!Instance._assetMap.Remove(oldAsset.assetId))
+            {
+                OvrAvatarLog.LogError($"Tried to remove {oldAsset.assetId}, but it wasn't tracked!", logScope);
+            }
+        }
+        #endregion
+
+        #region Avatar Loading
+
+        // This class tracks LoadRequests that are in progress and detects changes in state in order to trigger callbacks
+        // to the OvrAvatarEntity
+        private class LoadRequest
+        {
+            internal OvrAvatarEntity entity;
+            internal CAPI.ovrAvatar2LoadRequestId requestId;
+            internal CAPI.ovrAvatar2LoadRequestState lastState;
+
+            // Updates from internal CAPI state and triggers callbacks to the OvrAvatarEntity
+            // Returns true if the load request is in a terminal state (Success/Failed/Cancelled) or invalid
+            internal bool Update()
+            {
+                CAPI.ovrAvatar2LoadRequestInfo requestInfo;
+                if (CAPI.ovrAvatar2Result.Success != CAPI.ovrAvatar2Asset_GetLoadRequestInfo(requestId, out requestInfo))
+                {
+                    return true;
+                }
+
+                if (requestInfo.state != lastState)
+                {
+                    entity.InvokeOnLoadRequestStateChanged(requestInfo);
+                    lastState = requestInfo.state;
+                }
+
+                return (requestInfo.state == CAPI.ovrAvatar2LoadRequestState.Success
+                        || requestInfo.state == CAPI.ovrAvatar2LoadRequestState.Cancelled
+                        || requestInfo.state == CAPI.ovrAvatar2LoadRequestState.Failed);
+            }
+        }
+
+        private readonly List<LoadRequest> _loadRequests = new List<LoadRequest>();
+        private LoadRequest[] _loadRequestsArray = Array.Empty<LoadRequest>();
+        private bool _loadRequestsChanged = false;
+
+        // Registers an avatar load request. Active requests must be polled for updates
+        internal void RegisterLoadRequest(OvrAvatarEntity entity, CAPI.ovrAvatar2LoadRequestId requestId)
+        {
+            var request = new LoadRequest();
+            request.entity = entity;
+            request.requestId = requestId;
+            request.lastState = CAPI.ovrAvatar2LoadRequestState.None;
+
+            // Update immediately and call user callback for the initial state. This is done here because
+            // the state may change during the next SDK update, before callbacks are called
+            if (!request.Update())
+            {
+                _loadRequests.Add(request);
+                _loadRequestsChanged = true;
+            }
+        }
+
+        internal void RemoveLoadRequests(OvrAvatarEntity entity)
+        {
+            for (int idx = _loadRequests.Count - 1; idx >= 0; --idx)
+            {
+                if (_loadRequests[idx].entity == entity)
+                {
+                    _loadRequests.RemoveAt(idx);
+                }
+            }
+            _loadRequestsChanged = true;
+        }
+
+        internal void FinishedAvatarLoad()
+        {
+            Action nextLoadAction = null;
+            while (_loadQueue.Count > 0)
+            {
+                var e = _loadQueue.Dequeue();
+                if (_loadActions.TryGetValue(e, out nextLoadAction))
+                {
+                    _loadActions.Remove(e);
+                    break;
+                }
+            }
+
+            if (nextLoadAction != null)
+            {
+                nextLoadAction();
+            }
+            else
+            {
+                --_numAvatarsLoading;
+            }
+        }
+
+        internal void QueueLoadAvatar(OvrAvatarEntity entity, Action loadAction)
+        {
+            if (_numAvatarsLoading < MaxConcurrentAvatarsLoading)
+            {
+                ++_numAvatarsLoading;
+                loadAction();
+                return;
+            }
+
+            _loadActions.Add(entity, loadAction);
+            _loadQueue.Enqueue(entity);
+        }
+
+        internal void RemoveQueuedLoad(OvrAvatarEntity entity)
+        {
+            if (_loadActions.Remove(entity))
+            {
+                // Can only remove from front of queue, otherwise we will just ignore the entity when it comes up
+                // TODO: May not be worth even bothering with this
+                if (_loadQueue.Count > 0 && _loadQueue.Peek() == entity)
+                {
+                    _loadQueue.Dequeue();
+                }
+            }
+        }
+
+        #endregion // Avatar Loading
+
+        private void QueueResourceLoad(OvrAvatarResourceLoader loader)
+        {
+            OvrAvatarLog.Assert(!_resourceLoadQueue.Contains(loader), logScope, this);
+
+            if (_numResourcesLoading < MaxConcurrentResourcesLoading)
+            {
+                ++_numResourcesLoading;
+                loader.StartLoad();
+            }
+            else
+            {
+                _resourceLoadQueue.Enqueue(loader);
+            }
+        }
+        internal void ResourceLoadComplete(OvrAvatarResourceLoader finishedLoader)
+        {
+            ResourceLoadEnded(finishedLoader);
+        }
+        internal void ResourceLoadCancelled(OvrAvatarResourceLoader failedLoader)
+        {
+            ResourceLoadEnded(failedLoader);
+        }
+        private void ResourceLoadEnded(OvrAvatarResourceLoader finishedLoader)
+        {
+            while (_resourceLoadQueue.Count > 0)
+            {
+                var nextLoader = _resourceLoadQueue.Dequeue();
+                if (nextLoader.CanLoad)
+                {
+                    nextLoader.StartLoad();
+                    return;
+                }
+            }
+
+            --_numResourcesLoading;
+        }
+
+        internal Dictionary<CAPI.ovrAvatar2Id, OvrAvatarResourceLoader> GetResourceID()
+        {
+            return _resourcesByID;
+        }
+
+        private void _ShutdownSingleton<T>(T singletonInstance)
+            where T : OvrSingletonBehaviour<T>
+        {
+            _AvatarManagerCheckShutdown(this, singletonInstance);
+        }
+
+        private readonly struct AvatarRequestBoolResults
+        {
+            public AvatarRequestBoolResults(CAPI.ovrAvatar2Result res, bool? resBool)
+            { requestResult = res; resultBool = resBool; }
+
+            public readonly CAPI.ovrAvatar2Result requestResult;
+            public readonly bool? resultBool;
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarManager.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarManager.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..783c14d50b49e6d490da34ce744d7250448582c9
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarManager.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 0b47856a33046ce40862a2fddb86623b
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: -1
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarManager_Permissions.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarManager_Permissions.cs
new file mode 100644
index 0000000000000000000000000000000000000000..f8ae24aee4c3607af0877e45b2eca48cfb5a23b9
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarManager_Permissions.cs
@@ -0,0 +1,102 @@
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.Android;
+
+/// @file OvrAvatarManager_Textures.cs
+
+namespace Oculus.Avatar2
+{
+    public partial class OvrAvatarManager
+    {
+        public const string PERMISSION_EYE_TRACKING = "com.oculus.permission.EYE_TRACKING";
+        public const string PERMISSION_FACE_TRACKING = "com.oculus.permission.FACE_TRACKING";
+        public const string PERMISSION_BODY_TRACKING = "com.oculus.permission.BODY_TRACKING";
+
+        enum PermissionState { None, Requesting, Granted, Denied, DeniedNoAsk };
+        private readonly Queue<string> permissionQueue = new Queue<string>();
+        private readonly Dictionary<string, PermissionState> permissionCache = new Dictionary<string, PermissionState>();
+#pragma warning disable CS0414
+        private bool permissionManagerWaiting = false;
+#pragma warning restore CS0414
+
+        [Header("Permissions")]
+        [SerializeField]
+        public bool automaticallyRequestPermissions = true;
+
+        private void PermissionGranted(string permission)
+        {
+            OvrAvatarLog.LogInfo($"[GRANTED] {permission}");
+            permissionCache[permission] = PermissionState.Granted;
+            permissionManagerWaiting = false;
+        }
+        private void PermissionDenied(string permission)
+        {
+            OvrAvatarLog.LogInfo($"[DENIED] {permission}");
+            permissionCache[permission] = PermissionState.Denied;
+            permissionManagerWaiting = false;
+        }
+        private void PermissionDeniedAndDontAskAgain(string permission)
+        {
+            OvrAvatarLog.LogInfo($"[DENIED_NO_ASK] {permission}");
+            permissionCache[permission] = PermissionState.DeniedNoAsk;
+            permissionManagerWaiting = false;
+        }
+        public void RequestEyeTrackingPermission()
+        {
+            QueuePermissionRequest(PERMISSION_EYE_TRACKING);
+        }
+        public void RequestFaceTrackingPermission()
+        {
+            QueuePermissionRequest(PERMISSION_FACE_TRACKING);
+        }
+        public void RequestBodyTrackingPermission()
+        {
+            QueuePermissionRequest(PERMISSION_BODY_TRACKING);
+        }
+        public void RequestMicPermission()
+        {
+            QueuePermissionRequest(Permission.Microphone);
+        }
+        public void QueuePermissionRequest(string perm)
+        {
+            if (!permissionQueue.Contains(perm) && !permissionCache.ContainsKey(perm))
+            {
+                permissionQueue.Enqueue(perm);
+            }
+        }
+        public void EnablePermissionRequests()
+        {
+            automaticallyRequestPermissions = true;
+        }
+        private void Permission_Update()
+        {
+#if UNITY_ANDROID && !UNITY_EDITOR
+            if (automaticallyRequestPermissions && permissionQueue.Count>0 && !permissionManagerWaiting)
+            {
+                var perm = permissionQueue.Dequeue();
+                if (!permissionCache.ContainsKey(perm))
+                {
+                    if (!Permission.HasUserAuthorizedPermission(perm))
+                    {
+                        permissionManagerWaiting = true;
+                        OvrAvatarLog.LogInfo("Requesting: " + perm);
+                        var callbacks = new PermissionCallbacks();
+                        callbacks.PermissionGranted += PermissionGranted;
+                        callbacks.PermissionDenied += PermissionDenied;
+                        callbacks.PermissionDeniedAndDontAskAgain += PermissionDeniedAndDontAskAgain;
+                        Permission.RequestUserPermission(perm, callbacks);
+                    }
+                    else
+                    {
+                        permissionCache[perm] = PermissionState.Granted;
+                        OvrAvatarLog.LogInfo("Skipping Permission Request for: " + perm + " (Already requested and: " + permissionCache[perm] + ")");
+                    }
+                } else
+                {
+                    OvrAvatarLog.LogInfo("Skipping Permission Request for: "+perm+" (Already requested and: "+ permissionCache[perm] + ")");
+                }
+            }
+#endif
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarManager_Permissions.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarManager_Permissions.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..850eefac370ecae4eec0289c6c73b179b834e35f
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarManager_Permissions.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 433562f4086b11e4c8ba7a260d457273
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarManager_Skinning.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarManager_Skinning.cs
new file mode 100644
index 0000000000000000000000000000000000000000..76945208b166ebfb052d53ff8c9c6fc42c08652b
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarManager_Skinning.cs
@@ -0,0 +1,117 @@
+using System;
+
+using UnityEngine;
+
+using UnitySkinningQuality = UnityEngine.SkinWeights;
+
+/// @file OvrAvatarManager_Skinning.cs
+
+namespace Oculus.Avatar2
+{
+    public partial class OvrAvatarManager
+    {
+        ///
+        /// Skinning implementation types.
+        /// Used by @ref OvrAvatarManager to designate skinning implementation.
+        [Flags]
+        [System.Serializable]
+        public enum SkinnerSupport
+        {
+            /// NO RENDER - No rendering data is built or stored, sim only (headless server)
+            NONE = 0,
+            /// Mesh data is loaded into standard `Unity.Mesh` fields
+            UNITY = 1 << 0,
+            /// Animation mesh data is stored in AvatarSDK internal buffers, it is not available to Unity systems
+            OVR_CPU = 1 << 1,
+            /// Mesh data is primarily stored in textures and compute buffers, it is not available to Unity systems
+            OVR_GPU = 1 << 2,
+            /// DEBUG ONLY - All modes are supported, wastes lots of memory
+            ALL = ~0
+        }
+
+        [Header("Skinning Settings")]
+        [Tooltip("Skinning implementations which assets will be loaded for, use the smallest set possible.\nThere are significant memory and load time costs to enabling more than one.")]
+        [SerializeField]
+        [EnumMask]
+        private SkinnerSupport _skinnersSupported = SkinnerSupport.OVR_GPU;
+
+        [Header("Unity Skinning")]
+        [SerializeField]
+        private SkinQuality[] _skinQualityPerLOD = Array.Empty<SkinQuality>();
+
+        public bool UnitySMRSupported => (_skinnersSupported & SkinnerSupport.UNITY) == SkinnerSupport.UNITY;
+
+
+        private const int GpuSkinningRequiredFeatureLevel = 45;
+        private const int ComputeSkinningRequiredFeatureLevel = 45;
+
+        // OVR_CPU skinner currently unimplemented
+        public bool OvrCPUSkinnerSupported => false;
+
+        public bool OvrGPUSkinnerSupported =>
+            gpuSkinningShaderLevelSupported && (_skinnersSupported & SkinnerSupport.OVR_GPU) == SkinnerSupport.OVR_GPU;
+
+        public bool OvrComputeSkinnerSupported => false; // No support right now
+
+        public bool UnitySkinnerSupported =>
+            (_skinnersSupported & SkinnerSupport.UNITY) == SkinnerSupport.UNITY;
+
+        // Set via `Initialize`
+        private int _shaderLevelSupport = -1;
+        internal bool gpuSkinningShaderLevelSupported
+        {
+            get
+            {
+                Debug.Assert(_shaderLevelSupport >= 0);
+                return _shaderLevelSupport >= GpuSkinningRequiredFeatureLevel;
+            }
+        }
+
+        internal bool computeSkinningShaderLevelSupported
+        {
+            get
+            {
+                Debug.Assert(_shaderLevelSupport >= 0);
+                return _shaderLevelSupport >= ComputeSkinningRequiredFeatureLevel;
+            }
+        }
+
+        public SkinQuality GetUnitySkinQualityForLODIndex(uint lodIndex)
+        {
+            return lodIndex < _skinQualityPerLOD.Length ?
+                (SkinQuality)Mathf.Min((int)_skinQualityPerLOD[lodIndex], (int)HighestUnitySkinningQuality)
+                : HighestUnitySkinningQuality;
+        }
+
+        // Helper to query Unity skinWeights/boneWeights configuration as SkinningQuality enum
+        public SkinQuality HighestUnitySkinningQuality
+        {
+            get
+            {
+                switch (QualitySettings.skinWeights)
+                {
+                    case UnitySkinningQuality.OneBone:
+                        return SkinQuality.Bone1;
+                    case UnitySkinningQuality.TwoBones:
+                        return SkinQuality.Bone2;
+                    case UnitySkinningQuality.FourBones:
+                        return SkinQuality.Bone4;
+                }
+                return SkinQuality.Auto;
+            }
+        }
+
+        private void ValidateSupportedSkinners()
+        {
+            if (!gpuSkinningShaderLevelSupported && (_skinnersSupported & SkinnerSupport.OVR_GPU) == SkinnerSupport.OVR_GPU)
+            {
+                // gpu skinning not actually supported so remove from supported list.
+                _skinnersSupported &= ~SkinnerSupport.OVR_GPU;
+                if (_skinnersSupported == SkinnerSupport.NONE)
+                {
+                    _skinnersSupported = SkinnerSupport.UNITY;
+                }
+            }
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarManager_Skinning.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarManager_Skinning.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..5f6f3463c2dad9c7a2411b1ee178fc9793a99a24
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarManager_Skinning.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 5056ddf28969ce540b61a79d18ecc887
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarManager_Textures.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarManager_Textures.cs
new file mode 100644
index 0000000000000000000000000000000000000000..6df5a8e48e47134676e0cac3ca3a81758968071f
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarManager_Textures.cs
@@ -0,0 +1,30 @@
+using UnityEngine;
+
+/// @file OvrAvatarManager_Textures.cs
+
+namespace Oculus.Avatar2
+{
+    public partial class OvrAvatarManager
+    {
+        private const int TEXTURE_SETTINGS_INSPECTOR_ORDER = 256;
+
+        [Header("Avatar Texture Settings", order = TEXTURE_SETTINGS_INSPECTOR_ORDER)]
+
+        [SerializeField]
+        [Tooltip("Texture Filter Mode used for Avatar textures." +
+                 "\nTrilinear is highest quality with excellent performance." +
+                 "\nBilinear may produce artifacts at certain distances." +
+                 "\nPoint is primarily for stylistic usage.")]
+        private FilterMode _filterMode = OvrAvatarImage.defaultFilterMode;
+        
+        [SerializeField]
+        [Tooltip("Anisotropic filtering level used for Avatar textures. Higher values are more expensive to render." +
+                 "\n0 is off regardless of project Quality settings" +
+                 "\n1 is off unless forced on via project Quality settings")]
+        [Range(0, 16)]
+        private int _anisoLevel = OvrAvatarImage.defaultAnisoLevel;
+
+        public FilterMode TextureFilterMode => _filterMode;
+        public int TextureAnisoLevel => _anisoLevel;
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarManager_Textures.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarManager_Textures.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..47fdfc39f1e48fad7efbcd96f88bbb412fafd796
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarManager_Textures.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 4a7621be02002134cb2c1f2b10898384
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarMaterial.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarMaterial.cs
new file mode 100644
index 0000000000000000000000000000000000000000..78a82124d0a99e36f99ea3da8ccac5ed4544bb26
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarMaterial.cs
@@ -0,0 +1,720 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+/*
+ * @file OvrAvatarMaterial.cs
+ */
+
+namespace Oculus.Avatar2
+{
+    /**
+     * @class OvrAvatarMaterial
+     * Collects shader and material properties to apply to an avatar.
+     *
+     * OvrAvatarMaterial is like the Unity MaterialPropertyBlock.
+     * It has getters and setters for the various types of material properties.
+     * Unlike MaterialPropertyBlock, this class remembers and can serialize
+     * the names and values of the properties that have been changed.
+     *
+     * Each OvrAvatarEntity can have an instance of this class to maintain
+     * the current shader, its keywords and material properties.
+     * You can access it using OvrAvatarEntity.material and then
+     * set individual properties. OvrAvatarEntity.ApplyMaterial()
+     * will apply the new shader / material to all the renderables
+     * in the avatar.
+     *
+     * @code
+     *  OvrAvatarEntity avatar1;
+     *  avatar1.Material.SetColor("_EmissionColor", Color.blue);
+     *  avatar1.Material.SetKeyword("_EMISSION", true);
+     *  avatar1.ApplyMaterial();
+     * @endcode
+     *
+     * This material state is also applied to new renderables that
+     * are added to the avatar entity.
+     *
+     * You can also set the material properties based on the ID
+     * provided by Shader.PropertyToId(). Even if you don't provide
+     * an ID, OvrAvatarMaterial maintains one internally so you can
+     * add a property using the name but retrieve it from the ID.
+     *
+     * @code
+     *  OvrAvatarEntity avatar2;
+     *  int propID = Shader.PropertyToId("_EmissionColor");
+     *  Shader shader2 = Shader.Find("Avatar/Standard");
+     *  avatar2.Material.Shader = shader2;
+     *  avatar2.Material.SetColor(propID, Color.blue);
+     *  avatar2.ApplyMaterial();
+     * @endcode
+     *
+     *  Avatars can share materials as well. If a material is provided
+     *  before the avatar is loaded, the material will be applied to all the
+     *  future renderables as well.
+     *
+     * @code
+     *  OvrAvatarMaterial desat = new OvrAvatarMaterial();
+     *  desat.SetKeyword("DESAT", true);
+     *  desat.SetFloat("_DesatAmount", 0.7f);
+     *  desat.SetColor("_DesatTint", new Color(0.13f, 0.2f, 0.4f);
+     *  desat.SetFloat("_DesatLerp", 0.4f);
+     *  avatar1.Material = desat;
+     *  avatar2.Material = desat;
+     *  avatar1.ApplyMaterial();
+     *  avatar2.ApplyMaterial();
+     * @endcode
+     *
+     * As an optimization, you can set and get material properties
+     * based on their integer ID instead of a string name. This ID
+     * is obtained by calling Shader.PropertyToId() with the  material property name.
+     * If you first set the property using the name, OvrAvatarMaterial will
+     * be able to retrieve it by name or by ID. If you first set the property
+     * using the ID, you will not be able to retrieve it by name.
+     *
+     * @see OvrAvatarEntity.Material
+     */
+    public sealed class OvrAvatarMaterial
+    {
+        /*
+         * type tokens for material properties
+         */
+        private enum PropertyType
+        {
+            NONE = 0,
+            INTEGER = 1,
+            FLOAT = 2,
+            COLOR = 3,
+            VEC4 = 4,
+            MATRIX = 5,
+            FLOAT_ARRAY = 6,
+            VEC_ARRAY = 7,
+            TEXTURE = 8
+        };
+
+        private static Dictionary<string, int> g_shaderIdLookup = default;
+
+        private readonly Dictionary<int, ValueTuple<object, PropertyType>> properties_
+            = new Dictionary<int, ValueTuple<object, PropertyType>>();
+
+        private readonly Dictionary<string, bool> ShaderKeywords = new Dictionary<string, bool>();
+
+        private MaterialPropertyBlock propertyBlock_;
+        private Shader Shader = null;
+
+        public OvrAvatarMaterial()
+        {
+            propertyBlock_ = new MaterialPropertyBlock();
+
+            // Check for static initialization in instance ctor to avoid implicit static ctor
+            if (g_shaderIdLookup == null)
+            {
+                g_shaderIdLookup = new Dictionary<string, int>();
+            }
+        }
+
+
+        /**
+         * Clears the contents of this instance, removing shader,
+         * shader keywords and material properties.
+         *
+         * This does not change the appearance of avatars
+         * using this instance until OvrAvatarEntity.ApplyMaterial
+         * is explicitly called on each avatar using it.
+         *
+         * @see OvrAvatarEntity.ApplyMaterial()
+         * @see OvrAvatarEntity.Material
+         */
+        public void Clear()
+        {
+            properties_.Clear();
+            ShaderKeywords.Clear();
+
+            propertyBlock_ = null;
+            Shader = null;
+        }
+
+        public void SetShader(Shader shader) => Shader = shader;
+
+
+        /**
+         * Gets the value of the specified shader keyword.
+         * @param keyword  name of the shader keyword to check.
+         * @returns true if the keyword has been enabled by this material, else false.
+         *          false if the keyword is disabled or has never been set.
+         * @see HasKeyword(string)
+         * @see SetKeyword(string, boolean)
+         * @see RemoveKeyword(string)
+         */
+        public bool GetKeyword(string keyword)
+        {
+            return ShaderKeywords.TryGetValue(keyword, out bool val) && val;
+        }
+
+        /**
+         * Indicates whether a specific shader keyword has been set in this material.
+         * @param keyword  name of the shader keyword to check.
+         * @returns true if the keyword has been enabled or disabled,
+         *          false if it has never been set.
+         * @see GetKeyword(string)
+         * @see SetKeyword(string, boolean)
+         * @see RemoveKeyword(string)
+         */
+        public bool HasKeyword(string keyword)
+        {
+            return ShaderKeywords.ContainsKey(keyword);
+        }
+
+        /**
+         * Enables or disables a specific shader keyword.
+         * @param keyword  name of the shader keyword to change.
+         * @boolean val    true to enable the keyword, false to disable it.
+         *
+         * This change will not change the avatars which use
+         * this material unless you call OvrAvatarEntity.ApplyMaterial()
+         * for all avatars that share this instance. It will affect
+         * future renderables added to these avatars.
+         *
+         * After this call, HasKeyword() will return true.
+         *
+         * @see GetKeyword(string)
+         * @see HasKeyword(string)
+         * @see RemoveKeyword(string)
+         */
+        public void SetKeyword(string keyword, bool enable)
+        {
+            ShaderKeywords[keyword] = enable;
+        }
+
+        /**
+         * Removes a shader keyword from this material.
+         * @param keyword name of shader keyword to remove.
+         *
+         * After this call, HasKeyword() will return false.
+         * @see GetKeyword(string)
+         * @see HasKeyword(string)
+         * @see SetKeyword(string, boolean)
+         */
+        public void RemoveKeyword(string keyword)
+        {
+            ShaderKeywords.Remove(keyword);
+        }
+
+        /**
+         * Gets the value of a Color material property with the given name.
+         * @param name    name of the material property to retrieve.
+         * @returns Color value of property.
+         * @throws ArgumentNullException if the name is null.
+         * @throws KeyNotFoundException if there is no color property with that name.
+         * @see SetColor(string, Color)
+         * @see GetColor(int)
+         */
+        public Color GetColor(string name)
+        {
+            int nameID = GetShaderIDForProperty(name);
+            return GetColor(nameID);
+        }
+
+        /**
+         * Gets the value of a Color material property with the given ID.
+         * @param nameID  an ID returned by Shader.PropertyToId()
+         * @returns Color value of property.
+         * @throws ArgumentOutOfRangeException if there is no color property with that name.
+         * @see SetColor(int, Color)
+         * @see GetColor(string)
+         */
+        public Color GetColor(int nameID)
+        {
+            return (Color)properties_[nameID].Item1;
+        }
+
+        /**
+         * Gets the value of a float material property with the given name.
+         * @param name    name of the material property to retrieve.
+         * @returns float value of property.
+         * @throws ArgumentNullException if the name is null.
+         * @throws KeyNotFoundException if there is no float property with that name.
+         * @see SetFloat(string, float)
+         */
+        public float GetFloat(string name)
+        {
+            int nameID = GetShaderIDForProperty(name);
+            return GetFloat(nameID);
+        }
+
+        /**
+         * Gets the value of a float material property with the given ID.
+         * @param nameID  an ID returned by Shader.PropertyToId()
+         * @returns float value of property.
+         * @throws ArgumentOutOfRangeException if there is no float property with that name.
+         * @see SetFloat(int, float)
+         * @see GetFloat(string)
+         */
+        public float GetFloat(int nameID)
+        {
+            return (float)properties_[nameID].Item1;
+        }
+
+        /**
+         * Gets the value of a float array material property with the given name.
+         * @param name    name of the material property to retrieve.
+         * @returns float[] value of property.
+         * @throws ArgumentNullException if the name is null.
+         * @throws KeyNotFoundException if there is no float array property with that name.
+         * @see SetFloatArray(string, float[])
+         */
+        public float[] GetFloatArray(string name)
+        {
+            int nameID = GetShaderIDForProperty(name);
+            return GetFloatArray(nameID);
+        }
+
+        /**
+         * Gets the value of a float array material property with the given ID.
+         * @param nameID  an ID returned by Shader.PropertyToId()
+         * @returns float[] value of property.
+         * @throws ArgumentOutOfRangeException if there is no float array property with that name.
+         * @see SetFloatArray(int, float[])
+         * @see GetFloatArray(string)
+         */
+        public float[] GetFloatArray(int nameID)
+        {
+            return (float[])properties_[nameID].Item1;
+        }
+
+        /**
+         * Gets the value of an integer material property with the given name.
+         * @param name    name of the material property to retrieve.
+         * @returns integer value of property.
+         * @throws ArgumentNullException if the name is null.
+         * @throws KeyNotFoundException if there is no integer property with that name.
+         * @see SetInt(string, int)
+         */
+        public int GetInt(string name)
+        {
+            int nameID = GetShaderIDForProperty(name);
+            return GetInt(nameID);
+        }
+
+        /**
+         * Gets the value of a integer material property with the given ID.
+         * @param nameID  an ID returned by Shader.PropertyToId()
+         * @returns integer value of property.
+         * @throws ArgumentOutOfRangeException if there is no integer property with that name.
+         * @see SetInt(int, int)
+         * @see GetInt(string)
+         */
+        public int GetInt(int nameID)
+        {
+            return (int)properties_[nameID].Item1;
+        }
+
+        /**
+         * Gets the value of a Matrix4x4 material property with the given name.
+         * @param name    name of the material property to retrieve.
+         * @returns Matrix4x4 value of property.
+         * @throws ArgumentNullException if the name is null.
+         * @throws KeyNotFoundException if there is no matrix property with that name.
+         * @see SetMatrix(string, Matrix4x4)
+         */
+        public Matrix4x4 GetMatrix(string name)
+        {
+            int nameID = GetShaderIDForProperty(name);
+            return GetMatrix(nameID);
+        }
+
+        /**
+         * Gets the value of a matrix array material property with the given ID.
+         * @param nameID  an ID returned by Shader.PropertyToId()
+         * @returns Matrix4x4[] value of property.
+         * @throws ArgumentOutOfRangeException if there is no matrix array property with that name.
+         * @see SetMatrixArray(int, Matrix4x4[])
+         * @see GetMatrixArray(string)
+         */
+        public Matrix4x4 GetMatrix(int nameID)
+        {
+            return (Matrix4x4)properties_[nameID].Item1;
+        }
+
+        /**
+         * Gets the value of a Texture material property with the given name.
+         * @param name    name of the material property to retrieve.
+         * @returns Texture value of property.
+         * @throws ArgumentNullException if the name is null.
+         * @throws KeyNotFoundException if there is no Texture property with that name.
+         * @see SetTexture(string, Texture)
+         */
+        public Texture GetTexture(string name)
+        {
+            int nameID = GetShaderIDForProperty(name);
+            return GetTexture(nameID);
+        }
+
+        /**
+         * Gets the value of a Texture material property with the given ID.
+         * @param nameID  an ID returned by Shader.PropertyToId()
+         * @returns Texture value of property.
+         * @throws ArgumentOutOfRangeException if there is no Texture property with that name.
+         * @see SetTexture(int, CoTexturelor)
+         * @see GetTexture(string)
+         */
+        public Texture GetTexture(int nameID)
+        {
+            return (Texture)properties_[nameID].Item1;
+        }
+
+        /**
+         * Gets the value of a Vector4 material property with the given name.
+         * @param name    name of the material property to retrieve.
+         * @returns Vector4 value of property.
+         * @throws ArgumentNullException if the name is null.
+         * @throws KeyNotFoundException if there is no vector property with that name.
+         * @see SetVector(string, Vector4)
+         */
+        public Vector4 GetVector(string name)
+        {
+            int nameID = GetShaderIDForProperty(name);
+            return GetVector(nameID);
+        }
+
+        /**
+         * Gets the value of a Vector4 material property with the given ID.
+         * @param nameID  an ID returned by Shader.PropertyToId()
+         * @returns Vector4 value of property.
+         * @throws ArgumentOutOfRangeException if there is no color property with that name.
+         * @see SetVector(int, Vector4)
+         * @see GetVector(string)
+         */
+        public Vector4 GetVector(int nameID)
+        {
+            return (Vector4)properties_[nameID].Item1;
+        }
+
+        /**
+         * Gets the value of a vector array material property with the given name.
+         * @param name    name of the material property to retrieve.
+         * @returns Vector4[] value of property.
+         * @throws ArgumentNullException if the name is null.
+         * @throws KeyNotFoundException if there is no vector array property with that name.
+         * @see SetVectorArray(string, Vector4[])
+         */
+        public Vector4[] GetVectorArray(string name)
+        {
+            int nameID = GetShaderIDForProperty(name);
+            return GetVectorArray(nameID);
+        }
+
+        /**
+         * Gets the value of a vector array material property with the given ID.
+         * @param nameID  an ID returned by Shader.PropertyToId()
+         * @returns Vector4[] value of property.
+         * @throws ArgumentOutOfRangeException if there is no vector array property with that name.
+         * @see SetVecArray(int, Vector4[])
+         * @see GetVecArray(string)
+         */
+        public Vector4[] GetVectorArray(int nameID)
+        {
+            return properties_[nameID].Item1 as Vector4[];
+        }
+
+        /**
+         * Sets the value of a Color material property with the given name.
+         * @param name    name of the material property to set.
+         * @returns ShaderID of name
+         * @throws ArgumentNullException if the name is null.
+         * @throws KeyNotFoundException if there is no color property with that name.
+         * @see GetColor(string)
+         * @see SetColor(int, Color)
+         */
+        public int SetColor(string name, Color value)
+        {
+            int nameID = GetShaderIDForProperty(name);
+            SetColor(nameID, value);
+            return nameID;
+        }
+
+        /**
+         * Sets the value of a Color material property with the given ID.
+         * @param nameID  an ID returned by Shader.PropertyToId()
+         * @throws ArgumentNullException if the name is null.
+         * @throws KeyNotFoundException if there is no color property with that name.
+         * @see GetColor(int)
+         * @see SetColor(string, Color)
+         */
+        public void SetColor(int nameID, Color colorValue)
+            => SetProperty(nameID, in colorValue, PropertyType.COLOR);
+
+        /**
+         * Sets the value of a float material property with the given name.
+         * @param name    name of the material property to set.
+         * @returns ShaderID of name
+         * @throws ArgumentNullException if the name is null.
+         * @throws KeyNotFoundException if there is no float property with that name.
+         * @see GetFloat(string)
+         * @see SetFloat(int, float)
+         */
+        public int SetFloat(string name, float value)
+        {
+            int nameID = GetShaderIDForProperty(name);
+            SetFloat(nameID, value);
+            return nameID;
+        }
+
+        /**
+         * Sets the value of a float material property with the given ID.
+         * @param nameID  an ID returned by Shader.PropertyToId()
+         * @returns float new value of property.
+         * @throws ArgumentNullException if the name is null.
+         * @throws KeyNotFoundException if there is no float property with that name.
+         * @see GetFloat(int)
+         * @see SetFloat(string, float)
+         */
+        public void SetFloat(int nameID, float floatValue)
+            => SetProperty(nameID, floatValue, PropertyType.FLOAT);
+
+        /**
+         * Sets the value of a float array material property with the given name.
+         * @param name    name of the material property to set.
+         * @returns ShaderID of name
+         * @throws ArgumentNullException if the name is null.
+         * @throws KeyNotFoundException if there is no float array property with that name.
+         * @see GetFloatArray(string)
+         * @see SetFloatArray(int, float[])
+         */
+        public int SetFloatArray(string name, float[] values)
+        {
+            int nameID = GetShaderIDForProperty(name);
+            SetFloatArray(nameID, values);
+            return nameID;
+        }
+
+        /**
+         * Sets the value of a float array material property with the given ID.
+         * @param nameID  an ID returned by Shader.PropertyToId()
+         * @throws ArgumentNullException if the name is null.
+         * @throws KeyNotFoundException if there is no float array property with that name.
+         * @see GetFloatArray(int)
+         * @see SetFloatArray(string, float[])
+         */
+        public void SetFloatArray(int nameID, float[] floatArrayValues)
+            => SetProperty(nameID, in floatArrayValues, PropertyType.FLOAT_ARRAY);
+
+        /**
+         * Sets the value of an integer material property with the given name.
+         * @param name    name of the material property to set.
+         * @returns ShaderID of name
+         * @throws ArgumentNullException if the name is null.
+         * @throws KeyNotFoundException if there is no integer property with that name.
+         * @see GetInt(string)
+         * @see SetInt(int, int)
+         */
+        public int SetInt(string name, int value)
+        {
+            int nameID = GetShaderIDForProperty(name);
+            SetInt(nameID, value);
+            return nameID;
+        }
+
+        /**
+         * Sets the value of an integer material property with the given ID.
+         * @param nameID  an ID returned by Shader.PropertyToId()
+         * @throws ArgumentNullException if the name is null.
+         * @throws KeyNotFoundException if there is no integer property with that name.
+         * @see GetInt(int)
+         * @see SetInt(string, int)
+         */
+        public void SetInt(int nameID, int intValue)
+            => SetProperty(nameID, in intValue, PropertyType.INTEGER);
+
+        /**
+         * Sets the value of a Matrix4x4 material property with the given name.
+         * @param name    name of the material property to set.
+         * @returns ShaderID of name
+         * @throws ArgumentNullException if the name is null.
+         * @throws KeyNotFoundException if there is no matrix property with that name.
+         * @see GetMatrix(string)
+         * @see SetMatrix(int, Matrix4x4)
+         */
+        public int SetMatrix(string name, Matrix4x4 value)
+        {
+            int nameID = GetShaderIDForProperty(name);
+            SetMatrix(nameID, value);
+            return nameID;
+        }
+
+        /**
+         * Sets the value of a Matrix4x4 material property with the given ID.
+         * @param nameID  an ID returned by Shader.PropertyToId()
+         * @throws ArgumentNullException if the name is null.
+         * @throws KeyNotFoundException if there is no matrix property with that name.
+         * @see GetMatrix(int)
+         * @see SetMatrix(string, Matrix4x4)
+         */
+        public void SetMatrix(int nameID, Matrix4x4 matValue)
+            => SetProperty(nameID, in matValue, PropertyType.MATRIX);
+
+        /**
+         * Sets the value of a Texture material property with the given name.
+         * @param name    name of the material property to set.
+         * @returns ShaderID of name
+         * @throws ArgumentNullException if the name is null.
+         * @throws KeyNotFoundException if there is no Texture property with that name.
+         * @see GetTexture(string)
+         * @see SetTexture(int, Texture)
+         */
+        public int SetTexture(string name, Texture value)
+        {
+            int nameID = GetShaderIDForProperty(name);
+            SetTexture(nameID, value);
+            return nameID;
+        }
+
+        /**
+         * Sets the value of a Texture material property with the given ID.
+         * @param nameID  an ID returned by Shader.PropertyToId()
+         * @throws ArgumentNullException if the name is null.
+         * @throws KeyNotFoundException if there is no Texture property with that name.
+         * @see GetTexture(int)
+         * @see SetTexture(string, Texture)
+         */
+        public void SetTexture(int nameID, Texture textureValue)
+            => SetProperty(nameID, in textureValue, PropertyType.TEXTURE);
+
+        /**
+         * Sets the value of a Vector4 material property with the given name.
+         * @param name    name of the material property to set.
+         * @returns ShaderID of name
+         * @throws ArgumentNullException if the name is null.
+         * @throws KeyNotFoundException if there is no vector property with that name.
+         * @see GetVector(string)
+         * @see SetVector(int, Vector4)
+         */
+        public int SetVector(string name, Vector4 value)
+        {
+            int nameID = GetShaderIDForProperty(name);
+            SetVector(nameID, value);
+            return nameID;
+        }
+
+        /**
+         * Sets the value of a Vector4 material property with the given ID.
+         * @param nameID  an ID returned by Shader.PropertyToId()
+         * @throws ArgumentNullException if the name is null.
+         * @throws KeyNotFoundException if there is no vector property with that name.
+         * @see GetVector(int)
+         * @see SetVector(string, Vector4)
+         */
+        public void SetVector(int nameID, Vector4 vec4Value)
+            => SetProperty(nameID, in vec4Value, PropertyType.VEC4);
+
+        /**
+         * Sets the value of a vector array material property with the given name.
+         * @param name    name of the material property to set.
+         * @returns ShaderID of name
+         * @throws ArgumentNullException if the name is null.
+         * @throws KeyNotFoundException if there is no vector array property with that name.
+         * @see GetVectorArray(string)
+         * @see SetVectorArray(int, Vector4[])
+         */
+        public int SetVectorArray(string name, Vector4[] values)
+        {
+            int nameID = GetShaderIDForProperty(name);
+            SetVectorArray(nameID, values);
+            return nameID;
+        }
+
+        /**
+         * Sets the value of a vector array material property with the given ID.
+         * @param nameID  an ID returned by Shader.PropertyToId()
+         * @throws ArgumentNullException if the name is null.
+         * @throws KeyNotFoundException if there is no vector array property with that name.
+         * @see GetVectorArray(int)
+         * @see SetVectorArray(string, Color)
+         */
+        public void SetVectorArray(int nameID, Vector4[] vec4Values)
+            => SetProperty(nameID, in vec4Values, PropertyType.VEC_ARRAY);
+
+        /**
+         * Applies the current material state (shader, keywords, properties)
+         * to a specific renderable.
+         * @param renderable OvrAvatarRenderable to apply this material to.
+         * Updates the Unity MaterialPropertyBlock associated with the renderable.
+         */
+        public void Apply(OvrAvatarRenderable renderable)
+        {
+            Renderer rend = renderable.rendererComponent;
+            rend.GetPropertyBlock(propertyBlock_);
+            if (Shader != null)
+            {
+                renderable.SetShader(Shader);
+            }
+
+            foreach (var entry in ShaderKeywords)
+            {
+                renderable.SetMaterialKeyword((string)entry.Key, (Boolean)entry.Value);
+            }
+
+            foreach (var pair in properties_)
+            {
+                object val = pair.Value.Item1;
+
+                switch (pair.Value.Item2)
+                {
+                    case PropertyType.INTEGER:
+                        propertyBlock_.SetInt(pair.Key, (int)val);
+                        break;
+
+                    case PropertyType.FLOAT:
+                        propertyBlock_.SetFloat(pair.Key, (float)val);
+                        break;
+
+                    case PropertyType.COLOR:
+                        propertyBlock_.SetColor(pair.Key, (Color)val);
+                        break;
+
+                    case PropertyType.TEXTURE:
+                        propertyBlock_.SetTexture(pair.Key, (Texture)val);
+                        break;
+
+                    case PropertyType.VEC4:
+                        propertyBlock_.SetVector(pair.Key, (Vector4)val);
+                        break;
+
+                    case PropertyType.MATRIX:
+                        propertyBlock_.SetMatrix(pair.Key, (Matrix4x4)val);
+                        break;
+
+                    case PropertyType.FLOAT_ARRAY:
+                        propertyBlock_.SetFloatArray(pair.Key, (float[])val);
+                        break;
+
+                    case PropertyType.VEC_ARRAY:
+                        propertyBlock_.SetVectorArray(pair.Key, (Vector4[])val);
+                        break;
+
+                    case PropertyType.NONE:
+                        break;
+                }
+            }
+
+            rend.SetPropertyBlock(propertyBlock_);
+        }
+
+        private void SetProperty<T>(int nameID, in T val, PropertyType type)
+        {
+            // ReSharper disable once HeapView.PossibleBoxingAllocation
+            properties_[nameID] = new ValueTuple<object, PropertyType>(val, type);
+        }
+
+        private static int GetShaderIDForProperty(string propertyName)
+        {
+            System.Diagnostics.Debug.Assert(g_shaderIdLookup != null);
+            if (!g_shaderIdLookup.TryGetValue(propertyName, out var shaderId))
+            {
+                shaderId = Shader.PropertyToID(propertyName);
+                g_shaderIdLookup.Add(propertyName, shaderId);
+            }
+
+            return shaderId;
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarMaterial.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarMaterial.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..6b604e2bf460416dd02603fc8d6ac4fda638c631
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarMaterial.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 3349219840685e04798994568d583221
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarPerformanceAnalytics.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarPerformanceAnalytics.cs
new file mode 100644
index 0000000000000000000000000000000000000000..b1d1830344f0b28941c147d736f075e08b08d59c
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarPerformanceAnalytics.cs
@@ -0,0 +1,62 @@
+using System;
+namespace Oculus.Avatar2
+{
+    public static class OvrAvatarPerformanceAnalytics
+    {
+        //:: Constants
+        private const string logScope = "performance_analytics";
+
+        private static byte[] toByteArray(string str, ref UInt32 size)
+        {
+            if (str == null)
+            {
+                size = 0;
+                return null;
+            }
+
+            var bytes = System.Text.ASCIIEncoding.ASCII.GetBytes(str.ToCharArray());
+            size = (UInt32)bytes.Length;
+            return bytes;
+        }
+
+        public static void enable(string testAppName, uint approxSampleCount = 0)
+        {
+            unsafe
+            {
+                UInt32 size = 0;
+                var bytes = toByteArray(testAppName, ref size);
+                fixed (byte* ptr = bytes)
+                {
+                }
+            }
+        }
+
+
+        public static bool updateMetric(Int32 metric, double value)
+        {
+            return false;
+        }
+
+
+        public static bool sendMetric(Int32 metric, double value, string comment = null, byte[] payload = null)
+        {
+            var payloadSize = payload == null ? 0 : (UInt32)payload.Length;
+            UInt32 commentSize = 0;
+
+            unsafe
+            {
+                var commentBytes = toByteArray(comment, ref commentSize);
+                fixed (byte* commentPtr = commentBytes, payloadPtr = payload)
+                {
+                    return false;
+                }
+            }
+        }
+
+
+        public static void begin()
+        {
+        }
+
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarPerformanceAnalytics.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarPerformanceAnalytics.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..aa5c3536cee4b905a0589dbc8addab1efb2bf2d9
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarPerformanceAnalytics.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 043ce61ac59f09047bcde70fdb4a7a7f
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarPlugin.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarPlugin.cs
new file mode 100644
index 0000000000000000000000000000000000000000..7fa7951f1de448d52e79cc87ac85b973c860c8ae
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarPlugin.cs
@@ -0,0 +1,14 @@
+namespace Oculus.Avatar2
+{
+    public static class OvrAvatarPlugin
+    {
+#if UNITY_EDITOR_OSX
+        public const string PluginFolderPath = "Assets/Oculus/Avatar2/Plugins/";
+        public const string InternalPluginFolderPath = "Assets/Internal/Plugins/";
+        public const string FlavorFolder = "";
+        public const string OSFolder = "Macos/";
+        public const string FullPluginFolderPath = PluginFolderPath + FlavorFolder + OSFolder;
+        public const string FullInternalPluginFolderPath = InternalPluginFolderPath + FlavorFolder + OSFolder;
+#endif  // UNITY_EDITOR_OSX
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarPlugin.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarPlugin.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..184a25dbd2ba8a1ae9f296872fc3fb0c2b2aa634
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarPlugin.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: d799d302027e1404ca25a079847214f1
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarRenderable.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarRenderable.cs
new file mode 100644
index 0000000000000000000000000000000000000000..aa14aced3b5418076a8c4aabb277cf680edf56ef
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarRenderable.cs
@@ -0,0 +1,431 @@
+using System;
+
+using UnityEngine;
+
+using ShaderType = Oculus.Avatar2.OvrAvatarShaderManagerBase.ShaderType;
+
+/**
+ * @file OvrAvatarRenderable.cs
+ */
+namespace Oculus.Avatar2
+{
+    [RequireComponent(typeof(MeshFilter))]
+    /**
+     * @class OvrAvatarRenderable
+     * Component that encapsulates the meshes of an avatar.
+     * This component can only be added to game objects that
+     * have a Unity Mesh and a Mesh filter.
+     *
+     * Each OvrAvatarRenderable has one OvrAvatarPrimitive
+     * that encapsulates the Unity Mesh and Material rendered.
+     * Primitives may be shared across renderables but
+     * renderables cannot be shared across avatars.
+     *
+     * @see OvrAvatarPrimitive
+     * @see ApplyMeshPrimitive
+     */
+    public class OvrAvatarRenderable : MonoBehaviour, IDisposable
+    {
+        private string _logScopeCache = null;
+        protected string logScope => _logScopeCache ??= GetType().Name;
+
+        private const string OVR_VERTEX_HAS_TANGENTS_KEYWORD = "OVR_VERTEX_HAS_TANGENTS";
+        private const string OVR_VERTEX_NO_TANGENTS_KEYWORD = "OVR_VERTEX_NO_TANGENTS";
+
+        private const string OVR_VERTEX_INTERPOLATE_ATTRIBUTES_KEYWORD = "OVR_VERTEX_INTERPOLATE_ATTRIBUTES";
+        private const string OVR_VERTEX_DO_NOT_INTERPOLATE_ATTRIBUTES_KEYWORD = "OVR_VERTEX_DO_NOT_INTERPOLATE_ATTRIBUTES";
+
+        private const string OVR_VERTEX_FETCH_VERT_BUFFER = "OVR_VERTEX_FETCH_VERT_BUFFER";
+        private const string OVR_VERTEX_FETCH_TEXTURE_KEYWORD = "OVR_VERTEX_FETCH_TEXTURE";
+        private const string OVR_VERTEX_FETCH_EXTERNAL_BUFFER_KEYWORD = "OVR_VERTEX_FETCH_EXTERNAL_BUFFER";
+
+        // Make sure these match the shader
+        protected enum VertexFetchMode
+        {
+            VertexBuffer = 0,
+            ExternalBuffers = 1,
+            ExternalTextures = 2,
+        }
+
+        /// Designates whether this renderable is visible or not.
+        public bool Visible
+        {
+            get => _isVisible;
+            set
+            {
+                if (_isVisible != value)
+                {
+                    var wasRendered = IsRendered;
+                    _isVisible = value;
+                    var isRendered = IsRendered;
+                    if (wasRendered != isRendered)
+                    {
+                        OnVisibilityChanged(isRendered);
+                    }
+                }
+            }
+        }
+
+        /// Designates whether this renderable is hidden or not.
+        public bool IsHidden
+        {
+            get => _isHidden;
+            set
+            {
+                if (_isHidden != value)
+                {
+                    var wasRendered = IsRendered;
+                    _isHidden = value;
+                    var isRendered = IsRendered;
+                    if (wasRendered != isRendered)
+                    {
+                        OnVisibilityChanged(isRendered);
+                    }
+                }
+            }
+        }
+
+        public bool IsRendered => _isVisible && !_isHidden;
+
+        /// Triangle and vertex counts for all levels of detail.
+        public ref readonly AvatarLODCostData CostData => ref AppliedPrimitive.CostData;
+
+        /// Get which view(s) (first person, third person) this renderable applies to.
+        /// These are established when the renderable is loaded.
+        public CAPI.ovrAvatar2EntityViewFlags viewFlags => AppliedPrimitive.viewFlags;
+
+        /// LOD bit flags for this renderable.
+        /// These flags indicate which levels of detail this renderable is used by.
+        public CAPI.ovrAvatar2EntityLODFlags lodFlags => AppliedPrimitive.lodFlags;
+
+        /// Get which body parts of the avatar this renderable is used by.
+        /// These are established when the renderable is loaded.
+        public CAPI.ovrAvatar2EntityManifestationFlags manifestationFlags => AppliedPrimitive.manifestationFlags;
+
+        /// Get whether this renderable has a mesh
+        public bool HasMesh => MyMesh != null;
+
+        /// Get the submeshes that are to be rendered.
+        /// As excluded by the index buffer.
+        public CAPI.ovrAvatar2EntitySubMeshInclusionFlags subMeshInclusionFlags => AppliedPrimitive.subMeshInclusionFlags;
+
+        /// Get the quality flag preferences. This let's you render the asset differently, based on what's in the asset.
+        public CAPI.ovrAvatar2EntityHighQualityFlags highQualityFlags => AppliedPrimitive.highQualityFlags;
+
+        /// Get the vertex count of this renderable's mesh
+        public int MeshVertexCount => MyMesh.vertexCount;
+
+        /// Get the Unity Renderer used to render this renderable.
+        public Renderer rendererComponent { get; private set; } = null;
+
+#pragma warning disable CA2213 // Disposable fields should be disposed - it is not owned by this class
+        protected OvrAvatarPrimitive AppliedPrimitive { get; private set; } = null;
+#pragma warning restore CA2213 // Disposable fields should be disposed
+
+        protected Mesh MyMesh { get; private set; } = null;
+        protected MeshFilter MyMeshFilter { get; private set; } = null;
+
+        /// True if this renderable has tangents for each vertex.
+        protected bool HasTangents => AppliedPrimitive.hasTangents;
+
+        protected virtual bool InterpolateAttributes => false;
+
+        protected virtual VertexFetchMode VertexFetchType => VertexFetchMode.VertexBuffer;
+
+        private Material _materialCopy = null;
+
+        private bool _isVisible = false;
+        private bool _isHidden = false;
+
+        public int originalNumberIndices = 0;
+        public UInt16[] originalIndexBuffer = null;
+        private static AttributePropertyIds _propertyIds = default;
+
+        protected MaterialPropertyBlock MatBlock { get; private set; }
+
+        private static void CheckPropertyIdInit()
+        {
+            if (!_propertyIds.IsValid)
+            {
+                _propertyIds = new AttributePropertyIds(AttributePropertyIds.InitMethod.PropertyToId);
+            }
+        }
+
+        /**
+         * Sets the specified shader keyword for the material on this renderable.
+         * @see SetShader
+         * @see OvrAvatarMaterial
+         */
+        public void SetMaterialKeyword(string keyword, bool enable)
+        {
+            CopyMaterial();
+            if (enable)
+            {
+                _materialCopy.EnableKeyword(keyword);
+            }
+            else
+            {
+                _materialCopy.DisableKeyword(keyword);
+            }
+        }
+
+        /**
+         * Sets the shader for the material on this renderable.
+         * @see SetMaterialKeyword
+         * @see OvrAvatarMaterial
+         */
+        public void SetShader(Shader shader)
+        {
+            CopyMaterial();
+            _materialCopy.shader = shader;
+        }
+
+
+        /**
+         * Invoked to instantiate the appropriate renderer class for this instance.
+         */
+        protected virtual void AddDefaultRenderer()
+        {
+            AddRenderer<MeshRenderer>();
+        }
+
+        /**
+         * Invoked when `IsRendered` changes.
+         * @see IsVisible
+         * @see IsHidden
+         * @see IsRendering
+         */
+        protected virtual void OnVisibilityChanged(bool isNowRendered)
+        {
+            enabled = isNowRendered;
+            rendererComponent.forceRenderingOff = !isNowRendered;
+        }
+
+        protected void CheckDefaultRenderer()
+        {
+            if (rendererComponent == null)
+            {
+                AddDefaultRenderer();
+            }
+        }
+
+        protected virtual void Awake()
+        {
+            CheckPropertyIdInit();
+            AddDefaultRenderer();
+
+            MyMeshFilter = gameObject.GetOrAddComponent<MeshFilter>();
+            MatBlock = new MaterialPropertyBlock();
+        }
+
+        // TODO: This probably isn't a good pattern, too easy for subclasses to stomp on each other
+        protected T AddRenderer<T>() where T : Renderer
+        {
+            var customRenderer = GetComponent<T>();
+            if (!customRenderer)
+            {
+                customRenderer = gameObject.AddComponent<T>();
+            }
+
+            rendererComponent = customRenderer;
+            return customRenderer;
+        }
+
+        protected void CopyMaterial()
+        {
+            if (_materialCopy == null)
+            {
+                var sharedMaterial = rendererComponent.sharedMaterial;
+                OvrAvatarLog.AssertConstMessage(
+                    sharedMaterial != null, "RendererComponent has no material!", logScope, this);
+                _materialCopy = sharedMaterial is null ? new Material(EmergencyFallbackShader) : new Material(sharedMaterial);
+
+                rendererComponent.sharedMaterial = _materialCopy;
+            }
+        }
+
+        protected virtual void OnDestroy()
+        {
+            Dispose(true);
+        }
+
+        /**
+         * Replaces the primitive with the Unity mesh and material.
+         * Each renderable can reference a single primitive.
+         * This primitive can be changed at run-time.
+         *
+         * The *OVR_VERTEX_HAS_TANGENTS* shader keyword is set.
+         * based on whether this primitive has per-vertex tangents.
+         */
+        protected internal virtual void ApplyMeshPrimitive(OvrAvatarPrimitive primitive)
+        {
+            OvrAvatarLog.Assert(AppliedPrimitive == null);
+
+            CheckDefaultRenderer();
+
+            AppliedPrimitive = primitive;
+
+            MyMeshFilter.sharedMesh = MyMesh = primitive.mesh;
+            rendererComponent.sharedMaterial = primitive.material;
+
+            // Check if has tangents keywords needs enabling or not but don't enable/disable
+            // if material already has keyword enabled or not (save material copy at this point in time)
+            Material sharedMaterial = rendererComponent.sharedMaterial;
+            bool hasTangentsEnabled = sharedMaterial.IsKeywordEnabled(OVR_VERTEX_HAS_TANGENTS_KEYWORD);
+            bool hasInterpolationEnabled = sharedMaterial.IsKeywordEnabled(OVR_VERTEX_INTERPOLATE_ATTRIBUTES_KEYWORD);
+            VertexFetchMode keywordSpecifiedVertexFetchMode = GetVertexFetchModeFromKeywords(sharedMaterial);
+
+            if (HasTangents != hasTangentsEnabled)
+            {
+                SetMaterialKeyword(OVR_VERTEX_HAS_TANGENTS_KEYWORD, HasTangents);
+                SetMaterialKeyword(OVR_VERTEX_NO_TANGENTS_KEYWORD, !HasTangents);
+            }
+
+            if (InterpolateAttributes != hasInterpolationEnabled)
+            {
+                SetMaterialKeyword(OVR_VERTEX_INTERPOLATE_ATTRIBUTES_KEYWORD, InterpolateAttributes);
+                SetMaterialKeyword(OVR_VERTEX_DO_NOT_INTERPOLATE_ATTRIBUTES_KEYWORD, !InterpolateAttributes);
+            }
+
+            if (VertexFetchType != keywordSpecifiedVertexFetchMode)
+            {
+                SetMaterialVertexFetchKeyword(VertexFetchType);
+            }
+
+            // Update particular properties via a property block in case the keywords aren't enabled (and other
+            // properties)
+            rendererComponent.GetPropertyBlock(MatBlock);
+            MatBlock.SetInt(_propertyIds.HasTangentsPropId, HasTangents ? 1 : 0);
+            MatBlock.SetInt(_propertyIds.InterpolateAttributesPropId, InterpolateAttributes ? 1 : 0);
+            MatBlock.SetInt(_propertyIds.VertexFetchModePropId, (int)VertexFetchType);
+            rendererComponent.SetPropertyBlock(MatBlock);
+        }
+
+        private VertexFetchMode GetVertexFetchModeFromKeywords(Material mat)
+        {
+            if (mat.IsKeywordEnabled(OVR_VERTEX_FETCH_TEXTURE_KEYWORD))
+            {
+                return VertexFetchMode.ExternalTextures;
+            }
+
+            if (mat.IsKeywordEnabled(OVR_VERTEX_FETCH_EXTERNAL_BUFFER_KEYWORD))
+            {
+                return VertexFetchMode.ExternalBuffers;
+            }
+
+            if (mat.IsKeywordEnabled(OVR_VERTEX_FETCH_VERT_BUFFER))
+            {
+                return VertexFetchMode.VertexBuffer;
+            }
+
+            return VertexFetchMode.VertexBuffer;
+        }
+
+        private void SetMaterialVertexFetchKeyword(VertexFetchMode mode)
+        {
+            switch (mode)
+            {
+                case VertexFetchMode.VertexBuffer:
+                    SetMaterialKeyword(OVR_VERTEX_FETCH_VERT_BUFFER, true);
+                    SetMaterialKeyword(OVR_VERTEX_FETCH_EXTERNAL_BUFFER_KEYWORD, false);
+                    SetMaterialKeyword(OVR_VERTEX_FETCH_TEXTURE_KEYWORD, false);
+                    break;
+                case VertexFetchMode.ExternalBuffers:
+                    SetMaterialKeyword(OVR_VERTEX_FETCH_VERT_BUFFER, false);
+                    SetMaterialKeyword(OVR_VERTEX_FETCH_EXTERNAL_BUFFER_KEYWORD, true);
+                    SetMaterialKeyword(OVR_VERTEX_FETCH_TEXTURE_KEYWORD, false);
+                    break;
+                case VertexFetchMode.ExternalTextures:
+                    SetMaterialKeyword(OVR_VERTEX_FETCH_VERT_BUFFER, false);
+                    SetMaterialKeyword(OVR_VERTEX_FETCH_EXTERNAL_BUFFER_KEYWORD, false);
+                    SetMaterialKeyword(OVR_VERTEX_FETCH_TEXTURE_KEYWORD, true);
+                    break;
+            }
+        }
+
+        public void Dispose()
+        {
+            Dispose(true);
+        }
+
+        protected virtual void Dispose(bool isMainThread)
+        {
+            if (isMainThread)
+            {
+                if (rendererComponent != null) { Renderer.Destroy(rendererComponent); }
+                if (MyMeshFilter != null) { MyMeshFilter.sharedMesh = null; }
+                if (_materialCopy != null) { Material.Destroy(_materialCopy); }
+            }
+
+            rendererComponent = null;
+            AppliedPrimitive = null;
+            MyMesh = null;
+            MyMeshFilter = null;
+
+            _materialCopy = null;
+            MatBlock = null;
+        }
+
+        private struct AttributePropertyIds
+        {
+            public readonly int HasTangentsPropId;
+            public readonly int VertexFetchModePropId;
+            public readonly int InterpolateAttributesPropId;
+
+            // These will both be 0 if default initialized, otherwise they are guaranteed unique
+            public bool IsValid => HasTangentsPropId != VertexFetchModePropId;
+
+            public enum InitMethod { PropertyToId }
+            public AttributePropertyIds(InitMethod initMethod)
+            {
+                HasTangentsPropId = Shader.PropertyToID("_OvrHasTangents");
+                VertexFetchModePropId = Shader.PropertyToID("_OvrVertexFetchMode");
+                InterpolateAttributesPropId = Shader.PropertyToID("_OvrInterpolateAttributes");
+            }
+        }
+
+        public void GetRenderParameters(out Mesh mesh, out Material material, out Transform transform, MaterialPropertyBlock matrialProps)
+        {
+            mesh = null;
+            material = null;
+            transform = null;
+
+            MeshRenderer renderer = rendererComponent as MeshRenderer;
+            if (renderer == null)
+            {
+                return;
+            }
+            transform = renderer.transform;
+            var filter = renderer.GetComponent<MeshFilter>();
+            if (filter != null)
+            {
+                mesh = filter.sharedMesh;
+            }
+            material = renderer.material;
+            renderer.GetPropertyBlock(matrialProps);
+        }
+
+        private const ShaderType FallbackShaderType = ShaderType.Default;
+        private static Shader _fallbackShader = null;
+        private static Shader EmergencyFallbackShader
+        {
+            get
+            {
+                if (_fallbackShader == null)
+                {
+                    var manager = OvrAvatarManager.Instance;
+                    if (manager == null) { return null; }
+
+                    var shaderManager = manager.ShaderManager;
+                    if (shaderManager == null) { return null; }
+
+                    var configuration = shaderManager.GetConfiguration(FallbackShaderType);
+                    if (configuration == null) { return null;}
+
+                    _fallbackShader = configuration.Shader;
+                }
+                return _fallbackShader;
+            }
+        }
+    } // end class
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarRenderable.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarRenderable.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..5c5f8b5375906060e27433ac5e3c6bbc60a5c390
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarRenderable.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a371470b68f49a64e8d17aeb99b5c74b
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarShaderConfiguration.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarShaderConfiguration.cs
new file mode 100644
index 0000000000000000000000000000000000000000..054a1a0c1910049f1e75d1795ef8689b2bb3988d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarShaderConfiguration.cs
@@ -0,0 +1,86 @@
+using System;
+using UnityEngine;
+
+/// @file OvrAvatarShaderManagerBase.cs
+
+namespace Oculus.Avatar2
+{
+    ///
+    /// Contains material properties, shader keywords and texture map names
+    /// for shading an avatar. You can have multiple instances of this class
+    /// which apply to shading different parts of your avatar.
+    /// @see OvrAvatarShaderManagerBase
+    /// @see OvrAvatarShaderManagerSingle
+    /// @see OvrAvatarShaderManagerMulti
+    ///
+
+    [CreateAssetMenu(fileName = "DefaultShaderConfiguration", menuName = "Facebook/Avatar/SDK/OvrAvatarShaderConfiguration", order = 1)]
+    public class OvrAvatarShaderConfiguration : ScriptableObject
+    {
+        public Material Material;
+        public Shader Shader;
+
+        // These texture input names are based on what GLTF 2.0 has to offer,
+        // regardless of what the specific implementation application uses
+        public string NameTextureParameter_baseColorTexture = "_MainTex"; // for metallic roughness materials
+        public string NameTextureParameter_diffuseTexture = "_MainTex";  // for specular glossy materials
+        public string NameTextureParameter_metallicRoughnessTexture = "_MetallicGlossMap";
+        public string NameTextureParameter_specularGlossiness = "_Specular";
+        public string NameTextureParameter_normalTexture = "_BumpMap";
+        public string NameTextureParameter_occlusionTexture = "_OcclusionMap";
+        public string NameTextureParameter_emissiveTexture = "_EmissiveMap";
+        public string NameTextureParameter_flowTexture = "_FlowMap";
+
+        public string NameColorParameter_BaseColorFactor = "_Color";
+        public bool UseColorParameter_BaseColorFactor = false;
+
+        public string NameFloatParameter_MetallicFactor = "_Metallic";
+        public bool UseFloatParameter_MetallicFactor = false;
+
+        public string NameFloatParameter_RoughnessFactor = "_Roughness";
+        public bool UseFloatParameter_RoughnessFactor = false;
+
+        public string NameColorParameter_DiffuseFactor = "_Diffuse";
+        public bool UseColorParameter_DiffuseFactor = false;
+
+        public string[] KeywordsEnumerations;
+        public string[] KeywordsToEnable;
+
+        public string[] NameFloatConstants;
+        public float[] ValueFloatConstants;
+
+        public OvrAvatarMaterialExtensionConfig ExtensionConfiguration;
+
+        public void ApplyKeywords(Material material)
+        {
+            if (KeywordsEnumerations != null && KeywordsEnumerations.Length > 0)
+            {
+                foreach (var keyword in KeywordsEnumerations)
+                {
+                    material.DisableKeyword(keyword);
+                }
+            }
+            if (KeywordsToEnable != null && KeywordsToEnable.Length > 0)
+            {
+                foreach (var keyword in KeywordsToEnable)
+                {
+                    material.EnableKeyword(keyword);
+                }
+            }
+        }
+
+        public void ApplyFloatConstants(Material material)
+        {
+            if (NameFloatConstants != null && ValueFloatConstants != null &&
+                NameFloatConstants.Length > 0 && ValueFloatConstants.Length > 0)
+            {
+                for (int i = 0; i < NameFloatConstants.Length && i < ValueFloatConstants.Length; i++)
+                {
+                    string nameConstant = NameFloatConstants[i];
+                    float valueConstant = ValueFloatConstants[i];
+                    material.SetFloat(nameConstant, valueConstant);
+                }
+            }
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarShaderConfiguration.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarShaderConfiguration.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..f1c69354af173fb93507f3fa852b08f9a64e8f7f
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarShaderConfiguration.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 3861555276e1a07489d3c0971e091e46
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarShaderManagerBase.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarShaderManagerBase.cs
new file mode 100644
index 0000000000000000000000000000000000000000..8ec07910bd1b788a45d271a9b75dff6b21508983
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarShaderManagerBase.cs
@@ -0,0 +1,127 @@
+using System;
+
+using UnityEngine;
+
+/// @file OvrAvatarShaderManagerBase.cs
+
+namespace Oculus.Avatar2
+{
+    ///
+    /// Maintains a list of shader configurations containing
+    /// material properties and texture names for the various types
+    /// of shading performed on the avatar.
+    /// There are several distince shader types (eye, skin, hair, emissive, ...)
+    /// each with their own shader configuration. The shader manager suggests
+    /// the shader used to synthesize a material based off these configurations.
+    /// @see OvrAvatarShaderConfiguration
+    /// @see OvrAvatarShaderManagerSingle
+    /// @see OvrAvatarShaderManagerMultiple
+    ///
+
+    public abstract class OvrAvatarShaderManagerBase : MonoBehaviour
+    {
+
+        // The intent here is for each shader "type" to have it's own shader configuration.
+        // Possible shader types are "combined", "separate", "eyes", "hair", "transparent".
+        // We need to know, first from the "material" specification in the GLTF, and second
+        // from the parts metadata in GLTF meshes/primitives, what shader model to use...
+        public enum ShaderType
+        {
+            Default,
+            Array,
+            SolidColor,
+            Transparent,
+            Emmisive,
+            Skin,
+            LeftEye,
+            RightEye,
+            Hair,
+            FastLoad
+        }
+        /// Gets the number of shader types.
+        public static readonly int ShaderTypeCount = Enum.GetNames(typeof(ShaderType)).Length;
+
+        /// True if initialized, else false.
+        public bool Initialized { get; protected set; } = false;
+
+        ///
+        /// Get the shader configuration for the given shader type.
+        /// @param type shader type to get configuration for.
+        /// @return shader configuration for the input type, null if none specified.
+        /// @see ShaderType
+        ///
+        public abstract OvrAvatarShaderConfiguration GetConfiguration(ShaderType type);
+
+        protected virtual void Start()
+        {
+            Initialize(true);
+        }
+
+        protected void OnShutdown()
+        {
+            Initialized = false;
+        }
+
+        ///
+        /// Initialize the shader manager.
+        ///
+        protected virtual void Initialize(bool force)
+        {
+            if (!force && Initialized) return;
+
+            RegisterShaderConfigurationInitializers();
+        }
+
+        protected abstract void RegisterShaderConfigurationInitializers();
+
+        ///
+        /// Determine the shader type from material properties.
+        /// @param materialName name of the material.
+        /// @param hasMetallic  true if the material is metallic.
+        /// @param hasSpecular  true if the material has specular reflections.
+        /// @return shader type to use.
+        /// @see GetConfiguration
+        ///
+        public virtual ShaderType DetermineConfiguration(string materialName, bool hasMetallic, bool hasSpecular, bool hasTextures)
+        {
+            // TODO: look at the texture inputs for a material to determine if they are a texture array or not.
+            // This presents a difficult situation because we need to know information about the textures before
+            // determining the shader type to begin synthesis of the material. It may require an extra call to
+            // OvrAvatarLibrary.MakeTexture() or something equivalent.
+
+            if (!hasTextures)
+            {
+                return ShaderType.FastLoad;
+            }
+            if (hasMetallic || hasSpecular)
+            {
+                return ShaderType.Default;
+            }
+            return ShaderType.SolidColor;
+        }
+
+        ///
+        /// Automatically generate shader configurations.
+        /// Creates a @ref OvrAvatarShaderConfiguration ScriptableObject
+        /// for one or more shaders.
+        ///
+        public abstract bool AutoGenerateShaderConfigurations();
+
+        protected virtual void InitializeComponent(ref OvrAvatarShaderConfiguration configuration)
+        {
+            configuration.Shader = Shader.Find("Standard");
+
+            configuration.NameTextureParameter_baseColorTexture = "_MainTex";
+            configuration.NameTextureParameter_diffuseTexture = "_MainTex";
+            configuration.NameTextureParameter_metallicRoughnessTexture = "_MetallicGlossMap";
+            configuration.NameTextureParameter_specularGlossiness = "_Specular";
+            configuration.NameTextureParameter_normalTexture = "_BumpMap";
+            configuration.NameTextureParameter_occlusionTexture = "_OcclusionMap";
+            configuration.NameTextureParameter_emissiveTexture = "_EmissiveMap";
+            configuration.NameTextureParameter_flowTexture = "_FlowMap";
+
+            configuration.NameColorParameter_BaseColorFactor = "_Color";
+            configuration.NameColorParameter_DiffuseFactor = "_Diffuse";
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarShaderManagerBase.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarShaderManagerBase.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..639132024d329e6f675b781561255d0b41507893
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarShaderManagerBase.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 3aa5b1cfa9b8bfe439f635b4286dbed7
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarShaderManagerMultiple.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarShaderManagerMultiple.cs
new file mode 100644
index 0000000000000000000000000000000000000000..5277f656e91e03ba05dcdbc12263478fa4f9c662
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarShaderManagerMultiple.cs
@@ -0,0 +1,110 @@
+
+using UnityEngine;
+
+/// @file OvrAvatarShaderManagerMultiple.cs
+
+namespace Oculus.Avatar2
+{
+    ///
+    /// Configures shader properties for more than one shader type.
+    /// There are several distince shader types (eye, skin, hair, emissive, ...)
+    /// each with their own shader configuration. The shader manager suggests
+    /// the shader used to synthesize a material based off these configurations.
+    /// @see OvrAvatarShaderConfiguration
+    /// @see OvrAvatarShaderManagerSingle
+    /// @see OvrAvatarShaderManagerBase
+    ///
+    public class OvrAvatarShaderManagerMultiple : OvrAvatarShaderManagerBase
+    {
+        protected OvrAvatarShaderConfiguration[] _configurations = null;
+
+        // The following requires maintenance but is an easy alternative to creating a custom Unity editor for this manager
+        [SerializeField]
+        protected OvrAvatarShaderConfiguration DefaultShaderConfigurationInitializer;
+        [SerializeField]
+        protected OvrAvatarShaderConfiguration ArrayShaderConfigurationInitializer;
+        [SerializeField]
+        protected OvrAvatarShaderConfiguration SolidColorShaderConfigurationInitializer;
+        [SerializeField]
+        protected OvrAvatarShaderConfiguration TransparentShaderConfigurationInitializer;
+        [SerializeField]
+        protected OvrAvatarShaderConfiguration EmmisiveShaderConfigurationInitializer;
+        [SerializeField]
+        protected OvrAvatarShaderConfiguration SkinShaderConfigurationInitializer;
+        [SerializeField]
+        protected OvrAvatarShaderConfiguration LeftEyeShaderConfigurationInitializer;
+        [SerializeField]
+        protected OvrAvatarShaderConfiguration RightEyeShaderConfigurationInitializer;
+        [SerializeField]
+        protected OvrAvatarShaderConfiguration HairShaderConfigurationInitializer;
+        [SerializeField]
+        protected OvrAvatarShaderConfiguration FastLoadConfigurationInitializer;
+
+        public override OvrAvatarShaderConfiguration GetConfiguration(ShaderType type)
+        {
+            int typeNumber = (int)type;
+            if (_configurations == null || typeNumber >= _configurations.Length || _configurations[typeNumber] == null)
+            {
+                OvrAvatarLog.LogError(
+                  $"OvrAvatarShaderConfiguration for shader type [{type}] has not been initialized. Please add it to the ShaderManager.");
+                return (_configurations != null && _configurations.Length > 0) ? _configurations[(int)ShaderType.Default] : null;
+            }
+            return _configurations[typeNumber];
+        }
+
+        protected override void Initialize(bool force)
+        {
+            base.Initialize(force);
+
+            if (_configurations != null && _configurations.Length > 0)
+            {
+                Initialized = true;
+            }
+            else
+            {
+                Initialized = AutoGenerateShaderConfigurations();
+            }
+        }
+
+        protected override void RegisterShaderConfigurationInitializers()
+        {
+            // if all of these elements are null or empty just quit and let the system run AutoGenerateShaderConfigurations() later
+            if (null == DefaultShaderConfigurationInitializer &&
+                null == ArrayShaderConfigurationInitializer &&
+                null == SolidColorShaderConfigurationInitializer &&
+                null == TransparentShaderConfigurationInitializer &&
+                null == EmmisiveShaderConfigurationInitializer &&
+                null == SkinShaderConfigurationInitializer &&
+                null == LeftEyeShaderConfigurationInitializer &&
+                null == RightEyeShaderConfigurationInitializer &&
+                null == HairShaderConfigurationInitializer &&
+                null == FastLoadConfigurationInitializer)
+            {
+                return;
+            }
+
+            _configurations = new OvrAvatarShaderConfiguration[ShaderTypeCount];
+            _configurations[(int)ShaderType.Default] = DefaultShaderConfigurationInitializer;
+            _configurations[(int)ShaderType.Array] = ArrayShaderConfigurationInitializer;
+            _configurations[(int)ShaderType.SolidColor] = SolidColorShaderConfigurationInitializer;
+            _configurations[(int)ShaderType.Transparent] = TransparentShaderConfigurationInitializer;
+            _configurations[(int)ShaderType.Emmisive] = EmmisiveShaderConfigurationInitializer;
+            _configurations[(int)ShaderType.Skin] = SkinShaderConfigurationInitializer;
+            _configurations[(int)ShaderType.LeftEye] = LeftEyeShaderConfigurationInitializer;
+            _configurations[(int)ShaderType.RightEye] = RightEyeShaderConfigurationInitializer;
+            _configurations[(int)ShaderType.Hair] = HairShaderConfigurationInitializer;
+            _configurations[(int)ShaderType.FastLoad] = FastLoadConfigurationInitializer;
+        }
+
+        public override bool AutoGenerateShaderConfigurations()
+        {
+            _configurations = new OvrAvatarShaderConfiguration[ShaderTypeCount];
+            for (int i = 0; i < _configurations.Length; i++)
+            {
+                _configurations[i] = ScriptableObject.CreateInstance<OvrAvatarShaderConfiguration>();
+                InitializeComponent(ref _configurations[i]);
+            }
+            return true;
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarShaderManagerMultiple.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarShaderManagerMultiple.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..376eb5c4e0183118cfe4c1713f877347f6d7de70
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarShaderManagerMultiple.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 561fe8366de5e7d4fae882c491e3d7dc
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarShaderManagerSingle.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarShaderManagerSingle.cs
new file mode 100644
index 0000000000000000000000000000000000000000..f2716b35d6774980a4ecb1b71ed70214721d85c6
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarShaderManagerSingle.cs
@@ -0,0 +1,81 @@
+
+using UnityEngine;
+
+/// @file OvrAvatarShaderManagerBase.cs
+
+namespace Oculus.Avatar2
+{
+    ///
+    /// Configures avatar shader properties.
+    /// This shader configuration is used for all avatars.
+    /// This shader manager only supports one shader configuration.
+    /// Use @ref OvrAvatarShaderManagerMultiple if you need more than one.
+    /// @see OvrAvatarShaderManagerBase
+    /// @see OvrAvatarShaderConfiguration
+    ///
+    public class OvrAvatarShaderManagerSingle : OvrAvatarShaderManagerBase
+    {
+        protected OvrAvatarShaderConfiguration _configuration = null;
+        protected OvrAvatarShaderConfiguration _fastloadConfiguration = null;
+
+        // The following requires maintenance but is an easy alternative to creating a custom Unity editor for this manager
+        [SerializeField]
+        protected OvrAvatarShaderConfiguration DefaultShaderConfigurationInitializer;
+        [SerializeField]
+        protected OvrAvatarShaderConfiguration FastLoadConfigurationInitializer;
+
+        public override OvrAvatarShaderConfiguration GetConfiguration(ShaderType type)
+        {
+            int typeNumber = (int)type;
+            if (!(type == ShaderType.Default || type == ShaderType.FastLoad) || typeNumber >= OvrAvatarShaderManagerBase.ShaderTypeCount)
+            {
+                OvrAvatarLog.LogError(
+                  $"OvrAvatarShaderConfiguration for shader type [{type}] is invalid OvrAvatarShaderManagerSingle.");
+                return _configuration;
+            }
+            if (type == ShaderType.FastLoad)
+            {
+                return _fastloadConfiguration;
+            }
+            else
+            {
+                return _configuration;
+            }
+        }
+
+        protected override void Initialize(bool force)
+        {
+            base.Initialize(force);
+
+            if (_configuration != null && _fastloadConfiguration != null)
+            {
+                Initialized = true;
+            }
+            else
+            {
+                Initialized = AutoGenerateShaderConfigurations();
+            }
+        }
+
+        protected override void RegisterShaderConfigurationInitializers()
+        {
+            // if all of these elements are null or empty just quit and let the system run AutoGenerateShaderConfigurations() later
+            if (null == DefaultShaderConfigurationInitializer || null == FastLoadConfigurationInitializer)
+            {
+                return;
+            }
+
+            _configuration = DefaultShaderConfigurationInitializer;
+            _fastloadConfiguration = FastLoadConfigurationInitializer;
+        }
+
+        public override bool AutoGenerateShaderConfigurations()
+        {
+            _configuration = ScriptableObject.CreateInstance<OvrAvatarShaderConfiguration>();
+            InitializeComponent(ref _configuration);
+            _fastloadConfiguration = ScriptableObject.CreateInstance<OvrAvatarShaderConfiguration>();
+            InitializeComponent(ref _fastloadConfiguration);
+            return true;
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarShaderManagerSingle.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarShaderManagerSingle.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..fd1e9cc7698a3053be2cec121e682a2c6b0fa978
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarShaderManagerSingle.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 637f19aef5eccc146a6ab77a1d4ab0a8
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarSkinnedRenderable.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarSkinnedRenderable.cs
new file mode 100644
index 0000000000000000000000000000000000000000..7eb5e334c8731ddcb78a5c20af27ad5784eaa287
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarSkinnedRenderable.cs
@@ -0,0 +1,95 @@
+#define OVR_AVATAR_PRIMITIVE_HACK_BOUNDING_BOX
+
+using System;
+using Oculus.Avatar2;
+using UnityEngine;
+
+/// @file OvrAvatarSkinnedRenderable.cs
+
+public abstract class OvrAvatarSkinnedRenderable : OvrAvatarRenderable
+{
+    /**
+     * Component that encapsulates the meshes of a skinned avatar.
+     * This component can only be added to game objects that
+     * have a Unity Mesh, a Mesh filter and a SkinnedRenderer.
+     *
+     * In addition to vertex positions, texture coordinates and
+     * colors, a vertex in a skinned mesh can be driven by up
+     * to 4 bones in the avatar skeleton. Each frame the transforms
+     * of these bones are multiplied by the vertex weights for
+     * the bone and applied to compute the final vertex position.
+     * This can be done by Unity on the CPU or the GPU, or by
+     * the Avatar SDK using the GPU. Different variations of this
+     * class are provided to allow you to select which implementation
+     * best suits your application.
+     *
+     * @see OvrAvatarPrimitive
+     * @see ApplyMeshPrimitive
+     * @see OvrAvatarUnitySkinnedRenderable
+     */
+
+    /// Designates whether this renderable animations are enabled or not.
+    private bool _isAnimationEnabled = true;
+    public bool IsAnimationEnabled
+    {
+        get => _isAnimationEnabled;
+        internal set
+        {
+            if (_isAnimationEnabled != value)
+            {
+                _isAnimationEnabled = value;
+                OnAnimationEnabledChanged(_isAnimationEnabled);
+            }
+        }
+    }
+
+    protected internal override void ApplyMeshPrimitive(OvrAvatarPrimitive primitive)
+    {
+        CheckDefaultRenderer();
+
+        base.ApplyMeshPrimitive(primitive);
+    }
+
+    ///
+    /// Apply the given bone transforms from the avatar skeleton
+    /// to the Unity skinned mesh renderer.
+    /// @param bones    Array of Transforms for the skeleton bones.
+    ///                 These must be in the order the Unity SkinnedRenderer expects.
+    ///
+    public abstract void ApplySkeleton(Transform[] bones);
+
+    public abstract IDisposableBuffer CheckoutMorphTargetBuffer(uint morphCount);
+    public abstract void MorphTargetBufferUpdated(IDisposableBuffer buffer);
+
+    public virtual void UpdateSkinningOrigin(in CAPI.ovrAvatar2Transform skinningOrigin)
+    {
+        // Default implementation is just to apply to transform
+        transform.ApplyOvrTransform(skinningOrigin);
+    }
+
+    public abstract bool UpdateJointMatrices(
+        CAPI.ovrAvatar2EntityId entityId,
+        OvrAvatarPrimitive primitive,
+        CAPI.ovrAvatar2PrimitiveRenderInstanceID primitiveInstanceId);
+
+    // Invoked when `IsAnimationEnabled` changes.
+    protected abstract void OnAnimationEnabledChanged(bool isNowEnabled);
+    internal abstract void AnimationFrameUpdate();
+    internal abstract void RenderFrameUpdate();
+    internal abstract bool IsAnimationDataCompletelyValid { get; }
+
+    internal delegate void AnimationDataCompletionHandler(OvrAvatarSkinnedRenderable sender);
+    internal event AnimationDataCompletionHandler AnimationDataComplete;
+
+    protected void OnAnimationDataCompleted()
+    {
+        // Safely raise the event for all subscribers
+        AnimationDataComplete?.Invoke(this);
+    }
+
+    // TODO: Reference count
+    public interface IDisposableBuffer : IDisposable
+    {
+        IntPtr BufferPtr { get; }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarSkinnedRenderable.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarSkinnedRenderable.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..e53392df37b05968b201e56e7183cb32a886ceb0
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarSkinnedRenderable.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: aa50f6a3356c83548a429e8bf503b796
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarTrackingBodyState.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarTrackingBodyState.cs
new file mode 100644
index 0000000000000000000000000000000000000000..95847f9a5a25262ee50f4f8d138879eca4f2fd8c
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarTrackingBodyState.cs
@@ -0,0 +1,81 @@
+using System;
+
+/**
+ * @file OvrAvatarTrackingBodyState.cs
+ */
+namespace Oculus.Avatar2
+{
+    /**
+     * Collects the position and orientation of the headset and
+     * controllers, the controller button state,
+     * the type of controller and tracking method,
+     * whether the avatar is sitting or standing and
+     * converts to and from C# and C++ native versions.
+     * @see OvrAvatarBodyTrackingContextBase
+     */
+    public sealed class OvrAvatarTrackingBodyState
+    {
+        /// Position, orientation and scale of each controller.
+        public CAPI.ovrAvatar2InputTrackingState inputTrackingState;
+
+        /// Button and joystick state for each controller.
+        public CAPI.ovrAvatar2InputControlState inputControlState;
+
+        /// Type of hand tracking being performed.
+        public readonly CAPI.ovrAvatar2HandInputType[] handInputType = new CAPI.ovrAvatar2HandInputType[(int)CAPI.ovrAvatar2Side.Count];
+
+        /// Skeleton version number.
+        public Int32 skeletonVersion;
+
+        /// Number of bones in the skeleton.
+        public Int32 numBones;
+
+        /// Avatar body modality (sitting vs standing).
+        public CAPI.ovrAvatar2TrackingBodyModality bodyModality;
+
+        /// Scale of hand
+        public readonly float[] handScale = new float[(int)CAPI.ovrAvatar2Side.Count];
+
+        #region Native Conversions
+        /**
+         * Creates a native C++ pose from this C# pose.
+         * @see CAPI.ovrAvatar2TrackingBodyState
+         * @see FromNative
+         */
+        internal CAPI.ovrAvatar2TrackingBodyState ToNative()
+        {
+            return new CAPI.ovrAvatar2TrackingBodyState
+            {
+                inputTrackingState = inputTrackingState,
+                inputControlState = inputControlState,
+                leftHandInputType = handInputType[(int)CAPI.ovrAvatar2Side.Left],
+                rightHandInputType = handInputType[(int)CAPI.ovrAvatar2Side.Right],
+                skeletonVersion = skeletonVersion,
+                numBones = numBones,
+                bodyModality = bodyModality,
+                leftHandScale = handScale[(int)CAPI.ovrAvatar2Side.Left],
+                rightHandScale = handScale[(int)CAPI.ovrAvatar2Side.Right],
+            };
+        }
+
+        /**
+         * Copies the native pose provided to this C# pose.
+         * @see CAPI.ovrAvatar2TrackingBodyState
+         * @see ToNative
+         */
+        internal void FromNative(ref CAPI.ovrAvatar2TrackingBodyState native)
+        {
+            inputTrackingState = native.inputTrackingState;
+            inputControlState = native.inputControlState;
+            handInputType[(int)CAPI.ovrAvatar2Side.Left] = native.leftHandInputType;
+            handInputType[(int)CAPI.ovrAvatar2Side.Right] = native.rightHandInputType;
+            skeletonVersion = native.skeletonVersion;
+            numBones = native.numBones;
+            bodyModality = native.bodyModality;
+            handScale[(int)CAPI.ovrAvatar2Side.Left] = native.leftHandScale;
+            handScale[(int)CAPI.ovrAvatar2Side.Right] = native.rightHandScale;
+
+        }
+        #endregion
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarTrackingBodyState.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarTrackingBodyState.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..d3591b3ca67a879f0d85ef33728b1a888ea9ce81
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarTrackingBodyState.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 046c4cdc80104174a90dd6867635f4a8
+timeCreated: 1606840836
\ No newline at end of file
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarTrackingHandsState.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarTrackingHandsState.cs
new file mode 100644
index 0000000000000000000000000000000000000000..1bf7e327224f7375c21114f37cd741073805551e
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarTrackingHandsState.cs
@@ -0,0 +1,147 @@
+/**
+ * @file OvrAvatarTrackingHandsState.cs
+ */
+
+namespace Oculus.Avatar2
+{
+    /**
+     * Maintains wrist positions and hand bone rotations and
+     * converts to and from C# and C++ native versions.
+     * @see OvrAvatarBodyTrackingContextBase
+     */
+    public sealed class OvrAvatarTrackingHandsState
+    {
+        /// Transform for the left wrist.
+        public CAPI.ovrAvatar2Transform wristPosLeft;
+
+        /// Transform for the right wrist.
+        public CAPI.ovrAvatar2Transform wristPosRight;
+
+        /// Array of rotations for the hand bones.
+        public readonly CAPI.ovrAvatar2Quatf[] boneRotations = new CAPI.ovrAvatar2Quatf[(int)CAPI.MaxHandBones];
+
+        /// Tracked uniform scale for the left hand.
+        public float handScaleLeft;
+
+        /// Tracked uniform scale for the right hand.
+        public float handScaleRight;
+
+        /// True if the left hand is being tracked.
+        public bool isTrackedLeft;
+
+        /// True if the right hand is being tracked.
+        public bool isTrackedRight;
+
+        /// True if the tracking confidence is high for the left hand.
+        public bool isConfidentLeft;
+
+        /// True if the tracking confidence is high for the right hand.
+        public bool isConfidentRight;
+
+        /**
+         * Creates a native C++ hand pose from this C# pose.
+         * @see CAPI.ovrAvatar2HandTrackingState
+         * @see FromNative
+         */
+        internal CAPI.ovrAvatar2HandTrackingState ToNative()
+        {
+            return new CAPI.ovrAvatar2HandTrackingState
+            {
+                wristPosLeft = wristPosLeft,
+                wristPosRight = wristPosRight,
+                handScaleLeft = handScaleLeft,
+                handScaleRight = handScaleRight,
+                isTrackedLeft = isTrackedLeft,
+                isTrackedRight = isTrackedRight,
+                isConfidentLeft = isConfidentLeft,
+                isConfidentRight = isConfidentRight,
+                boneRotation0 = boneRotations[0],
+                boneRotation1 = boneRotations[1],
+                boneRotation2 = boneRotations[2],
+                boneRotation3 = boneRotations[3],
+                boneRotation4 = boneRotations[4],
+                boneRotation5 = boneRotations[5],
+                boneRotation6 = boneRotations[6],
+                boneRotation7 = boneRotations[7],
+                boneRotation8 = boneRotations[8],
+                boneRotation9 = boneRotations[9],
+                boneRotation10 = boneRotations[10],
+                boneRotation11 = boneRotations[11],
+                boneRotation12 = boneRotations[12],
+                boneRotation13 = boneRotations[13],
+                boneRotation14 = boneRotations[14],
+                boneRotation15 = boneRotations[15],
+                boneRotation16 = boneRotations[16],
+                boneRotation17 = boneRotations[17],
+                boneRotation18 = boneRotations[18],
+                boneRotation19 = boneRotations[19],
+                boneRotation20 = boneRotations[20],
+                boneRotation21 = boneRotations[21],
+                boneRotation22 = boneRotations[22],
+                boneRotation23 = boneRotations[23],
+                boneRotation24 = boneRotations[24],
+                boneRotation25 = boneRotations[25],
+                boneRotation26 = boneRotations[26],
+                boneRotation27 = boneRotations[27],
+                boneRotation28 = boneRotations[28],
+                boneRotation29 = boneRotations[29],
+                boneRotation30 = boneRotations[30],
+                boneRotation31 = boneRotations[31],
+                boneRotation32 = boneRotations[32],
+                boneRotation33 = boneRotations[33],
+            };
+        }
+
+        /**
+         * Copies the native hand pose provided to this C# pose.
+         * @see CAPI.ovrAvatar2HandTrackingState
+         * @see ToNative
+         */
+        internal void FromNative(ref CAPI.ovrAvatar2HandTrackingState native)
+        {
+            wristPosLeft = native.wristPosLeft;
+            wristPosRight = native.wristPosRight;
+            handScaleLeft = native.handScaleLeft;
+            handScaleRight = native.handScaleRight;
+            isTrackedLeft = native.isTrackedLeft;
+            isTrackedRight = native.isTrackedRight;
+            isConfidentLeft = native.isConfidentLeft;
+            isConfidentRight = native.isConfidentRight;
+
+            boneRotations[0] = native.boneRotation0;
+            boneRotations[1] = native.boneRotation1;
+            boneRotations[2] = native.boneRotation2;
+            boneRotations[3] = native.boneRotation3;
+            boneRotations[4] = native.boneRotation4;
+            boneRotations[5] = native.boneRotation5;
+            boneRotations[6] = native.boneRotation6;
+            boneRotations[7] = native.boneRotation7;
+            boneRotations[8] = native.boneRotation8;
+            boneRotations[9] = native.boneRotation9;
+            boneRotations[10] = native.boneRotation10;
+            boneRotations[11] = native.boneRotation11;
+            boneRotations[12] = native.boneRotation12;
+            boneRotations[13] = native.boneRotation13;
+            boneRotations[14] = native.boneRotation14;
+            boneRotations[15] = native.boneRotation15;
+            boneRotations[16] = native.boneRotation16;
+            boneRotations[17] = native.boneRotation17;
+            boneRotations[18] = native.boneRotation18;
+            boneRotations[19] = native.boneRotation19;
+            boneRotations[20] = native.boneRotation20;
+            boneRotations[21] = native.boneRotation21;
+            boneRotations[22] = native.boneRotation22;
+            boneRotations[23] = native.boneRotation23;
+            boneRotations[24] = native.boneRotation24;
+            boneRotations[25] = native.boneRotation25;
+            boneRotations[26] = native.boneRotation26;
+            boneRotations[27] = native.boneRotation27;
+            boneRotations[28] = native.boneRotation28;
+            boneRotations[29] = native.boneRotation29;
+            boneRotations[30] = native.boneRotation30;
+            boneRotations[31] = native.boneRotation31;
+            boneRotations[32] = native.boneRotation32;
+            boneRotations[33] = native.boneRotation33;
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarTrackingHandsState.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarTrackingHandsState.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..17a0cfda4715893832665e91192d7a96a2877c14
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarTrackingHandsState.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 7832c3c6efa84bff8b0ec2e4b3d4b58c
+timeCreated: 1606861594
\ No newline at end of file
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarTrackingPose.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarTrackingPose.cs
new file mode 100644
index 0000000000000000000000000000000000000000..76de88fdde4e233fe2c6ed756831fa11245adba3
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarTrackingPose.cs
@@ -0,0 +1,129 @@
+using System;
+using UnityEngine;
+
+/**
+ * @file OvrAvatarTrackingPose.cs
+ */
+namespace Oculus.Avatar2
+{
+    /**
+     * The purpose of this struct is to wrap CAPI.ovrAvatar2TrackingBodyPose,
+     * so that application code can write pose data into
+     * a native ovrAvatar2TrackingBodyPose struct owned by the avatar SDK.
+     * @see OvrAvatarBodyTrackingContextBase
+     * @see CAPI.ovrAvatar2TrackingBodyPose
+     */
+    public ref struct OvrAvatarTrackingPose
+    {
+        /// Coordinate space of this pose (local or object).
+        public CAPI.ovrAvatar2Space space;
+
+        /// A reference to an array of bone transforms inside the underlying native struct.
+        public readonly OvrSpan<CAPI.ovrAvatar2Transform> transforms;
+
+        /**
+         * Set bone transform by index.
+         * @param newTransform  new transform.
+         * @param index         which index in array to copy to.
+         * @returns true if index is valid, false otherwise.
+         */
+        public bool SetTransform(CAPI.ovrAvatar2Transform newTransform, int index)
+        {
+            if (index < 0 || index >= transforms.Length)
+            {
+                return false;
+            }
+            unsafe
+            {
+                CAPI.ovrAvatar2Transform* ptr = (CAPI.ovrAvatar2Transform*)transforms.Address;
+                *(ptr + index) = newTransform;
+            }
+            return true;
+        }
+
+        /**
+         * Set transform of multiple bones.
+         * @param newTransforms     new transform array.
+         * @param offset            offset index to start copy.
+         * @param count             number of transforms to copy.
+         * @returns true if copy succeed, false otherwise.
+         */
+        public bool SetTransforms(CAPI.ovrAvatar2Transform[] newTransforms, int offset, int count)
+        {
+            if (offset < 0 || count <= 0 || (offset + count) > newTransforms.Length)
+            {
+                return false;
+            }
+            unsafe
+            {
+                CAPI.ovrAvatar2Transform* ptr = (CAPI.ovrAvatar2Transform*)transforms.Address;
+                for (int i = offset; i < offset + count; ++i)
+                {
+                    *(ptr + i) = newTransforms[i];
+                }
+            }
+            return true;
+        }
+
+        /**
+         * Create a C# struct from a native pose.
+         */
+        internal OvrAvatarTrackingPose(ref CAPI.ovrAvatar2TrackingBodyPose pose)
+        {
+            space = pose.space;
+            unsafe
+            {
+                transforms = new OvrSpan<CAPI.ovrAvatar2Transform>(pose.bones, (int)pose.numBones);
+            }
+        }
+
+        /**
+         * Copy the pose from this C# instance to the native pose provided.
+         * @param native    where to store the native pose copied.
+         * @see CAPI.ovrAvatar2TrackingBodyPose
+         */
+        internal void CopyToNative(ref CAPI.ovrAvatar2TrackingBodyPose native)
+        {
+            unsafe
+            {
+                Debug.Assert(transforms.Length == native.numBones);
+                CAPI.ovrAvatar2Transform* ptr = (CAPI.ovrAvatar2Transform*)transforms.Address;
+                Debug.Assert(ptr == native.bones);
+            }
+            native.space = space;
+        }
+
+        /**
+         * Create a native pose from this C# instance.
+         * @returns native pose copied.
+         * @see CAPI.ovrAvatar2TrackingBodyPose
+         */
+        internal CAPI.ovrAvatar2TrackingBodyPose GetNative()
+        {
+            unsafe
+            {
+                CAPI.ovrAvatar2Transform* ptr = (CAPI.ovrAvatar2Transform*)transforms.Address;
+                return new CAPI.ovrAvatar2TrackingBodyPose(ptr, (UInt32)transforms.Length)
+                {
+                    space = space
+                };
+            }
+        }
+
+        /**
+         * Copy the native pose provided to this C# instance.
+         * @param native   native pose to be copied.
+         * @see CAPI.ovrAvatar2TrackingBodyPose
+         */
+        internal void CopyFromNative(ref CAPI.ovrAvatar2TrackingBodyPose native)
+        {
+            unsafe
+            {
+                Debug.Assert(transforms.Length == native.numBones);
+                CAPI.ovrAvatar2Transform* ptr = (CAPI.ovrAvatar2Transform*)transforms.Address;
+                Debug.Assert(ptr == native.bones);
+            }
+            space = native.space;
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarTrackingPose.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarTrackingPose.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..8bcea07b773e2a04e1335273f46768d42893df42
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarTrackingPose.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: 22d941154869404b98b15b4c76cb95b9
+timeCreated: 1612295610
\ No newline at end of file
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarTrackingSkeleton.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarTrackingSkeleton.cs
new file mode 100644
index 0000000000000000000000000000000000000000..b3fd45a8500943f578e8f1f6335f632bc6d598a0
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarTrackingSkeleton.cs
@@ -0,0 +1,164 @@
+using System;
+using UnityEngine;
+
+/**
+ * @file OvrAvatarTrackingSkeleton.cs
+ */
+namespace Oculus.Avatar2
+{
+    /**
+     * Describes a contiguous array of structures.
+     * Used to transfer data between C# and native.
+     */
+    public ref struct OvrSpan<T> where T : struct
+    {
+        private readonly IntPtr _ptr;
+        private readonly int _length;
+
+        public IntPtr Address => _ptr;
+        public int Length => _length;
+
+        public unsafe OvrSpan(void* ptr, int length)
+        {
+            _ptr = new IntPtr(ptr);
+            _length = length;
+        }
+    }
+
+    /**
+     * The purpose of this struct is to wrap CAPI.ovrAvatar2TrackingBodySkeleton,
+     * so that application code can write skeleton and reference pose data into
+     * a native ovrAvatar2TrackingBodySkeleton struct owned by the avatar SDK.
+     * @see OvrAvatarBodyTrackingContextBase
+     * @see CAPI.ovrAvatar2TrackingBodySkeleton
+     */
+    public ref struct OvrAvatarTrackingSkeleton
+    {
+        /// Vector giving the forward bone direction.
+        public CAPI.ovrAvatar2Vector3f forwardDir;
+
+        /// Indices for each bone and its corresponding parent bone.
+        /// This describes the structure of the skeleton.
+        public readonly OvrSpan<CAPI.ovrAvatar2Bone> bones;
+
+        /// Reference pose for the skeleton. This is the pose it is
+        /// in if no external transformations are applied.
+        public OvrAvatarTrackingPose referencePose;
+
+        /**
+         * Set bone by index
+         * @param newBone   new bone
+         * @param index     where to copy
+         * @returns true if index is valid, false otherwise
+         */
+        public bool SetBone(CAPI.ovrAvatar2Bone newBone, int index)
+        {
+            if (index < 0 || index >= bones.Length)
+            {
+                return false;
+            }
+            unsafe
+            {
+                CAPI.ovrAvatar2Bone* ptr = (CAPI.ovrAvatar2Bone*)bones.Address.ToPointer();
+                *(ptr + index) = newBone;
+            }
+            return true;
+        }
+
+        /**
+         * Set multiple bones
+         * @param newBones      new bone array
+         * @param offset        offset index to start copy
+         * @param count         number of bones to copy
+         * @returns true if copy succeed, false otherwise
+         */
+        public bool SetBones(CAPI.ovrAvatar2Bone[] newBones, int offset, int count)
+        {
+            if (offset < 0 || count <= 0 || (offset + count) > newBones.Length)
+            {
+                return false;
+            }
+            unsafe
+            {
+                CAPI.ovrAvatar2Bone* ptr = (CAPI.ovrAvatar2Bone*)bones.Address.ToPointer();
+                for (int i = offset; i < offset + count; ++i)
+                {
+                    *(ptr + i) = newBones[i];
+                }
+            }
+            return true;
+        }
+
+        /**
+         * Constructs a C# skeleton from a native skeleton.
+         * @param skeleton  native skeleton to copy.
+         * @see CAPI.ovrAvatar2TrackingBodySkeleton
+         */
+        internal OvrAvatarTrackingSkeleton(ref CAPI.ovrAvatar2TrackingBodySkeleton skeleton)
+        {
+            forwardDir = skeleton.forwardDir;
+            referencePose = new OvrAvatarTrackingPose(ref skeleton.referencePose);
+            unsafe
+            {
+                bones = new OvrSpan<CAPI.ovrAvatar2Bone>(skeleton.bones, (int)skeleton.numBones);
+            }
+        }
+
+        /**
+         * Copy the skeleton from this C# instance to a native skeleton.
+         * @param skeleton  native skeleton to update.
+         * @see CAPI.ovrAvatar2TrackingBodySkeleton
+         * @see GetNative
+         * @see FromNative
+         */
+        internal void CopyToNative(ref CAPI.ovrAvatar2TrackingBodySkeleton native)
+        {
+            unsafe
+            {
+                Debug.Assert(bones.Length == native.numBones);
+                CAPI.ovrAvatar2Bone* ptr = (CAPI.ovrAvatar2Bone*)bones.Address.ToPointer();
+                Debug.Assert(ptr == native.bones);
+            }
+            native.forwardDir = forwardDir;
+            referencePose.CopyToNative(ref native.referencePose);
+        }
+
+        /**
+         * Create a native skeleton from this C# instance.
+         * @returns native skeleton structure.
+         * @see CAPI.ovrAvatar2TrackingBodySkeleton
+         * @see CopyToNative
+         * @see FromNative
+         */
+        internal CAPI.ovrAvatar2TrackingBodySkeleton GetNative()
+        {
+            unsafe
+            {
+                CAPI.ovrAvatar2Bone* ptr = (CAPI.ovrAvatar2Bone*)bones.Address.ToPointer();
+                return new CAPI.ovrAvatar2TrackingBodySkeleton(ptr, (UInt32)bones.Length, referencePose.GetNative())
+                {
+                    forwardDir = forwardDir
+                };
+            }
+        }
+
+        /**
+         * Copy a native skeleton to this C# instance.
+         * @param skeleton  native skeleton to copy.
+         * @see CAPI.ovrAvatar2TrackingBodySkeleton
+         * @see CopyToNative
+         * @see GetNative
+         */
+        internal void CopyFromNative(ref CAPI.ovrAvatar2TrackingBodySkeleton native)
+        {
+            unsafe
+            {
+                Debug.Assert(bones.Length == native.numBones);
+                CAPI.ovrAvatar2Bone* ptr = (CAPI.ovrAvatar2Bone*)bones.Address.ToPointer();
+                Debug.Assert(ptr == native.bones);
+            }
+            forwardDir = native.forwardDir;
+            referencePose.CopyFromNative(ref native.referencePose);
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarTrackingSkeleton.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarTrackingSkeleton.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..44b6f4c707d8bc6d37d91b8a825bc384b584fb85
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarTrackingSkeleton.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: ea64c5825b7142958d101ebdaeba5e48
+timeCreated: 1612295645
\ No newline at end of file
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarUtility.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarUtility.cs
new file mode 100644
index 0000000000000000000000000000000000000000..c22a6416aa1621ecb1a67db6e0dd7e8051510f71
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarUtility.cs
@@ -0,0 +1,213 @@
+using System.Diagnostics.Contracts;
+
+using UnityEngine;
+
+namespace Oculus.Avatar2
+{
+    public static class OvrAvatarConversions
+    {
+        // TODO: Make internal, used by BodyAnimTracking.Update()
+        public static CAPI.ovrAvatar2Transform ConvertSpace(this in CAPI.ovrAvatar2Transform from)
+        {
+            return new CAPI.ovrAvatar2Transform(
+                from.position.ConvertSpace(), from.orientation.ConvertSpace(), in from.scale);
+        }
+
+        // TODO: Make internal, used by BodyAnimTracking.Start()
+        public static CAPI.ovrAvatar2Quatf ConvertSpace(this in CAPI.ovrAvatar2Quatf from)
+        {
+            return new CAPI.ovrAvatar2Quatf(-from.x, -from.y, from.z, from.w);
+        }
+
+        // TODO: Make internal, used by BodyAnimTracking.Start()
+        public static CAPI.ovrAvatar2Vector3f ConvertSpace(this in CAPI.ovrAvatar2Vector3f from)
+        {
+            return new CAPI.ovrAvatar2Vector3f(from.x, from.y, -from.z);
+        }
+
+        // TODO: Make internal, used by BodyAnimTracking.Update()
+        public static CAPI.ovrAvatar2Transform ConvertSpace(this Transform from)
+        {
+            return new CAPI.ovrAvatar2Transform(
+                from.localPosition.ConvertSpace(), from.localRotation.ConvertSpace(), (CAPI.ovrAvatar2Vector3f)from.localScale);
+        }
+
+        internal static CAPI.ovrAvatar2Quatf ConvertSpace(this in Quaternion from)
+        {
+            return new CAPI.ovrAvatar2Quatf(-from.x, -from.y, from.z, from.w);
+        }
+
+        internal static CAPI.ovrAvatar2Vector3f ConvertSpace(this in Vector3 from)
+        {
+            return new CAPI.ovrAvatar2Vector3f(from.x, from.y, -from.z);
+        }
+
+        internal static void ApplyWorldOvrTransform(this Transform transform, in CAPI.ovrAvatar2Transform from)
+        {
+            transform.position = from.position;
+            transform.rotation = from.orientation;
+            transform.localScale = from.scale;
+        }
+
+        internal static void ApplyOvrTransform(this Transform transform, in CAPI.ovrAvatar2Transform from)
+        {
+            // NOTE: We could route this to the * version, but `fixed` has non-trivial overhead
+            transform.localPosition = from.position;
+            transform.localRotation = from.orientation;
+            transform.localScale = from.scale;
+        }
+
+        internal unsafe static void ApplyOvrTransform(this Transform transform, CAPI.ovrAvatar2Transform* from)
+        {
+            transform.localPosition = from->position;
+            transform.localRotation = from->orientation;
+            transform.localScale = from->scale;
+        }
+
+        internal static CAPI.ovrAvatar2Transform ToWorldOvrTransform(this Transform t)
+        {
+            return new CAPI.ovrAvatar2Transform(t.position, t.rotation, t.localScale);
+        }
+
+        internal static Matrix4x4 ToMatrix(this in CAPI.ovrAvatar2Transform t)
+        {
+            return Matrix4x4.TRS(t.position, t.orientation, t.scale);
+        }
+    }
+
+    public static class OvrAvatarUtility
+    {
+        public static CAPI.ovrAvatar2Transform CombineOvrTransforms(
+            in CAPI.ovrAvatar2Transform parent, in CAPI.ovrAvatar2Transform child)
+        {
+            var scaledChildPose = new CAPI.ovrAvatar2Vector3f
+            {
+                x = child.position.x * parent.scale.x,
+                y = child.position.y * parent.scale.y,
+                z = child.position.z * parent.scale.z
+            };
+
+            var parentQuat = (Quaternion)parent.orientation;
+            var result = new CAPI.ovrAvatar2Transform
+            {
+                position =
+                  parent.position + (CAPI.ovrAvatar2Vector3f) (parentQuat * scaledChildPose),
+
+                orientation = parentQuat * child.orientation,
+
+                scale = new CAPI.ovrAvatar2Vector3f
+                {
+                    x = parent.scale.x * child.scale.x,
+                    y = parent.scale.y * child.scale.y,
+                    z = parent.scale.z * child.scale.z
+                }
+            };
+            OvrAvatarLog.Assert(!result.position.IsNaN());
+            OvrAvatarLog.Assert(!result.orientation.IsNaN());
+            OvrAvatarLog.Assert(!result.scale.IsNaN());
+
+            OvrAvatarLog.Assert(result.orientation.IsNormalized());
+
+            return result;
+        }
+
+        [Pure]
+        public static string GetAsString(this in CAPI.ovrAvatar2Transform transform, int decimalPlaces = 2)
+        {
+            string format = "F" + decimalPlaces;
+            return $"{((Vector3)transform.position).ToString(format)}, {((Quaternion)transform.orientation).eulerAngles.ToString(format)}, {((Vector3)transform.scale).ToString(format)}";
+        }
+
+        [Pure]
+        public static bool IsNaN(this in CAPI.ovrAvatar2Vector3f v)
+        {
+            return float.IsNaN(v.x) || float.IsNaN(v.y) || float.IsNaN(v.z);
+        }
+
+        [Pure]
+        public static bool IsNaN(this in CAPI.ovrAvatar2Quatf q)
+        {
+            return float.IsNaN(q.x) || float.IsNaN(q.y) || float.IsNaN(q.z) || float.IsNaN(q.w);
+        }
+
+        [Pure]
+        public static bool IsNan(this in CAPI.ovrAvatar2Transform t)
+        {
+            return t.position.IsNaN() || t.orientation.IsNaN() || t.scale.IsNaN();
+        }
+
+        [Pure]
+        public static bool IsZero(this in CAPI.ovrAvatar2Vector3f vec)
+            => vec.x == 0.0f && vec.y == 0.0f && vec.z == 0.0f;
+
+        [Pure]
+        public static bool IsOne(this in CAPI.ovrAvatar2Vector3f vec)
+            => vec.x == 1.0f && vec.y == 1.0f && vec.z == 1.0f;
+
+        [Pure]
+        public static bool IsIdentity(this in CAPI.ovrAvatar2Quatf quat)
+            => quat.x == 0.0f && quat.y == 0.0f && quat.z == 0.0f && (quat.w == 1.0f || quat.w == -1.0f);
+
+        [Pure]
+
+        public static bool IsNormalized(this in CAPI.ovrAvatar2Quatf quat)
+            => Mathf.Approximately(quat.LengthSquared, 1.0f);
+
+
+        [Pure]
+        public static bool IsIdentity(this in CAPI.ovrAvatar2Transform transform)
+            => transform.position.IsZero() && transform.orientation.IsIdentity() && transform.scale.IsOne();
+
+        public static Matrix4x4 ToUnityMatrix(this in CAPI.ovrAvatar2Matrix4f m)
+        {
+            m.CopyToUnityMatrix(out var unityM);
+            return unityM;
+        }
+
+        public static CAPI.ovrAvatar2Matrix4f ToAvatarMatrix(this in Matrix4x4 m)
+        {
+            m.CopyToAvatarMatrix(out var avatarMat);
+            return avatarMat;
+        }
+
+        public static void CopyToUnityMatrix(this in CAPI.ovrAvatar2Matrix4f m, out Matrix4x4 unityMatrix)
+        {
+            unityMatrix.m00 = m.m00;
+            unityMatrix.m10 = m.m10;
+            unityMatrix.m20 = m.m20;
+            unityMatrix.m30 = m.m30;
+            unityMatrix.m01 = m.m01;
+            unityMatrix.m11 = m.m11;
+            unityMatrix.m21 = m.m21;
+            unityMatrix.m31 = m.m31;
+            unityMatrix.m02 = m.m02;
+            unityMatrix.m12 = m.m12;
+            unityMatrix.m22 = m.m22;
+            unityMatrix.m32 = m.m32;
+            unityMatrix.m03 = m.m03;
+            unityMatrix.m13 = m.m13;
+            unityMatrix.m23 = m.m23;
+            unityMatrix.m33 = m.m33;
+        }
+
+        public static void CopyToAvatarMatrix(this in Matrix4x4 unityMatrix, out CAPI.ovrAvatar2Matrix4f m)
+        {
+            m.m00 = unityMatrix.m00;
+            m.m10 = unityMatrix.m10;
+            m.m20 = unityMatrix.m20;
+            m.m30 = unityMatrix.m30;
+            m.m01 = unityMatrix.m01;
+            m.m11 = unityMatrix.m11;
+            m.m21 = unityMatrix.m21;
+            m.m31 = unityMatrix.m31;
+            m.m02 = unityMatrix.m02;
+            m.m12 = unityMatrix.m12;
+            m.m22 = unityMatrix.m22;
+            m.m32 = unityMatrix.m32;
+            m.m03 = unityMatrix.m03;
+            m.m13 = unityMatrix.m13;
+            m.m23 = unityMatrix.m23;
+            m.m33 = unityMatrix.m33;
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarUtility.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarUtility.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..8cc33e30653501cfc07ecc1cc786705595cf4f0f
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarUtility.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 358e4c51d03427a4bbf5412eede77cb3
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarVisemeContext.cs b/Assets/Oculus/Avatar2/Scripts/OvrAvatarVisemeContext.cs
new file mode 100644
index 0000000000000000000000000000000000000000..a647bbd25ca36a5611a8c00c2525bd7b287ed70b
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarVisemeContext.cs
@@ -0,0 +1,227 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Oculus.Avatar2
+{
+    /// <summary>
+    /// C# wrapper around OVRBody lip sync api.
+    /// </summary>
+    public sealed class OvrAvatarVisemeContext : OvrAvatarLipSyncContextBase
+    {
+        private IntPtr _context;
+        private CAPI.ovrAvatar2LipSyncContext _nativeCallbacks;
+
+        internal CAPI.ovrAvatar2LipSyncContextNative NativeCallbacks { get; }
+
+        // Cached so we can keep some previous options when calling SetSampleRate/SetMode
+        private CAPI.ovrAvatar2LipSyncProviderConfig _config;
+
+        #region Public Methods
+
+        public OvrAvatarVisemeContext(CAPI.ovrAvatar2LipSyncProviderConfig config)
+        {
+            _config = config;
+            var result = CAPI.ovrAvatar2LipSync_CreateProvider(ref config, ref _context);
+            if (result != CAPI.ovrAvatar2Result.Success)
+            {
+                OvrAvatarLog.LogError($"ovrAvatar2LipSync_CreateProvider failed with {result}");
+                // New exception type?
+                throw new Exception("Failed to create viseme context");
+            }
+
+            var callbacks = CreateLipSyncContext();
+            if (callbacks.HasValue)
+            {
+                _nativeCallbacks = callbacks.Value;
+            }
+
+            result = CAPI.ovrAvatar2LipSync_InitializeContextNative(_context, out var nativeCb);
+            if (result == CAPI.ovrAvatar2Result.Success)
+            {
+                NativeCallbacks = nativeCb;
+            }
+            else
+            {
+                OvrAvatarLog.LogError($"ovrAvatar2LipSync_InitializeContextNative failed with {result}");
+            }
+        }
+
+        public void FeedAudio(float[] data, int channels)
+        {
+            FeedAudio(data, 0, data.Length, channels);
+        }
+
+        public void FeedAudio(ArraySegment<float> data, int channels)
+        {
+            FeedAudio(data.Array, data.Offset, data.Count, channels);
+        }
+
+        private void FeedAudio(float[] data, int offset, int count, int channels)
+        {
+            bool isStereo = channels == 2;
+            CAPI.ovrAvatar2AudioDataFormat format =
+                isStereo ? CAPI.ovrAvatar2AudioDataFormat.F32_Stereo : CAPI.ovrAvatar2AudioDataFormat.F32_Mono;
+
+            uint samples = (uint) (isStereo ? count / 2 : count);
+
+            var handle = GCHandle.Alloc(data, GCHandleType.Pinned);
+            var offsetAddress = IntPtr.Add(handle.AddrOfPinnedObject(), offset * sizeof(float));
+            var result = CAPI.ovrAvatar2LipSync_FeedAudio(_context, format, offsetAddress, samples);
+            handle.Free();
+            if (result != CAPI.ovrAvatar2Result.Success)
+            {
+                OvrAvatarLog.LogError($"ovrAvatar2LipSync_FeedAudio failed with {result}");
+            }
+        }
+
+        public void FeedAudio(short[] data, int channels)
+        {
+            FeedAudio(data, 0, data.Length, channels);
+        }
+
+        public void FeedAudio(ArraySegment<short> data, int channels)
+        {
+            FeedAudio(data.Array, data.Offset, data.Count, channels);
+        }
+
+        private void FeedAudio(short[] data, int offset, int count, int channels)
+        {
+            bool isStereo = channels == 2;
+            CAPI.ovrAvatar2AudioDataFormat format =
+                isStereo ? CAPI.ovrAvatar2AudioDataFormat.S16_Stereo : CAPI.ovrAvatar2AudioDataFormat.S16_Mono;
+
+            uint samples = (uint) (isStereo ? count / 2 : count);
+
+            var handle = GCHandle.Alloc(data, GCHandleType.Pinned);
+            var offsetAddress = IntPtr.Add(handle.AddrOfPinnedObject(), offset * sizeof(short));
+            var result = CAPI.ovrAvatar2LipSync_FeedAudio(_context, format, offsetAddress, samples);
+            handle.Free();
+            if (result != CAPI.ovrAvatar2Result.Success)
+            {
+                OvrAvatarLog.LogError($"ovrAvatar2LipSync_FeedAudio failed with {result}");
+            }
+        }
+
+        public void Reconfigure(CAPI.ovrAvatar2LipSyncProviderConfig config)
+        {
+            _config = config;
+            Reconfigure();
+        }
+
+        public void SetMode(CAPI.ovrAvatar2LipSyncMode newMode)
+        {
+            _config.mode = newMode;
+            Reconfigure();
+        }
+
+        public void SetSampleRate(UInt32 sampleRate, UInt32 bufferSize)
+        {
+            _config.audioSampleRate = sampleRate;
+            _config.audioBufferSize = bufferSize;
+            Reconfigure();
+        }
+
+        public void SetSmoothing(int smoothing)
+        {
+            var result = CAPI.ovrAvatar2LipSync_SetSmoothing(_context, smoothing);
+            if (result != CAPI.ovrAvatar2Result.Success)
+            {
+                OvrAvatarLog.LogWarning($"ovrAvatar2LipSync_SetSmoothing failed with {result}");
+            }
+        }
+
+        public void EnableViseme(CAPI.ovrAvatar2Viseme viseme)
+        {
+            var result = CAPI.ovrAvatar2LipSync_EnableViseme(_context, viseme);
+            if (result != CAPI.ovrAvatar2Result.Success)
+            {
+                OvrAvatarLog.LogWarning($"ovrAvatar2LipSync_EnableViseme failed with {result}");
+            }
+        }
+
+        public void DisableViseme(CAPI.ovrAvatar2Viseme viseme)
+        {
+            var result = CAPI.ovrAvatar2LipSync_DisableViseme(_context, viseme);
+            if (result != CAPI.ovrAvatar2Result.Success)
+            {
+                OvrAvatarLog.LogWarning($"ovrAvatar2LipSync_DisableViseme failed with {result}");
+            }
+        }
+
+        public void SetViseme(CAPI.ovrAvatar2Viseme viseme, int amount)
+        {
+            var result = CAPI.ovrAvatar2LipSync_SetViseme(_context, viseme, amount);
+            if (result != CAPI.ovrAvatar2Result.Success)
+            {
+                OvrAvatarLog.LogWarning($"ovrAvatar2LipSync_SetViseme failed with {result}");
+            }
+        }
+
+        public void SetLaughter(int amount)
+        {
+            var result = CAPI.ovrAvatar2LipSync_SetLaughter(_context, amount);
+            if (result != CAPI.ovrAvatar2Result.Success)
+            {
+                OvrAvatarLog.LogWarning($"ovrAvatar2LipSync_SetLaughter failed with {result}");
+            }
+        }
+
+        #endregion
+
+        private CAPI.ovrAvatar2LipSyncContext? CreateLipSyncContext()
+        {
+            var lipSyncContext = new CAPI.ovrAvatar2LipSyncContext();
+            var result = CAPI.ovrAvatar2LipSync_InitializeContext(_context, ref lipSyncContext);
+            if (result != CAPI.ovrAvatar2Result.Success)
+            {
+                OvrAvatarLog.LogError($"ovrAvatar2LipSync_InitializeContext failed with {result}");
+                return null;
+            }
+
+            return lipSyncContext;
+        }
+
+        private void ReleaseUnmanagedResources()
+        {
+            if (_context == IntPtr.Zero) return;
+            var result = CAPI.ovrAvatar2LipSync_DestroyProvider(_context);
+            if (result != CAPI.ovrAvatar2Result.Success)
+            {
+                OvrAvatarLog.LogError($"ovrAvatar2LipSync_DestroyProvider failed with {result}");
+            }
+
+            _context = IntPtr.Zero;
+        }
+
+        protected override bool GetLipSyncState(OvrAvatarLipSyncState lipsyncState)
+        {
+            if (_nativeCallbacks.lipSyncCallback != null &&
+                   _nativeCallbacks.lipSyncCallback(out var nativeState, _nativeCallbacks.context))
+            {
+                lipsyncState.FromNative(ref nativeState);
+                return true;
+            }
+            return false;
+        }
+
+        private void Reconfigure()
+        {
+            var result = CAPI.ovrAvatar2LipSync_ReconfigureProvider(_context, ref _config);
+            if (!result.IsSuccess())
+            {
+                OvrAvatarLog.LogWarning($"ovrAvatar2LipSync_ReconfigureProvider failed with {result}");
+            }
+        }
+
+        protected override void Dispose(bool disposing)
+        {
+            ReleaseUnmanagedResources();
+            base.Dispose(disposing);
+        }
+
+        ~OvrAvatarVisemeContext()
+        {
+            ReleaseUnmanagedResources();
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrAvatarVisemeContext.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrAvatarVisemeContext.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..b84f5ee1aaaa23f413de1ff9a48f977ba5941a63
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrAvatarVisemeContext.cs.meta
@@ -0,0 +1,3 @@
+fileFormatVersion: 2
+guid: e01d1be1057f43069c1e414791a163c1
+timeCreated: 1605743561
\ No newline at end of file
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrPluginTracking.cs b/Assets/Oculus/Avatar2/Scripts/OvrPluginTracking.cs
new file mode 100644
index 0000000000000000000000000000000000000000..4a2837d672353be1b9baeb5169664d41b6651d8e
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrPluginTracking.cs
@@ -0,0 +1,235 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Oculus.Avatar2
+{
+    internal static class OvrPluginTracking
+    {
+#if UNITY_EDITOR || !UNITY_IOS
+#if UNITY_EDITOR_OSX
+        private const string LibFile = OvrAvatarPlugin.FullPluginFolderPath + "libovrplugintracking.framework/libovrplugintracking";
+#else
+        private const string LibFile = OvrAvatarManager.IsAndroidStandalone ? "ovrplugintracking" : "libovrplugintracking";
+#endif  // UNITY_EDITOR_OSX
+#else   // !UNITY_EDITOR && UNITY_IOS
+        private const string LibFile = "__Internal";
+#endif  // !UNITY_EDITOR && UNITY_IOS
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        [return: MarshalAs(UnmanagedType.U1)]
+        private static extern bool ovrpTracking_Initialize(CAPI.LoggingDelegate loggingDelegate, IntPtr loggingContext);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        private static extern void ovrpTracking_Shutdown();
+
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        [return: MarshalAs(UnmanagedType.U1)]
+        private static extern bool ovrpTracking_CreateFaceTrackingContext(out CAPI.ovrAvatar2FacePoseProvider outContext);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ovrpTracking_CreateFaceTrackingContext")]
+        [return: MarshalAs(UnmanagedType.U1)]
+        private static extern bool ovrpTracking_CreateFaceTrackingContextNative(out CAPI.ovrAvatar2FacePoseProviderNative outContext);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        [return: MarshalAs(UnmanagedType.U1)]
+        private static extern bool ovrpTracking_CreateEyeTrackingContext(out CAPI.ovrAvatar2EyePoseProvider outContext);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ovrpTracking_CreateEyeTrackingContext")]
+        [return: MarshalAs(UnmanagedType.U1)]
+        private static extern bool ovrpTracking_CreateEyeTrackingContextNative(out CAPI.ovrAvatar2EyePoseProviderNative outContext);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl)]
+        [return: MarshalAs(UnmanagedType.U1)]
+        private static extern bool ovrpTracking_CreateHandTrackingContext(
+            out CAPI.ovrAvatar2HandTrackingDataContext outContext);
+
+        [DllImport(LibFile, CallingConvention = CallingConvention.Cdecl, EntryPoint = "ovrpTracking_CreateHandTrackingContext")]
+        [return: MarshalAs(UnmanagedType.U1)]
+        private static extern bool ovrpTracking_CreateHandTrackingContextNative(
+            out CAPI.ovrAvatar2HandTrackingDataContextNative outContext);
+
+
+        public static bool Initialize(CAPI.LoggingDelegate cb, IntPtr logContext)
+        {
+            try
+            {
+                return ovrpTracking_Initialize(cb, logContext);
+            }
+            catch (DllNotFoundException)
+            {
+                OvrAvatarLog.LogWarning($"Lib {LibFile} not found");
+                return false;
+            }
+        }
+
+        public static void Shutdown()
+        {
+            try
+            {
+                ovrpTracking_Shutdown();
+            }
+            catch (DllNotFoundException)
+            {
+
+            }
+        }
+
+
+        private static CAPI.ovrAvatar2FacePoseProvider? CreateInternalFaceTrackingContext()
+        {
+            if (ovrpTracking_CreateFaceTrackingContext(out var context))
+            {
+                return context;
+            }
+
+            return null;
+        }
+
+        private static CAPI.ovrAvatar2FacePoseProviderNative? CreateInternalFaceTrackingContextNative()
+        {
+            if (ovrpTracking_CreateFaceTrackingContextNative(out var context))
+            {
+                return context;
+            }
+
+            return null;
+        }
+
+        private static CAPI.ovrAvatar2EyePoseProvider? CreateInternalEyeTrackingContext()
+        {
+            if (ovrpTracking_CreateEyeTrackingContext(out var context))
+            {
+                return context;
+            }
+
+            return null;
+        }
+
+        private static CAPI.ovrAvatar2EyePoseProviderNative? CreateInternalEyeTrackingContextNative()
+        {
+            if (ovrpTracking_CreateEyeTrackingContextNative(out var context))
+            {
+                return context;
+            }
+
+            return null;
+        }
+
+        private static CAPI.ovrAvatar2HandTrackingDataContext? CreateHandTrackingContext()
+        {
+            if (ovrpTracking_CreateHandTrackingContext(out var context))
+            {
+                return context;
+            }
+
+            return null;
+        }
+
+        private static CAPI.ovrAvatar2HandTrackingDataContextNative? CreateHandTrackingContextNative()
+        {
+            if (ovrpTracking_CreateHandTrackingContextNative(out var context))
+            {
+                return context;
+            }
+
+            return null;
+        }
+
+        public static IOvrAvatarHandTrackingDelegate CreateHandTrackingDelegate()
+        {
+            var context = CreateHandTrackingContext();
+            var native = CreateHandTrackingContextNative();
+            return context.HasValue && native.HasValue ? new HandTrackingDelegate(context.Value, native.Value) : null;
+        }
+
+
+        public static OvrAvatarFacePoseProviderBase CreateFaceTrackingContext()
+        {
+            var context = CreateInternalFaceTrackingContext();
+            var nativeContext = CreateInternalFaceTrackingContextNative();
+            return context.HasValue && nativeContext.HasValue ? new OvrPluginFaceTrackingProvider(context.Value, nativeContext.Value) : null;
+        }
+
+        public static OvrAvatarEyePoseProviderBase CreateEyeTrackingContext()
+        {
+            var context = CreateInternalEyeTrackingContext();
+            var nativeContext = CreateInternalEyeTrackingContextNative();
+            return context.HasValue && nativeContext.HasValue ? new OvrPluginEyeTrackingProvider(context.Value, nativeContext.Value) : null;
+        }
+
+        private class HandTrackingDelegate : IOvrAvatarHandTrackingDelegate, IOvrAvatarNativeHandDelegate
+        {
+            private CAPI.ovrAvatar2HandTrackingDataContext _context;
+            public CAPI.ovrAvatar2HandTrackingDataContextNative NativeContext { get; }
+
+            public HandTrackingDelegate(CAPI.ovrAvatar2HandTrackingDataContext context, CAPI.ovrAvatar2HandTrackingDataContextNative native)
+            {
+                _context = context;
+                NativeContext = native;
+            }
+
+            public bool GetHandData(OvrAvatarTrackingHandsState handData)
+            {
+                if (_context.handTrackingCallback(out var native, _context.context))
+                {
+                    handData.FromNative(ref native);
+                    return true;
+                }
+
+                return false;
+            }
+        }
+
+
+        private sealed class OvrPluginFaceTrackingProvider : OvrAvatarFacePoseProviderBase, IOvrAvatarNativeFacePose
+        {
+            private readonly CAPI.ovrAvatar2FacePoseProvider _context;
+            private readonly CAPI.ovrAvatar2FacePoseProviderNative _nativeContext;
+
+            CAPI.ovrAvatar2FacePoseProviderNative IOvrAvatarNativeFacePose.NativeProvider => _nativeContext;
+
+            public OvrPluginFaceTrackingProvider(CAPI.ovrAvatar2FacePoseProvider context, CAPI.ovrAvatar2FacePoseProviderNative nativeContext)
+            {
+                _context = context;
+                _nativeContext = nativeContext;
+            }
+
+            protected override bool GetFacePose(OvrAvatarFacePose faceState)
+            {
+                if (_context.facePoseCallback != null &&
+                    _context.facePoseCallback(out var nativeFaceState, _context.provider))
+                {
+                    faceState.FromNative(in nativeFaceState);
+                    return true;
+                }
+                return false;
+            }
+        }
+
+        private sealed class OvrPluginEyeTrackingProvider : OvrAvatarEyePoseProviderBase, IOvrAvatarNativeEyePose
+        {
+            private readonly CAPI.ovrAvatar2EyePoseProvider _context;
+            private readonly CAPI.ovrAvatar2EyePoseProviderNative _nativeContext;
+
+            CAPI.ovrAvatar2EyePoseProviderNative IOvrAvatarNativeEyePose.NativeProvider => _nativeContext;
+
+            public OvrPluginEyeTrackingProvider(CAPI.ovrAvatar2EyePoseProvider context, CAPI.ovrAvatar2EyePoseProviderNative nativeContext)
+            {
+                _context = context;
+                _nativeContext = nativeContext;
+            }
+
+            protected override bool GetEyePose(OvrAvatarEyesPose eyeState)
+            {
+                if (_context.eyePoseCallback != null &&
+                    _context.eyePoseCallback(out var nativeEyeState, _context.provider))
+                {
+                    eyeState.FromNative(in nativeEyeState);
+                    return true;
+                }
+                return false;
+            }
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/OvrPluginTracking.cs.meta b/Assets/Oculus/Avatar2/Scripts/OvrPluginTracking.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..6db5b297eb71d896e77d16e659851bfeedb3576f
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/OvrPluginTracking.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 513611b6719e05a4b95d82889f81bfab
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/ShaderUtils.meta b/Assets/Oculus/Avatar2/Scripts/ShaderUtils.meta
new file mode 100644
index 0000000000000000000000000000000000000000..3d4c20d08826105c05c6b16f51cacf0b76efd3c4
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/ShaderUtils.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: cbe7edd183133f8439119dbd13815cc8
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData:
+  assetBundleName:
+  assetBundleVariant:
diff --git a/Assets/Oculus/Avatar2/Scripts/ShaderUtils/AvatarCustom.cginc b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/AvatarCustom.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..3646456a2a576a97a5451dcc54567f8f18bdad7d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/AvatarCustom.cginc
@@ -0,0 +1,199 @@
+#ifndef AVATAR_CUSTOM_INCLUDED
+#define AVATAR_CUSTOM_INCLUDED
+
+// TODO*: Documentation here
+//#include "UnityCG.cginc"
+#include "UnityInstancing.cginc"
+#include "AvatarCustomTypes.cginc"
+#include "OvrAvatarSupportDefines.hlsl"
+#include "OvrAvatarVertexFetch.hlsl"
+#include "OvrAvatarCommonVertexParams.hlsl"
+
+void OvrPopulateVertexDataFromExternalTexture(bool hasTangents, bool applyOffsetAndBias, inout OvrVertexData vertData) {
+  const uint vertexId = vertData.vertexId;
+
+  int numAttributes = 2;
+
+  if (hasTangents) {
+    numAttributes = 3;
+    vertData.tangent = OvrGetVertexTangentFromTexture(
+      vertexId,
+      numAttributes,
+      applyOffsetAndBias,
+      _OvrAttributeInterpolationValue);
+  }
+
+  vertData.position = OvrGetVertexPositionFromTexture(
+    vertexId,
+    numAttributes,
+    applyOffsetAndBias,
+    _OvrAttributeInterpolationValue);
+  vertData.normal = OvrGetVertexNormalFromTexture(
+    vertexId,
+    numAttributes,
+    applyOffsetAndBias,
+    _OvrAttributeInterpolationValue);
+}
+
+#if defined(OVR_SUPPORT_EXTERNAL_BUFFERS)
+  void OvrPopulateVertexDataFromExternalBuffers(bool hasTangents, bool interpolateAttributes, inout OvrVertexData vertData) {
+    const uint vertexId = vertData.vertexId;
+
+    const uint numPosEntriesPerVert = _OvrNumOutputEntriesPerAttribute;
+    const uint numFrenetEntriesPerVert = (hasTangents ? 2u : 1u) * _OvrNumOutputEntriesPerAttribute;
+
+    const uint startIndexOfPositionEntries = vertexId * numPosEntriesPerVert;
+    const uint startIndexOfFrenetEntries = vertexId * numFrenetEntriesPerVert;
+
+    const uint posEntryIndex = startIndexOfPositionEntries + _OvrAttributeOutputLatestAnimFrameEntryOffset;
+    const uint normEntryIndex = startIndexOfFrenetEntries + _OvrAttributeOutputLatestAnimFrameEntryOffset;
+
+    [branch]
+    if (interpolateAttributes) {
+      const float lerpValue = _OvrAttributeInterpolationValue;
+
+      // Grab the latest animation frame's position as well as the previous animation frame's position
+      const float3 latestPos = OvrGetPositionEntryFromExternalBuffer(posEntryIndex);
+      const float3 prevPos = OvrGetPositionEntryFromExternalBuffer(startIndexOfPositionEntries + _OvrAttributeOutputPrevAnimFrameEntryOffset);
+      const float3 latestNorm = OvrGetFrenetEntryFromExternalBuffer(normEntryIndex);
+      const float3 prevNorm = OvrGetFrenetEntryFromExternalBuffer(startIndexOfFrenetEntries + _OvrAttributeOutputPrevAnimFrameEntryOffset);
+
+      vertData.position = float4(lerp(prevPos, latestPos, lerpValue), 1.0);
+      vertData.normal = lerp(prevNorm, latestNorm, lerpValue);
+
+      [branch]
+      if (hasTangents) {
+        const uint tanEntriesStartIndex = startIndexOfFrenetEntries + _OvrNumOutputEntriesPerAttribute;
+        const float4 latestTan = OvrGetFrenetEntryFromExternalBuffer(tanEntriesStartIndex + _OvrAttributeOutputLatestAnimFrameEntryOffset);
+        const float3 prevTan = OvrGetFrenetEntryFromExternalBuffer(tanEntriesStartIndex + _OvrAttributeOutputPrevAnimFrameEntryOffset).xyz;
+
+        vertData.tangent = float4(lerp(prevTan, latestTan, lerpValue), latestTan.w);
+      }
+    } else {
+      vertData.position = float4(OvrGetPositionEntryFromExternalBuffer(posEntryIndex), 1.0);
+      vertData.normal = OvrGetFrenetEntryFromExternalBuffer(normEntryIndex);
+
+      [branch]
+      if (hasTangents) {
+        const uint tanEntryIndex = normEntryIndex + _OvrNumOutputEntriesPerAttribute;
+        vertData.tangent = OvrGetFrenetEntryFromExternalBuffer(tanEntryIndex);
+      }
+    }
+  }
+#endif
+
+
+// First, define a function which takes explicit data types, then define a macro which expands
+// an arbitrary vertex structure definition into the function parameters
+OvrVertexData OvrCreateVertexData(
+  float4 vPos,
+  float3 vNorm,
+  float4 vTan,
+  uint vertexId)
+{
+  // Backward compatibility/optimization support if application is ok with additional variants
+  // The shader compiler should optimize out branches that are based on static const values
+#if defined(OVR_VERTEX_FETCH_VERT_BUFFER)
+  static const int fetchMode = OVR_VERTEX_FETCH_MODE_STRUCT;
+#elif defined(OVR_VERTEX_FETCH_EXTERNAL_BUFFER) && defined(OVR_SUPPORT_EXTERNAL_BUFFERS)
+  static const int fetchMode = OVR_VERTEX_FETCH_MODE_EXTERNAL_BUFFERS;
+#elif defined(OVR_VERTEX_FETCH_TEXTURE) || defined(OVR_VERTEX_FETCH_TEXTURE_UNORM)
+  static const int fetchMode = OVR_VERTEX_FETCH_MODE_EXTERNAL_TEXTURES;
+#else
+  const int fetchMode = _OvrVertexFetchMode;
+#endif
+
+#if defined(OVR_VERTEX_HAS_TANGENTS)
+  static const bool hasTangents = true;
+#elif defined(OVR_VERTEX_NO_TANGENTS)
+  static const bool hasTangents = false;
+#else
+  const bool hasTangents = _OvrHasTangents;
+#endif
+
+#if defined(OVR_VERTEX_INTERPOLATE_ATTRIBUTES)
+  static const bool interpolateAttributes = true;
+#elif defined(OVR_VERTEX_DO_NOT_INTERPOLATE_ATTRIBUTES)
+  static const bool interpolateAttributes = false;
+#else
+  const bool interpolateAttributes = _OvrInterpolateAttributes;
+#endif
+
+  OvrVertexData vertData;
+  vertData.vertexId = vertexId;
+  vertData.position = vPos;
+  vertData.normal = vNorm;
+  vertData.tangent = vTan;
+
+  // Hope that the compiler branches here. The [branch] attribute here seems to lead to compile
+  // probably due to "use of gradient function, such as tex3d"
+  if (fetchMode == OVR_VERTEX_FETCH_MODE_EXTERNAL_TEXTURES) {
+    // Backwards compatibility with existing keywords.
+    // OVR_VERTEX_FETCH_TEXTURE_UNORM means normalized attributes, OVR_VERTEX_FETCH_TEXTURE
+    // means not normalized. Neither keyword means that the "fetch mode" was via
+    // a property and there is no property for normalized attributes or not. So in that
+    // scenario, always apply offset and bias
+    #if defined(OVR_VERTEX_FETCH_TEXTURE)
+      static const bool applyOffsetAndBias = false;
+    #else
+      static const bool applyOffsetAndBias = true;
+    #endif
+
+    OvrPopulateVertexDataFromExternalTexture(hasTangents, applyOffsetAndBias, vertData);
+#if defined(OVR_SUPPORT_EXTERNAL_BUFFERS)
+  } else if (fetchMode == OVR_VERTEX_FETCH_MODE_EXTERNAL_BUFFERS) {
+    OvrPopulateVertexDataFromExternalBuffers(hasTangents, interpolateAttributes, vertData);
+#endif
+  }
+
+  return vertData;
+} // end OvrCreateVertexData
+
+#define OVR_CREATE_VERTEX_DATA(v) \
+  OvrCreateVertexData( \
+    OVR_GET_VERTEX_POSITION_FIELD(v), \
+    OVR_GET_VERTEX_NORMAL_FIELD(v), \
+    OVR_GET_VERTEX_TANGENT_FIELD(v), \
+    OVR_GET_VERTEX_VERT_ID_FIELD(v))
+
+// Initialization for "required fields" in the vertex input struct for the vertex shader.
+// Written as a macro to be expandable in future
+#define OVR_INITIALIZE_VERTEX_FIELDS(v)
+
+// Initializes the fields for a defined default vertex structure
+void OvrInitializeDefaultAppdata(inout OvrDefaultAppdata v) {
+  OVR_INITIALIZE_VERTEX_FIELDS(v);
+#ifdef UNIVERSAL_LIGHTING_INCLUDED
+  #ifdef UNITY_STEREO_INSTANCING_ENABLED
+    #if defined(SHADER_API_GLES3)
+      unity_StereoEyeIndex = round(fmod(v.instanceID, 2.0));
+      unity_InstanceID = unity_BaseInstanceID + (v.instanceID >> 1);
+    #else
+      unity_StereoEyeIndex = v.instanceID & 0x01;
+      unity_InstanceID = unity_BaseInstanceID + (v.instanceID >> 1);
+    #endif
+  #else
+  // there used to be a global variable unity_InstanceID? For now, do nothing
+  //  unity_InstanceID = v.instanceID + unity_BaseInstanceID;
+  #endif
+#else
+  UNITY_SETUP_INSTANCE_ID(v);
+#endif
+}
+
+// Initializes the fields for a defined default vertex structure
+// and creates the OvrVertexData for the vertex as well as overrides
+// applicable fields in OvrDefaultAppdata with fields from OvrVertexData.
+// Mainly useful in surface shader vertex functions.
+OvrVertexData OvrInitializeDefaultAppdataAndPopulateWithVertexData(inout OvrDefaultAppdata v) {
+  OvrInitializeDefaultAppdata(v);
+  OvrVertexData vertexData = OVR_CREATE_VERTEX_DATA(v);
+
+  OVR_SET_VERTEX_POSITION_FIELD(v, vertexData.position);
+  OVR_SET_VERTEX_NORMAL_FIELD(v, vertexData.normal);
+  OVR_SET_VERTEX_TANGENT_FIELD(v, vertexData.tangent);
+
+  return vertexData;
+}
+
+#endif // AVATAR_CUSTOM_INCLUDED
diff --git a/Assets/Oculus/Avatar2/Scripts/ShaderUtils/AvatarCustom.cginc.meta b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/AvatarCustom.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..8687d5cad698269fc55df4512a32a2818baa32f3
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/AvatarCustom.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 7085c4c8be6b89b4ca085f3d41211902
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/ShaderUtils/AvatarCustomTypes.cginc b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/AvatarCustomTypes.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..234f19296ab65f3a147b976099047158fe369a79
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/AvatarCustomTypes.cginc
@@ -0,0 +1,157 @@
+#ifndef AVATAR_CUSTOM_TYPES_INCLUDED
+#define AVATAR_CUSTOM_TYPES_INCLUDED
+
+// Keep this around for backwards compatibility
+#define OVR_VERTEX_HAS_VERTEX_ID
+
+// ------------------------------------------------------------------------------
+// Define some macros for getting/setting a vertex field.
+// Defined as macros to make potential field name changes easier to deal with.
+// These macros are not required but may make it easier to integrate the SDK
+// and use custom shaders
+#ifndef OVR_VERTEX_POSITION_FIELD_NAME
+#define OVR_VERTEX_POSITION_FIELD_NAME vertex
+#endif
+
+#define OVR_GET_VERTEX_POSITION_FIELD(v) v.OVR_VERTEX_POSITION_FIELD_NAME
+#define OVR_SET_VERTEX_POSITION_FIELD(v, val) v.OVR_VERTEX_POSITION_FIELD_NAME = val;
+
+// A "default" definition of a field
+#define OVR_VERTEX_POSITION_FIELD float4 OVR_VERTEX_POSITION_FIELD_NAME : POSITION;
+
+// ------------------------------------------------------------------------------
+// Define some macros for getting/setting normal field
+// Defined as macros to make potential field name changes easier to deal with.
+#ifndef OVR_VERTEX_NORMAL_FIELD_NAME
+#define OVR_VERTEX_NORMAL_FIELD_NAME normal
+#endif
+
+#define OVR_GET_VERTEX_NORMAL_FIELD(v) v.OVR_VERTEX_NORMAL_FIELD_NAME
+#define OVR_SET_VERTEX_NORMAL_FIELD(v, val) v.OVR_VERTEX_NORMAL_FIELD_NAME = val;
+
+// Define the "normal" field
+#define OVR_VERTEX_NORMAL_FIELD float3 OVR_VERTEX_NORMAL_FIELD_NAME : NORMAL;
+
+// ------------------------------------------------------------------------------
+// Define some macros for getting/setting tangent field
+// Defined as macros to make potential field name changes easier to deal with.
+#ifndef OVR_VERTEX_TANGENT_FIELD_NAME
+#define OVR_VERTEX_TANGENT_FIELD_NAME tangent
+#endif
+
+#define OVR_GET_VERTEX_TANGENT_FIELD(v) v.OVR_VERTEX_TANGENT_FIELD_NAME
+#define OVR_SET_VERTEX_TANGENT_FIELD(v, val) v.OVR_VERTEX_TANGENT_FIELD_NAME = val;
+
+// Define the "tangent" field
+#define OVR_VERTEX_TANGENT_FIELD float4 OVR_VERTEX_TANGENT_FIELD_NAME : TANGENT;
+
+// ------------------------------------------------------------------------------
+// Define some macros for getting vertex ID field
+// Defined as macros to make potential field name changes easier to deal with.
+#ifndef OVR_VERTEX_VERT_ID_FIELD_NAME
+#define OVR_VERTEX_VERT_ID_FIELD_NAME vid
+#endif
+
+#define OVR_GET_VERTEX_VERT_ID_FIELD(v) v.OVR_VERTEX_VERT_ID_FIELD_NAME
+
+// Define the "vertex ID" field
+#define OVR_VERTEX_VERT_ID_FIELD uint OVR_VERTEX_VERT_ID_FIELD_NAME : SV_VertexID;
+
+// ------------------------------------------------------------------------------
+// Define "required" fields for an vertex input structure
+
+// NOTE: Due to the way surface shader work in Unity, some surface shaders may require
+// having both the normal and tangent defined in the vertex input structure due to some
+// surface shader inner workings. Due to that fact, the normal and tangent will be listed
+// as "required" even if the information will be stored in a buffer instead. The hope/idea
+// is that the shader compiler will optimize out the unused fields for vertex and fragment
+// shaders and just fill in with some default values for surface shaders
+
+#define OVR_REQUIRED_VERTEX_FIELDS \
+  OVR_VERTEX_POSITION_FIELD \
+  OVR_VERTEX_NORMAL_FIELD \
+  OVR_VERTEX_TANGENT_FIELD \
+  OVR_VERTEX_VERT_ID_FIELD
+
+
+// Define next "set" of texture coordinate semantics which are available.
+// This is useful to not require updating structs as more texture coordinate
+// semantics become required
+// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+// NOTE: Update these definitions if more interpolators become required
+// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+#define OVR_FIRST_AVAILABLE_VERTEX_TEXCOORD_SEMANTIC TEXCOORD0
+#define OVR_SECOND_AVAILABLE_VERTEX_TEXCOORD_SEMANTIC TEXCOORD1
+#define OVR_THIRD_AVAILABLE_VERTEX_TEXCOORD_SEMANTIC TEXCOORD2
+#define OVR_FOURTH_AVAILABLE_VERTEX_TEXCOORD_SEMANTIC TEXCOORD3
+#define OVR_FIFTH_AVAILABLE_VERTEX_TEXCOORD_SEMANTIC TEXCOORD4
+#define OVR_SIXTH_AVAILABLE_VERTEX_TEXCOORD_SEMANTIC TEXCOORD5
+#define OVR_SEVENTH_AVAILABLE_VERTEX_TEXCOORD_SEMANTIC TEXCOORD6
+#define OVR_EIGHTH_AVAILABLE_VERTEX_TEXCOORD_SEMANTIC TEXCOORD7
+
+// ------------------------------------------------------------------------------
+// Define a "default" vertex structure
+
+// ------------------------------------------------------------------------------
+// Define some macros for getting/setting texture coordinate
+// Defined as macros to make potential field name changes easier to deal with.
+#ifndef OVR_VERTEX_TEXCOORD_FIELD_NAME
+#define OVR_VERTEX_TEXCOORD_FIELD_NAME texcoord
+#endif
+
+#define OVR_GET_VERTEX_TEXCOORD(v) v.OVR_VERTEX_TEXCOORD_FIELD_NAME
+#define OVR_SET_VERTEX_TEXCOORD(v, val) v.OVR_VERTEX_TEXCOORD_FIELD_NAME = val;
+
+#define OVR_VERTEX_TEXCOORD_FIELD float4 OVR_VERTEX_TEXCOORD_FIELD_NAME : OVR_FIRST_AVAILABLE_VERTEX_TEXCOORD_SEMANTIC;
+
+// ------------------------------------------------------------------------------
+// Define some macros for getting/setting color
+// Defined as macros to make potential field name changes easier to deal with.
+#ifndef OVR_VERTEX_COLOR_FIELD_NAME
+#define OVR_VERTEX_COLOR_FIELD_NAME color
+#endif
+
+#define OVR_GET_VERTEX_COLOR(v) v.OVR_VERTEX_COLOR_FIELD_NAME
+#define OVR_SET_VERTEX_COLOR(v, val) v.OVR_VERTEX_COLOR_FIELD_NAME = val;
+
+#define OVR_VERTEX_COLOR_FIELD float4 OVR_VERTEX_COLOR_FIELD_NAME : COLOR;
+
+// ------------------------------------------------------------------------------
+// Define some macros for getting/setting ormt
+// Defined as macros to make potential field name changes easier to deal with.
+#ifndef OVR_VERTEX_ORMT_FIELD_NAME
+#define OVR_VERTEX_ORMT_FIELD_NAME ormt
+#endif
+
+#define OVR_GET_VERTEX_ORMT(v) v.OVR_VERTEX_ORMT_FIELD_NAME
+#define OVR_SET_VERTEX_ORMT(v, val) v.OVR_VERTEX_ORMT_FIELD_NAME = val;
+
+#define OVR_VERTEX_ORMT_FIELD \
+  float4 OVR_VERTEX_ORMT_FIELD_NAME : OVR_SECOND_AVAILABLE_VERTEX_TEXCOORD_SEMANTIC;
+
+#define OVR_DEFAULT_VERTEX_FIELDS \
+  OVR_REQUIRED_VERTEX_FIELDS      \
+  OVR_VERTEX_TEXCOORD_FIELD       \
+  OVR_VERTEX_COLOR_FIELD          \
+  OVR_VERTEX_ORMT_FIELD           \
+  uint instanceID : SV_InstanceID;
+  //UNITY_VERTEX_INPUT_INSTANCE_ID
+
+// Define a default vertex input struct
+struct OvrDefaultAppdata {
+  OVR_DEFAULT_VERTEX_FIELDS
+};
+
+// ------------------------------------------------
+// Define a structure for required data, per vertex
+// This struct will be used in vertex programs instead
+// of the vertex structure for Oculus related functions
+struct OvrVertexData {
+  float4 position;
+  float3 normal;
+  float4 tangent;
+
+  uint vertexId;
+};
+
+#endif // AVATAR_CUSTOM_TYPES_INCLUDED
diff --git a/Assets/Oculus/Avatar2/Scripts/ShaderUtils/AvatarCustomTypes.cginc.meta b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/AvatarCustomTypes.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..9b1260e20d8cc2d7e57c7dade55ca9397c3c8f77
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/AvatarCustomTypes.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: b4cbbc02e699f0e469f417fe43540a40
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/ShaderUtils/AvatarSubmesh.cginc b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/AvatarSubmesh.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..a53b662611d2a6086641b68c1663c5e6a270b1e6
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/AvatarSubmesh.cginc
@@ -0,0 +1,14 @@
+#define SUBMESH_TYPE_NONE 0
+#define SUBMESH_TYPE_OUTFIT     (float)(1<<0)
+#define SUBMESH_TYPE_BODY       (float)(1<<1)
+#define SUBMESH_TYPE_HEAD       (float)(1<<2)
+#define SUBMESH_TYPE_HAIR       (float)(1<<3)
+#define SUBMESH_TYPE_EYEBROW    (float)(1<<4)
+#define SUBMESH_TYPE_L_EYE      (float)(1<<5)
+#define SUBMESH_TYPE_R_EYE      (float)(1<<6)
+#define SUBMESH_TYPE_LASHES     (float)(1<<7)
+#define SUBMESH_TYPE_FACIALHAIR (float)(1<<8)
+#define SUBMESH_TYPE_HEADWEAR   (float)(1<<9)
+#define SUBMESH_TYPE_EARRINGS   (float)(1<<10)
+
+#define SUBMESH_TYPE_BUFFER 0.5
diff --git a/Assets/Oculus/Avatar2/Scripts/ShaderUtils/AvatarSubmesh.cginc.meta b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/AvatarSubmesh.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..6c4c2c9c2c7828f6aadfd6e4601c75a8976eb98c
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/AvatarSubmesh.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 5dc8e6783aeb0b64ea431de90ae6eac8
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrAvatarCommonVertexParams.hlsl b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrAvatarCommonVertexParams.hlsl
new file mode 100644
index 0000000000000000000000000000000000000000..2358e2594589416d840b956d5f3efd1245673dac
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrAvatarCommonVertexParams.hlsl
@@ -0,0 +1,32 @@
+#ifndef OVR_AVATAR_COMMON_VERTEX_PARAMS_INCLUDED
+#define OVR_AVATAR_COMMON_VERTEX_PARAMS_INCLUDED
+
+#include "OvrAvatarSupportDefines.hlsl"
+
+// This file is for parameters used by meta avatars SDK vertex programs
+// for fetching the normal rendering pass(es) as well as the "motion vectors" pass
+
+// In an effort to not require third parties to define or multi_compile some specific
+// keywords, make some logic use branching instead of preprocessor directives. This
+// will be a performance hit, but should hopefully be minimal since
+// the branches are based on uniforms. Should only be the cost of the branch and not
+// vary across warps/waveforms.
+static const int OVR_VERTEX_FETCH_MODE_STRUCT = 0;
+static const int OVR_VERTEX_FETCH_MODE_EXTERNAL_BUFFERS  = 1;
+static const int OVR_VERTEX_FETCH_MODE_EXTERNAL_TEXTURES  = 2;
+
+bool _OvrHasTangents;
+int _OvrVertexFetchMode;
+bool _OvrInterpolateAttributes;
+
+// Interpolation value for the "current" render frame
+float _OvrAttributeInterpolationValue;
+
+#if defined(OVR_SUPPORT_EXTERNAL_BUFFERS)
+  int _OvrNumOutputEntriesPerAttribute;
+  int _OvrAttributeOutputLatestAnimFrameEntryOffset;
+  int _OvrAttributeOutputPrevAnimFrameEntryOffset;
+
+#endif
+
+#endif // OVR_AVATAR_COMMON_VERTEX_PARAMS_INCLUDED
diff --git a/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrAvatarCommonVertexParams.hlsl.meta b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrAvatarCommonVertexParams.hlsl.meta
new file mode 100644
index 0000000000000000000000000000000000000000..7223b551fa75f3a277e5a6067639cae93e6b018b
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrAvatarCommonVertexParams.hlsl.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 9239f0f4e24d887479eeaf9271733b31
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData:
+  assetBundleName:
+  assetBundleVariant:
diff --git a/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrAvatarMaterialExtension.cs b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrAvatarMaterialExtension.cs
new file mode 100644
index 0000000000000000000000000000000000000000..8d28bee19577363b83b64f62e0c114719120024f
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrAvatarMaterialExtension.cs
@@ -0,0 +1,399 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+
+using Unity.Collections.LowLevel.Unsafe;
+
+using UnityEngine;
+
+namespace Oculus.Avatar2
+{
+    public readonly struct OvrAvatarMaterialExtension
+    {
+        //////////////////////////////////////////////////
+        // ExtenstionEntry<T>
+        //////////////////////////////////////////////////
+        private struct ExtensionEntry<T>
+        {
+            private string _name;
+            private T _payload;
+
+            public ExtensionEntry(string name, T payload)
+            {
+                _name = name;
+                _payload = payload;
+            }
+
+            public string Name => _name;
+            public T Payload => _payload;
+        }
+
+        //////////////////////////////////////////////////
+        // ExtenstionEntries
+        //////////////////////////////////////////////////
+        private class ExtensionEntries
+        {
+            private const string extensionLogScope = "OvrAvatarMaterialExtension_ExtensionEntries";
+
+            private readonly List<ExtensionEntry<Vector3>> _vector3Entries = new List<ExtensionEntry<Vector3>>();
+            private readonly List<ExtensionEntry<Vector4>> _vector4Entries = new List<ExtensionEntry<Vector4>>();
+            private readonly List<ExtensionEntry<float>> _floatEntries = new List<ExtensionEntry<float>>();
+            private readonly List<ExtensionEntry<int>> _intEntries = new List<ExtensionEntry<int>>();
+            private readonly List<ExtensionEntry<Texture2D>> _textureEntries = new List<ExtensionEntry<Texture2D>>();
+
+            public void ApplyToMaterial(Material mat, string extensionName,
+                OvrAvatarMaterialExtensionConfig extensionConfig)
+            {
+                Debug.Assert(extensionConfig != null);
+
+                string nameInShader;
+                foreach (var entry in _vector3Entries)
+                {
+                    if (extensionConfig.TryGetNameInShader(extensionName, entry.Name, out nameInShader))
+                    {
+                        mat.SetVector(nameInShader, entry.Payload);
+                    }
+                }
+
+                foreach (var entry in _vector4Entries)
+                {
+                    if (extensionConfig.TryGetNameInShader(extensionName, entry.Name, out nameInShader))
+                    {
+                        mat.SetVector(nameInShader, entry.Payload);
+                    }
+                }
+
+                foreach (var entry in _floatEntries)
+                {
+                    if (extensionConfig.TryGetNameInShader(extensionName, entry.Name, out nameInShader))
+                    {
+                        mat.SetFloat(nameInShader, entry.Payload);
+                    }
+                }
+
+                foreach (var entry in _intEntries)
+                {
+                    if (extensionConfig.TryGetNameInShader(extensionName, entry.Name, out nameInShader))
+                    {
+                        mat.SetInt(nameInShader, entry.Payload);
+                    }
+                }
+
+                foreach (var entry in _textureEntries)
+                {
+                    if (extensionConfig.TryGetNameInShader(extensionName, entry.Name, out nameInShader))
+                    {
+                        mat.SetTexture(nameInShader, entry.Payload);
+                    }
+                }
+            }
+
+            public bool LoadEntry(CAPI.ovrAvatar2Id primitiveId, UInt32 extensionIndex, UInt32 entryIndex)
+            {
+                bool success = GetEntryMetaData(primitiveId, extensionIndex, entryIndex, out var metaData);
+
+                if (!success) { return false; }
+
+                // Now grab the name and the data of the entry
+                switch (metaData.entryType)
+                {
+                    case CAPI.ovrAvatar2MaterialExtensionEntryType.Float:
+                        success = StoreNameAndPayloadForEntry(
+                            primitiveId,
+                            extensionIndex,
+                            entryIndex,
+                            metaData,
+                            _floatEntries);
+                        break;
+                    case CAPI.ovrAvatar2MaterialExtensionEntryType.Int:
+                        success = StoreNameAndPayloadForEntry(
+                            primitiveId,
+                            extensionIndex,
+                            entryIndex,
+                            metaData,
+                            _intEntries);
+                        break;
+                    case CAPI.ovrAvatar2MaterialExtensionEntryType.Vector3f:
+                        success = StoreNameAndPayloadForEntry(
+                            primitiveId,
+                            extensionIndex,
+                            entryIndex,
+                            metaData,
+                            _vector3Entries);
+                        break;
+                    case CAPI.ovrAvatar2MaterialExtensionEntryType.Vector4f:
+                        success = StoreNameAndPayloadForEntry(
+                            primitiveId,
+                            extensionIndex,
+                            entryIndex,
+                            metaData,
+                            _vector4Entries);
+                        break;
+                    case CAPI.ovrAvatar2MaterialExtensionEntryType.ImageId:
+                        string entryName;
+                        var payload = CAPI.ovrAvatar2Id.Invalid;
+                        unsafe
+                        {
+                            success = GetNameAndPayloadForEntry(
+                                primitiveId,
+                                extensionIndex,
+                                entryIndex,
+                                metaData,
+                                out entryName,
+                                &payload);
+                        }
+
+                        if (success)
+                        {
+                            OvrAvatarLog.Assert(payload != CAPI.ovrAvatar2Id.Invalid, extensionLogScope);
+                            // Convert image ID to texture
+                            success = OvrAvatarManager.GetOvrAvatarAsset(payload, out OvrAvatarImage image);
+                            if (success)
+                            {
+                                _textureEntries.Add(new ExtensionEntry<Texture2D>(entryName, image.texture));
+                            }
+                            else
+                            {
+                                OvrAvatarLog.LogError(
+                                    $"Could not find image entryName:{entryName} assetId:{payload}"
+                                    , extensionLogScope
+                                    , image?.texture);
+                            }
+                        }
+
+                        break;
+
+                    case CAPI.ovrAvatar2MaterialExtensionEntryType.Invalid:
+                        OvrAvatarLog.LogError(
+                            $"Invalid extension type for primitiveId:{primitiveId} extensionIndex:{extensionIndex} entryIndex:{entryIndex}"
+                            , extensionLogScope);
+
+                        // Invalid signals an internal error in `libovravatar2` - should not have returned success
+                        success = false;
+                        break;
+
+                    default:
+                        OvrAvatarLog.LogWarning(
+                            $"Unrecognized extension type ({metaData.entryType}) for primitiveId:{primitiveId} extensionIndex:{extensionIndex} entryIndex:{entryIndex}"
+                            , extensionLogScope);
+                        break;
+                }
+
+                return success;
+            }
+
+            private static bool GetEntryMetaData(
+                CAPI.ovrAvatar2Id primitiveId,
+                UInt32 extensionIdx,
+                UInt32 entryIdx,
+                out CAPI.ovrAvatar2MaterialExtensionEntry metaData)
+            {
+                var success = CAPI.OvrAvatar2Primitive_MaterialExtensionEntryMetaDataByIndex(
+                    primitiveId,
+                    extensionIdx,
+                    entryIdx,
+                    out metaData);
+
+                if (!success)
+                {
+                    OvrAvatarLog.LogError(
+                        $"MaterialExtensionEntryMetaDataByIndex ({extensionIdx}, {entryIdx}) bufferSize:{metaData.dataBufferSize}"
+                        , LOG_SCOPE);
+                }
+
+                return success;
+            }
+
+            private static unsafe bool GetNameAndPayloadForEntry<T>(
+                CAPI.ovrAvatar2Id primitiveId,
+                UInt32 extensionIndex,
+                UInt32 entryIndex,
+                in CAPI.ovrAvatar2MaterialExtensionEntry metaData,
+                out string entryName,
+                T* outPayload)
+                where T : unmanaged
+            {
+                OvrAvatarLog.Assert(metaData.nameBufferSize > 0);
+                OvrAvatarLog.Assert(metaData.dataBufferSize > 0);
+
+                uint nameBufferSize = metaData.nameBufferSize;
+                var nameBuffer = stackalloc byte[(int)nameBufferSize];
+
+                bool success;
+
+                uint managedSize = (uint)UnsafeUtility.SizeOf<T>();
+                uint dataBufferSize = metaData.dataBufferSize;
+                bool noMarshal = managedSize == dataBufferSize;
+                if (noMarshal)
+                {
+                    success = CAPI.OvrAvatar2Primitive_MaterialExtensionEntryDataByIndex(
+                        primitiveId,
+                        extensionIndex,
+                        entryIndex,
+                        nameBuffer,
+                        nameBufferSize,
+                        (byte*)outPayload,
+                        managedSize);
+                }
+                else
+                {
+                    var dataBuffer = stackalloc byte[(int)dataBufferSize];
+
+                    success = CAPI.OvrAvatar2Primitive_MaterialExtensionEntryDataByIndex(
+                        primitiveId,
+                        extensionIndex,
+                        entryIndex,
+                        nameBuffer,
+                        nameBufferSize,
+                        dataBuffer,
+                        dataBufferSize);
+
+                    if (success) { *outPayload = Marshal.PtrToStructure<T>((IntPtr)dataBuffer); }
+                }
+
+                if (!success)
+                {
+                    OvrAvatarLog.LogWarning(
+                        @$"MaterialExtensionEntryDataByIndex (extensionIdx:{extensionIndex}, entryIdx:{entryIndex})"
+                        + $"nameSize:{nameBufferSize} managedSize:{managedSize} bufferSize:{dataBufferSize}"
+                        , LOG_SCOPE);
+
+                    entryName = string.Empty;
+                    *outPayload = default;
+                    return false;
+                }
+
+                entryName = Marshal.PtrToStringAnsi((IntPtr)nameBuffer);
+                return true;
+            }
+
+            private static bool StoreNameAndPayloadForEntry<T>(
+                CAPI.ovrAvatar2Id primitiveId,
+                UInt32 extensionIdx,
+                UInt32 entryIdx,
+                in CAPI.ovrAvatar2MaterialExtensionEntry metaData,
+                in List<ExtensionEntry<T>> listToStoreInto)
+                where T : unmanaged
+            {
+                bool success;
+                string entryName;
+                T payload;
+                unsafe
+                {
+                    success = GetNameAndPayloadForEntry(
+                        primitiveId,
+                        extensionIdx,
+                        entryIdx,
+                        metaData,
+                        out entryName,
+                        &payload);
+                }
+
+                if (success) { listToStoreInto.Add(new ExtensionEntry<T>(entryName, payload)); }
+
+                return success;
+            }
+        }
+
+        //////////////////////////////////////////////////
+        // OvrAvatarMaterialExtension
+        //////////////////////////////////////////////////
+        private readonly ExtensionEntries _entries;
+        private readonly string _name;
+
+        private const string LOG_SCOPE = "OvrAvatarMaterialExtension";
+
+        private OvrAvatarMaterialExtension(string extensionName, ExtensionEntries entries)
+        {
+            _name = extensionName;
+            _entries = entries;
+        }
+
+        public string Name => _name;
+
+        public void ApplyEntriesToMaterial(Material material, OvrAvatarMaterialExtensionConfig extensionConfig)
+        {
+            if (_entries == null || material == null || extensionConfig == null) { return; }
+
+            _entries.ApplyToMaterial(material, _name, extensionConfig);
+        }
+
+        public static bool LoadExtension(CAPI.ovrAvatar2Id primitiveId, UInt32 extensionIndex,
+            out OvrAvatarMaterialExtension materialExtension)
+        {
+            materialExtension = default;
+
+            // Get extension name
+            if (!GetMaterialExtensionName(primitiveId, extensionIndex, out string extensionName)) { return false; }
+
+            // Get entries for the extension
+            ExtensionEntries entries = new ExtensionEntries();
+            if (!GetNumEntries(primitiveId, extensionIndex, out uint numEntries)) { return false; }
+
+            // Loop over all entries
+            for (UInt32 entryIdx = 0; entryIdx < numEntries; entryIdx++)
+            {
+                if (!entries.LoadEntry(primitiveId, extensionIndex, entryIdx)) { return false; }
+            }
+
+            materialExtension = new OvrAvatarMaterialExtension(extensionName, entries);
+
+            return true;
+        }
+
+        private static bool GetMaterialExtensionName(CAPI.ovrAvatar2Id primitiveId, UInt32 extensionIdx,
+            out string extensionName)
+        {
+            unsafe
+            {
+                extensionName = String.Empty;
+
+                // Get extension name
+                uint nameSize = 0;
+                var result = CAPI.ovrAvatar2Primitive_GetMaterialExtensionName(
+                    primitiveId,
+                    extensionIdx,
+                    null,
+                    &nameSize);
+
+                if (!result.EnsureSuccess($"GetMaterialExtensionName ({extensionIdx}) {result}", LOG_SCOPE))
+                {
+                    return false;
+                }
+
+                var nameBuffer = stackalloc byte[(int)nameSize];
+                result = CAPI.ovrAvatar2Primitive_GetMaterialExtensionName(
+                    primitiveId,
+                    extensionIdx,
+                    nameBuffer,
+                    &nameSize);
+                if (!result.EnsureSuccess($"GetMaterialExtensionName ({extensionIdx}) {result}", LOG_SCOPE))
+                {
+                    return false;
+                }
+
+                extensionName = Marshal.PtrToStringAnsi((IntPtr)nameBuffer);
+            }
+
+            return true;
+        }
+
+        private static bool GetNumEntries(CAPI.ovrAvatar2Id primitiveId, UInt32 extensionIndex, out UInt32 count)
+        {
+            count = 0;
+            var result =
+                CAPI.ovrAvatar2Primitive_GetNumEntriesInMaterialExtensionByIndex(
+                    primitiveId, extensionIndex
+                    , out count);
+
+            if (!result.IsSuccess())
+            {
+                OvrAvatarLog.LogError($"GetNumEntriesInMaterialExtensionByIndex ({extensionIndex}) {result}"
+                    , LOG_SCOPE);
+                return false;
+            }
+
+            return true;
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrAvatarMaterialExtension.cs.meta b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrAvatarMaterialExtension.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..6b0d90691fbe126a3fb44eb29f097a5f5fb56301
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrAvatarMaterialExtension.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: e139296f53bf47944979c4c30b62d263
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrAvatarMaterialExtensionConfig.cs b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrAvatarMaterialExtensionConfig.cs
new file mode 100644
index 0000000000000000000000000000000000000000..0ba836b76f16b2a14683877873db71b584d46091
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrAvatarMaterialExtensionConfig.cs
@@ -0,0 +1,169 @@
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace Oculus.Avatar2
+{
+    [Serializable]
+    public class OvrAvatarMaterialExtensionConfig : ISerializationCallbackReceiver
+    {
+        private const string LOG_SCOPE = nameof(OvrAvatarMaterialExtensionConfig);
+
+        private Dictionary<string, Dictionary<string, string>> _entryNameRemapping =
+            new Dictionary<string, Dictionary<string, string>>();
+
+        // Yes, it sucks to serialize out the keys and values as separate lists
+        // just to read them into a dictionary, but Unity doesn't seem to handle serializing
+        // dictionaries well *shrug*
+
+        // Unity also can't serialize list of lists without wrapping because...Unity
+        [Serializable]
+        private class StringListWrapper
+        {
+            [SerializeField] public List<string> list = new List<string>(0);
+        }
+
+        [SerializeField] private List<string> _extensionNames = new List<string>(0);
+        [SerializeField] private List<StringListWrapper> _entryNamesPerExtension = new List<StringListWrapper>(0);
+        [SerializeField] private List<StringListWrapper> _replacementNamesPerExtension = new List<StringListWrapper>(0);
+
+        public static string ExtensionNamesPropertyName => nameof(_extensionNames);
+        public static string EntryNamesPropertyName => nameof(_entryNamesPerExtension);
+        public static string ReplacementNamesPropertyName => nameof(_replacementNamesPerExtension);
+
+        public static string InnerListProperyName = nameof(StringListWrapper.list);
+
+        public bool TryGetNameInShader(string extensionName, string entryName, out string nameInShader)
+        {
+            if (_entryNameRemapping.TryGetValue(extensionName, out Dictionary<string, string> extensionEntries))
+            {
+                return extensionEntries.TryGetValue(entryName, out nameInShader);
+            }
+
+            nameInShader = string.Empty;
+            return false;
+        }
+
+        public void OnBeforeSerialize()
+        {
+            _extensionNames.Clear();
+            _entryNamesPerExtension.Clear();
+            _replacementNamesPerExtension.Clear();
+
+            foreach (var kvp in _entryNameRemapping)
+            {
+                _extensionNames.Add(kvp.Key);
+
+                var entries = new StringListWrapper();
+                var replacements = new StringListWrapper();
+
+                foreach (var evp in _entryNameRemapping[kvp.Key])
+                {
+                    entries.list.Add(evp.Key);
+                    replacements.list.Add(evp.Value);
+                }
+
+                _entryNamesPerExtension.Add(entries);
+                _replacementNamesPerExtension.Add(replacements);
+            }
+        }
+
+        public void OnAfterDeserialize()
+        {
+            _entryNameRemapping = new Dictionary<string, Dictionary<string, string>>();
+
+            // There are some constraints in naming
+            // 1. Extension names must be unique
+            // 2. Combination of extension name + entry name must be unique
+            // 3. Replacement names must be unique
+
+            // NOTE* It is possible that these constraints may change in the future
+
+            // TODO*: Have the property drawer for this validate input then
+            // for better feedback to user
+
+            HashSet<string> existingExtensionNames = new HashSet<string>();
+            HashSet<Tuple<string, string>> existingCombos = new HashSet<Tuple<string, string>>();
+            HashSet<string> existingReplacementNames = new HashSet<string>();
+
+            Debug.Assert(_extensionNames.Count == _entryNamesPerExtension.Count);
+            Debug.Assert(_entryNamesPerExtension.Count == _replacementNamesPerExtension.Count);
+
+            for (int extensionIndex = 0; extensionIndex < _extensionNames.Count; extensionIndex++)
+            {
+                var extensionName = _extensionNames[extensionIndex];
+
+                // See if extension name is a duplicate and find a new name if it is a dupe
+                if (existingExtensionNames.Contains(extensionName))
+                {
+                    extensionName = FindNonDuplicateName(extensionName, existingExtensionNames);
+                }
+
+                var entryNames = _entryNamesPerExtension[extensionIndex].list;
+                var replacementNames = _replacementNamesPerExtension[extensionIndex].list;
+                Debug.Assert(entryNames.Count == replacementNames.Count);
+
+                // Loop over all extensions and replacement names for that extension
+                var extensionDict = new Dictionary<string, string>();
+                for (int entryIndex = 0; entryIndex < entryNames.Count; entryIndex++)
+                {
+                    var entryName = entryNames[entryIndex];
+
+                    // Check for validity of extension name + entry name combo and change
+                    // entry name if a dupe
+                    var combo = new Tuple<string, string>(extensionName, entryName);
+                    if (existingCombos.Contains(combo))
+                    {
+                        entryName = FindNonDuplicateNameTuple(extensionName, entryName, existingCombos);
+                    }
+
+                    var replacementName = replacementNames[entryIndex];
+                    if (existingReplacementNames.Contains(replacementName))
+                    {
+                        replacementName = FindNonDuplicateName(replacementName, existingReplacementNames);
+                    }
+
+                    // Add to validity bookkeeping
+                    existingCombos.Add(combo);
+                    existingReplacementNames.Add(replacementName);
+
+                    // Add to dictionary
+                    extensionDict.Add(entryName, replacementName);
+                }
+
+                existingExtensionNames.Add(extensionName);
+                _entryNameRemapping[extensionName] = extensionDict;
+            }
+        }
+
+        private static string FindNonDuplicateName(string original, HashSet<string> existingExtensionNames)
+        {
+            var count = 1;
+            var newName = string.Concat(original, $"({count})");
+            while (existingExtensionNames.Contains(newName) && count < int.MaxValue)
+            {
+                count++;
+                newName = string.Concat(original, $"({count})");
+            }
+
+            return newName;
+        }
+
+        private static string FindNonDuplicateNameTuple(
+            string firstString,
+            string original,
+            HashSet<Tuple<string, string>> existingCombos)
+        {
+            var count = 1;
+            var newSecondString = string.Concat(original, $"({count})");
+            while (existingCombos.Contains(new Tuple<string, string>(firstString, newSecondString)) &&
+                   count < int.MaxValue)
+            {
+                count++;
+                newSecondString = string.Concat(original, $"({count})");
+            }
+
+            return newSecondString;
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrAvatarMaterialExtensionConfig.cs.meta b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrAvatarMaterialExtensionConfig.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..44048428cf4cb923d69e09a18ed83759d162b74f
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrAvatarMaterialExtensionConfig.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 58d5d5f8e2771734784bab81066be79d
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrAvatarMotionVectorsCore.hlsl b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrAvatarMotionVectorsCore.hlsl
new file mode 100644
index 0000000000000000000000000000000000000000..0c0239f6d645a3e5bac07a66d9b5e92a774c96e8
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrAvatarMotionVectorsCore.hlsl
@@ -0,0 +1,177 @@
+#ifndef OVR_AVATAR_MOTION_VECTORS_CORE_INCLUDED
+#define OVR_AVATAR_MOTION_VECTORS_CORE_INCLUDED
+#include "UnityCG.cginc"
+#include "AvatarCustomTypes.cginc"
+#include "OvrAvatarSupportDefines.hlsl"
+#include "OvrAvatarVertexFetch.hlsl"
+#include "OvrAvatarCommonVertexParams.hlsl"
+
+float _OvrPrevRenderFrameInterpolationValue;
+int _OvrAttributeOutputPrevRenderFrameLatestAnimFrameOffset;
+int _OvrAttributeOutputPrevRenderFramePrevAnimFrameOffset;
+
+float4x4 unity_MatrixPreviousM;
+float4x4 unity_StereoMatrixPrevVP[2];
+
+#define unity_MatrixPrevVP unity_StereoMatrixPrevVP[unity_StereoEyeIndex]
+
+struct OvrMotionVectorsVertex
+{
+  OVR_VERTEX_POSITION_FIELD
+  float3 previousPositionOS : TEXCOORD4; // Needs to be TEXCOORD4 to be auto populated by Unity
+  OVR_VERTEX_VERT_ID_FIELD
+  uint instanceID : SV_InstanceID;
+  //UNITY_VERTEX_INPUT_INSTANCE_ID
+};
+
+struct OvrMotionVectorsVaryings
+{
+  float4 positionCS : SV_POSITION;
+  float4 curPositionCS : TEXCOORD0;
+  float4 prevPositionCS : TEXCOORD1;
+  UNITY_VERTEX_OUTPUT_STEREO
+};
+
+
+float3 OvrMotionVectorsGetObjectSpacePositionFromTexture(uint vertexId, int numAttributes, bool applyOffsetAndBias) {
+  return OvrGetVertexPositionFromTexture(vertexId, numAttributes, applyOffsetAndBias, _OvrAttributeInterpolationValue);
+}
+
+float3 OvrMotionVectorsGetPrevObjectSpacePositionFromTexture(uint vertexId, int numAttributes, bool applyOffsetAndBias) {
+  return OvrGetVertexPositionFromTexture(vertexId, numAttributes, applyOffsetAndBias, _OvrPrevRenderFrameInterpolationValue);
+}
+
+#if defined(OVR_SUPPORT_EXTERNAL_BUFFERS)
+  float3 OvrMotionVectorsGetObjectSpacePositionFromBuffer(uint vertexId) {
+    const uint numPosEntriesPerVert = _OvrNumOutputEntriesPerAttribute;
+    const uint startIndexOfPositionEntries = vertexId * numPosEntriesPerVert;
+    const uint posEntryIndex = startIndexOfPositionEntries + _OvrAttributeOutputLatestAnimFrameEntryOffset;
+
+    return OvrGetPositionEntryFromExternalBuffer(posEntryIndex);
+  }
+
+  float3 OvrMotionVectorsGetInterpolatedObjectSpacePositionFromBuffer(uint vertexId) {
+    const uint numPosEntriesPerVert = _OvrNumOutputEntriesPerAttribute;
+    const uint startIndexOfPositionEntries = vertexId * numPosEntriesPerVert;
+
+    const uint latestPosEntryIndex = startIndexOfPositionEntries + _OvrAttributeOutputLatestAnimFrameEntryOffset;
+    const uint prevPosEntryIndex = startIndexOfPositionEntries + _OvrAttributeOutputPrevAnimFrameEntryOffset;
+
+    const float3 p0 = OvrGetPositionEntryFromExternalBuffer(prevPosEntryIndex);
+    const float3 p1 = OvrGetPositionEntryFromExternalBuffer(latestPosEntryIndex);
+
+    return lerp(p0, p1, _OvrAttributeInterpolationValue);
+  }
+
+  float3 OvrMotionVectorsGetPrevObjectSpacePositionFromBuffer(uint vertexId) {
+    const uint numPosEntriesPerVert = _OvrNumOutputEntriesPerAttribute;
+    const uint startIndexOfPositionEntries = vertexId * numPosEntriesPerVert;
+    const uint posEntryIndex = startIndexOfPositionEntries + _OvrAttributeOutputPrevRenderFrameLatestAnimFrameOffset;
+
+    return OvrGetPositionEntryFromExternalBuffer(posEntryIndex);
+  }
+
+  float3 OvrMotionVectorsGetPrevInterpolatedObjectSpacePositionFromBuffer(uint vertexId) {
+    const uint numPosEntriesPerVert = _OvrNumOutputEntriesPerAttribute;
+    const uint startIndexOfPositionEntries = vertexId * numPosEntriesPerVert;
+
+    const uint pos1EntryIndex = startIndexOfPositionEntries + _OvrAttributeOutputPrevRenderFrameLatestAnimFrameOffset;
+    const uint pos0EntryIndex = startIndexOfPositionEntries + _OvrAttributeOutputPrevRenderFramePrevAnimFrameOffset;
+
+    const float3 p0 = OvrGetPositionEntryFromExternalBuffer(pos0EntryIndex);
+    const float3 p1 = OvrGetPositionEntryFromExternalBuffer(pos1EntryIndex);
+
+    return lerp(p0, p1, _OvrPrevRenderFrameInterpolationValue);
+  }
+#endif
+
+void OvrMotionVectorsGetPositions(uint vertexId, inout float3 currentPos, inout float3 prevPos)
+{
+  // Backward compatibility/optimization support if application is ok with additional variants
+  // The shader compiler should optimize out branches that are based on static const values
+  #if defined(OVR_VERTEX_FETCH_VERT_BUFFER)
+    static const int fetchMode = OVR_VERTEX_FETCH_MODE_STRUCT;
+  #elif defined(OVR_VERTEX_FETCH_EXTERNAL_BUFFER) && defined(OVR_SUPPORT_EXTERNAL_BUFFERS)
+    static const int fetchMode = OVR_VERTEX_FETCH_MODE_EXTERNAL_BUFFERS;
+  #elif defined(OVR_VERTEX_FETCH_TEXTURE) || defined(OVR_VERTEX_FETCH_TEXTURE_UNORM)
+    static const int fetchMode = OVR_VERTEX_FETCH_MODE_EXTERNAL_TEXTURES;
+  #else
+    const int fetchMode = _OvrVertexFetchMode;
+  #endif
+
+  #if defined(OVR_VERTEX_HAS_TANGENTS)
+    static const bool hasTangents = true;
+  #elif defined(OVR_VERTEX_NO_TANGENTS)
+    static const bool hasTangents = false;
+  #else
+    const bool hasTangents = _OvrHasTangents;
+  #endif
+
+  #if defined(OVR_VERTEX_INTERPOLATE_ATTRIBUTES)
+    static const bool interpolateAttributes = true;
+  #elif defined(OVR_VERTEX_DO_NOT_INTERPOLATE_ATTRIBUTES)
+    static const bool interpolateAttributes = false;
+  #else
+    const bool interpolateAttributes = _OvrInterpolateAttributes;
+  #endif
+
+  // Hope that the compiler branches here. The [branch] attribute here seems to lead to compile
+  // probably due to "use of gradient function, such as tex3d"
+  if (fetchMode == OVR_VERTEX_FETCH_MODE_EXTERNAL_TEXTURES) {
+    const int numAttributes =  hasTangents ? 3 : 2;
+
+    #if defined(OVR_VERTEX_FETCH_TEXTURE)
+        static const bool applyOffsetAndBias = false;
+    #else
+        static const bool applyOffsetAndBias = true;
+    #endif
+
+    currentPos = OvrMotionVectorsGetObjectSpacePositionFromTexture(vertexId, numAttributes, applyOffsetAndBias);
+    prevPos = OvrMotionVectorsGetPrevObjectSpacePositionFromTexture(vertexId, numAttributes, applyOffsetAndBias);
+#ifdef OVR_SUPPORT_EXTERNAL_BUFFERS
+  } else if (fetchMode == OVR_VERTEX_FETCH_MODE_EXTERNAL_BUFFERS) {
+    [branch]
+    if (interpolateAttributes) {
+      currentPos = OvrMotionVectorsGetInterpolatedObjectSpacePositionFromBuffer(vertexId);
+      prevPos = OvrMotionVectorsGetPrevInterpolatedObjectSpacePositionFromBuffer(vertexId);
+    } else {
+      currentPos = OvrMotionVectorsGetObjectSpacePositionFromBuffer(vertexId);
+      prevPos = OvrMotionVectorsGetPrevObjectSpacePositionFromBuffer(vertexId);
+    }
+#endif
+  }
+}
+
+OvrMotionVectorsVaryings OvrMotionVectorsVertProgram(OvrMotionVectorsVertex input)
+{
+  UNITY_SETUP_INSTANCE_ID(input);
+  OvrMotionVectorsVaryings output;
+  UNITY_INITIALIZE_OUTPUT(OvrMotionVectorsVaryings, output);
+  UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
+
+  // Pull from vertex buffer
+  float3 currentPos = OVR_GET_VERTEX_POSITION_FIELD(input).xyz;
+  float3 prevPos = input.previousPositionOS;
+  const uint vertexId = OVR_GET_VERTEX_VERT_ID_FIELD(input);
+
+  OvrMotionVectorsGetPositions(vertexId, currentPos, prevPos);
+
+  output.positionCS = UnityObjectToClipPos(currentPos);
+  output.curPositionCS = output.positionCS;
+  float3 prevPosWorld = mul(unity_MatrixPreviousM, float4(prevPos, 1.0f)).xyz;
+  output.prevPositionCS = mul(unity_MatrixPrevVP, float4(prevPosWorld, 1.0));
+
+  return output;
+}
+
+half4 OvrMotionVectorsFragProgram(OvrMotionVectorsVaryings IN) : SV_Target
+{
+  float3 screenPos = IN.curPositionCS.xyz / IN.curPositionCS.w;
+  float3 screenPosPrev = IN.prevPositionCS.xyz / IN.prevPositionCS.w;
+
+  half4 color = 1;
+  color.xyz = screenPos - screenPosPrev;
+  return color;
+}
+
+#endif // OVR_AVATAR_MOTION_VECTORS_CORE_INCLUDED
diff --git a/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrAvatarMotionVectorsCore.hlsl.meta b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrAvatarMotionVectorsCore.hlsl.meta
new file mode 100644
index 0000000000000000000000000000000000000000..b032d79b8a17bb4e34d24c6afe8ace8af0c8061a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrAvatarMotionVectorsCore.hlsl.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 18eae3c263b887c489cdc1922ec3d102
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrAvatarSupportDefines.hlsl b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrAvatarSupportDefines.hlsl
new file mode 100644
index 0000000000000000000000000000000000000000..1da4b96581d81673ad2f668b68e958dbf841b1b3
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrAvatarSupportDefines.hlsl
@@ -0,0 +1,8 @@
+#ifndef OVR_AVATAR_SUPPORT_DEFINES_INCLUDED
+#define OVR_AVATAR_SUPPORT_DEFINES_INCLUDED
+
+#if SHADER_TARGET >= 35 && (defined(SHADER_API_D3D11) || defined(SHADER_API_GLES3) || defined(SHADER_API_GLCORE) || defined(SHADER_API_VULKAN) || (defined(SHADER_API_METAL) && defined(UNITY_COMPILER_HLSLCC)))
+#define OVR_SUPPORT_EXTERNAL_BUFFERS
+#endif
+
+#endif // OVR_AVATAR_SUPPORT_DEFINES_INCLUDED
diff --git a/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrAvatarSupportDefines.hlsl.meta b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrAvatarSupportDefines.hlsl.meta
new file mode 100644
index 0000000000000000000000000000000000000000..51eb4301821aecc29ceba5c6fae88f537253741e
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrAvatarSupportDefines.hlsl.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 97a4028e13c36e14599a8f0de58a7993
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData:
+  assetBundleName:
+  assetBundleVariant:
diff --git a/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrAvatarVertexFetch.hlsl b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrAvatarVertexFetch.hlsl
new file mode 100644
index 0000000000000000000000000000000000000000..eb153f7440e5b4e32ce1e5d978fd8a0b8feb6c37
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrAvatarVertexFetch.hlsl
@@ -0,0 +1,211 @@
+#ifndef OVR_AVATAR_VERTEX_FETCH_INCLUDED
+#define OVR_AVATAR_VERTEX_FETCH_INCLUDED
+// TODO*: Documentation here
+
+#include "OvrDecodeFormats.cginc"
+
+#include "OvrAvatarSupportDefines.hlsl"
+
+//-------------------------------------------------------------------------------------
+// Vertex based texture fetching related uniforms and functions.
+
+// NOTE: This texture can be visualized in the Unity editor, just expand in inspector and manually change "Dimension" to "3D" on top line
+
+#define OVR_ATTRIBUTE_PRECISION_FLOAT // hard coding this for now to test perf
+
+// NOTE: According to Unity documentation here https://docs.unity3d.com/Manual/SL-DataTypesAndPrecision.html
+// The standard declaration of sampler3D yields the following
+// "For mobile platforms, these translate into “low precision samplers”, i.e. the textures are expected to
+// have low precision data in them."
+// Upon shader inspection, the declarations become "uniform mediump sampler3D" which
+// is 16-bit precision. This is not desired as some of the data in the textures is
+// expected to have 32-bit precision. So, for mobile platforms, make an option for explicitly
+// setting 32-bit precision
+#if defined(SHADER_API_MOBILE) && defined(OVR_ATTRIBUTE_PRECISION_FLOAT)
+sampler3D_float u_AttributeTexture;
+#else
+sampler3D u_AttributeTexture;
+#endif
+
+int u_AttributeTexelX;
+int u_AttributeTexelY;
+int u_AttributeTexelW;
+
+float u_AttributeTexInvSizeW;
+float u_AttributeTexInvSizeH;
+float u_AttributeTexInvSizeD;
+
+float2 u_AttributeScaleBias;
+
+//-------------------------------------------------------------------------------------
+// Vertex based texture fetching related uniforms and functions.
+
+float3 ovrGetAttributeTexCoord(
+    int attributeRowOffset,
+    uint vertIndex,
+    int numAttributes,
+    float slice) {
+  // Compute texture coordinate in the attribute texture
+
+  // Compute which row in the texel rect
+  // the vertex index is
+  int row = vertIndex / u_AttributeTexelW;
+  int column = vertIndex % u_AttributeTexelW;
+
+  row = row * numAttributes;
+
+  // Calculate texel centers
+  column = u_AttributeTexelX + column;
+  row = u_AttributeTexelY + row + attributeRowOffset;
+
+  const float3 coord = float3(float(column), float(row), slice);
+  const float3 invSize = float3(
+      u_AttributeTexInvSizeW, u_AttributeTexInvSizeH, u_AttributeTexInvSizeD);
+
+  // Compute texture coordinate for texel center
+  return (coord + 0.5) * invSize;
+}
+
+float3 ovrGetPositionTexCoord(uint vid, int numAttributes, float slice) {
+  return ovrGetAttributeTexCoord(0, vid, numAttributes, slice);
+}
+
+float3 ovrGetNormalTexCoord(uint vid, int numAttributes, float slice) {
+  return ovrGetAttributeTexCoord(1, vid, numAttributes, slice);
+}
+
+float3 ovrGetTangentTexCoord(uint vid, int numAttributes, float slice) {
+  return ovrGetAttributeTexCoord(2, vid, numAttributes, slice);
+}
+
+// The original uvw mechanism for sampling the attributes (position/normal/
+// tangents) would do a linear interpolation between 2 sheets of a texture
+// array(w). However, while this is great for interpolating between two
+// frames for motion smoothing, it is not ideal for picking out the percise
+// pixel values in the uv dimensions. Therefore we use this function to take
+// two point samples in UV and manually interpolate.
+//
+// What follows has been tried and tested for about a year in Horizon Workrooms. 
+float4 GetInterpolatedAttribute(uint attributeIndex, uint vid, int numAttributes, float slice) {
+    float lowerSlice = trunc(slice);
+    float higherSlice = lowerSlice + 1;
+    float delta = slice - lowerSlice;
+    float4 pos1 = tex3Dlod(
+        u_AttributeTexture,
+        float4(
+            ovrGetAttributeTexCoord(
+                attributeIndex, vid, numAttributes, lowerSlice),
+            0));
+    float4 pos2 = tex3Dlod(
+        u_AttributeTexture,
+        float4(
+            ovrGetAttributeTexCoord(
+                attributeIndex, vid, numAttributes, higherSlice),
+            0));
+    return lerp(pos1, pos2, delta);
+}
+
+float4 OvrGetVertexPositionFromTexture(
+    uint vid,
+    int numAttributes,
+    bool applyOffsetAndBias,
+    float slice) {
+  float4 pos = GetInterpolatedAttribute(0, vid, numAttributes, slice);
+  [branch]
+  if (applyOffsetAndBias) {
+    pos = pos * u_AttributeScaleBias.x + u_AttributeScaleBias.y;
+  }
+  return pos;
+}
+
+float4 OvrGetVertexNormalFromTexture(
+    uint vid,
+    int numAttributes,
+    bool applyOffsetAndBias,
+    float slice) {
+  float4 norm = GetInterpolatedAttribute(1, vid, numAttributes, slice);
+  [branch]
+  if (applyOffsetAndBias) {
+    norm = norm * u_AttributeScaleBias.x + u_AttributeScaleBias.y;
+  }
+  return norm;
+}
+
+float4 OvrGetVertexTangentFromTexture(
+    uint vid,
+    int numAttributes,
+    bool applyOffsetAndBias,
+    float slice) {
+  float4 tan = GetInterpolatedAttribute(2, vid, numAttributes, slice);
+  [branch]
+  if (applyOffsetAndBias) {
+    tan = tan * u_AttributeScaleBias.x + u_AttributeScaleBias.y;
+  }
+  return tan;
+}
+
+//-------------------------------------------------------------------------------------
+// "External Buffers" vertex fetch setup
+#if defined(OVR_SUPPORT_EXTERNAL_BUFFERS)
+  #include "OvrDecodeUtils.cginc"
+
+  ByteAddressBuffer _OvrPositionBuffer; // Bag of uints
+  ByteAddressBuffer _OvrFrenetBuffer; // Bag of uints
+  float3 _OvrPositionScale;
+  float3 _OvrPositionBias;
+
+  int _OvrPositionEncodingPrecision;
+
+  int _OvrPositionsStartAddress;
+  int _OvrFrenetStartAddress;
+
+  //-------------------------------------------------------------------------------------
+  // Avatar Vertex fetch setup
+
+  float3 OvrGetPositionEntryFromExternalBuffer(uint entryIndex) {
+    static const uint STRIDE_32 = 4u * 4u; // 4 32-bit uints for 4 32-bit floats
+    static const uint STRIDE_16 = 4u * 2u; // 2 32-bit uints for 4 16 bit unorms or halfs
+
+    float3 position = float3(0.0, 0.0, 0.0);
+
+    [branch] switch(_OvrPositionEncodingPrecision) {
+      case OVR_FORMAT_UNORM_16:
+        // 2 32-bit uints for 4 16 bit unorms
+        position.xyz = OvrUnpackUnorm3x16(
+          _OvrPositionBuffer,
+          mad(entryIndex, STRIDE_16, _OvrPositionsStartAddress));
+
+        // Apply scale and offset to "de-normalize"
+        position.xyz = mad(position.xyz, _OvrPositionScale, _OvrPositionBias);
+      break;
+
+      case OVR_FORMAT_FLOAT_32:
+        // 4 32-bit uints for 4 32-bit floats
+        position.xyz = OvrUnpackFloat3x32(
+          _OvrPositionBuffer,
+          mad(entryIndex, STRIDE_32, _OvrPositionsStartAddress));
+      break;
+      case OVR_FORMAT_HALF_16:
+        position.xyz = OvrUnpackHalf3x16(
+          _OvrPositionBuffer,
+          mad(entryIndex, STRIDE_16, _OvrPositionsStartAddress));
+      break;
+      default:
+        // error?
+        break;
+    }
+
+    return position;
+  }
+
+  float4 OvrGetFrenetEntryFromExternalBuffer(uint entryIndex) {
+    // Only supporting 10-10-10-2 snorm at the moment
+    static const uint STRIDE = 4u; // 1 32-bit uint for 3 10-bit SNorm and 1 2-bit extra
+    return OvrUnpackSnorm4x10_10_10_2(
+      _OvrFrenetBuffer,
+      mad(entryIndex, STRIDE, _OvrFrenetStartAddress));
+  }
+
+#endif
+
+#endif // OVR_AVATAR_VERTEX_FETCH_INCLUDED
diff --git a/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrAvatarVertexFetch.hlsl.meta b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrAvatarVertexFetch.hlsl.meta
new file mode 100644
index 0000000000000000000000000000000000000000..1562efcdb333b82a75b650f23723ea25f5eca27d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrAvatarVertexFetch.hlsl.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 8216a1240c5828c4b80117c22d6abba8
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrDecodeFormats.cginc b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrDecodeFormats.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..e00abd41b82d7822d76cadb857542329f72d2052
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrDecodeFormats.cginc
@@ -0,0 +1,15 @@
+#ifndef OVR_DECODE_FORMATS_INCLUDED
+#define OVR_DECODE_FORMATS_INCLUDED
+
+// NOTE: Changing these formats
+// will also require updating the dispatching/drawing code as well
+// to make sure the constants are the same
+static const int OVR_FORMAT_FLOAT_32 = 0;
+static const int OVR_FORMAT_HALF_16  = 1;
+static const int OVR_FORMAT_UNORM_16 = 2;
+static const int OVR_FORMAT_UINT_16 = 3;
+static const int OVR_FORMAT_SNORM_10_10_10_2 = 4;
+static const int OVR_FORMAT_UNORM_8 = 5;
+static const int OVR_FORMAT_UINT_8 = 6;
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrDecodeFormats.cginc.meta b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrDecodeFormats.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..34e539356cd498a02b0986943b11b179ff595388
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrDecodeFormats.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: d94f710ee9db5ea4cb7714988033a566
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrDecodeUtils.cginc b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrDecodeUtils.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..2dce4f6de4d80a637fff164a9fc3f4b3f3c465e8
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrDecodeUtils.cginc
@@ -0,0 +1,539 @@
+#ifndef OVR_DECODE_UTILS_INCLUDED
+#define OVR_DECODE_UTILS_INCLUDED
+
+static const uint SIZE_OF_UINT = 4u; // in bytes
+
+int OvrBitfieldExtract10(int value, int offset) {
+  value = value >> offset;
+  value &= 0x03ff;
+  if ((value & 0x0200) != 0) {
+    value |= 0xfffffc00;
+  }
+  return value;
+}
+
+uint OvrBitfieldExtract(uint data, uint offset, uint numBits)
+{
+  const uint mask = (1u << numBits) - 1u;
+  return (data >> offset) & mask;
+}
+
+// With sign extension
+int OvrBitfieldExtract(int data, uint offset, uint numBits)
+{
+  int  shifted = data >> offset;      // Sign-extending (arithmetic) shift
+  int  signBit = shifted & (1u << (numBits - 1u));
+  uint mask    = (1u << numBits) - 1u;
+
+  return -signBit | (shifted & mask); // Use 2-complement for negation to replicate the sign bit
+}
+
+uint OvrBitfieldInsert(uint base, uint insert, uint offset, uint numBits)
+{
+  uint mask = ~(0xffffffffu << numBits) << offset;
+  mask = ~mask;
+  base = base & mask;
+  return base | (insert << offset);
+}
+
+// Unpack 1x float (32 bit) from a single 32 bit uint
+float OvrUnpackFloat1x32(uint u) {
+  // Just re-interpret as an float
+  return asfloat(u);
+}
+
+// Pack 1x float (32 bit) into a single 32 bit uint
+uint OvrPackFloat1x32(float val) {
+  // Just re-interpret as a uint
+  return asuint(val);
+}
+
+// Unpack 2x "half floats" (16 bit) from a single 32 bit uint
+float2 OvrUnpackHalf2x16(uint u) {
+  const uint y = (u >> 16) & 0xFFFFu;
+  const uint x = u & 0xFFFFu;
+
+  return float2(f16tof32(x), f16tof32(y));
+}
+
+// Pack 2x "half floats" (16 bit) from a single 32 bit uint
+uint OvrPackHalf2x16(float2 halfs) {
+  const uint x = f32tof16(halfs.x);
+  const uint y = f32tof16(halfs.y);
+  return x + (y << 16);
+}
+
+// Unpack 2 16-bit unsigned integers
+uint2 OvrUnpackUint2x16(uint packed_values) {
+  uint y = (packed_values >> 16) & 0xffffu;
+  uint x = packed_values & 0xffffu;
+
+  return uint2(x, y);
+}
+
+// Pack 2 16-bit unsigned integers into a single 32-bit uint
+uint OvrPackUint2x16(uint2 vals) {
+  return vals.x + (vals.y << 16);
+}
+
+// Unpack UNorm [0, 1] 2 16 bit entries (packed in a single 32 bit uint)
+float2 OvrUnpackUnorm2x16(uint packed_values) {
+  uint2 non_normalized = OvrUnpackUint2x16(packed_values);
+
+  // Convert from 0 -> 65535 to 0 -> 1
+  const float inv = 1.0 / 65535.0;
+
+  return float2(non_normalized.x * inv, non_normalized.y * inv);
+}
+
+// Pack 2x unsigned normalized values (16 bit) into a single 32 bit uint
+uint OvrPackUnorm2x16(float2 unorms) {
+  // Convert from 0 -> 1 to 0 -> 65535
+  const float factor = 65535.0;
+  const uint x = round(saturate(unorms.x) * factor);
+  const uint y = round(saturate(unorms.y) * factor);
+
+  return OvrPackUint2x16(uint2(x, y));
+}
+
+// Unpack 4 8-bit unsigned integers from a single 32-bit uint
+uint4 OvrUnpackUint4x8(uint four_packed_values) {
+  uint w = (four_packed_values >> 24) & 0xFFu;
+  uint z = (four_packed_values >> 16) & 0xFFu;
+  uint y = (four_packed_values >> 8) & 0xFFu;
+  uint x = four_packed_values & 0xFFu;
+
+  return uint4(x, y, z, w);
+}
+
+// Pack 4 8-bit unsigned integers into a single 32-bit uint
+uint OvrPackUint4x8(uint4 vals) {
+  return vals.x + (vals.y << 8) + (vals.z << 16) + (vals.w << 24);
+}
+
+// Unpack UNorm [0, 1] 4 bytes (as a 32 bit uint)
+float4 OvrUnpackUnorm4x8(uint four_packed_values) {
+  uint4 non_normalized = OvrUnpackUint4x8(four_packed_values);
+
+  // Convert from 0 -> 255 to 0 -> 1
+  const float inv255 = 1.0 / 255.0;
+
+  return float4(
+    non_normalized.x * inv255,
+    non_normalized.y * inv255,
+    non_normalized.z * inv255,
+    non_normalized.w * inv255);
+}
+
+uint OvrPackUnorm4x8(float4 unorms) {
+  const float factor = 255.0;
+  const uint x = round(saturate(unorms.x) * factor);
+  const uint y = round(saturate(unorms.y) * factor);
+  const uint z = round(saturate(unorms.z) * factor);
+  const uint w = round(saturate(unorms.w) * factor);
+
+  return OvrPackUint4x8(uint4(x, y, z, w));
+}
+
+float4 OvrUnpackSnorm4x10_10_10_2(int four_packed_values) {
+  int4 unpackedInt;
+  unpackedInt.x = OvrBitfieldExtract(four_packed_values, 0, 10);
+  unpackedInt.y = OvrBitfieldExtract(four_packed_values, 10, 10);
+  unpackedInt.z = OvrBitfieldExtract(four_packed_values, 20, 10);
+  unpackedInt.w = OvrBitfieldExtract(four_packed_values, 30, 2);
+
+  // xyz is -511-511 w is -1-1
+  float4 unpacked = float4(unpackedInt);
+  // convert all to -1-1
+  unpacked.xyz *= 1.0/511.0;
+
+  return unpacked;
+}
+
+uint OvrPackSnorm4x10_10_10_2(float4 snorms) {
+  static const float3 range = 511.0;
+  float4 scaled = 0.0;
+  scaled.xyz = snorms.xyz * range; // Convert from -1.0 -> 1.0 to -511.0 -> 511.0
+  scaled.xyz = clamp(scaled.xyz, -range, range);
+  scaled.xyz = round(scaled.xyz); // Round to nearest int
+  scaled.w = clamp(scaled.w, -1.0, 1.0);
+  scaled.w = round(scaled.w);
+
+  // now convert from 16 bit to 10 bits, and pack into 32 bits
+  int4 integers = int4(scaled);
+  uint result = 0;
+  result = OvrBitfieldInsert(result, uint(integers.x), 0, 10);
+  result = OvrBitfieldInsert(result, uint(integers.y), 10, 10);
+  result = OvrBitfieldInsert(result, uint(integers.z), 20, 10);
+  result = OvrBitfieldInsert(result, uint(integers.w), 30, 2);
+
+  return result;
+}
+
+// Takes 4 "raw, packed" bytes in a 10/10/10/2 format as a signed 32 bit integer (4 bytes).
+// The 2 bits is used as a "bonus scale".
+// Returns a 3 component (x,y,z) float vector
+float3 OvrUnpackVector_10_10_10_2(int packed_value) {
+  // bonus scale is still a unorm, if I convert it to an snorm, I lose one value.
+  // that does mean I can't use the hardware to convert this format though, it has
+  // to be unpacked by hand. If you do have hardware 10_10_10_2 conversion, it may
+  // be better to just sample twice? once as unorm, once as snorm.
+  uint bonusScaleIndex = uint(packed_value >> 30 & 0x03);
+
+  const float bonus_scale_lookup[4] = {1.0f, 0.5f, 0.25f, 0.125f};
+  const float bonus_scale = bonus_scale_lookup[bonusScaleIndex];
+
+  int3 unpackedInt;
+  unpackedInt.x = OvrBitfieldExtract10(packed_value, 0);
+  unpackedInt.y = OvrBitfieldExtract10(packed_value, 10);
+  unpackedInt.z = OvrBitfieldExtract10(packed_value, 20);
+
+  float3 unpacked = float3(unpackedInt);
+  // convert all to -1 to 1
+  const float inv511 = 1.0 / 511.0;
+  unpacked *= float3(inv511, inv511, inv511);
+
+  unpacked = unpacked * bonus_scale;
+
+  return unpacked;
+}
+
+/////////////////////////////////
+// ByteAddressBuffer functions
+/////////////////////////////////
+
+uint OvrLoadUint(in ByteAddressBuffer data_buffer, uint address) {
+  return data_buffer.Load(address);
+}
+
+uint2 OvrLoadUint2(in ByteAddressBuffer data_buffer, uint address) {
+  return data_buffer.Load2(address);
+}
+
+uint3 OvrLoadUint3(in ByteAddressBuffer data_buffer, uint address) {
+  return data_buffer.Load3(address);
+}
+
+uint4 OvrLoadUint4(in ByteAddressBuffer data_buffer, uint address) {
+  return data_buffer.Load4(address);
+}
+
+void OvrStoreUint(in RWByteAddressBuffer data_buffer, uint address, uint data) {
+  data_buffer.Store(address, data);
+}
+
+void OvrStoreUint2(in RWByteAddressBuffer data_buffer, uint address, uint2 data) {
+  data_buffer.Store2(address, data);
+}
+
+void OvrStoreUint3(in RWByteAddressBuffer data_buffer, uint address, uint3 data) {
+  data_buffer.Store3(address, data);
+}
+
+void OvrStoreUint4(in RWByteAddressBuffer data_buffer, uint address, uint4 data) {
+  data_buffer.Store4(address, data);
+}
+
+// Unity SurfaceShaders require any ByteAddressBuffers to be wrapped in SHADER_API_D3D11
+float3 OvrUnpackVector_10_10_10_2(in ByteAddressBuffer data_buffer, uint address) {
+  return OvrUnpackVector_10_10_10_2(OvrLoadUint(data_buffer, address));
+}
+
+float4 OvrUnpackSnorm4x10_10_10_2(in ByteAddressBuffer data_buffer, uint address) {
+  const int packed_value = OvrLoadUint(data_buffer, address);
+  return OvrUnpackSnorm4x10_10_10_2(packed_value);
+}
+
+float3 OvrUnpackSnorm3x10_10_10_2(in ByteAddressBuffer data_buffer, uint address) {
+  return OvrUnpackSnorm4x10_10_10_2(data_buffer, address).xyz;
+}
+
+// 4x 32 bit uint -> 4x 32 bit float
+float4 OvrUnpackFloat4x32(in ByteAddressBuffer data_buffer, uint address) {
+  const uint4 packed_data = OvrLoadUint4(data_buffer, address);
+  return asfloat(packed_data);
+}
+
+// 3x 32 bit uint -> 3x 32 bit float
+float3 OvrUnpackFloat3x32(in ByteAddressBuffer data_buffer, uint address) {
+  const uint3 packed_data = OvrLoadUint3(data_buffer, address);
+  return asfloat(packed_data);
+}
+
+// 1x 32 bit uint -> 1x 32 bit float
+float OvrUnpackFloat1x32(in ByteAddressBuffer data_buffer, uint address) {
+  return OvrUnpackFloat1x32(OvrLoadUint(data_buffer, address));
+}
+
+// 16x 32 bit uint -> 32 bit float4x4
+float4x4 OvrUnpackFloat16x32(in ByteAddressBuffer data_buffer, uint address) {
+  float4 c0 = OvrUnpackFloat4x32(data_buffer, address);
+  float4 c1 = OvrUnpackFloat4x32(data_buffer, address + 16u);
+  float4 c2 = OvrUnpackFloat4x32(data_buffer, address + 32u);
+  float4 c3 = OvrUnpackFloat4x32(data_buffer, address + 48u);
+
+  return float4x4(
+    c0.x, c1.x, c2.x, c3.x,
+    c0.y, c1.y, c2.y, c3.y,
+    c0.z, c1.z, c2.z, c3.z,
+    c0.w, c1.w, c2.w, c3.w);
+}
+
+// 2x 32 bit uint -> 3x 16 bit "half floats"
+float3 OvrUnpackHalf3x16(in ByteAddressBuffer data_buffer, uint address) {
+  uint2 packed_data = OvrLoadUint2(data_buffer, address);
+  float2 xy = OvrUnpackHalf2x16(packed_data.x);
+  float z = OvrUnpackHalf2x16(packed_data.y).x;
+
+  return float3(xy, z);
+}
+
+// 2x 32 bit uint -> 4x 16 bit "half floats"
+float4 OvrUnpackHalf4x16(in ByteAddressBuffer data_buffer, uint address) {
+  uint2 packed_data = OvrLoadUint2(data_buffer, address);
+  float2 xy = OvrUnpackHalf2x16(packed_data.x);
+  float2 zw = OvrUnpackHalf2x16(packed_data.y);
+
+  return float4(xy, zw);
+}
+
+// 2x 32 bit uint -> 3x 16-bit unsigned int
+uint3 OvrUnpackUint3x16(in ByteAddressBuffer data_buffer, uint address) {
+  uint2 packed_data = OvrLoadUint2(data_buffer, address);
+  float2 xy = OvrUnpackUint2x16(packed_data.x);
+  float z = OvrUnpackUint2x16(packed_data.y).x;
+
+  return float3(xy, z);
+}
+
+// 1x 32 bit uint -> 2x 16-bit unsigned int
+uint2 OvrUnpackUint2x16(in ByteAddressBuffer data_buffer, uint address) {
+  const uint packed_data = OvrLoadUint(data_buffer, address);
+  return OvrUnpackUint2x16(packed_data);
+}
+
+// 2x 32 bit uint -> 4x 16-bit unsigned int
+uint4 OvrUnpackUint4x16(in ByteAddressBuffer data_buffer, uint address) {
+  uint2 packed_data = OvrLoadUint2(data_buffer, address);
+  float2 xy = OvrUnpackUint2x16(packed_data.x);
+  float2 zw = OvrUnpackUint2x16(packed_data.y);
+
+  return float4(xy, zw);
+}
+
+// 2x 32-bit uint -> 3x 16-bit unsigned normalized
+float3 OvrUnpackUnorm3x16(in ByteAddressBuffer data_buffer, uint address) {
+  uint2 packed_data = OvrLoadUint2(data_buffer, address);
+  float2 xy = OvrUnpackUnorm2x16(packed_data.x);
+  float z = OvrUnpackUnorm2x16(packed_data.y).x;
+
+  return float3(xy, z);
+}
+
+// 2x 32-bit uint -> 4x 16-bit unsigned normalized
+float4 OvrUnpackUnorm4x16(in ByteAddressBuffer data_buffer, uint address) {
+  uint2 packed_data = OvrLoadUint2(data_buffer, address);
+  float2 xy = OvrUnpackUnorm2x16(packed_data.x);
+  float2 zw = OvrUnpackUnorm2x16(packed_data.y);
+
+  return float4(xy, zw);
+}
+
+// 1x 32 bit uint -> 4x 8 bit unsigned normalized
+float4 OvrUnpackUint4x8(in ByteAddressBuffer data_buffer, uint address) {
+  return OvrUnpackUint4x8(OvrLoadUint(data_buffer, address));
+}
+
+// 1x 32-bit uint -> 3x 8-bit unsigned int
+uint3 OvrUnpackUint3x8(in ByteAddressBuffer data_buffer, uint address) {
+  return OvrUnpackUint4x8(data_buffer, address).xyz;
+}
+
+// 1x 32-bit uint -> 4x 8-bit unsigned normalized
+float4 OvrUnpackUnorm4x8(in ByteAddressBuffer data_buffer, uint address) {
+  return OvrUnpackUnorm4x8(OvrLoadUint(data_buffer, address));
+}
+
+// 1x 32-bit uint -> 3x 8-bit unsigned normalized
+float3 OvrUnpackUnorm3x8(in ByteAddressBuffer data_buffer, uint address) {
+  return OvrUnpackUnorm4x8(data_buffer, address).xyz;
+}
+
+
+//////////////////////////////////////////////////////////
+// StructuredBuffer<uint> functions
+//////////////////////////////////////////////////////////
+uint OvrLoadUint(in StructuredBuffer<uint> data_buffer, uint address) {
+  const uint start_position = address / SIZE_OF_UINT;
+  return data_buffer[start_position];
+}
+
+uint2 OvrLoadUint2(in StructuredBuffer<uint> data_buffer, uint address) {
+  const uint start_position = address / SIZE_OF_UINT;
+  // Load 2x uints
+  uint x = data_buffer[start_position];
+  uint y = data_buffer[start_position + 1];
+  return uint2(x, y);
+}
+
+uint3 OvrLoadUint3(in StructuredBuffer<uint> data_buffer, uint address) {
+  const uint start_position = address / SIZE_OF_UINT;
+  // Load 3x uints
+  uint x = data_buffer[start_position];
+  uint y = data_buffer[start_position + 1];
+  uint z = data_buffer[start_position + 2];
+  return uint3(x, y, z);
+}
+
+uint4 OvrLoadUint4(in StructuredBuffer<uint> data_buffer, uint address) {
+  const uint start_position = address / SIZE_OF_UINT;
+  // Load 4x uints
+  uint x = data_buffer[start_position];
+  uint y = data_buffer[start_position + 1];
+  uint z = data_buffer[start_position + 2];
+  uint w = data_buffer[start_position + 3];
+  return uint4(x, y, z, w);
+}
+
+void OvrStoreUint(in RWStructuredBuffer<uint> data_buffer, uint address, uint data) {
+  const uint start_position = address / SIZE_OF_UINT;
+  data_buffer[start_position] = data;
+}
+
+void OvrStoreUint2(in RWStructuredBuffer<uint> data_buffer, uint address, uint2 data) {
+  const uint start_position = address / SIZE_OF_UINT;
+  // Store 2x uints
+  data_buffer[start_position] = data.x;
+  data_buffer[start_position + 1] = data.y;
+}
+
+void OvrStoreUint3(in RWStructuredBuffer<uint> data_buffer, uint address, uint3 data) {
+  const uint start_position = address / SIZE_OF_UINT;
+  // Store 3x uints
+  data_buffer[start_position] = data.x;
+  data_buffer[start_position + 1] = data.y;
+  data_buffer[start_position + 2] = data.z;
+}
+
+void OvrStoreUint4(in RWStructuredBuffer<uint> data_buffer, uint address, uint4 data) {
+  const uint start_position = address / SIZE_OF_UINT;
+  // Store 4x uints
+  data_buffer[start_position] = data.x;
+  data_buffer[start_position + 1] = data.y;
+  data_buffer[start_position + 2] = data.z;
+  data_buffer[start_position + 3] = data.w;
+}
+
+float3 OvrUnpackVector_10_10_10_2(in StructuredBuffer<uint> data_buffer, uint address) {
+  return OvrUnpackVector_10_10_10_2(OvrLoadUint(data_buffer, address));
+}
+
+float4 OvrUnpackSnorm4x10_10_10_2(in StructuredBuffer<uint> data_buffer, uint address) {
+  return OvrUnpackSnorm4x10_10_10_2(OvrLoadUint(data_buffer, address));
+}
+
+float3 OvrUnpackSnorm3x10_10_10_2(in StructuredBuffer<uint> data_buffer, uint address) {
+  return OvrUnpackSnorm4x10_10_10_2(data_buffer, address).xyz;
+}
+
+// 4x 32 bit uint -> 4x 32 bit float
+float4 OvrUnpackFloat4x32(in StructuredBuffer<uint> data_buffer, uint address) {
+  // Load 4x uints
+  const uint4 packed_data = OvrLoadUint4(data_buffer, address);
+  return asfloat(packed_data);
+}
+
+// 3x 32 bit uint -> 3x 32 bit float
+float3 OvrUnpackFloat3x32(in StructuredBuffer<uint> data_buffer, uint address) {
+  // Load 3x uints
+  const uint3 packed_data = OvrLoadUint3(data_buffer, address);
+  return asfloat(packed_data);
+}
+
+// 1x 32 bit uint -> 1x 32 bit float
+float OvrUnpackFloat1x32(in StructuredBuffer<uint> data_buffer, uint address) {
+  return OvrUnpackFloat1x32(OvrLoadUint(data_buffer, address));
+}
+
+// 16x 32 bit uint -> 32 bit float4x4
+float4x4 OvrUnpackFloat16x32(in StructuredBuffer<uint> data_buffer, uint address) {
+  float4 c0 = OvrUnpackFloat4x32(data_buffer, address);
+  float4 c1 = OvrUnpackFloat4x32(data_buffer, address + 16);
+  float4 c2 = OvrUnpackFloat4x32(data_buffer, address + 32);
+  float4 c3 = OvrUnpackFloat4x32(data_buffer, address + 48);
+  return float4x4(
+    c0.x, c1.x, c2.x, c3.x,
+    c0.y, c1.y, c2.y, c3.y,
+    c0.z, c1.z, c2.z, c3.z,
+    c0.w, c1.w, c2.w, c3.w);
+}
+
+// 2x 32 bit uint -> 4x 16 bit "half floats"
+float4 OvrUnpackHalf4x16(in StructuredBuffer<uint> data_buffer, uint address) {
+  const uint2 packed_data = OvrLoadUint2(data_buffer, address);
+  float2 xy = OvrUnpackHalf2x16(packed_data.x);
+  float2 zw = OvrUnpackHalf2x16(packed_data.y);
+
+  return float4(xy, zw);
+}
+
+// 2x 32 bit uint -> 3x 16 bit "half floats"
+float3 OvrUnpackHalf3x16(in StructuredBuffer<uint> data_buffer, uint address) {
+  return OvrUnpackHalf4x16(data_buffer, address).xyz;
+}
+
+// 2x 32 bit uint -> 4x 16-bit unsigned int
+uint4 OvrUnpackUint4x16(in StructuredBuffer<uint> data_buffer, uint address) {
+  const uint2 packed_data = OvrLoadUint2(data_buffer, address);
+  float2 xy = OvrUnpackUint2x16(packed_data.x);
+  float2 zw = OvrUnpackUint2x16(packed_data.y);
+
+  return float4(xy, zw);
+}
+
+// 2x 32 bit uint -> 3x 16-bit unsigned int
+uint3 OvrUnpackUint3x16(in StructuredBuffer<uint> data_buffer, uint address) {
+  return OvrUnpackUint4x16(data_buffer, address).xyz;
+}
+
+// 1x 32 bit uint -> 2x 16-bit unsigned int
+uint2 OvrUnpackUint2x16(in StructuredBuffer<uint> data_buffer, uint address) {
+  const uint packed_data = OvrLoadUint(data_buffer, address);
+  return OvrUnpackUint2x16(packed_data);
+}
+
+// 2x 32-bit uint -> 4x 16-bit unsigned normalized
+float4 OvrUnpackUnorm4x16(in StructuredBuffer<uint> data_buffer, uint address) {
+  const uint2 packed_data = OvrLoadUint2(data_buffer, address);
+  float2 xy = OvrUnpackUnorm2x16(packed_data.x);
+  float2 zw = OvrUnpackUnorm2x16(packed_data.y);
+
+  return float4(xy, zw);
+}
+
+// 2x 32-bit uint -> 3x 16-bit unsigned normalized
+float3 OvrUnpackUnorm3x16(in StructuredBuffer<uint> data_buffer, uint address) {
+  return OvrUnpackUnorm4x16(data_buffer, address).xyz;
+}
+
+// 1x 32 bit uint -> 4x 8 bit unsigned normalized
+uint4 OvrUnpackUint4x8(in StructuredBuffer<uint> data_buffer, uint address) {
+  return OvrUnpackUint4x8(OvrLoadUint(data_buffer, address));
+}
+
+// 1x 32-bit uint -> 3x 8-bit unsigned int
+uint3 UnpackUint3x8(in StructuredBuffer<uint> data_buffer, uint address) {
+  return OvrUnpackUint4x8(data_buffer, address).xyz;
+}
+
+// 1x 32-bit uint -> 4x 8-bit unsigned normalized
+float4 OvrUnpackUnorm4x8(in StructuredBuffer<uint> data_buffer, uint address) {
+  return OvrUnpackUnorm4x8(OvrLoadUint(data_buffer, address));
+}
+
+// 1x 32-bit uint -> 3x 8-bit unsigned normalized
+float3 OvrUnpackUnorm3x8(in StructuredBuffer<uint> data_buffer, uint address) {
+  return OvrUnpackUnorm4x8(data_buffer, address).xyz;
+}
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrDecodeUtils.cginc.meta b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrDecodeUtils.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..9994e494f9264562ae4fdd7cb8572430cd7a540c
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrDecodeUtils.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: f5fe283828d9be0428221bb2e0e27e6e
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrGlobalIlluminationTypes.hlsl b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrGlobalIlluminationTypes.hlsl
new file mode 100644
index 0000000000000000000000000000000000000000..fff7eed7f68eb22bbbab6585dd1f7567e0698f19
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrGlobalIlluminationTypes.hlsl
@@ -0,0 +1,13 @@
+#ifndef OVR_GLOBAL_ILLUMINATION_TYPES_INCLUDED
+#define OVR_GLOBAL_ILLUMINATION_TYPES_INCLUDED
+
+#include "OvrLightTypes.hlsl"
+
+struct OvrGlobalIllumination {
+  OvrLight light;
+
+  half3 indirectDiffuse;
+  half3 indirectSpecular;
+};
+
+#endif // OVR_GLOBAL_ILLUMINATION_TYPES_INCLUDED
diff --git a/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrGlobalIlluminationTypes.hlsl.meta b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrGlobalIlluminationTypes.hlsl.meta
new file mode 100644
index 0000000000000000000000000000000000000000..246ed6241f7429d8bd42b2c07208e486709c585d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrGlobalIlluminationTypes.hlsl.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 6dd332dede2750b4c8cc435ef544d0a5
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData:
+  assetBundleName:
+  assetBundleVariant:
diff --git a/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrLightTypes.hlsl b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrLightTypes.hlsl
new file mode 100644
index 0000000000000000000000000000000000000000..c9f5219ab5d299ff8587538108728270b955a493
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrLightTypes.hlsl
@@ -0,0 +1,12 @@
+#ifndef OVR_LIGHT_TYPES_INCLUDED
+#define OVR_LIGHT_TYPES_INCLUDED
+
+struct OvrLight {
+  half3 direction;
+  half3 color;
+
+  half distanceAttenuation;
+  half shadowAttenuation;
+};
+
+#endif // OVR_LIGHT_TYPES_INCLUDED
diff --git a/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrLightTypes.hlsl.meta b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrLightTypes.hlsl.meta
new file mode 100644
index 0000000000000000000000000000000000000000..4e6a4f2788ded09db21b4c63dca74a03bb67d57c
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrLightTypes.hlsl.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 332c0383f777db14bbb77011467e6a61
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData:
+  assetBundleName:
+  assetBundleVariant:
diff --git a/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrUnityGlobalIlluminationBuiltIn.hlsl b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrUnityGlobalIlluminationBuiltIn.hlsl
new file mode 100644
index 0000000000000000000000000000000000000000..26617834399d4572a0697cb47de47fe029de4367
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrUnityGlobalIlluminationBuiltIn.hlsl
@@ -0,0 +1,89 @@
+#ifndef OVR_UNITY_GLOBAL_ILLUMINATION_BUILT_IN_INCLUDED
+#define OVR_UNITY_GLOBAL_ILLUMINATION_BUILT_IN_INCLUDED
+
+#include "OvrGlobalIlluminationTypes.hlsl"
+
+#include "UnityStandardCore.cginc"
+#include "UnityLightingCommon.cginc"
+
+inline OvrGlobalIllumination ConvertUnityGIToOvrGI(in UnityGI unityGI) {
+  OvrGlobalIllumination ovr_gi;
+
+  ovr_gi.light.color = unityGI.light.color;
+  ovr_gi.light.direction = unityGI.light.dir;
+
+  // Attentuation(s) already applied to color when using built-in pipeline GI
+  ovr_gi.light.distanceAttenuation = 1.0;
+  ovr_gi.light.shadowAttenuation = 1.0;
+
+  ovr_gi.indirectDiffuse = unityGI.indirect.diffuse;
+  ovr_gi.indirectSpecular = unityGI.indirect.specular;
+
+  return ovr_gi;
+}
+
+inline OvrGlobalIllumination OvrGetUnityFragmentGI(
+    float3 posWorld,
+    half occlusion,
+    half4 i_ambientOrLightmapUV,
+    half smoothness,
+    half3 normalWorld,
+    half3 eyeVec,
+    OvrLight light,
+    bool reflections)
+{
+  UnityLight unity_light;
+  unity_light.color = light.color;
+  unity_light.dir = light.direction;
+  unity_light.ndotl = 1.0; // ndotl is deprecated
+
+  const UnityGI unity_gi = FragmentGI(
+    posWorld,
+    occlusion,
+    i_ambientOrLightmapUV,
+    light.distanceAttenuation * light.shadowAttenuation,
+    smoothness,
+    normalWorld,
+    eyeVec,
+    unity_light,
+    reflections);
+
+  return ConvertUnityGIToOvrGI(unity_gi);
+}
+
+inline OvrGlobalIllumination OvrGetUnityFragmentGI (
+    float3 posWorld,
+    half occlusion,
+    half4 i_ambientOrLightmapUV,
+    half smoothness,
+    half3 normalWorld,
+    half3 eyeVec,
+    OvrLight light)
+{
+  return OvrGetUnityFragmentGI(posWorld, occlusion, i_ambientOrLightmapUV, smoothness, normalWorld, eyeVec, light, true);
+}
+
+inline void OvrGetUnityDiffuseGlobalIllumination(half3 lightColor, half3 lightDirection, float3 worldPos, float3 worldViewDir,
+    half attenuation, half3 ambient, half smoothness, half metallic, half occlusion,
+    half3 albedo, half3 normal, half3 specular_contribution, out float3 diffuse)
+{
+    OvrLight light;
+    light.color = lightColor;
+    light.direction = lightDirection;
+    OvrGlobalIllumination ovr_gi = OvrGetUnityFragmentGI(worldPos, occlusion, half4(ambient, 1.0), smoothness, normal, -worldViewDir, light, false);
+    diffuse = ovr_gi.indirectDiffuse;
+}
+inline void OvrGetUnityGlobalIllumination(half3 lightColor, half3 lightDirection, float3 worldPos, float3 worldViewDir,
+    half attenuation, half3 ambient, half smoothness, half metallic, half occlusion,
+    half3 albedo, half3 normal, half3 specular_contribution, out float3 diffuse, out float3 specular)
+{
+    OvrLight light;
+    light.color = lightColor;
+    light.direction = lightDirection;
+    OvrGlobalIllumination ovr_gi = OvrGetUnityFragmentGI(worldPos, occlusion, half4(ambient, 1.0), smoothness, normal, -worldViewDir, light, true);
+    diffuse = ovr_gi.indirectDiffuse;
+
+    specular = ovr_gi.indirectSpecular;
+}
+
+#endif // OVR_UNITY_GLOBAL_ILLUMINATION_BUILT_IN_INCLUDED
diff --git a/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrUnityGlobalIlluminationBuiltIn.hlsl.meta b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrUnityGlobalIlluminationBuiltIn.hlsl.meta
new file mode 100644
index 0000000000000000000000000000000000000000..adb4e0e56071e58e99c7b23a57e53d7391a6511d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrUnityGlobalIlluminationBuiltIn.hlsl.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: dd3b798eb81075b4bb4be2c99bde902c
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData:
+  assetBundleName:
+  assetBundleVariant:
diff --git a/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrUnityGlobalIlluminationURP.hlsl b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrUnityGlobalIlluminationURP.hlsl
new file mode 100644
index 0000000000000000000000000000000000000000..56e23e8cc484bb1167364f028890223af89c6563
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrUnityGlobalIlluminationURP.hlsl
@@ -0,0 +1,132 @@
+#ifndef OVR_UNITY_GLOBAL_ILLUMINATION_URP_INCLUDED
+#define OVR_UNITY_GLOBAL_ILLUMINATION_URP_INCLUDED
+
+#include "OvrLightTypes.hlsl"
+#include "OvrGlobalIlluminationTypes.hlsl"
+
+// ASSUMPTION: This path exists if using URP
+#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
+
+inline OvrGlobalIllumination OvrGetUnityGlobalIllumination(
+  OvrLight light,
+  BRDFData brdfData,
+  BRDFData brdfDataClearCoat,
+  float clearCoatMask,
+  half3 bakedGI,
+  half occlusion,
+  half3 normalWS,
+  half3 viewDirectionWS,
+  bool reflections = true)
+{
+  // Pulled this straight from URP 10.1 Lighting.hlsl (with minor modifications to keep
+  // specular and diffuse separate)
+  half3 reflectVector = reflect(-viewDirectionWS, normalWS);
+  half NoV = saturate(dot(normalWS, viewDirectionWS));
+  half fresnelTerm = Pow4(1.0 - NoV);
+
+  half3 indirectDiffuse = bakedGI * occlusion;
+  half3 indirectSpecular = half3(0.0,0.0,0.0);
+  if(reflections) {
+    indirectSpecular = GlossyEnvironmentReflection(reflectVector, brdfData.perceptualRoughness, occlusion);
+    indirectSpecular += indirectSpecular * EnvironmentBRDFSpecular(brdfData, fresnelTerm);
+  }
+  // Avatar SDK modifications
+  indirectDiffuse = indirectDiffuse * brdfData.diffuse;
+  // half3 color = EnvironmentBRDF(brdfData, indirectDiffuse, indirectSpecular, fresnelTerm);
+  // END Avatar SDK modifications
+
+  #if defined(_CLEARCOAT) || defined(_CLEARCOATMAP)
+    half3 coatIndirectSpecular = GlossyEnvironmentReflection(reflectVector, brdfDataClearCoat.perceptualRoughness, occlusion);
+    // TODO: "grazing term" causes problems on full roughness
+    half3 coatColor = EnvironmentBRDFClearCoat(brdfDataClearCoat, clearCoatMask, coatIndirectSpecular, fresnelTerm);
+
+    // Blend with base layer using khronos glTF recommended way using NoV
+    // Smooth surface & "ambiguous" lighting
+    // NOTE: fresnelTerm (above) is pow4 instead of pow5, but should be ok as blend weight.
+    half coatFresnel = kDielectricSpec.x + kDielectricSpec.a * fresnelTerm;
+
+    // Avatar SDK modifications
+    // return color * (1.0 - coatFresnel * clearCoatMask) + coatColor;
+    // In the above, color = indirectDiffuse + indirectSpecular....therefore both terms should
+    // get multiplied. Then the specular will add the coatColor (somewhat arbitrary decision)
+    half coatFactor = (1.0 - coatFresnel * clearCoatMask);
+    indirectDiffuse *= coatFactor;
+    if(reflections) {
+      indirectSpecular = coatFactor * indirectSpecular + coatColor;
+    }
+    // END Avatar SDK modifications
+  #endif
+
+  OvrGlobalIllumination gi;
+  gi.light = light;
+  gi.indirectDiffuse = indirectDiffuse;
+  gi.indirectSpecular = indirectSpecular;
+
+  return gi;
+}
+
+inline OvrGlobalIllumination OvrGetUnityGlobalIllumination(
+  OvrLight light,
+  BRDFData brdfData,
+  half3 bakedGI,
+  half occlusion,
+  half3 normalWS,
+  half3 viewDirectionWS,
+  bool reflections = true)
+{
+  const BRDFData noClearCoat = (BRDFData)0;
+  return OvrGetUnityGlobalIllumination(
+    light,
+    brdfData,
+    noClearCoat,
+    0.0,
+    bakedGI,
+    occlusion,
+    normalWS,
+    viewDirectionWS,
+    reflections);
+}
+
+inline void OvrGetUnityDiffuseGlobalIllumination(half3 lightColor, half3 lightDirection, float3 worldPos, float3 worldViewDir,
+    half attenuation, half3 ambient, half smoothness, half metallic, half occlusion,
+    half3 albedo, half3 normal, half3 specular_contribution, out float3 diffuse)
+{
+      OvrLight light;
+      light.color = lightColor;
+      light.direction = lightDirection;
+      BRDFData brdfData;
+      half3 spec = specular_contribution;
+      half alpha = 1.0;
+      InitializeBRDFData(albedo, metallic, spec, smoothness, alpha, brdfData);
+      half3 ambient_contrib = 0.0;
+      ambient_contrib += SHEvalLinearL0L1(normal,  unity_SHAr, unity_SHAg, unity_SHAb);
+      ambient_contrib += SHEvalLinearL2(normal, unity_SHBr, unity_SHBg, unity_SHBb, unity_SHC);
+      half3 bakedGI = ambient_contrib;
+
+      OvrGlobalIllumination gi = OvrGetUnityGlobalIllumination(light, brdfData, bakedGI, occlusion, normal, worldViewDir, false);
+      diffuse = gi.indirectDiffuse.rgb;
+}
+
+inline void OvrGetUnityGlobalIllumination(half3 lightColor, half3 lightDirection, float3 worldPos, float3 worldViewDir,
+half attenuation, half3 ambient, half smoothness, half metallic, half occlusion,
+half3 albedo, half3 normal, half3 specular_contribution, out float3 diffuse, out float3 specular)
+    {
+      OvrLight light;
+      light.color = lightColor;
+      light.direction = lightDirection;
+      BRDFData brdfData;
+      half3 spec = specular_contribution;
+      half alpha = 1.0;
+      InitializeBRDFData(albedo, metallic, spec, smoothness, alpha, brdfData);
+      half3 ambient_contrib = 0.0;
+      ambient_contrib += SHEvalLinearL0L1(normal,  unity_SHAr, unity_SHAg, unity_SHAb);
+      ambient_contrib += SHEvalLinearL2(normal, unity_SHBr, unity_SHBg, unity_SHBb, unity_SHC);
+      half3 bakedGI = ambient_contrib;
+
+      OvrGlobalIllumination gi = OvrGetUnityGlobalIllumination(light, brdfData, bakedGI, occlusion, normal, worldViewDir, true);
+      diffuse = gi.indirectDiffuse.rgb;
+      specular = gi.indirectSpecular.rgb;
+
+}
+
+#endif // OVR_UNITY_GLOBAL_ILLUMINATION_URP_INCLUDED
diff --git a/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrUnityGlobalIlluminationURP.hlsl.meta b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrUnityGlobalIlluminationURP.hlsl.meta
new file mode 100644
index 0000000000000000000000000000000000000000..5dc620b43dc0806f8da84bc17970f99ad58ff0b6
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrUnityGlobalIlluminationURP.hlsl.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: a878e8b2ab522f946a1c8275fce7e071
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData:
+  assetBundleName:
+  assetBundleVariant:
diff --git a/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrUnityLightsBuiltIn.hlsl b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrUnityLightsBuiltIn.hlsl
new file mode 100644
index 0000000000000000000000000000000000000000..8b0234647765b517a8f710a4480cbc11af02066a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrUnityLightsBuiltIn.hlsl
@@ -0,0 +1,39 @@
+#ifndef OVR_UNITY_LIGHTS_BUILT_IN_INCLUDED
+#define OVR_UNITY_LIGHTS_BUILT_IN_INCLUDED
+
+#include "OvrLightTypes.hlsl"
+
+// Wrapping structs/API for getting a defined light struct out of Unity's "built in" light system
+
+#include "UnityCG.cginc"
+#include "AutoLight.cginc"
+#include "UnityLightingCommon.cginc"
+
+half3 OvrGetUnityLightDirection(half3 worldPos) {
+  return normalize(_WorldSpaceLightPos0.xyz - worldPos);
+}
+
+half3 OvrGetUnityDirectionalLightDirection() {
+  return _WorldSpaceLightPos0.xyz;
+}
+
+#if defined(POINT) || defined(SPOT) || defined(POINT_COOKIE)
+#  define OVR_GET_UNITY_LIGHT_DIRECTION(worldPos) OvrGetUnityLightDirection(worldPos);
+#else
+#  define OVR_GET_UNITY_LIGHT_DIRECTION(worldPos) OvrGetUnityDirectionalLightDirection();
+#endif
+
+// Forward Rendering
+#define OVR_GET_FRAGMENT_UNITY_LIGHT(lightName, vertexInput, worldPos) \
+  OvrLight lightName; \
+\
+  lightName.color = _LightColor0; \
+\
+  lightName.direction = OVR_GET_UNITY_LIGHT_DIRECTION(worldPos); \
+\
+  /* For built-in pipeline, cannot separate shadow and distance attenuation. */ \
+  UNITY_LIGHT_ATTENUATION(attenuation, vertexInput, worldPos) \
+  lightName.distanceAttenuation = attenuation; \
+  lightName.shadowAttenuation = 1.0;
+
+#endif // OVR_UNITY_LIGHTS_BUILT_IN_INCLUDED
diff --git a/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrUnityLightsBuiltIn.hlsl.meta b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrUnityLightsBuiltIn.hlsl.meta
new file mode 100644
index 0000000000000000000000000000000000000000..b621b8eb585db9199fbc71db04cb7d3125ab7a08
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrUnityLightsBuiltIn.hlsl.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: dc25144d2cc3c984f93b34546528eb55
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData:
+  assetBundleName:
+  assetBundleVariant:
diff --git a/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrUnityLightsURP.hlsl b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrUnityLightsURP.hlsl
new file mode 100644
index 0000000000000000000000000000000000000000..222774abdf9f244fab08a58d8a392e8e3b7a6c5d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrUnityLightsURP.hlsl
@@ -0,0 +1,62 @@
+#ifndef OVR_UNITY_LIGHTS_URP_INCLUDED
+#define OVR_UNITY_LIGHTS_URP_INCLUDED
+
+// Wrapping structs/API for getting a defined light struct out of Unity's "URP" light system
+
+// ASSUMPTION: This path exists if using URP
+#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
+
+#include "OvrLightTypes.hlsl"
+
+///////////////////////////////////////////////////////////////////////////////
+//                      Light Abstraction                                    //
+///////////////////////////////////////////////////////////////////////////////
+
+
+// Convert from Unity struct to Ovr struct
+OvrLight ConvertUnityLightToOvrLight(in Light light) {
+  OvrLight ovrLight;
+  ovrLight.color = light.color;
+  ovrLight.direction = light.direction;
+  ovrLight.distanceAttenuation = light.distanceAttenuation;
+  ovrLight.shadowAttenuation = light.shadowAttenuation;
+
+  return ovrLight;
+}
+
+OvrLight OvrGetUnityMainLight()
+{
+  // Convert from Unity struct to Ovr struct
+  return ConvertUnityLightToOvrLight(GetMainLight());
+}
+
+OvrLight OvrGetUnityMainLight(float4 shadowCoord)
+{
+    return ConvertUnityLightToOvrLight(GetMainLight(shadowCoord));
+}
+
+OvrLight OvrGetUnityMainLight(float4 shadowCoord, float3 positionWS, half4 shadowMask)
+{
+  return ConvertUnityLightToOvrLight(GetMainLight(shadowCoord, positionWS, shadowMask));
+}
+
+// Fills a light struct given a perObjectLightIndex
+OvrLight OvrGetAdditionalPerObjectLight(int perObjectLightIndex, float3 positionWS)
+{
+  return ConvertUnityLightToOvrLight(GetAdditionalPerObjectLight(perObjectLightIndex, positionWS));
+}
+
+// Fills a light struct given a loop i index. This will convert the i
+// index to a perObjectLightIndex
+OvrLight OvrGetAdditionalLight(uint i, float3 positionWS)
+{
+  const int perObjectLightIndex = GetPerObjectLightIndex(i);
+  return ConvertUnityLightToOvrLight(GetAdditionalPerObjectLight(perObjectLightIndex, positionWS));
+}
+
+OvrLight OvrGetAdditionalLight(uint i, float3 positionWS, half4 shadowMask)
+{
+  return ConvertUnityLightToOvrLight(GetAdditionalLight(i, positionWS, shadowMask));
+}
+
+#endif // OVR_UNITY_LIGHTS_URP_INCLUDED
diff --git a/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrUnityLightsURP.hlsl.meta b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrUnityLightsURP.hlsl.meta
new file mode 100644
index 0000000000000000000000000000000000000000..b37fc51197d3f9bb332334670cd47149c05ef655
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/ShaderUtils/OvrUnityLightsURP.hlsl.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 494a00d7eca54074cb00805855c70f8a
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData:
+  assetBundleName:
+  assetBundleVariant:
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning.meta b/Assets/Oculus/Avatar2/Scripts/Skinning.meta
new file mode 100644
index 0000000000000000000000000000000000000000..83ec4f1c56cc15eabad758c3d622f5c553ba6dd0
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 07286a0e71b08d340b9a7441c1141a10
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/DummySkinningBufferPropertySetter.cs b/Assets/Oculus/Avatar2/Scripts/Skinning/DummySkinningBufferPropertySetter.cs
new file mode 100644
index 0000000000000000000000000000000000000000..a125305792c162f6e9cd7c6127eedf8e23a78efa
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/DummySkinningBufferPropertySetter.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace Oculus.Skinning
+{
+    // This class is required to workaround a bug/feature? when using Vulkan. If there are
+    // ByteAddressBuffers or StructuredBuffers that exist in the shader (even if not used at runtime), they
+    // must have their buffers set with something. This also causes issues with the Unity Editor that can't
+    // be worked around here.
+    internal class DummySkinningBufferPropertySetter : IDisposable
+    {
+        private static AttributePropertyIds _propertyIds = default;
+
+        private ComputeBuffer _dummyBuffer;
+
+        // Dummy buffers method
+        public DummySkinningBufferPropertySetter()
+        {
+            CheckPropertyIdInit();
+
+            _dummyBuffer = new ComputeBuffer(1, sizeof(uint));
+        }
+
+        public void SetComputeSkinningBuffersInMatBlock(MaterialPropertyBlock matBlock)
+        {
+            matBlock.SetBuffer(_propertyIds.ComputeSkinnerPositionBuffer, _dummyBuffer);
+            matBlock.SetBuffer(_propertyIds.ComputeSkinnerFrenetBuffer, _dummyBuffer);
+        }
+
+        public void Dispose()
+        {
+            _dummyBuffer.Dispose();
+        }
+
+        private static void CheckPropertyIdInit()
+        {
+            if (!_propertyIds.IsValid)
+            {
+                _propertyIds = new AttributePropertyIds(AttributePropertyIds.InitMethod.PropertyToId);
+            }
+        }
+
+        //////////////////////////
+        // AttributePropertyIds //
+        //////////////////////////
+        private struct AttributePropertyIds
+        {
+            public readonly int ComputeSkinnerPositionBuffer;
+            public readonly int ComputeSkinnerFrenetBuffer;
+
+            // These will both be 0 if default initialized, otherwise they are guaranteed unique
+            public bool IsValid => ComputeSkinnerPositionBuffer != ComputeSkinnerFrenetBuffer;
+
+            public enum InitMethod { PropertyToId }
+            public AttributePropertyIds(InitMethod initMethod)
+            {
+                ComputeSkinnerPositionBuffer = Shader.PropertyToID("_OvrPositionBuffer");
+                ComputeSkinnerFrenetBuffer = Shader.PropertyToID("_OvrFrenetBuffer");
+            }
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/DummySkinningBufferPropertySetter.cs.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/DummySkinningBufferPropertySetter.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..c7eb7d24f2f363a538239c171200752be8f0b6d2
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/DummySkinningBufferPropertySetter.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 2be0a673e9973de4ab7427dc7c2a36ee
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning.meta
new file mode 100644
index 0000000000000000000000000000000000000000..a521d8d67c0575a7230b862da33c17faca28ff6f
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 57f78cac782cd5b44a79c9e118b152d6
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/IOvrGpuSkinner.cs b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/IOvrGpuSkinner.cs
new file mode 100644
index 0000000000000000000000000000000000000000..064eb53ad5f881f48f8b852c769ae75dbbc16be2
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/IOvrGpuSkinner.cs
@@ -0,0 +1,24 @@
+using System;
+
+using Oculus.Avatar2;
+
+using Unity.Collections;
+
+using UnityEngine;
+using UnityEngine.Experimental.Rendering;
+
+namespace Oculus.Skinning.GpuSkinning
+{
+    internal abstract class IOvrGpuSkinner
+    {
+        public abstract GraphicsFormat GetOutputTexGraphicFormat();
+        public abstract RenderTexture GetOutputTex();
+        public abstract CAPI.ovrTextureLayoutResult GetLayoutInOutputTex(OvrSkinningTypes.Handle handle);
+        public abstract void EnableBlockToRender(OvrSkinningTypes.Handle handle, SkinningOutputFrame outputFrame);
+        public abstract void UpdateOutputTexture();
+        public abstract bool HasJoints { get; }
+        public abstract OvrAvatarGpuSkinningController ParentController { get; set; }
+        public abstract IntPtr GetJointTransformMatricesArray(OvrSkinningTypes.Handle handle);
+        public abstract void Destroy();
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/IOvrGpuSkinner.cs.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/IOvrGpuSkinner.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..5180d2039c9c18ef10bf33833d41f19135b090af
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/IOvrGpuSkinner.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 910b2325dedac1c4eaa29502c60935b3
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/IOvrGpuSkinnerDrawCall.cs b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/IOvrGpuSkinnerDrawCall.cs
new file mode 100644
index 0000000000000000000000000000000000000000..e90a4933fc167ffc85d1ce98f6584331b0c58385
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/IOvrGpuSkinnerDrawCall.cs
@@ -0,0 +1,17 @@
+using System.Collections.Generic;
+
+namespace Oculus.Skinning.GpuSkinning
+{
+    internal enum SkinningOutputFrame { FrameZero, FrameOne, FrameTwo }
+
+    internal interface IOvrGpuSkinnerDrawCall
+    {
+        bool EnableBlock(OvrSkinningTypes.Handle handle, SkinningOutputFrame writeDest);
+        void RemoveBlock(OvrSkinningTypes.Handle handle);
+
+        bool NeedsDraw(SkinningOutputFrame writeDest);
+        void Draw(SkinningOutputFrame writeDest);
+
+        void Destroy();
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/IOvrGpuSkinnerDrawCall.cs.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/IOvrGpuSkinnerDrawCall.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..bc4ee6ca748988f4eb07cc0d2d6bfb4873d1ddc0
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/IOvrGpuSkinnerDrawCall.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 2691f687fcdf376428959fbf758bf947
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarComputeInterpolatedSkinnedMvRenderable.cs b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarComputeInterpolatedSkinnedMvRenderable.cs
new file mode 100644
index 0000000000000000000000000000000000000000..f96b861b592d6f29755bcb81e05e721caa31fa2e
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarComputeInterpolatedSkinnedMvRenderable.cs
@@ -0,0 +1,187 @@
+using Oculus.Avatar2;
+using UnityEngine;
+using static Oculus.Skinning.GpuSkinning.OvrComputeMeshAnimator;
+
+/// @file OvrAvatarGpuInterpolatedSkinningRenderable
+
+namespace Oculus.Skinning.GpuSkinning
+{
+    /**
+     * Component that encapsulates the meshes of a skinned avatar.
+     * This component implements skinning using the Avatar SDK
+     * and uses the GPU. It performs skinning on every avatar
+     * but not at every frame. Instead, it interpolates between
+     * frames, reducing the performance overhead of skinning
+     * when there are lots of avatars. It is used when the skinning configuration
+     * is set to SkinningConfig.OVR_COMPUTE, "motion smoothing" and "support application spacewarp"
+     * is enabled in the GPU skinning configuration.
+     *
+     * @see OvrAvatarSkinnedRenderable
+     * @see OvrAvatarComputeSkinnedRenderable
+     * @see OvrGpuSkinningConfiguration.MotionSmoothing
+     */
+    public class OvrAvatarComputeInterpolatedSkinnedMvRenderable : OvrAvatarComputeSkinnedRenderableBase
+    {
+        // Number of animation frames required to be considered "completely valid"
+        private const int NUM_ANIM_FRAMES_NEEDED_FOR_CURRENT_RENDER = 2;
+
+        protected override string LogScope => nameof(OvrAvatarComputeInterpolatedSkinnedMvRenderable);
+
+        internal override MaxOutputFrames MeshAnimatorOutputFrames => MaxOutputFrames.THREE;
+        protected override bool InterpolateAttributes => true;
+
+        public IInterpolationValueProvider InterpolationValueProvider { get; set; }
+
+        private CAPI.ovrAvatar2Transform _prevSkinningOrigin;
+        private CAPI.ovrAvatar2Transform _currentSkinningOrigin;
+
+        private int _numValidAnimationFrames;
+        private bool _hasValidPreviousRenderFrame;
+
+        private SkinningOutputFrame _writeDestination = SkinningOutputFrame.FrameZero;
+        private SkinningOutputFrame _prevAnimFrameWriteDest = SkinningOutputFrame.FrameZero;
+
+        private SkinningOutputFrame _renderFrameWriteDest = SkinningOutputFrame.FrameZero;
+        private SkinningOutputFrame _prevRenderFrameWriteDest = SkinningOutputFrame.FrameZero;
+
+        private float _renderFrameLerpVal;
+        private float _prevRenderFrameLerpVal;
+
+        protected override void Dispose(bool isDisposing)
+        {
+            InterpolationValueProvider = null;
+
+            base.Dispose(isDisposing);
+        }
+
+        public override void UpdateSkinningOrigin(in CAPI.ovrAvatar2Transform skinningOrigin)
+        {
+            // Should be called every "animation frame"
+
+            _prevSkinningOrigin = IsAnimationDataCompletelyValid ? skinningOrigin : _currentSkinningOrigin;
+
+            _currentSkinningOrigin = skinningOrigin;
+        }
+
+        protected override void OnAnimationEnabledChanged(bool isNowEnabled)
+        {
+            if (isNowEnabled)
+            {
+                // Reset valid frame counter on re-enabling animation
+                _numValidAnimationFrames = 0;
+                _writeDestination = SkinningOutputFrame.FrameOne;
+                _prevAnimFrameWriteDest = _writeDestination;
+            }
+        }
+
+        protected virtual void OnEnable()
+        {
+            // No animation data yet since object just enabled (becoming visible)
+            _renderFrameLerpVal = 0.0f;
+            _prevRenderFrameLerpVal = 0.0f;
+            _hasValidPreviousRenderFrame = false;
+
+            _renderFrameWriteDest = _writeDestination;
+            _prevRenderFrameWriteDest = _writeDestination;
+        }
+
+        internal override void AnimationFrameUpdate()
+        {
+            // Replaces logic in base class
+
+            // ASSUMPTION: This call will always follow calls to update morphs and/or skinning.
+            // With that assumption, new data will be written by the morph target combiner and/or skinner, so there
+            // will be valid data at end of frame.
+            bool wasAnimDataCompletedValid = IsAnimationDataCompletelyValid;
+
+            if (_numValidAnimationFrames < NUM_ANIM_FRAMES_NEEDED_FOR_CURRENT_RENDER)
+            {
+                _numValidAnimationFrames++;
+            }
+
+            if (!wasAnimDataCompletedValid && IsAnimationDataCompletelyValid)
+            {
+                OnAnimationDataCompleted();
+            }
+
+            // Update "current" and "previous" animation frame related data
+            _prevAnimFrameWriteDest = _writeDestination;
+            _writeDestination = GetNextOutputFrame(_writeDestination, MeshAnimatorOutputFrames);
+
+            MeshAnimator?.SetWriteDestinationInDynamicBuffer(_writeDestination);
+            OvrAvatarManager.Instance.GpuSkinningController.AddActivateComputeAnimator(MeshAnimator);
+        }
+
+        internal override void RenderFrameUpdate()
+        {
+            Debug.Assert(InterpolationValueProvider != null);
+
+            float lerpValue = InterpolationValueProvider.GetRenderInterpolationValue();
+
+            // Guard against insufficient animation frames available
+            // by "slamming" value to be 1.0 ("the newest value").
+            // Should hopefully not happen frequently/at all if caller manages state well (maybe on first enabling)
+            if (_numValidAnimationFrames < NUM_ANIM_FRAMES_NEEDED_FOR_CURRENT_RENDER)
+            {
+                lerpValue = 1.0f;
+            }
+
+            // Update "current" and "previous" render frame related data
+            // Slam "previous" values to be current values if there was no previous render frame
+            if (!_hasValidPreviousRenderFrame)
+            {
+                _prevRenderFrameWriteDest = _writeDestination;
+                _prevRenderFrameLerpVal = lerpValue;
+                _hasValidPreviousRenderFrame = true;
+            }
+            else
+            {
+                _prevRenderFrameWriteDest = _renderFrameWriteDest;
+                _prevRenderFrameLerpVal = _renderFrameLerpVal;
+            }
+
+            _renderFrameWriteDest = _writeDestination;
+            _renderFrameLerpVal = lerpValue;
+
+            InterpolateSkinningOrigin(lerpValue);
+            SetAnimationInterpolationValuesInMaterial(lerpValue);
+        }
+
+        private void SetAnimationInterpolationValuesInMaterial(float lerpValue)
+        {
+            // Update the interpolation value
+            rendererComponent.GetPropertyBlock(MatBlock);
+
+            MatBlock.SetInt(PropIds.AttributeOutputLatestAnimFrameEntryOffset, (int)_writeDestination);
+            MatBlock.SetInt(PropIds.AttributeOutputPrevAnimFrameEntryOffset, (int)_prevAnimFrameWriteDest);
+            MatBlock.SetFloat(PropIds.AttributeLerpValuePropId, lerpValue);
+
+            MatBlock.SetInt(PropIds.AttributeOutputPrevRenderFrameLatestAnimFrameOffset, (int)_renderFrameWriteDest);
+            MatBlock.SetInt(PropIds.AttributeOutputPrevRenderFramePrevAnimFrameOffset, (int)_prevRenderFrameWriteDest);
+            MatBlock.SetFloat(PropIds.AttributePrevRenderFrameLerpValuePropId, _prevRenderFrameLerpVal);
+
+            rendererComponent.SetPropertyBlock(MatBlock);
+        }
+
+        private void InterpolateSkinningOrigin(float lerpValue)
+        {
+            // Update the "skinning origin" via lerp/slerp.
+            // NOTE: This feels dirty as we are converting from `OvrAvatar2Vector3f/Quat` to Unity
+            // versions just to do the lerp/slerp. Unnecessary conversions
+            transform.localPosition = Vector3.Lerp(
+                _prevSkinningOrigin.position,
+                _currentSkinningOrigin.position,
+                lerpValue);
+            transform.localRotation = Quaternion.Slerp(
+                _prevSkinningOrigin.orientation,
+                _currentSkinningOrigin.orientation,
+                lerpValue);
+            transform.localScale = Vector3.Lerp(
+                _prevSkinningOrigin.scale,
+                _currentSkinningOrigin.scale,
+                lerpValue);
+        }
+
+        internal override bool IsAnimationDataCompletelyValid => _numValidAnimationFrames >= NUM_ANIM_FRAMES_NEEDED_FOR_CURRENT_RENDER;
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarComputeInterpolatedSkinnedMvRenderable.cs.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarComputeInterpolatedSkinnedMvRenderable.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..7e95c509fcd18e67a25a19792d4102b5259a5fce
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarComputeInterpolatedSkinnedMvRenderable.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 7900f57b0d645ba4191216653192535b
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarComputeInterpolatedSkinnedRenderable.cs b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarComputeInterpolatedSkinnedRenderable.cs
new file mode 100644
index 0000000000000000000000000000000000000000..7dcf47d21e2123067ce2765abef746d8d866342e
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarComputeInterpolatedSkinnedRenderable.cs
@@ -0,0 +1,163 @@
+using Oculus.Avatar2;
+using UnityEngine;
+using static Oculus.Skinning.GpuSkinning.OvrComputeMeshAnimator;
+
+/// @file OvrAvatarGpuInterpolatedSkinningRenderable
+
+namespace Oculus.Skinning.GpuSkinning
+{
+    /**
+     * Component that encapsulates the meshes of a skinned avatar.
+     * This component implements skinning using the Avatar SDK
+     * and uses the GPU. It performs skinning on every avatar
+     * but not at every frame. Instead, it interpolates between
+     * frames, reducing the performance overhead of skinning
+     * when there are lots of avatars. It is used when the skinning configuration
+     * is set to SkinningConfig.OVR_COMPUTE and motion smoothing
+     * is enabled in the GPU skinning configuration.
+     *
+     * @see OvrAvatarSkinnedRenderable
+     * @see OvrAvatarComputeSkinnedRenderable
+     * @see OvrGpuSkinningConfiguration.MotionSmoothing
+     */
+    public class OvrAvatarComputeInterpolatedSkinnedRenderable : OvrAvatarComputeSkinnedRenderableBase
+    {
+        // Number of animation frames required to be considered "completely valid"
+        private const int NUM_ANIM_FRAMES_NEEDED_FOR_CURRENT_RENDER = 2;
+
+        protected override string LogScope => nameof(OvrAvatarComputeInterpolatedSkinnedRenderable);
+
+        internal override MaxOutputFrames MeshAnimatorOutputFrames => MaxOutputFrames.TWO;
+        protected override bool InterpolateAttributes => true;
+
+        public IInterpolationValueProvider InterpolationValueProvider { get; set; }
+
+        private CAPI.ovrAvatar2Transform _skinningOriginFrameZero;
+        private CAPI.ovrAvatar2Transform _skinningOriginFrameOne;
+
+        private bool _invertInterpolationValue;
+        private int _numValidAnimationFrames;
+
+        private SkinningOutputFrame _writeDestination = SkinningOutputFrame.FrameZero;
+        private SkinningOutputFrame _prevAnimFrameWriteDest = SkinningOutputFrame.FrameZero;
+
+        protected override void Dispose(bool isDisposing)
+        {
+            InterpolationValueProvider = null;
+
+            base.Dispose(isDisposing);
+        }
+
+        public override void UpdateSkinningOrigin(in CAPI.ovrAvatar2Transform skinningOrigin)
+        {
+            switch (_writeDestination)
+            {
+                case SkinningOutputFrame.FrameZero:
+                    _skinningOriginFrameZero = skinningOrigin;
+                    break;
+                case SkinningOutputFrame.FrameOne:
+                    _skinningOriginFrameOne = skinningOrigin;
+                    break;
+            }
+        }
+
+        protected override void OnAnimationEnabledChanged(bool isNowEnabled)
+        {
+            if (isNowEnabled)
+            {
+                // Reset valid frame counter on re-enabling animation
+                _numValidAnimationFrames = 0;
+                _writeDestination = SkinningOutputFrame.FrameOne;
+                _prevAnimFrameWriteDest = _writeDestination;
+            }
+        }
+
+        internal override void AnimationFrameUpdate()
+        {
+            // Replaces logic in base class
+
+            // ASSUMPTION: This call will always follow calls to update morphs and/or skinning.
+            // With that assumption, new data will be written by the morph target combiner and/or skinner, so there
+            // will be valid data at end of frame.
+            _writeDestination = GetNextOutputFrame(_writeDestination, MeshAnimatorOutputFrames);
+
+            bool wasAnimDataCompletedValid = IsAnimationDataCompletelyValid;
+
+            if (_numValidAnimationFrames < NUM_ANIM_FRAMES_NEEDED_FOR_CURRENT_RENDER)
+            {
+                _numValidAnimationFrames++;
+            }
+
+            if (!wasAnimDataCompletedValid && IsAnimationDataCompletelyValid)
+            {
+                OnAnimationDataCompleted();
+            }
+
+            MeshAnimator?.SetWriteDestinationInDynamicBuffer(_writeDestination);
+            OvrAvatarManager.Instance.GpuSkinningController.AddActivateComputeAnimator(MeshAnimator);
+
+            // Set "previous anim frame" field
+            _prevAnimFrameWriteDest = _writeDestination;
+        }
+
+        internal override void RenderFrameUpdate()
+        {
+            Debug.Assert(InterpolationValueProvider != null);
+
+            float lerpValue = InterpolationValueProvider.GetRenderInterpolationValue();
+
+            // Guard against insufficient animation frames available
+            // by "slamming" value to be 1.0 ("the newest value").
+            // Should hopefully not happen frequently/at all if caller manages state well (maybe on first enabling)
+            if (_numValidAnimationFrames < NUM_ANIM_FRAMES_NEEDED_FOR_CURRENT_RENDER)
+            {
+                lerpValue = 1.0f;
+            }
+
+            if (ShouldInverseInterpolationValue)
+            {
+                // Convert from the 0 -> 1 interpolation value to one that "ping pongs" between
+                // the slices here so that an additional GPU copy isn't needed to
+                // transfer from "slice 1" to "slice 0"
+                lerpValue = 1.0f - lerpValue;
+            }
+
+            InterpolateSkinningOrigin(lerpValue);
+            SetAnimationInterpolationValuesInMaterial(lerpValue);
+        }
+
+        private void SetAnimationInterpolationValuesInMaterial(float lerpValue)
+        {
+            // Update the interpolation value
+            rendererComponent.GetPropertyBlock(MatBlock);
+
+            MatBlock.SetFloat(PropIds.AttributeLerpValuePropId, lerpValue);
+            MatBlock.SetInt(PropIds.AttributeOutputLatestAnimFrameEntryOffset, (int)_writeDestination);
+            MatBlock.SetInt(PropIds.AttributeOutputPrevAnimFrameEntryOffset, (int)_prevAnimFrameWriteDest);
+
+            rendererComponent.SetPropertyBlock(MatBlock);
+        }
+
+        private void InterpolateSkinningOrigin(float lerpValue)
+        {
+            // Update the "skinning origin" via lerp/slerp.
+            // NOTE: This feels dirty as we are converting from `OvrAvatar2Vector3f/Quat` to Unity
+            // versions just to do the lerp/slerp. Unnecessary conversions
+            transform.localPosition = Vector3.Lerp(
+                _skinningOriginFrameZero.position,
+                _skinningOriginFrameOne.position,
+                lerpValue);
+            transform.localRotation = Quaternion.Slerp(
+                _skinningOriginFrameZero.orientation,
+                _skinningOriginFrameOne.orientation,
+                lerpValue);
+            transform.localScale = Vector3.Lerp(
+                _skinningOriginFrameZero.scale,
+                _skinningOriginFrameOne.scale,
+                lerpValue);
+        }
+
+        internal override bool IsAnimationDataCompletelyValid => _numValidAnimationFrames >= NUM_ANIM_FRAMES_NEEDED_FOR_CURRENT_RENDER;
+        private bool ShouldInverseInterpolationValue => _writeDestination == SkinningOutputFrame.FrameZero;
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarComputeInterpolatedSkinnedRenderable.cs.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarComputeInterpolatedSkinnedRenderable.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..6b665ffb394f692f59d59bd90dfb087905f90cf4
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarComputeInterpolatedSkinnedRenderable.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: ecab5e9da69e35e4a98c77366dec262b
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarComputeSkinnedMvRenderable.cs b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarComputeSkinnedMvRenderable.cs
new file mode 100644
index 0000000000000000000000000000000000000000..b2e6d9eab9ca4658fc48420f826a09074b718841
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarComputeSkinnedMvRenderable.cs
@@ -0,0 +1,90 @@
+using Oculus.Avatar2;
+
+namespace Oculus.Skinning.GpuSkinning
+{
+    /**
+     * Component that encapsulates the meshes of a skinned avatar.
+     * This component implements skinning using the Avatar SDK
+     * and uses the GPU. It performs skinning on every avatar
+     * but not at every frame. Instead, it interpolates between
+     * frames, reducing the performance overhead of skinning
+     * when there are lots of avatars. It is used when the skinning configuration
+     * is set to SkinningConfig.OVR_UNITY_GPU_COMPUTE, , motion smoothing
+     * is *not* enabled, and "App Space Warp" is enabled in the GPU skinning configuration.
+     *
+     * @see OvrAvatarSkinnedRenderable
+     * @see OvrAvatarComputeSkinnedRenderable
+     * @see OvrGpuSkinningConfiguration.SupportApplicationSpacewarp
+     */
+    public class OvrAvatarComputeSkinnedMvRenderable : OvrAvatarComputeSkinnedRenderableBase
+    {
+        protected override string LogScope => nameof(OvrAvatarComputeSkinnedMvRenderable);
+
+        // Only need 2 single output frames (one for current frame, one for previous frame)
+        internal override OvrComputeMeshAnimator.MaxOutputFrames MeshAnimatorOutputFrames => OvrComputeMeshAnimator.MaxOutputFrames.TWO;
+
+        // Only 1 "animation frame" is required for this renderable's animation frames to be valid (not interpolating)
+        private bool _isAnimationFrameDataValid;
+        private bool _hasValidPreviousRenderFrame;
+
+        private SkinningOutputFrame _writeDestination = SkinningOutputFrame.FrameZero;
+        private SkinningOutputFrame _prevRenderWriteDest = SkinningOutputFrame.FrameZero;
+
+        protected override void OnAnimationEnabledChanged(bool isNowEnabled)
+        {
+            if (isNowEnabled)
+            {
+                _isAnimationFrameDataValid = false;
+                _writeDestination = SkinningOutputFrame.FrameOne;
+            }
+        }
+
+        protected virtual void OnEnable()
+        {
+            // Reset the previous render frame slice and render frame count
+            _hasValidPreviousRenderFrame = false;
+            _prevRenderWriteDest = _writeDestination;
+        }
+
+        internal override void AnimationFrameUpdate()
+        {
+            // ASSUMPTION: This call will always be followed by calls to update morphs and/or skinning.
+            // With that assumption, new data will be written by the morph target combiner and/or skinner, so there
+            // will be valid data at end of frame.
+            _isAnimationFrameDataValid = true;
+
+            _writeDestination = GetNextOutputFrame(_writeDestination, MeshAnimatorOutputFrames);
+            MeshAnimator?.SetWriteDestinationInDynamicBuffer(_writeDestination);
+            OvrAvatarManager.Instance.GpuSkinningController.AddActivateComputeAnimator(MeshAnimator);
+        }
+
+        internal override void RenderFrameUpdate()
+        {
+            // Need at least 1 "previous render frame"
+            if (!_hasValidPreviousRenderFrame)
+            {
+                // Not enough render frames, just make the motion vectors "previous frame" the same
+                // as the current one
+                _prevRenderWriteDest = _writeDestination;
+                _hasValidPreviousRenderFrame = true;
+            }
+
+            SetRenderFrameOutputSlices();
+
+            // Update "previous frame" value for next frame
+            _prevRenderWriteDest = _writeDestination;
+        }
+
+        internal override bool IsAnimationDataCompletelyValid => _isAnimationFrameDataValid;
+
+        private void SetRenderFrameOutputSlices()
+        {
+            rendererComponent.GetPropertyBlock(MatBlock);
+
+            MatBlock.SetInt(PropIds.AttributeOutputLatestAnimFrameEntryOffset, (int)_writeDestination);
+            MatBlock.SetInt(PropIds.AttributeOutputPrevRenderFrameLatestAnimFrameOffset, (int)_prevRenderWriteDest);
+
+            rendererComponent.SetPropertyBlock(MatBlock);
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarComputeSkinnedMvRenderable.cs.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarComputeSkinnedMvRenderable.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..7064278ab5e52b6338173163f576322d7b2b0e87
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarComputeSkinnedMvRenderable.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 3632504fa5469e44f9523ead64317b6d
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarComputeSkinnedRenderable.cs b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarComputeSkinnedRenderable.cs
new file mode 100644
index 0000000000000000000000000000000000000000..e0237c531ad9cf02511bbbf39062f087f105e896
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarComputeSkinnedRenderable.cs
@@ -0,0 +1,54 @@
+using Oculus.Avatar2;
+
+namespace Oculus.Skinning.GpuSkinning
+{
+    /**
+     * Component that encapsulates the meshes of a skinned avatar.
+     * This component implements skinning using the Avatar SDK
+     * and uses the GPU. It performs skinning on every avatar
+     * at each frame. It is used when the skinning configuration
+     * is set to SkinningConfig.OVR_UNITY_GPU_COMPUTE and motion smoothing
+     * is *not* enabled in the GPU skinning configuration.
+     *
+     * @see OvrAvatarSkinnedRenderable
+     * @see OvrAvatarComputeSkinnedRenderable
+     * @see OvrGpuSkinningConfiguration.MotionSmoothing
+     */
+    public class OvrAvatarComputeSkinnedRenderable : OvrAvatarComputeSkinnedRenderableBase
+    {
+        protected override string LogScope => nameof(OvrAvatarComputeSkinnedRenderable);
+
+        // Only need a single output frame
+        internal override OvrComputeMeshAnimator.MaxOutputFrames MeshAnimatorOutputFrames => OvrComputeMeshAnimator.MaxOutputFrames.ONE;
+
+        // Only 1 "animation frame" is required for this renderable, so a boolean is sufficient
+        // to serve as "is valid
+        private bool _isAnimationDataCompletelyValid;
+
+        protected override void OnAnimationEnabledChanged(bool isNowEnabled)
+        {
+            if (isNowEnabled)
+            {
+                _isAnimationDataCompletelyValid = false;
+            }
+        }
+
+        internal override void AnimationFrameUpdate()
+        {
+            // ASSUMPTION: This call will always be followed by calls to update morphs and/or skinning.
+            // With that assumption, new data will be written by the morph target combiner and/or skinner, so there
+            // will be valid data at end of frame.
+            _isAnimationDataCompletelyValid = true;
+            OnAnimationDataCompleted();
+
+            OvrAvatarManager.Instance.GpuSkinningController.AddActivateComputeAnimator(MeshAnimator);
+        }
+
+        internal override void RenderFrameUpdate()
+        {
+            // Intentionally empty
+        }
+
+        internal override bool IsAnimationDataCompletelyValid => _isAnimationDataCompletelyValid;
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarComputeSkinnedRenderable.cs.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarComputeSkinnedRenderable.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..f18469c6e0fd2573ff4abfd95ca8fba4a874add8
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarComputeSkinnedRenderable.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: f871c8fea7e4c0041831727b6882ea81
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarComputeSkinnedRenderableBase.cs b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarComputeSkinnedRenderableBase.cs
new file mode 100644
index 0000000000000000000000000000000000000000..324fcff67377368e4176202a993a7fb016569025
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarComputeSkinnedRenderableBase.cs
@@ -0,0 +1,341 @@
+using System;
+
+using Oculus.Avatar2;
+
+using Unity.Collections;
+using Unity.Collections.LowLevel.Unsafe;
+
+using UnityEngine;
+using UnityEngine.Profiling;
+
+namespace Oculus.Skinning.GpuSkinning
+{
+    public abstract class OvrAvatarComputeSkinnedRenderableBase : OvrAvatarSkinnedRenderable
+    {
+        protected abstract string LogScope { get; }
+        internal abstract OvrComputeMeshAnimator.MaxOutputFrames MeshAnimatorOutputFrames { get; }
+
+
+        protected override VertexFetchMode VertexFetchType => VertexFetchMode.ExternalBuffers;
+
+        // Specifies the skinning quality (many bones per vertex).
+        public OvrSkinningTypes.SkinningQuality SkinningQuality
+        {
+            get => _skinningQuality;
+            set
+            {
+                if (_skinningQuality != value)
+                {
+                    _skinningQuality = value;
+                    UpdateSkinningQuality();
+                }
+            }
+        }
+
+        private OvrComputeMeshAnimator _meshAnimator;
+        internal OvrComputeMeshAnimator MeshAnimator => _meshAnimator;
+
+        // This is technically configurable, but mostly just for debugging
+        [SerializeField]
+        [Tooltip("Configuration to override SkinningQuality, otherwise indicates which Quality was selected for this LOD")]
+        private OvrSkinningTypes.SkinningQuality _skinningQuality = OvrSkinningTypes.SkinningQuality.Invalid;
+
+        private static PropertyIds _propertyIds = default;
+        protected static PropertyIds PropIds => _propertyIds;
+        private static void CheckPropertyIdInit()
+        {
+            if (!_propertyIds.IsValid)
+            {
+                _propertyIds = new PropertyIds(PropertyIds.InitMethod.PropertyToId);
+            }
+        }
+
+        protected override void Awake()
+        {
+            base.Awake();
+            CheckPropertyIdInit();
+        }
+
+        protected override void Dispose(bool isMainThread)
+        {
+            if (isMainThread)
+            {
+                DestroyGpuSkinningObjects();
+            }
+            else
+            {
+                OvrAvatarLog.LogError($"{nameof(OvrAvatarComputeSkinnedRenderable)} was disposed not on main thread, memory has been leaked.", LogScope);
+            }
+
+            _meshAnimator = null;
+            base.Dispose(isMainThread);
+        }
+
+        private void DestroyGpuSkinningObjects()
+        {
+            _meshAnimator?.Dispose();
+            _morphWeightsHandle.Dispose();
+            _jointMatricesArray.Reset();
+        }
+
+        private void UpdateSkinningQuality()
+        {
+            if (_meshAnimator != null)
+            {
+                _meshAnimator.SkinningQuality = _skinningQuality;
+            }
+        }
+
+        protected internal override void ApplyMeshPrimitive(OvrAvatarPrimitive primitive)
+        {
+            // The base call adds a mesh filter already and material
+            base.ApplyMeshPrimitive(primitive);
+
+            try
+            {
+                if (_skinningQuality == OvrSkinningTypes.SkinningQuality.Invalid)
+                {
+                    _skinningQuality = GpuSkinningConfiguration.Instance.GetQualityForLOD(primitive.HighestQualityLODIndex);
+                }
+
+                if (primitive.morphTargetCount != 0)
+                {
+                    // Create native array for morph target weights
+                    _morphWeightsHandle.Resize((int)primitive.morphTargetCount);
+                }
+
+                if (primitive.joints.Length != 0)
+                {
+                    // Create native array for joint matrices
+                    // joints.Length * 2 due to interleaving normal matrices
+                    _jointMatricesArray = new NativeArray<Matrix4x4>(primitive.joints.Length * 2, Allocator.Persistent);
+                }
+
+                AddGpuSkinningObjects(primitive);
+                ApplyGpuSkinningMaterial(primitive);
+            }
+            catch (Exception e)
+            {
+                OvrAvatarLog.LogError($"Exception applying primitive ({primitive}) - {e}", LogScope, this);
+            }
+        }
+
+        private void ApplyGpuSkinningMaterial(OvrAvatarPrimitive primitive)
+        {
+            rendererComponent.GetPropertyBlock(MatBlock);
+
+            ComputeBuffer positionBuffer = null;
+            ComputeBuffer frenetBuffer = null;
+
+            var positionScale = Vector3.one;
+            var positionBias = Vector3.zero;
+
+            if (_meshAnimator != null)
+            {
+                positionBuffer = _meshAnimator.GetPositionOutputBuffer();
+                frenetBuffer = _meshAnimator.GetFrenetOutputBuffer();
+
+                // ASSUMPTION: Assuming the mesh animator will normalize
+                // by doing output = (input - PositionOutputBias) * (1.0f / PositionOutputScale)
+                // Do un-normalize, need to scale by PositionOutputScale and add PositionOutputBias.
+                // If the shader changes its logic, this will change as well
+                positionScale = _meshAnimator.GetPositionOutputScale();
+                positionBias = _meshAnimator.GetPositionOutputBias();
+            }
+
+            MatBlock.SetBuffer(_propertyIds.PositionOutputBufferPropId, positionBuffer);
+            MatBlock.SetBuffer(_propertyIds.FrenetOutputBufferPropId, frenetBuffer);
+            MatBlock.SetVector(_propertyIds.PositionScalePropId, positionScale);
+            MatBlock.SetVector(_propertyIds.PositionBiasPropId, positionBias);
+            MatBlock.SetInt(
+                _propertyIds.PositionEncodingPrecisionPropId,
+                OvrComputeUtils.GetEncodingPrecisionShaderValue(primitive.computePrimitive.SourceMetaData.outputPositionPrecision));
+
+            MatBlock.SetInt(_propertyIds.InterpolateAttributesPropId, InterpolateAttributes ? 1 : 0);
+
+            Debug.Assert(positionBuffer != null, "No position buffer for compute skinning, avatars may not be able to move.");
+            Debug.Assert(frenetBuffer != null, "No frenet buffer for compute skinning, avatars may not be able to move.");
+
+            rendererComponent.SetPropertyBlock(MatBlock);
+        }
+
+        public override void ApplySkeleton(Transform[] bones)
+        {
+            // No-op
+        }
+
+        public override IDisposableBuffer CheckoutMorphTargetBuffer(uint morphCount)
+        {
+            Debug.Assert(_morphWeightsHandle.NativeArr.Length >= morphCount);
+            return _morphWeightsHandle;
+        }
+
+        public override void MorphTargetBufferUpdated(IDisposableBuffer buffer)
+        {
+            if (buffer != _morphWeightsHandle || _meshAnimator == null)
+            {
+                return;
+            }
+
+            _meshAnimator.SetMorphTargetWeights(_morphWeightsHandle.NativeArr);
+        }
+
+        public override bool UpdateJointMatrices(CAPI.ovrAvatar2EntityId entityId, OvrAvatarPrimitive primitive, CAPI.ovrAvatar2PrimitiveRenderInstanceID primitiveInstanceId)
+        {
+            if (_meshAnimator == null || !_jointMatricesArray.IsCreated)
+            {
+                return false;
+            }
+
+            int jointsCount = primitive.joints.Length;
+            UInt32 bufferSize = (UInt32)(OvrComputeBufferPool.JointDataSize * jointsCount);
+
+            IntPtr transformsPtr;
+            unsafe { transformsPtr = (IntPtr)_jointMatricesArray.GetUnsafePtr(); }
+
+            Profiler.BeginSample("GetSkinTransforms");
+            const bool interLeaveNormalMatrices = true;
+            var result =
+                CAPI.ovrAvatar2Render_GetSkinTransforms(entityId, primitiveInstanceId, transformsPtr, bufferSize, interLeaveNormalMatrices);
+            Profiler.EndSample();
+
+            if (result == CAPI.ovrAvatar2Result.Success)
+            {
+                _meshAnimator.SetJointMatrices(_jointMatricesArray);
+            }
+            else
+            {
+                Debug.LogError($"[OvrAvatarComputeSkinnedRenderable] Error: GetSkinTransforms ({primitive}) {result}");
+            }
+
+            return true;
+        }
+
+        private void AddGpuSkinningObjects(OvrAvatarPrimitive primitive)
+        {
+            // For now, just create source textures at runtime
+            // TODO*: The texture creation should really be part of pipeline
+            // and part of the input files from SDK and should be handled via
+            // native plugin, but, for now, create via C#
+            if (MyMesh)
+            {
+                var gpuSkinningConfig = GpuSkinningConfiguration.Instance;
+
+                int numMorphTargets = (int)primitive.morphTargetCount;
+                int numJoints = primitive.joints.Length;
+
+                // Before we begin, check to see if we already have a skinner/morph target system set up:
+                Debug.Assert(_meshAnimator == null,
+                    "Only one compute animator system can be created for Renderable.");
+                Debug.Assert(primitive.computePrimitive != null);
+
+                _meshAnimator = new OvrComputeMeshAnimator(
+                    null, // TODO*: Specify appropriate compute shader
+                    (int)primitive.meshVertexCount,
+                    numMorphTargets,
+                    numJoints,
+                    primitive.computePrimitive,
+                    HasTangents,
+                    MeshAnimatorOutputFrames);
+                _meshAnimator.SkinningQuality = SkinningQuality;
+            } // if has mesh
+        }
+
+        internal static SkinningOutputFrame GetNextOutputFrame(SkinningOutputFrame current, OvrComputeMeshAnimator.MaxOutputFrames maxFrames)
+        {
+            return (SkinningOutputFrame)(((int)current + 1) % (int)maxFrames);
+        }
+
+        private class BufferHandle<T> : IDisposableBuffer where T : struct
+        {
+            private NativeArray<T> _nativeArr;
+            public NativeArray<T> NativeArr => _nativeArr;
+
+            public IntPtr BufferPtr
+            {
+                get
+                {
+                    IntPtr bufferPtr = IntPtr.Zero;
+                    if (_nativeArr.IsCreated)
+                    {
+                        unsafe { bufferPtr = (IntPtr)_nativeArr.GetUnsafePtr(); }
+                    }
+                    return bufferPtr;
+                }
+            }
+
+            public void Dispose()
+            {
+                _nativeArr.Reset();
+            }
+
+            public void Resize(int newCount)
+            {
+                _nativeArr.Reset();
+                _nativeArr = new NativeArray<T>(newCount, Allocator.Persistent);
+            }
+        }
+
+        // Really only 1 native array across all avatars is needed for these
+        // since their data is only temporary as it should be immediately uploaded to GPU
+        private readonly BufferHandle<float> _morphWeightsHandle = new BufferHandle<float>();
+        private NativeArray<Matrix4x4> _jointMatricesArray;
+
+#if UNITY_EDITOR
+        void OnDrawGizmosSelected()
+        {
+            Gizmos.color = Color.magenta;
+            if (MyMeshFilter != null)
+            {
+                Mesh m = MyMeshFilter.sharedMesh;
+                if (m != null)
+                {
+                    Gizmos.matrix = MyMeshFilter.transform.localToWorldMatrix;
+                    Gizmos.DrawWireCube(m.bounds.center, m.bounds.size);
+                }
+            }
+        }
+
+        protected void OnValidate()
+        {
+            UpdateSkinningQuality();
+        }
+#endif
+
+        protected readonly struct PropertyIds
+        {
+            public readonly int InterpolateAttributesPropId;
+            public readonly int PositionOutputBufferPropId;
+            public readonly int FrenetOutputBufferPropId;
+            public readonly int PositionScalePropId;
+            public readonly int PositionBiasPropId;
+            public readonly int PositionEncodingPrecisionPropId;
+            public readonly int AttributeLerpValuePropId;
+            public readonly int AttributeOutputLatestAnimFrameEntryOffset;
+            public readonly int AttributeOutputPrevAnimFrameEntryOffset;
+            public readonly int AttributeOutputPrevRenderFrameLatestAnimFrameOffset;
+            public readonly int AttributeOutputPrevRenderFramePrevAnimFrameOffset;
+            public readonly int AttributePrevRenderFrameLerpValuePropId;
+
+            // This will be 0 if default initialized, otherwise they are guaranteed unique
+            public bool IsValid => InterpolateAttributesPropId != PositionOutputBufferPropId;
+
+            public enum InitMethod { PropertyToId }
+            public PropertyIds(InitMethod initMethod)
+            {
+                InterpolateAttributesPropId = Shader.PropertyToID("_OvrInterpolateAttributes");
+                PositionOutputBufferPropId = Shader.PropertyToID("_OvrPositionBuffer");
+                FrenetOutputBufferPropId = Shader.PropertyToID("_OvrFrenetBuffer");
+                PositionScalePropId = Shader.PropertyToID("_OvrPositionScale");
+                PositionBiasPropId = Shader.PropertyToID("_OvrPositionBias");
+                PositionEncodingPrecisionPropId = Shader.PropertyToID("_OvrPositionEncodingPrecision");
+                AttributeLerpValuePropId = Shader.PropertyToID("_OvrAttributeInterpolationValue");
+                AttributeOutputLatestAnimFrameEntryOffset = Shader.PropertyToID("_OvrAttributeOutputLatestAnimFrameEntryOffset");
+                AttributeOutputPrevAnimFrameEntryOffset = Shader.PropertyToID("_OvrAttributeOutputPrevAnimFrameEntryOffset");
+                AttributeOutputPrevRenderFrameLatestAnimFrameOffset = Shader.PropertyToID("_OvrAttributeOutputPrevRenderFrameLatestAnimFrameOffset");
+                AttributeOutputPrevRenderFramePrevAnimFrameOffset = Shader.PropertyToID("_OvrAttributeOutputPrevRenderFramePrevAnimFrameOffset");
+                AttributePrevRenderFrameLerpValuePropId = Shader.PropertyToID("_OvrPrevRenderFrameInterpolationValue");
+            }
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarComputeSkinnedRenderableBase.cs.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarComputeSkinnedRenderableBase.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..5aede621f9ae784933714cae1342743f6df572a6
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarComputeSkinnedRenderableBase.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 955913325a3305b499317f6ef0c23de6
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarGpuInterpolatedSkinnedMvRenderable.cs b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarGpuInterpolatedSkinnedMvRenderable.cs
new file mode 100644
index 0000000000000000000000000000000000000000..a8cbe90939b480d8155e62f3d9958983f8701951
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarGpuInterpolatedSkinnedMvRenderable.cs
@@ -0,0 +1,233 @@
+using Oculus.Avatar2;
+using UnityEngine;
+
+/// @file OvrAvatarGpuInterpolatedSkinningRenderable
+
+namespace Oculus.Skinning.GpuSkinning
+{
+    /**
+     * Component that encapsulates the meshes of a skinned avatar.
+     * This component implements skinning using the Avatar SDK
+     * and uses the GPU. It performs skinning on every avatar
+     * but not at every frame. Instead, it interpolates between
+     * frames, reducing the performance overhead of skinning
+     * when there are lots of avatars. It is used when the skinning configuration
+     * is set to SkinningConfig.OVR_UNITY_GPU_FULL, motion smoothing
+     * is enabled, and "App Space Warp" is enabled in the GPU skinning configuration.
+     *
+     * @see OvrAvatarSkinnedRenderable
+     * @see OvrAvatarGpuSkinnedRenderable
+     * @see OvrGpuSkinningConfiguration.MotionSmoothing
+     */
+    public class OvrAvatarGpuInterpolatedSkinnedMvRenderable : OvrAvatarGpuSkinnedRenderableBase
+    {
+        // Number of animation frames required to be considered "completely valid"
+        private const int NUM_ANIM_FRAMES_NEEDED_FOR_CURRENT_RENDER = 2;
+
+        public IInterpolationValueProvider InterpolationValueProvider { get; internal set; }
+
+        private CAPI.ovrAvatar2Transform _skinningOriginFrameZero;
+        private CAPI.ovrAvatar2Transform _skinningOriginFrameOne;
+
+        private int _renderFrameF0;
+        private float _renderFrameLerpVal;
+        private int _prevRenderFrameF0;
+        private float _prevRenderFrameLerpVal;
+
+        // 2 "output depth texels" per "atlas packer" slice to interpolate between
+        // and enable bilinear filtering to have hardware to the interpolation
+        // between depth texels for us
+        protected override string LogScope => "OvrAvatarGpuInterpolatedSkinnedMvRenderable";
+        protected override FilterMode SkinnerOutputFilterMode => FilterMode.Point;
+
+        // 4 slices per "block" 2 for animation frames needed for the "current render frame"
+        // and 2 slices needed for animation frames for the "previous render frame"
+        protected override int SkinnerOutputDepthTexelsPerSlice => 4;
+        protected override bool InterpolateAttributes => true;
+
+        private int _numValidAnimationFrames;
+        private bool _hasValidPreviousRenderFrame;
+
+        protected override void Awake()
+        {
+            base.Awake();
+            CopyMaterial(); // Probably not necessary due to material property block usage, but just to be safe
+        }
+
+        protected virtual void OnEnable()
+        {
+            // No animation data yet since object just enabled (becoming visible)
+            _renderFrameLerpVal = 0.0f;
+            _prevRenderFrameLerpVal = 0.0f;
+            _renderFrameF0 = 0;
+            _prevRenderFrameF0 = 0;
+            _hasValidPreviousRenderFrame = false;
+        }
+
+        protected override void Dispose(bool isDisposing)
+        {
+            InterpolationValueProvider = null;
+
+            base.Dispose(isDisposing);
+        }
+
+        public override void UpdateSkinningOrigin(in CAPI.ovrAvatar2Transform skinningOrigin)
+        {
+            // Replace base implementation
+            switch (SkinnerWriteDestination)
+            {
+                case SkinningOutputFrame.FrameZero:
+                    _skinningOriginFrameZero = skinningOrigin;
+                    break;
+                case SkinningOutputFrame.FrameOne:
+                    _skinningOriginFrameOne = skinningOrigin;
+                    break;
+            }
+        }
+
+        protected override void OnAnimationEnabledChanged(bool isNowEnabled)
+        {
+            if (isNowEnabled)
+            {
+                // Reset valid frame counter
+                _numValidAnimationFrames = 0;
+                SkinnerWriteDestination = SkinningOutputFrame.FrameOne;
+            }
+        }
+
+        internal override void AnimationFrameUpdate()
+        {
+            // Replaces logic in base class
+
+            // ASSUMPTION: This call will always follow calls to update morphs and/or skinning.
+            // With that assumption, new data will be written by the morph target combiner and/or skinner, so there
+            // will be valid data at end of frame.
+            SwapWriteDestination();
+
+            // Copy "previous 2 animation frame's" worth of data to slices 3 and 4
+            // and manipulate some "render frame" internal fields to reflect that
+            var outputTexture = GetOutputTexture();
+            Debug.Assert(outputTexture);
+            const int mipLevel = 0;
+
+            int srcSlice = (int)SkinnerLayoutSlice + 0;
+            int dstSlice = (int)SkinnerLayoutSlice + 2;
+            Graphics.CopyTexture(
+                outputTexture,
+                srcSlice,
+                mipLevel,
+                SkinnerLayout.x,
+                SkinnerLayout.y,
+                SkinnerLayout.width,
+                SkinnerLayout.height,
+                outputTexture,
+                dstSlice,
+                mipLevel,
+                SkinnerLayout.x,
+                SkinnerLayout.y);
+
+            srcSlice = (int)SkinnerLayoutSlice + 1;
+            dstSlice = (int)SkinnerLayoutSlice + 3;
+            Graphics.CopyTexture(
+                outputTexture,
+                srcSlice,
+                mipLevel,
+                SkinnerLayout.x,
+                SkinnerLayout.y,
+                SkinnerLayout.width,
+                SkinnerLayout.height,
+                outputTexture,
+                dstSlice,
+                mipLevel,
+                SkinnerLayout.x,
+                SkinnerLayout.y);
+
+            _renderFrameF0 = 2;
+            bool wasAnimDataCompletedValid = IsAnimationDataCompletelyValid;
+            if (_numValidAnimationFrames < NUM_ANIM_FRAMES_NEEDED_FOR_CURRENT_RENDER)
+            {
+              _numValidAnimationFrames++;
+            }
+            if (!wasAnimDataCompletedValid && IsAnimationDataCompletelyValid)
+            {
+              OnAnimationDataCompleted();
+            }
+        }
+
+        internal override void RenderFrameUpdate()
+        {
+            Debug.Assert(InterpolationValueProvider != null);
+
+            float lerpValue = InterpolationValueProvider.GetRenderInterpolationValue();
+
+            // Guard against frame interpolation value set, but insufficient animation frames available
+            // by "slamming" value to 1.0 (take the latest animation data available)
+            // Should hopefully not happen frequently/at all if caller manages state well (maybe on first enabling)
+            if (_numValidAnimationFrames < NUM_ANIM_FRAMES_NEEDED_FOR_CURRENT_RENDER)
+            {
+                lerpValue = 1.0f;
+            }
+
+            if (ShouldInverseInterpolationValue)
+            {
+                // Convert from the 0 -> 1 interpolation value to one that "ping pongs" between
+                // the slices here so that an additional GPU copy isn't needed to
+                // transfer from "slice 1" to "slice 0"
+                lerpValue = 1.0f - lerpValue;
+            }
+
+            _prevRenderFrameF0 = _renderFrameF0;
+            _prevRenderFrameLerpVal = _renderFrameLerpVal;
+
+            _renderFrameF0 = 0;
+            _renderFrameLerpVal = lerpValue;
+
+            if (!_hasValidPreviousRenderFrame)
+            {
+                // Slam "previous" values to be current values
+                _prevRenderFrameF0 = _renderFrameF0;
+                _prevRenderFrameLerpVal = _renderFrameLerpVal;
+                _hasValidPreviousRenderFrame = true;
+            }
+
+            InterpolateSkinningOrigin(lerpValue);
+            SetAnimationInterpolationValueInMaterial(lerpValue);
+        }
+
+        // Since animation frames are updated slower or at same rate as render frames, having
+        // more than 2 animation frames implies having more than 2 render frames, so validity
+        // can just be based on animation frames
+        internal override bool IsAnimationDataCompletelyValid => _numValidAnimationFrames >= NUM_ANIM_FRAMES_NEEDED_FOR_CURRENT_RENDER;
+
+        private void SetAnimationInterpolationValueInMaterial(float lerpValue)
+        {
+            // Update the depth texel value to interpolate between skinning output slices
+            rendererComponent.GetPropertyBlock(MatBlock);
+
+            MatBlock.SetFloat(U_ATTRIBUTE_TEXEL_SLICE_PROP_ID, SkinnerLayoutSlice + lerpValue);
+            MatBlock.SetFloat(U_PREV_POSITION_TEXEL_SLICE_PROP_ID, SkinnerLayoutSlice + _prevRenderFrameF0 + _prevRenderFrameLerpVal);
+            rendererComponent.SetPropertyBlock(MatBlock);
+        }
+
+        private void InterpolateSkinningOrigin(float lerpValue)
+        {
+            // Update the "skinning origin" via lerp/slerp.
+            // NOTE: This feels dirty as we are converting from `OvrAvatar2Vector3f/Quat` to Unity
+            // versions just to do the lerp/slerp. Unnecessary conversions
+            transform.localPosition = Vector3.Lerp(
+                _skinningOriginFrameZero.position,
+                _skinningOriginFrameOne.position,
+                lerpValue);
+            transform.localRotation = Quaternion.Slerp(
+                _skinningOriginFrameZero.orientation,
+                _skinningOriginFrameOne.orientation,
+                lerpValue);
+            transform.localScale = Vector3.Lerp(
+                _skinningOriginFrameZero.scale,
+                _skinningOriginFrameOne.scale,
+                lerpValue);
+        }
+
+        private bool ShouldInverseInterpolationValue => SkinnerWriteDestination == SkinningOutputFrame.FrameZero;
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarGpuInterpolatedSkinnedMvRenderable.cs.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarGpuInterpolatedSkinnedMvRenderable.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..9a0407b8d3dff5a04ed11cc487fe1422e9b23d6f
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarGpuInterpolatedSkinnedMvRenderable.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: c5f57bf377d38ba49b05857d450b5f94
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarGpuInterpolatedSkinnedRenderable.cs b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarGpuInterpolatedSkinnedRenderable.cs
new file mode 100644
index 0000000000000000000000000000000000000000..fcb2c00ce9ec246074b13254eee6f02777a0d834
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarGpuInterpolatedSkinnedRenderable.cs
@@ -0,0 +1,153 @@
+using Oculus.Avatar2;
+using UnityEngine;
+
+/// @file OvrAvatarGpuInterpolatedSkinningRenderable
+
+namespace Oculus.Skinning.GpuSkinning
+{
+    /**
+     * Component that encapsulates the meshes of a skinned avatar.
+     * This component implements skinning using the Avatar SDK
+     * and uses the GPU. It performs skinning on every avatar
+     * but not at every frame. Instead, it interpolates between
+     * frames, reducing the performance overhead of skinning
+     * when there are lots of avatars. It is used when the skinning configuration
+     * is set to SkinningConfig.OVR_UNITY_GPU_FULL and motion smoothing
+     * is enabled in the GPU skinning configuration.
+     *
+     * @see OvrAvatarSkinnedRenderable
+     * @see OvrAvatarGpuSkinnedRenderable
+     * @see OvrGpuSkinningConfiguration.MotionSmoothing
+     */
+    public class OvrAvatarGpuInterpolatedSkinnedRenderable : OvrAvatarGpuSkinnedRenderableBase
+    {
+        // Number of animation frames required to be considered "completely valid"
+        private const int NUM_ANIM_FRAMES_NEEDED_FOR_CURRENT_RENDER = 2;
+
+        public IInterpolationValueProvider InterpolationValueProvider { get; internal set; }
+
+        private CAPI.ovrAvatar2Transform _skinningOriginFrameZero;
+        private CAPI.ovrAvatar2Transform _skinningOriginFrameOne;
+
+        // 2 "output depth texels" per "atlas packer" slice to interpolate between
+        // and enable bilinear filtering to have hardware to the interpolation
+        // between depth texels for us
+        protected override string LogScope => "OvrAvatarGpuInterpolatedSkinnedRenderable";
+        protected override FilterMode SkinnerOutputFilterMode => FilterMode.Point;
+        protected override int SkinnerOutputDepthTexelsPerSlice => 2;
+        protected override bool InterpolateAttributes => true;
+
+        private int _numValidAnimationFrames;
+
+        protected override void Dispose(bool isDisposing)
+        {
+            InterpolationValueProvider = null;
+
+            base.Dispose(isDisposing);
+        }
+
+        public override void UpdateSkinningOrigin(in CAPI.ovrAvatar2Transform skinningOrigin)
+        {
+            // Replace base implementation
+            switch (SkinnerWriteDestination)
+            {
+                case SkinningOutputFrame.FrameZero:
+                    _skinningOriginFrameZero = skinningOrigin;
+                    break;
+                case SkinningOutputFrame.FrameOne:
+                    _skinningOriginFrameOne = skinningOrigin;
+                    break;
+            }
+        }
+
+        protected override void OnAnimationEnabledChanged(bool isNowEnabled)
+        {
+            if (isNowEnabled)
+            {
+                // Reset valid frame counter on re-enabling animation
+                _numValidAnimationFrames = 0;
+                SkinnerWriteDestination = SkinningOutputFrame.FrameOne;
+            }
+        }
+
+        internal override void AnimationFrameUpdate()
+        {
+            // Replaces logic in base class
+
+            // ASSUMPTION: This call will always follow calls to update morphs and/or skinning.
+            // With that assumption, new data will be written by the morph target combiner and/or skinner, so there
+            // will be valid data at end of frame.
+            SwapWriteDestination();
+
+            bool wasAnimDataCompletedValid = IsAnimationDataCompletelyValid;
+
+            if (_numValidAnimationFrames < NUM_ANIM_FRAMES_NEEDED_FOR_CURRENT_RENDER)
+            {
+                _numValidAnimationFrames++;
+            }
+
+            if (!wasAnimDataCompletedValid && IsAnimationDataCompletelyValid)
+            {
+                OnAnimationDataCompleted();
+            }
+        }
+
+        internal override void RenderFrameUpdate()
+        {
+            Debug.Assert(InterpolationValueProvider != null);
+
+            float lerpValue = InterpolationValueProvider.GetRenderInterpolationValue();
+
+            // Guard against insufficient animation frames available
+            // by "slamming" value to be 1.0 ("the newest value").
+            // Should hopefully not happen frequently/at all if caller manages state well (maybe on first enabling)
+            if (_numValidAnimationFrames < NUM_ANIM_FRAMES_NEEDED_FOR_CURRENT_RENDER)
+            {
+                lerpValue = 1.0f;
+            }
+
+            if (ShouldInverseInterpolationValue)
+            {
+                // Convert from the 0 -> 1 interpolation value to one that "ping pongs" between
+                // the slices here so that an additional GPU copy isn't needed to
+                // transfer from "slice 1" to "slice 0"
+                lerpValue = 1.0f - lerpValue;
+            }
+
+            InterpolateSkinningOrigin(lerpValue);
+            SetAnimationInterpolationValueInMaterial(lerpValue);
+        }
+
+        internal override bool IsAnimationDataCompletelyValid => _numValidAnimationFrames >= NUM_ANIM_FRAMES_NEEDED_FOR_CURRENT_RENDER;
+
+        private void SetAnimationInterpolationValueInMaterial(float lerpValue)
+        {
+            // Update the depth texel value to interpolate between skinning output slices
+            rendererComponent.GetPropertyBlock(MatBlock);
+
+            MatBlock.SetFloat(U_ATTRIBUTE_TEXEL_SLICE_PROP_ID, SkinnerLayoutSlice + lerpValue);
+            rendererComponent.SetPropertyBlock(MatBlock);
+        }
+
+        private void InterpolateSkinningOrigin(float lerpValue)
+        {
+            // Update the "skinning origin" via lerp/slerp.
+            // NOTE: This feels dirty as we are converting from `OvrAvatar2Vector3f/Quat` to Unity
+            // versions just to do the lerp/slerp. Unnecessary conversions
+            transform.localPosition = Vector3.Lerp(
+                _skinningOriginFrameZero.position,
+                _skinningOriginFrameOne.position,
+                lerpValue);
+            transform.localRotation = Quaternion.Slerp(
+                _skinningOriginFrameZero.orientation,
+                _skinningOriginFrameOne.orientation,
+                lerpValue);
+            transform.localScale = Vector3.Lerp(
+                _skinningOriginFrameZero.scale,
+                _skinningOriginFrameOne.scale,
+                lerpValue);
+        }
+
+        private bool ShouldInverseInterpolationValue => SkinnerWriteDestination == SkinningOutputFrame.FrameZero;
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarGpuInterpolatedSkinnedRenderable.cs.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarGpuInterpolatedSkinnedRenderable.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..45bd359307966602939370db93ff2e8d131806bb
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarGpuInterpolatedSkinnedRenderable.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 34cec81a7eed3c64486f51886ea6e5ac
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarGpuSkinnedMvRenderable.cs b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarGpuSkinnedMvRenderable.cs
new file mode 100644
index 0000000000000000000000000000000000000000..e0da869243b8692d116ccf94325aceae50c2804d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarGpuSkinnedMvRenderable.cs
@@ -0,0 +1,112 @@
+/// @file OvrAvatarGpuSkinnedRenderable.cs
+
+using System;
+using Oculus.Avatar2;
+using UnityEngine;
+
+namespace Oculus.Skinning.GpuSkinning
+{
+    /**
+     * Component that encapsulates the meshes of a skinned avatar.
+     * This component implements skinning using the Avatar SDK
+     * and uses the GPU. It performs skinning on every avatar
+     * at each frame. It is used when the skinning configuration
+     * is set to SkinningConfig.OVR_UNITY_GPU_FULL, motion smoothing
+     * is *not* enabled, and "App Space Warp" is enabled in the GPU skinning configuration.
+     *
+     * @see OvrAvatarSkinnedRenderable
+     * @see OvrAvatarGpuSkinnedRenderable
+     * @see OvrGpuSkinningConfiguration.MotionSmoothing
+     * @see OvrAvatarGpuInterpolatedSkinnedRenderable
+     */
+    public class OvrAvatarGpuSkinnedMvRenderable : OvrAvatarGpuSkinnedRenderableBase
+    {
+        protected override string LogScope => "OvrAvatarGpuSkinnedMvRenderable";
+
+        // Only 1 "animation frame" is required for this renderable
+        private bool _isAnimationFrameDataValid;
+        private bool _hasValidPreviousRenderFrame;
+
+        private SkinningOutputFrame _prevRenderFrameSlice = SkinningOutputFrame.FrameZero;
+
+        // 2 "output depth texels" per "atlas packer" slice to have a current and previous animation frame
+        protected override int SkinnerOutputDepthTexelsPerSlice => 2;
+
+        protected override void Awake()
+        {
+            base.Awake();
+            CopyMaterial(); // Probably not necessary since this class uses material property blocks, but, just to be safe
+        }
+
+        protected override void OnAnimationEnabledChanged(bool isNowEnabled)
+        {
+            if (isNowEnabled)
+            {
+                _isAnimationFrameDataValid = false;
+                SkinnerWriteDestination = SkinningOutputFrame.FrameOne;
+            }
+        }
+
+        protected virtual void OnEnable()
+        {
+            // Reset the previous render frame slice and render frame count
+            _hasValidPreviousRenderFrame = false;
+            _prevRenderFrameSlice = SkinnerWriteDestination;
+        }
+
+        internal override void AnimationFrameUpdate()
+        {
+            // ASSUMPTION: This call will always be followed by calls to update morphs and/or skinning.
+            // With that assumption, new data will be written by the morph target combiner and/or skinner, so there
+            // will be valid data at end of frame.
+            _isAnimationFrameDataValid = true;
+
+            OnAnimationDataCompleted();
+            SwapWriteDestination();
+        }
+
+        internal override void RenderFrameUpdate()
+        {
+            // Need at least 1 "previous render frame" to have a previous
+            // render frame slice
+            if (!_hasValidPreviousRenderFrame)
+            {
+                // Not enough render frames, just make the motion vectors "previous frame" the same
+                // as the current one
+                _prevRenderFrameSlice = SkinnerWriteDestination;
+                _hasValidPreviousRenderFrame = true;
+            }
+
+            SetRenderFrameTextureSlices();
+
+            // Update "previous frame" value for next frame
+            _prevRenderFrameSlice = SkinnerWriteDestination;
+        }
+
+        internal override bool IsAnimationDataCompletelyValid => _isAnimationFrameDataValid;
+
+        private void SetRenderFrameTextureSlices()
+        {
+            // Update the depth texel value to interpolate between skinning output slices
+            rendererComponent.GetPropertyBlock(MatBlock);
+
+            MatBlock.SetFloat(U_ATTRIBUTE_TEXEL_SLICE_PROP_ID, SkinnerLayoutSlice + GetSliceValue(SkinnerWriteDestination));
+            MatBlock.SetFloat(U_PREV_POSITION_TEXEL_SLICE_PROP_ID, SkinnerLayoutSlice + GetSliceValue(_prevRenderFrameSlice));
+
+            rendererComponent.SetPropertyBlock(MatBlock);
+        }
+
+        private float GetSliceValue(SkinningOutputFrame outputFrame)
+        {
+            switch (outputFrame)
+            {
+                case SkinningOutputFrame.FrameZero:
+                    return 0.0f;
+                case SkinningOutputFrame.FrameOne:
+                    return 1.0f;
+            }
+
+            return 0.0f;
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarGpuSkinnedMvRenderable.cs.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarGpuSkinnedMvRenderable.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..9ad0e628ac3c613bbd6e7d8538c8f26ee392b3d9
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarGpuSkinnedMvRenderable.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 40b68737bdddb324f8310b635c1f0117
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarGpuSkinnedRenderable.cs b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarGpuSkinnedRenderable.cs
new file mode 100644
index 0000000000000000000000000000000000000000..db3072c14ca578f5f8552451d39ff43d0dfbf970
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarGpuSkinnedRenderable.cs
@@ -0,0 +1,66 @@
+using System;
+using System.Runtime.InteropServices;
+
+using Oculus.Avatar2;
+
+using Unity.Collections;
+using Unity.Collections.LowLevel.Unsafe;
+
+using UnityEngine;
+using UnityEngine.Experimental.Rendering;
+using UnityEngine.Profiling;
+using UnityEngine.Rendering;
+
+/// @file OvrAvatarGpuSkinnedRenderable.cs
+
+namespace Oculus.Skinning.GpuSkinning
+{
+    /**
+     * Component that encapsulates the meshes of a skinned avatar.
+     * This component implements skinning using the Avatar SDK
+     * and uses the GPU. It performs skinning on every avatar
+     * at each frame. It is used when the skinning configuration
+     * is set to SkinningConfig.OVR_UNITY_GPU_FULL and motion smoothing
+     * is *not* enabled in the GPU skinning configuration.
+     *
+     * @see OvrAvatarSkinnedRenderable
+     * @see OvrAvatarGpuSkinnedRenderable
+     * @see OvrGpuSkinningConfiguration.MotionSmoothing
+     * @see OvrAvatarGpuInterpolatedSkinnedRenderable
+     */
+    public class OvrAvatarGpuSkinnedRenderable : OvrAvatarGpuSkinnedRenderableBase
+    {
+        protected override string LogScope => "OvrAvatarGpuSkinnedRenderable";
+
+        // Only 1 "animation frame" is required for this renderable, so a boolean is sufficient
+        // to serve as "is valid
+        private bool _isAnimationDataCompletelyValid;
+
+        protected override void OnAnimationEnabledChanged(bool isNowEnabled)
+        {
+            if (isNowEnabled)
+            {
+                _isAnimationDataCompletelyValid = false;
+            }
+        }
+
+        internal override void AnimationFrameUpdate()
+        {
+            // Mark this block as enabled and note that there will be
+            // valid data this frame
+
+            // ASSUMPTION: This call will always be followed by calls to update morphs and/or skinning.
+            // With that assumption, new data will be written by the morph target combiner and/or skinner, so there
+            // will be valid data at end of frame.
+            _isAnimationDataCompletelyValid = true;
+            OnAnimationDataCompleted();
+        }
+
+        internal override void RenderFrameUpdate()
+        {
+            // Intentionally empty
+        }
+
+        internal override bool IsAnimationDataCompletelyValid => _isAnimationDataCompletelyValid;
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarGpuSkinnedRenderable.cs.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarGpuSkinnedRenderable.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..912729c8142d5aaf199de412c440765c789ca7fd
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarGpuSkinnedRenderable.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: e549d0026aeabd14e8cd723d44e19e16
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarGpuSkinnedRenderableBase.cs b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarGpuSkinnedRenderableBase.cs
new file mode 100644
index 0000000000000000000000000000000000000000..f04f9bf18e845f7c4af30790df8a208cff0bbfa3
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarGpuSkinnedRenderableBase.cs
@@ -0,0 +1,825 @@
+using System;
+using System.Runtime.InteropServices;
+
+using Oculus.Avatar2;
+
+using Unity.Collections;
+using Unity.Collections.LowLevel.Unsafe;
+
+using UnityEngine;
+using UnityEngine.Experimental.Rendering;
+using UnityEngine.Profiling;
+using UnityEngine.Rendering;
+
+/// @file OvrAvatarGpuSkinnedRenderable.cs
+
+namespace Oculus.Skinning.GpuSkinning
+{
+    /**
+     * Component that encapsulates the meshes of a skinned avatar.
+     * This component implements skinning using the Avatar SDK
+     * and uses the GPU. It performs skinning on every avatar
+     * at each frame. It is used when the skinning configuration
+     * is set to SkinningConfig.OVR_UNITY_GPU_FULL and motion smoothing
+     * is *not* enabled in the GPU skinning configuration.
+     *
+     * @see OvrAvatarSkinnedRenderable
+     * @see OvrAvatarGpuSkinnedRenderable
+     * @see OvrGpuSkinningConfiguration.MotionSmoothing
+     * @see OvrAvatarGpuInterpolatedSkinnedRenderable
+     */
+    public abstract class OvrAvatarGpuSkinnedRenderableBase : OvrAvatarSkinnedRenderable
+    {
+        protected abstract string LogScope { get; }
+
+        private static AttributePropertyIds _propertyIds = default;
+        private static void CheckPropertyIdInit()
+        {
+            if (!_propertyIds.IsValid)
+            {
+                _propertyIds = new AttributePropertyIds(AttributePropertyIds.InitMethod.PropertyToId);
+            }
+        }
+
+        protected RectInt SkinnerLayout { get; private set; }
+        protected float SkinnerLayoutSlice { get; private set; }
+
+        // TODO: SkinnerOutputFilterMode and SkinnerOutputDepthTexelsPerSlice should likely just be member variables?
+        protected virtual FilterMode SkinnerOutputFilterMode => FilterMode.Point;
+        protected virtual int SkinnerOutputDepthTexelsPerSlice => 1;
+
+        protected override VertexFetchMode VertexFetchType => VertexFetchMode.ExternalTextures;
+
+        private protected SkinningOutputFrame SkinnerWriteDestination { get; set; } =
+            SkinningOutputFrame.FrameZero;
+
+        private DummySkinningBufferPropertySetter _dummyBufferSetter;
+
+        /// Specifies the skinning quality (many bones per vertex).
+        public OvrSkinningTypes.SkinningQuality SkinningQuality
+        {
+            get => _skinningQuality;
+            set
+            {
+                if (_skinningQuality != value)
+                {
+                    _skinningQuality = value;
+                    UpdateSkinningQuality();
+                }
+            }
+        }
+
+        private readonly BufferHandle _bufferHandle = new BufferHandle();
+
+        // This is technically configurable, but mostly just for debugging
+        [SerializeField]
+        [Tooltip("Configuration to override SkinningQuality, otherwise indicates which Quality was selected for this LOD")]
+        private OvrSkinningTypes.SkinningQuality _skinningQuality = OvrSkinningTypes.SkinningQuality.Invalid;
+
+        protected override void Awake()
+        {
+            CheckPropertyIdInit();
+
+            _dummyBufferSetter = new DummySkinningBufferPropertySetter();
+
+            base.Awake();
+        }
+
+        protected override void Dispose(bool isDisposing)
+        {
+            if (isDisposing)
+            {
+                DestroyGpuSkinningObjects();
+            }
+
+            _bufferHandle.Dispose();
+            _dummyBufferSetter?.Dispose();
+
+            base.Dispose(isDisposing);
+        }
+
+        protected void SwapWriteDestination()
+        {
+            Debug.Assert(SkinnerWriteDestination == SkinningOutputFrame.FrameZero ||
+                         SkinnerWriteDestination == SkinningOutputFrame.FrameOne);
+            // Ping pongs between two frames (max that GPU skinners support)
+            SkinningOutputFrame nextDest = SkinningOutputFrame.FrameZero;
+            switch (SkinnerWriteDestination)
+            {
+                case SkinningOutputFrame.FrameZero:
+                    nextDest = SkinningOutputFrame.FrameOne;
+                    break;
+
+                case SkinningOutputFrame.FrameOne:
+                    nextDest = SkinningOutputFrame.FrameZero;
+                    break;
+                default:
+                    OvrAvatarLog.LogWarning("Unexpected skinner write destination", LogScope, this);
+                    nextDest = SkinningOutputFrame.FrameZero;
+                    break;
+            }
+
+            // Update the skinning write destination
+            SkinnerWriteDestination = nextDest;
+        }
+
+        private void DestroyGpuSkinningObjects()
+        {
+            _gpuCombiner?.Destroy();
+            _gpuCombiner = null;
+            _indirectionTex?.Destroy();
+            _indirectionTex = null;
+            _skinner?.Destroy();
+            _skinner = null;
+        }
+
+        private void UpdateSkinningQuality()
+        {
+            if (_skinner is IOvrGpuJointSkinner jointSkinner)
+            {
+                jointSkinner.Quality = _skinningQuality;
+            }
+        }
+
+        protected internal override void ApplyMeshPrimitive(OvrAvatarPrimitive primitive)
+        {
+            // The base call adds a mesh filter already and material
+            base.ApplyMeshPrimitive(primitive);
+
+            try
+            {
+                if (_skinningQuality == OvrSkinningTypes.SkinningQuality.Invalid)
+                {
+                    _skinningQuality = GpuSkinningConfiguration.Instance.GetQualityForLOD(primitive.HighestQualityLODIndex);
+                }
+
+                AddGpuSkinningObjects(primitive);
+                ActivateGpuSkinningInMaterial();
+                ApplyGpuSkinningMaterial();
+            }
+            catch (Exception e)
+            {
+                OvrAvatarLog.LogError($"Exception applying primitive ({primitive}) - {e}", LogScope, this);
+            }
+        }
+
+        private void ActivateGpuSkinningInMaterial()
+        {
+            CopyMaterial();
+
+            var renderMat = rendererComponent.sharedMaterial;
+            if (_skinner?.GetOutputTexGraphicFormat() == GraphicsFormat.R16G16B16A16_UNorm)
+            {
+                renderMat.EnableKeyword(OVR_VERTEX_FETCH_TEXTURE_UNORM_KEYWORD);
+                renderMat.DisableKeyword(OVR_VERTEX_FETCH_TEXTURE_KEYWORD);
+            }
+            else
+            {
+                renderMat.DisableKeyword(OVR_VERTEX_FETCH_TEXTURE_UNORM_KEYWORD);
+                renderMat.EnableKeyword(OVR_VERTEX_FETCH_TEXTURE_KEYWORD);
+            }
+        }
+
+        private void ApplyGpuSkinningMaterial()
+        {
+            rendererComponent.GetPropertyBlock(MatBlock);
+
+            Texture outputTexture = null;
+            CAPI.ovrTextureLayoutResult layout = new CAPI.ovrTextureLayoutResult();
+
+            if (_skinner != null)
+            {
+                outputTexture = _skinner.GetOutputTex();
+                layout = _skinner.GetLayoutInOutputTex(_handleInSkinner);
+            }
+
+            MatBlock.SetTexture(U_ATTRIBUTE_TEXTURE_PROP_ID, outputTexture);
+
+            if (_skinner?.GetOutputTexGraphicFormat() == GraphicsFormat.R16G16B16A16_UNorm)
+            {
+                var scale = GpuSkinningConfiguration.Instance.SkinnerUnormScale;
+                var scaleBias = new Vector2(2.0f * scale, -scale);
+                MatBlock.SetVector(U_ATTRIBUTE_SCALE_BIAS_PROP_ID, scaleBias);
+            }
+            else
+            {
+                MatBlock.SetVector(U_ATTRIBUTE_SCALE_BIAS_PROP_ID, new Vector4(1.0f, 0.0f, 0.0f, 0.0f));
+            }
+
+            MatBlock.SetInt(U_ATTRIBUTE_TEXEL_X_PROP_ID, layout.x);
+            MatBlock.SetInt(U_ATTRIBUTE_TEXEL_Y_PROP_ID, layout.y);
+            MatBlock.SetInt(U_ATTRIBUTE_TEXEL_W_PROP_ID, layout.w);
+            MatBlock.SetInt(U_ATTRIBUTE_TEXEL_H_PROP_ID, layout.h);
+            MatBlock.SetFloat(U_ATTRIBUTE_TEXEL_SLICE_PROP_ID, layout.texSlice);
+
+            SkinnerLayout = new RectInt(layout.x, layout.y, layout.w, layout.h);
+            SkinnerLayoutSlice = layout.texSlice;
+
+            Debug.Assert(outputTexture != null, "No output texture for GPU skinning, avatars may not be able to move.");
+            if (outputTexture != null)
+            {
+                MatBlock.SetFloat(U_ATTRIBUTE_TEX_INV_SIZE_W_PROP_ID, 1.0f / outputTexture.width);
+                MatBlock.SetFloat(U_ATTRIBUTE_TEX_INV_SIZE_H_PROP_ID, 1.0f / outputTexture.height);
+
+                if (outputTexture.dimension == TextureDimension.Tex3D)
+                {
+                    RenderTexture rt = outputTexture as RenderTexture;
+                    if (rt)
+                    {
+                        MatBlock.SetFloat(U_ATTRIBUTE_TEX_INV_SIZE_D_PROP_ID, 1.0f / rt.volumeDepth);
+                    }
+                }
+            }
+
+            _dummyBufferSetter.SetComputeSkinningBuffersInMatBlock(MatBlock);
+
+            rendererComponent.SetPropertyBlock(MatBlock);
+        }
+
+        public override void ApplySkeleton(Transform[] bones)
+        {
+            // No-op
+        }
+
+        public override IDisposableBuffer CheckoutMorphTargetBuffer(uint morphCount)
+        {
+            _bufferHandle.nativeBuffer = _gpuCombiner.GetMorphBuffer(_handleInCombiner);
+            return _bufferHandle;
+        }
+
+        public override void MorphTargetBufferUpdated(IDisposableBuffer buffer)
+        {
+            _gpuCombiner.FinishMorphUpdate(_handleInCombiner);
+        }
+
+        public override bool UpdateJointMatrices(CAPI.ovrAvatar2EntityId entityId, OvrAvatarPrimitive primitive, CAPI.ovrAvatar2PrimitiveRenderInstanceID primitiveInstanceId)
+        {
+            // TODO: Caller should be handling this check
+            if (_skinner == null || !_skinner.HasJoints)
+            {
+                return false;
+            }
+
+            int jointsCount = primitive.joints.Length;
+            UInt32 bufferSize = (UInt32)(OvrComputeBufferPool.JointDataSize * jointsCount);
+
+            IntPtr transformsSlice = _skinner.GetJointTransformMatricesArray(_handleInSkinner);
+
+            if (transformsSlice == null)
+            {
+                return false;
+            }
+
+            Profiler.BeginSample("GetSkinTransforms");
+            var result =
+                CAPI.ovrAvatar2Render_GetSkinTransforms(entityId, primitiveInstanceId, transformsSlice, bufferSize, true);
+            Profiler.EndSample();
+
+            if (result == CAPI.ovrAvatar2Result.Success)
+            {
+                _skinner.EnableBlockToRender(_handleInSkinner, SkinnerWriteDestination);
+            }
+            else
+            {
+                Debug.LogError($"[OvrAvatarEntity] Error: GetSkinTransforms ({primitive}) {result}");
+            }
+
+            return true;
+        }
+
+        protected Texture GetOutputTexture()
+        {
+            return _skinner?.GetOutputTex();
+        }
+
+        private void AddGpuSkinningObjects(OvrAvatarPrimitive primitive)
+        {
+            // For now, just create source textures at runtime
+            // TODO*: The texture creation should really be part of pipeline
+            // and part of the input files from SDK and should be handled via
+            // native plugin, but, for now, create via C#
+            if (HasMesh)
+            {
+                var gpuSkinningConfig = GpuSkinningConfiguration.Instance;
+
+                int numMorphTargets = (int)primitive.morphTargetCount;
+                int numBones = primitive.joints.Length;
+                bool hasJoints = numBones > 0;
+                int numAttributes = HasTangents ? 3 : 2;
+
+                var metaData = primitive.gpuPrimitive.MetaData;
+
+                int numAffectedVerts = (int)metaData.NumMorphTargetAffectedVerts;
+                bool hasMorphTargets = numAffectedVerts > 0;
+
+                OvrSkinningTypes.Handle handleInIndirectionTex = OvrSkinningTypes.Handle.kInvalidHandle;
+                if (hasMorphTargets)
+                {
+                    var morphTextureFormat = gpuSkinningConfig.CombinedMorphFormat;
+                    Vector2Int morphTargetCombinedDims = OvrGpuSkinningUtils.findMorphTargetCombinerSize(numAffectedVerts, numAttributes);
+
+                    // TODO: This seems like it should be a call into gpuskinning?
+                    Vector2Int morphDimensions = OvrGpuSkinningUtils.findOptimalTextureDimensions(
+                        numAffectedVerts,
+                        numAttributes,
+                        (uint)Mathf.Max(morphTargetCombinedDims.x, morphTargetCombinedDims.y));
+
+                    int morphTargetCombinerTexels = morphTargetCombinedDims.x * morphTargetCombinedDims.y;
+                    Debug.Assert(morphTargetCombinerTexels > 0, this);
+                    {
+                        _gpuCombiner = new OvrGpuMorphTargetsCombiner(
+                            "morphCombined(" + primitive.shortName + ")",
+                            morphTargetCombinedDims.x,
+                            morphTargetCombinedDims.y,
+                            morphTextureFormat.GetGraphicsFormat(),
+                            primitive.gpuPrimitive.MorphTargetSourceTex,
+                            primitive.gpuPrimitive.MetaData.PositionRange,
+                            primitive.gpuPrimitive.MetaData.NormalRange,
+                            primitive.gpuPrimitive.MetaData.TangentRange,
+                            HasTangents,
+                            gpuSkinningConfig.SourceMorphFormat == GpuSkinningConfiguration.TexturePrecision.Snorm10,
+                            GpuSkinningConfiguration.Instance.CombineMorphTargetsShader);
+                        _gpuCombiner.parentController = OvrAvatarManager.Instance.GpuSkinningController;
+
+                        var layoutRect = metaData.LayoutInMorphTargetsTex.ToRectInt();
+                        _handleInCombiner = _gpuCombiner.AddMorphTargetBlock(
+                            layoutRect,
+                            in morphDimensions,
+                            (int)metaData.LayoutInMorphTargetsTex.texSlice,
+                            numMorphTargets);
+
+                        OvrAvatarLog.AssertParam(_handleInCombiner.IsValid()
+                            , _gpuCombiner, _MorphTargetAssertStringBuilder, LogScope, this);
+                    }
+
+                    var indirectTextureFormat = gpuSkinningConfig.IndirectionFormat;
+                    Vector2Int indirectionDims = OvrGpuSkinningUtils.findIndirectionTextureSize(MeshVertexCount, numAttributes);
+
+                    int indirectionTexels = indirectionDims.x * indirectionDims.y;
+                    Debug.Assert(indirectionTexels > 0, this);
+                    {
+                        _indirectionTex = new OvrExpandableTextureArray(
+                            "indirection(" + primitive.shortName + ")",
+                            indirectionDims.x,
+                            indirectionDims.y,
+                            indirectTextureFormat);
+                    }
+
+                    CAPI.ovrTextureLayoutResult layout =
+                        _gpuCombiner.GetLayoutInCombinedTex(_handleInCombiner);
+                    // TODO: Figure out why checking combinedLayoutTexels breaks blinking... but not visemes?!?
+                    //int combinedLayoutTexels = layout.x * layout.y;
+                    //if (combinedLayoutTexels > 0)
+                    {
+                        handleInIndirectionTex = IndirectionTextureAddBlock(
+                            ref _indirectionTex,
+                            layout.ExtractRectiOnly(),
+                            layout.texSlice,
+                            (uint)_gpuCombiner.Width,
+                            (uint)_gpuCombiner.Height,
+                            _gpuCombiner.GetTexCoordForUnaffectedVertices(),
+                            (uint)MeshVertexCount,
+                            metaData.NumMorphTargetAffectedVerts,
+                            metaData.MeshVertexToAffectedIndex
+                        );
+                        OvrAvatarLog.AssertParam(handleInIndirectionTex.IsValid(), name
+                            , _IndirectionTextureAssertStringBuilder, LogScope, this);
+                    }
+                }
+
+                // Before we begin, check to see if we already have a skinner/morph target system set up:
+                Debug.Assert(_skinner == null,
+                    "Only one Skinning / Morph Target system can be created for Renderable.");
+
+                var outputFormat = gpuSkinningConfig.SkinnerOutputFormat.GetGraphicsFormat();
+                if (hasJoints)
+                {
+                    if (hasMorphTargets)
+                    {
+                        // Joints and morph targets
+                        var fullSkinner = new OvrGpuSkinner(
+                            metaData.LayoutInNeutralPoseTex.w,
+                            metaData.LayoutInNeutralPoseTex.h,
+                            outputFormat,
+                            SkinnerOutputFilterMode,
+                            SkinnerOutputDepthTexelsPerSlice,
+                            primitive.gpuPrimitive.NeutralPoseTex,
+                            primitive.gpuPrimitive.JointsTex,
+                            _skinningQuality,
+                            _indirectionTex,
+                            _gpuCombiner,
+                            GpuSkinningConfiguration.Instance.SkinToTextureShader);
+                        _handleInSkinner = fullSkinner.AddBlock(
+                            metaData.LayoutInNeutralPoseTex.w,
+                            metaData.LayoutInNeutralPoseTex.h,
+                            metaData.LayoutInNeutralPoseTex,
+                            metaData.LayoutInJointsTex,
+                            numBones,
+                            _indirectionTex.GetLayout(handleInIndirectionTex));
+
+                        _skinner = fullSkinner;
+                    }
+                    else
+                    {
+                        // Joints only
+                        var jointsOnlySkinner = new OvrGpuSkinnerJointsOnly(
+                            metaData.LayoutInNeutralPoseTex.w,
+                            metaData.LayoutInNeutralPoseTex.h,
+                            outputFormat,
+                            SkinnerOutputFilterMode,
+                            SkinnerOutputDepthTexelsPerSlice,
+                            primitive.gpuPrimitive.NeutralPoseTex,
+                            primitive.gpuPrimitive.JointsTex,
+                            _skinningQuality,
+                            GpuSkinningConfiguration.Instance.SkinToTextureShader);
+                        _handleInSkinner = jointsOnlySkinner.AddBlock(
+                            metaData.LayoutInNeutralPoseTex.w,
+                            metaData.LayoutInNeutralPoseTex.h,
+                            metaData.LayoutInNeutralPoseTex,
+                            metaData.LayoutInJointsTex,
+                            numBones);
+
+                        _skinner = jointsOnlySkinner;
+                    }
+                }
+                else
+                {
+                    // Morph targets only
+                    var morphTargetsOnlySkinner = new OvrGpuSkinnerMorphTargetsOnly(
+                        metaData.LayoutInNeutralPoseTex.w,
+                        metaData.LayoutInNeutralPoseTex.h,
+                        outputFormat,
+                        SkinnerOutputFilterMode,
+                        SkinnerOutputDepthTexelsPerSlice,
+                        primitive.gpuPrimitive.NeutralPoseTex,
+                        _indirectionTex,
+                        _gpuCombiner,
+                        GpuSkinningConfiguration.Instance.SkinToTextureShader);
+                    _handleInSkinner = morphTargetsOnlySkinner.AddBlock(
+                        metaData.LayoutInNeutralPoseTex.w,
+                        metaData.LayoutInNeutralPoseTex.h,
+                        metaData.LayoutInNeutralPoseTex,
+                        metaData.LayoutInMorphTargetsTex);
+
+                    _skinner = morphTargetsOnlySkinner;
+                }
+
+                if (_skinner != null)
+                {
+                    if (_skinner != null)
+                    {
+                        _skinner.ParentController = OvrAvatarManager.Instance.GpuSkinningController;
+                    }
+
+                }
+            } // if has mesh
+        }
+        private static string _MorphTargetAssertStringBuilder(in OvrGpuMorphTargetsCombiner combiner)
+            => $"Morph Target block unsuccesfully added to combiner {combiner}.";
+        private static string _IndirectionTextureAssertStringBuilder(in string meshName)
+            => $"Indirection block unsuccessfully added to indirection texture {meshName}.";
+
+
+        private void DestroyTempTexture(Texture2D tempTex)
+        {
+            Texture2D.Destroy(tempTex);
+        }
+
+        // simple port of CAPI.ovrGpuSkinning_IndirectionTextureInfoPopulateTextureCoordinateArrays from native C++ to managed C#
+        unsafe CAPI.ovrGpuSkinningResult ovrGpuSkinning_IndirectionTextureInfoPopulateTextureCoordinateArrays(
+                    CAPI.ovrGpuSkinningRecti texelsInCombinedTex,
+                    UInt32 combinedTexSlice,
+                    UInt32 combinedTexWidth,
+                    UInt32 combinedTexHeight,
+                    ref CAPI.ovrAvatar2Vector3f unaffectedVertTexCoordInCombinedTex,
+                    UInt32 meshVertCount,
+                    UInt32 morphTargetAffectedVertCount,
+                    bool hasTangents,
+                    UInt32 numAttributes,
+                    int[] meshVertIndexToAffectedVertIndex,
+                    IntPtr positionTexCoordsPtr,
+                    IntPtr normalTexCoordsPtr,
+                    IntPtr tangentTexCoordsPtr)
+        {
+#if !UNITY_EDITOR
+            unchecked
+#endif
+            {
+                float* positionTexCoords = (float*)positionTexCoordsPtr.ToPointer();
+                float* normalTexCoords = (float*)normalTexCoordsPtr.ToPointer();
+                float* tangentTexCoords = hasTangents ? (float*)tangentTexCoordsPtr.ToPointer() : null;
+
+                float invTexWidth = 1.0f / combinedTexWidth;
+                float invTwoTexWidth = invTexWidth * 0.5f;
+                float invTexHeight = 1.0f / combinedTexHeight;
+                float invTwoTexHeight = invTexHeight * 0.5f;
+
+                CAPI.ovrAvatar2Vector3f firstTexelCenter = new CAPI.ovrAvatar2Vector3f();
+                firstTexelCenter.x = (2.0f * texelsInCombinedTex.x + 1.0f) * invTwoTexWidth;
+                firstTexelCenter.y = (2.0f * texelsInCombinedTex.y + 1.0f) * invTwoTexHeight;
+                firstTexelCenter.z = combinedTexSlice;
+
+                // Loop over mesh vertices, calculating the texture coordinate for each vert in the combined
+                // morph target texture.
+                UInt32 rowWidth = (uint)texelsInCombinedTex.w;
+                Debug.Assert(rowWidth > 0);
+
+                int kNumFloatsPerTexCoord = 3;
+                for (Int32 vertIndex = 0, floatIndex = 0; vertIndex < meshVertCount;
+                     vertIndex++, floatIndex += kNumFloatsPerTexCoord)
+                {
+                    // See if affected by morph targets
+                    Int32 affectedVertIndex = meshVertIndexToAffectedVertIndex[vertIndex];
+
+                    int kUnaffectedVertexIndex = -1;
+                    if (affectedVertIndex == kUnaffectedVertexIndex)
+                    {
+                        // Not affected
+                        positionTexCoords[floatIndex + 0] = unaffectedVertTexCoordInCombinedTex.x;
+                        positionTexCoords[floatIndex + 1] = unaffectedVertTexCoordInCombinedTex.y;
+                        positionTexCoords[floatIndex + 2] = unaffectedVertTexCoordInCombinedTex.z;
+
+                        normalTexCoords[floatIndex + 0] = unaffectedVertTexCoordInCombinedTex.x;
+                        normalTexCoords[floatIndex + 1] = unaffectedVertTexCoordInCombinedTex.y;
+                        normalTexCoords[floatIndex + 2] = unaffectedVertTexCoordInCombinedTex.z;
+
+                        if (hasTangents)
+                        {
+                            tangentTexCoords[floatIndex + 0] = unaffectedVertTexCoordInCombinedTex.x;
+                            tangentTexCoords[floatIndex + 1] = unaffectedVertTexCoordInCombinedTex.y;
+                            tangentTexCoords[floatIndex + 2] = unaffectedVertTexCoordInCombinedTex.z;
+                        }
+                    }
+                    else
+                    {
+                        // Is affected by at least one morph target, calculate texture coordinate
+                        // for each attribute (if needed)
+                        UInt32 rowForPosition = (uint)affectedVertIndex / rowWidth;
+                        UInt32 column = (uint)affectedVertIndex % rowWidth;
+
+                        // Account for other rows for the other attributes (normals, tangents)
+                        rowForPosition *= numAttributes;
+
+                        // Convert from row/column to texel center texture coordinates
+                        float texCoordX = firstTexelCenter.x + (column * invTexWidth);
+                        positionTexCoords[floatIndex + 0] = texCoordX;
+                        positionTexCoords[floatIndex + 1] = firstTexelCenter.y + (rowForPosition * invTexHeight);
+                        positionTexCoords[floatIndex + 2] = firstTexelCenter.z;
+
+                        normalTexCoords[floatIndex + 0] = texCoordX;
+                        normalTexCoords[floatIndex + 1] = firstTexelCenter.y + ((rowForPosition + 1) * invTexHeight);
+                        normalTexCoords[floatIndex + 2] = firstTexelCenter.z;
+
+                        if (hasTangents)
+                        {
+                            tangentTexCoords[floatIndex + 0] = texCoordX;
+                            tangentTexCoords[floatIndex + 1] =
+                                firstTexelCenter.y + ((rowForPosition + 2) * invTexHeight);
+                            tangentTexCoords[floatIndex + 2] = firstTexelCenter.z;
+                        }
+                    }
+                }
+            }
+
+            return CAPI.ovrGpuSkinningResult.Success;
+        }
+
+        private OvrSkinningTypes.Handle IndirectionTextureAddBlock(
+            ref OvrExpandableTextureArray texArray,
+            CAPI.ovrGpuSkinningRecti texelsInCombinedRect,
+            UInt32 combinedTexSlice,
+            UInt32 combinedTexWidth,
+            UInt32 combinedTexHeight,
+            CAPI.ovrAvatar2Vector3f unaffectedVertTexCoordInCombinedTex,
+            UInt32 meshVertCount,
+            UInt32 morphTargetAffectedVertCount,
+            Int32[] meshVertIndexToAffectedVertIndex
+        )
+        {
+            // Calculate the rectangle for adding the block into the indirection
+            // texture. It cannot be larger than the texture array's largest dimension
+            // so that it will fit
+            int numAttributes = HasTangents ? 3 : 2;
+
+            Vector2Int dimensions = OvrGpuSkinningUtils.findOptimalTextureDimensions(
+                (int)meshVertCount,
+                numAttributes,
+                (uint)Mathf.Max(texArray.Width, texArray.Height));
+            UInt32 texelWidth = (UInt32)dimensions.x;
+            UInt32 texelHeight = (UInt32)dimensions.y;
+
+            OvrSkinningTypes.Handle handleInIndirectionTex = _indirectionTex.AddEmptyBlock(texelWidth, texelHeight);
+            if (!handleInIndirectionTex.IsValid())
+            {
+                return OvrSkinningTypes.Handle.kInvalidHandle;
+            }
+
+            const int kNumFloatsPerTexCoord = 3;
+            const int kNumFloatsPerTexel = 4;
+            {
+                int coordSizeFromCAPI = (int)CAPI.OvrGpuSkinning_IndirectionTextureInfoTexCoordsSizeInBytes();
+                Debug.Assert(coordSizeFromCAPI == kNumFloatsPerTexCoord * sizeof(float));
+                int numBytes = MeshVertexCount * coordSizeFromCAPI;
+
+                IntPtr posDataPtr = Marshal.AllocHGlobal(numBytes);
+                IntPtr normDataPtr = Marshal.AllocHGlobal(numBytes);
+                IntPtr tanDataPtr = Marshal.AllocHGlobal(numBytes);
+
+                // C# port from CAPI handles with or without tangents
+                ovrGpuSkinning_IndirectionTextureInfoPopulateTextureCoordinateArrays(texelsInCombinedRect,
+                    combinedTexSlice,
+                    combinedTexWidth,
+                    combinedTexHeight,
+                    ref unaffectedVertTexCoordInCombinedTex,
+                    meshVertCount,
+                    morphTargetAffectedVertCount,
+                    HasTangents,
+                    (uint)numAttributes,
+                    meshVertIndexToAffectedVertIndex,
+                    posDataPtr, // float results in a raw byte array
+                    normDataPtr, // float results in a raw byte array
+                    tanDataPtr
+                    )
+                    .LogErrors("get indirection data", this);
+
+                UInt32 texelSizeFromCAPI = CAPI.OvrGpuSkinning_IndirectionTextureInfoTexelSizeInBytes();
+                Debug.Assert(texelSizeFromCAPI == kNumFloatsPerTexel * sizeof(float));
+                int numResultBytes = texArray.Width * texArray.Height * (int)texelSizeFromCAPI;
+
+                // next fill in the indirection texture
+                {
+                    CAPI.ovrTextureLayoutResult layout = _indirectionTex.GetLayout(handleInIndirectionTex);
+
+                    Texture2D tempTex = new Texture2D(
+                        layout.w,
+                        layout.h,
+                        _indirectionTex.Format,
+                        _indirectionTex.HasMips,
+                        _indirectionTex.IsLinear);
+
+                    var texData = tempTex.GetRawTextureData<byte>();
+
+                    Debug.Assert(texData.Length == numResultBytes);
+
+                    IntPtr dataPtr;
+                    unsafe { dataPtr = (IntPtr)texData.GetUnsafePtr(); }
+                    var bufferSize = texData.GetBufferSize();
+
+                    bool populatedTextureData;
+                    if (!HasTangents)
+                    {
+                        populatedTextureData = CAPI.OvrGpuSkinning_IndirectionTextureInfoPopulateTextureData(
+                            texelWidth,
+                            texelHeight,
+                            meshVertCount,
+                            posDataPtr,
+                            normDataPtr,
+                            dataPtr,
+                            bufferSize
+                        );
+                    }
+                    else
+                    {
+                        populatedTextureData = CAPI.OvrGpuSkinning_IndirectionTextureInfoPopulateTextureDataWithTangents(
+                            texelWidth,
+                            texelHeight,
+                            meshVertCount,
+                            posDataPtr,
+                            normDataPtr,
+                            tanDataPtr,
+                            dataPtr,
+                            bufferSize
+                        );
+                    }
+                    if (!populatedTextureData)
+                    {
+                        OvrAvatarLog.LogError("Failed to populate gpuskinning texture data", LogScope, this);
+                    }
+
+                    tempTex.Apply(false, true);
+
+                    _indirectionTex.CopyFromTexture(layout, tempTex);
+
+                    DestroyTempTexture(tempTex);
+                }
+
+                Marshal.FreeHGlobal(posDataPtr);
+                Marshal.FreeHGlobal(normDataPtr);
+                Marshal.FreeHGlobal(tanDataPtr);
+            }
+
+            return handleInIndirectionTex;
+        }
+
+        private OvrExpandableTextureArray _indirectionTex = null;
+        private OvrGpuMorphTargetsCombiner _gpuCombiner = null;
+        private IOvrGpuSkinner _skinner = null;
+
+        private OvrSkinningTypes.Handle _handleInCombiner;
+        private OvrSkinningTypes.Handle _handleInSkinner;
+
+        private sealed class BufferHandle : IDisposableBuffer
+        {
+            public BufferHandle() { }
+
+            public IntPtr nativeBuffer = IntPtr.Zero;
+
+            public IntPtr BufferPtr
+            {
+                get
+                {
+                    return nativeBuffer;
+                }
+            }
+
+            public void Dispose()
+            {
+                // Buffer is persistent, no need for cleanup
+                // TODO: Reference count to cover race conditions
+            }
+        }
+
+        protected static int U_ATTRIBUTE_TEXEL_SLICE_PROP_ID => _propertyIds.TEXEL_SLICE_PROP_ID;
+        protected static int U_PREV_POSITION_TEXEL_SLICE_PROP_ID => _propertyIds.PREV_POS_TEXEL_SLICE_PROP_ID;
+
+        private static int U_ATTRIBUTE_TEXTURE_PROP_ID => _propertyIds.TEXTURE_PROP_ID;
+        private static int U_ATTRIBUTE_SCALE_BIAS_PROP_ID => _propertyIds.SCALE_BIAS_PROP_ID;
+
+        // TODO: Change the following parameter in the native implementation to match:
+        // public static readonly int U_ATTRIBUTE_TEXEL_RECT_PROP_ID = Shader.PropertyToID("u_AttributeTexelRect");
+        private static int U_ATTRIBUTE_TEXEL_X_PROP_ID => _propertyIds.TEXEL_X_PROP_ID;
+        private static int U_ATTRIBUTE_TEXEL_Y_PROP_ID => _propertyIds.TEXEL_Y_PROP_ID;
+        private static int U_ATTRIBUTE_TEXEL_W_PROP_ID => _propertyIds.TEXEL_W_PROP_ID;
+        private static int U_ATTRIBUTE_TEXEL_H_PROP_ID => _propertyIds.TEXEL_H_PROP_ID;
+
+        // TODO: Change the following parameter in the native implementation to match:
+        // public static readonly int U_ATTRIBUTE_TEX_INV_SIZE_PROP_ID = Shader.PropertyToID("u_AttributeTexInvSize");
+        private static int U_ATTRIBUTE_TEX_INV_SIZE_W_PROP_ID => _propertyIds.TEX_INV_SIZE_W_PROP_ID;
+        private static int U_ATTRIBUTE_TEX_INV_SIZE_H_PROP_ID => _propertyIds.TEX_INV_SIZE_H_PROP_ID;
+        private static int U_ATTRIBUTE_TEX_INV_SIZE_D_PROP_ID => _propertyIds.TEX_INV_SIZE_D_PROP_ID;
+
+        private const string OVR_VERTEX_FETCH_TEXTURE_KEYWORD = "OVR_VERTEX_FETCH_TEXTURE";
+        private const string OVR_VERTEX_FETCH_TEXTURE_UNORM_KEYWORD = "OVR_VERTEX_FETCH_TEXTURE_UNORM";
+
+        private struct AttributePropertyIds
+        {
+            public readonly int TEXEL_SLICE_PROP_ID;
+            public readonly int PREV_POS_TEXEL_SLICE_PROP_ID;
+
+            public readonly int TEXTURE_PROP_ID;
+            public readonly int SCALE_BIAS_PROP_ID;
+
+            public readonly int TEXEL_X_PROP_ID;
+            public readonly int TEXEL_Y_PROP_ID;
+            public readonly int TEXEL_W_PROP_ID;
+            public readonly int TEXEL_H_PROP_ID;
+
+            public readonly int TEX_INV_SIZE_W_PROP_ID;
+            public readonly int TEX_INV_SIZE_H_PROP_ID;
+            public readonly int TEX_INV_SIZE_D_PROP_ID;
+
+            // These will both be 0 if default initialized, otherwise they are guaranteed unique
+            public bool IsValid => U_ATTRIBUTE_TEXTURE_PROP_ID != U_ATTRIBUTE_SCALE_BIAS_PROP_ID;
+
+            public enum InitMethod { PropertyToId }
+            public AttributePropertyIds(InitMethod initMethod)
+            {
+                TEXEL_SLICE_PROP_ID = Shader.PropertyToID("_OvrAttributeInterpolationValue");
+                PREV_POS_TEXEL_SLICE_PROP_ID = Shader.PropertyToID("_OvrPrevRenderFrameInterpolationValue");
+
+                TEXTURE_PROP_ID = Shader.PropertyToID("u_AttributeTexture");
+                SCALE_BIAS_PROP_ID = Shader.PropertyToID("u_AttributeScaleBias");
+
+                TEXEL_X_PROP_ID = Shader.PropertyToID("u_AttributeTexelX");
+                TEXEL_Y_PROP_ID = Shader.PropertyToID("u_AttributeTexelY");
+                TEXEL_W_PROP_ID = Shader.PropertyToID("u_AttributeTexelW");
+                TEXEL_H_PROP_ID = Shader.PropertyToID("u_AttributeTexelH");
+
+                TEX_INV_SIZE_W_PROP_ID = Shader.PropertyToID("u_AttributeTexInvSizeW");
+                TEX_INV_SIZE_H_PROP_ID = Shader.PropertyToID("u_AttributeTexInvSizeH");
+                TEX_INV_SIZE_D_PROP_ID = Shader.PropertyToID("u_AttributeTexInvSizeD");
+            }
+        }
+
+#if UNITY_EDITOR
+        void OnDrawGizmosSelected()
+        {
+            Gizmos.color = Color.magenta;
+            if (MyMeshFilter != null)
+            {
+                Mesh m = MyMeshFilter.sharedMesh;
+                if (m != null)
+                {
+                    Gizmos.matrix = MyMeshFilter.transform.localToWorldMatrix;
+                    Gizmos.DrawWireCube(m.bounds.center, m.bounds.size);
+                }
+            }
+        }
+
+        protected virtual void OnValidate()
+        {
+            UpdateSkinningQuality();
+        }
+#endif
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarGpuSkinnedRenderableBase.cs.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarGpuSkinnedRenderableBase.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..5fc87ea3630bc0f129d20368537ac04592670134
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarGpuSkinnedRenderableBase.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 9d19508252f3c76458c11576e9888b3f
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarGpuSkinningController.cs b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarGpuSkinningController.cs
new file mode 100644
index 0000000000000000000000000000000000000000..9e40283022766d1dabe2ed04b198fbf989f96e84
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarGpuSkinningController.cs
@@ -0,0 +1,132 @@
+using Oculus.Skinning.GpuSkinning;
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+using UnityEngine.Profiling;
+
+namespace Oculus.Avatar2
+{
+    public sealed class OvrAvatarGpuSkinningController : System.IDisposable
+    {
+        // Avoid skinning more avatars than technically feasible
+        public const uint MaxGpuSkinnedAvatars = MaxSkinnedAvatarsPerFrame * 8;
+
+        // Avoid skinning more avatars than GPU resources are preallocated for
+        public const uint MaxSkinnedAvatarsPerFrame = 32;
+
+        private const int NumExpectedAvatars = 16;
+
+        private readonly List<OvrGpuMorphTargetsCombiner> _activeCombinerList = new List<OvrGpuMorphTargetsCombiner>(NumExpectedAvatars);
+        private readonly List<IOvrGpuSkinner> _activeSkinnerList = new List<IOvrGpuSkinner>(NumExpectedAvatars);
+        private readonly List<OvrComputeMeshAnimator> _activeAnimators = new List<OvrComputeMeshAnimator>(NumExpectedAvatars);
+
+        private OvrComputeBufferPool bufferPool = new OvrComputeBufferPool();
+
+        public void Dispose()
+        {
+            Dispose(true);
+            GC.SuppressFinalize(this);
+        }
+
+        private void Dispose(bool isMainThread) { bufferPool.Dispose(); }
+
+        ~OvrAvatarGpuSkinningController()
+        {
+            Dispose(false);
+        }
+
+        internal void AddActiveCombiner(OvrGpuMorphTargetsCombiner combiner)
+        {
+            AddGpuSkinningElement(_activeCombinerList, combiner);
+        }
+
+        internal void AddActiveSkinner(IOvrGpuSkinner skinner)
+        {
+            AddGpuSkinningElement(_activeSkinnerList, skinner);
+        }
+
+        internal void AddActivateComputeAnimator(OvrComputeMeshAnimator meshAnimator)
+        {
+            AddGpuSkinningElement(_activeAnimators, meshAnimator);
+        }
+
+        // This behaviour is manually updated at a specific time during OvrAvatarManager::Update()
+        // to prevent issues with Unity script update ordering
+        internal void UpdateInternal()
+        {
+            Profiler.BeginSample("OvrAvatarGpuSkinningController::UpdateInternal");
+
+            if (_activeCombinerList.Count > 0)
+            {
+                Profiler.BeginSample("OvrAvatarGpuSkinningController.CombinerCalls");
+                foreach (var combiner in _activeCombinerList)
+                {
+                    combiner.CombineMorphTargetWithCurrentWeights();
+                }
+                _activeCombinerList.Clear();
+                Profiler.EndSample(); // "OvrAvatarGpuSkinningController.CombinerCalls"
+            }
+
+            if (_activeSkinnerList.Count > 0)
+            {
+                Profiler.BeginSample("OvrAvatarGpuSkinningController.SkinnerCalls");
+                foreach (var skinner in _activeSkinnerList)
+                {
+                    skinner.UpdateOutputTexture();
+                }
+                _activeSkinnerList.Clear();
+                Profiler.EndSample(); // "OvrAvatarGpuSkinningController.SkinnerCalls"
+            }
+
+            if (_activeAnimators.Count > 0)
+            {
+                Profiler.BeginSample("OvrAvatarGpuSkinningController.AnimatorDispatches");
+                foreach (var animator in _activeAnimators)
+                {
+                    animator.DispatchAndUpdateOutputs();
+                }
+                _activeAnimators.Clear();
+                Profiler.EndSample(); // "OvrAvatarGpuSkinningController.AnimatorDispatches"
+            }
+
+            Profiler.EndSample();
+        }
+
+        private void AddGpuSkinningElement<T>(List<T> list, T element) where T : class
+        {
+            Debug.Assert(element != null);
+            Debug.Assert(!list.Contains(element));
+            list.Add(element);
+        }
+
+        internal void StartFrame()
+        {
+            bufferPool.StartFrame();
+        }
+
+        internal void EndFrame()
+        {
+            bufferPool.EndFrame();
+        }
+
+        internal OvrComputeBufferPool.EntryJoints GetNextEntryJoints()
+        {
+            return bufferPool.GetNextEntryJoints();
+        }
+
+        internal ComputeBuffer GetJointBuffer()
+        {
+            return bufferPool.GetJointBuffer();
+        }
+
+        internal ComputeBuffer GetWeightsBuffer()
+        {
+            return bufferPool.GetWeightsBuffer();
+        }
+
+        internal OvrComputeBufferPool.EntryWeights GetNextEntryWeights(int numMorphTargets)
+        {
+            return bufferPool.GetNextEntryWeights(numMorphTargets);
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarGpuSkinningController.cs.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarGpuSkinningController.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..c2e5049200ffea6553942d04bb988a5c2a62ce58
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrAvatarGpuSkinningController.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 9b713990e854e9b48bb29778214d39c2
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrCombinedMorphTargetsQuads.cs b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrCombinedMorphTargetsQuads.cs
new file mode 100644
index 0000000000000000000000000000000000000000..b3a3b27b76b506a40ac75699af83bad411f127f2
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrCombinedMorphTargetsQuads.cs
@@ -0,0 +1,179 @@
+#define OVR_GPU_PACK_TANGENT_INFO
+
+using System;
+using UnityEngine;
+
+namespace Oculus.Skinning.GpuSkinning
+{
+    public static class OvrCombinedMorphTargetsQuads
+    {
+        private const int NUM_VERTS_PER_MORPH_TARGET = 4; // 1 quad per
+        private const int NUM_INDICES_PER_MORPH_TARGET = 6; // 1 quad per
+        private const float Z_POSITION = 0.75f;
+
+        public static void ExpandMeshToFitQuads(Mesh existingMesh, int additionalNumMorphTargets)
+        {
+            Vector3[] verts = existingMesh.vertices;
+            Vector2[] uvs = existingMesh.uv;
+#if OVR_GPU_PACK_TANGENT_INFO
+            Vector4[] info = existingMesh.tangents;
+#endif
+            Color[] colors = existingMesh.colors;
+            int[] indices = existingMesh.triangles;
+
+            int oldNumVerts = verts.Length;
+            int newNumVerts = verts.Length + (additionalNumMorphTargets * NUM_VERTS_PER_MORPH_TARGET);
+            int oldNumIndices = indices.Length;
+            int newNumIndices = indices.Length + (additionalNumMorphTargets * NUM_INDICES_PER_MORPH_TARGET);
+
+            Array.Resize(ref verts, newNumVerts);
+            Array.Resize(ref uvs, newNumVerts);
+            Array.Resize(ref colors, newNumVerts);
+#if OVR_GPU_PACK_TANGENT_INFO
+            Array.Resize(ref info, newNumVerts);
+#endif
+            Array.Resize(ref indices, newNumIndices);
+
+            for (
+              int vertIndex = oldNumVerts, indicesIndex = oldNumIndices;
+              vertIndex < newNumVerts;
+              vertIndex += NUM_VERTS_PER_MORPH_TARGET, indicesIndex += NUM_INDICES_PER_MORPH_TARGET)
+            {
+                indices[indicesIndex + 0] = vertIndex + 0;
+                indices[indicesIndex + 1] = vertIndex + 2;
+                indices[indicesIndex + 2] = vertIndex + 1;
+                indices[indicesIndex + 3] = vertIndex + 2;
+                indices[indicesIndex + 4] = vertIndex + 3;
+                indices[indicesIndex + 5] = vertIndex + 1;
+            }
+
+            // Unity documentation says resizing the vertices will also resize colors, uvs, etc.
+            existingMesh.vertices = verts;
+            existingMesh.uv = uvs;
+#if OVR_GPU_PACK_TANGENT_INFO
+            existingMesh.tangents = info;
+#endif
+            existingMesh.colors = colors;
+            existingMesh.triangles = indices;
+        }
+
+        public static void UpdateQuadsInMesh(
+          int meshVertexStartIndex,
+          int blockIndex,
+          int morphTargetStartIndex,
+          RectInt texelRectInCombinedTex,
+          int combinedTexWidth,
+          int combinedTexHeight,
+          RectInt texelRectInSource,
+          int texelSliceInSource,
+          int sourceTexWidth,
+          int sourceTexHeight,
+          int numMorphTargets,
+          Mesh existingMesh)
+        {
+            float invTexWidth = 1.0f / combinedTexWidth;
+            float invTexHeight = 1.0f / combinedTexHeight;
+
+            float invSourceTexWidth = 1.0f / sourceTexWidth;
+            float invSourceTexHeight = 1.0f / sourceTexHeight;
+
+            // Transform the "row and column" into clip space [-1 to 1] for the rectangle origins
+            // 4 positions per quad rect change with blend shapes)
+
+            // Each morph target will share same quad positions
+            Vector3[] quadPositions = new Vector3[NUM_VERTS_PER_MORPH_TARGET];
+
+            // Convert from "texels" to clip space
+            // origin
+            quadPositions[0] = new Vector3(texelRectInCombinedTex.xMin * invTexWidth, texelRectInCombinedTex.yMin * invTexHeight, Z_POSITION) * 2.0f - Vector3.one;
+            // "x corner"
+            quadPositions[1] = new Vector3(texelRectInCombinedTex.xMax * invTexWidth, texelRectInCombinedTex.yMin * invTexHeight, Z_POSITION) * 2.0f - Vector3.one;
+            // "y corner"
+            quadPositions[2] = new Vector3(texelRectInCombinedTex.xMin * invTexWidth, texelRectInCombinedTex.yMax * invTexHeight, Z_POSITION) * 2.0f - Vector3.one;
+            // "opposite corner"
+            quadPositions[3] = new Vector3(texelRectInCombinedTex.xMax * invTexWidth, texelRectInCombinedTex.yMax * invTexHeight, Z_POSITION) * 2.0f - Vector3.one;
+
+            var existingVerts = existingMesh.vertices;
+            var existingUvs = existingMesh.uv;
+            var existingColors = existingMesh.colors;
+
+#if OVR_GPU_PACK_TANGENT_INFO
+            var existingInfo = existingMesh.tangents;
+#endif
+
+            int heightForSingleMorphTarget = texelRectInSource.height / numMorphTargets; // only calculate this once and cache off
+            float uvHeightForSingleTarget = heightForSingleMorphTarget * invSourceTexHeight;
+
+            // Now, for each morph target add new quads (all of which have same positions, differing
+            // texture coordinates and color)
+            Color encodedValues = new Color(blockIndex, morphTargetStartIndex, 0f, texelSliceInSource);
+#if OVR_GPU_PACK_TANGENT_INFO
+            Vector4 encodedInfo = new Vector4(blockIndex, morphTargetStartIndex, 0f, texelSliceInSource);
+#endif
+
+
+            Rect texelRectInSourceForTarget = new Rect(
+                texelRectInSource.xMin * invSourceTexWidth,
+                texelRectInSource.yMin * invSourceTexHeight,
+                texelRectInSource.width * invSourceTexWidth,
+                uvHeightForSingleTarget);
+
+            for (
+              int targetIndex = 0, vertIndex = meshVertexStartIndex;
+              targetIndex < numMorphTargets;
+              targetIndex++, vertIndex += NUM_VERTS_PER_MORPH_TARGET)
+            {
+                int combinedTargetIndex = targetIndex + morphTargetStartIndex;
+                encodedValues.g = combinedTargetIndex;
+#if OVR_GPU_PACK_TANGENT_INFO
+                encodedInfo.y = combinedTargetIndex;
+#endif
+
+                existingVerts[vertIndex + 0] = quadPositions[0];
+                existingVerts[vertIndex + 1] = quadPositions[1];
+                existingVerts[vertIndex + 2] = quadPositions[2];
+                existingVerts[vertIndex + 3] = quadPositions[3];
+
+                existingUvs[vertIndex + 0] = new Vector2(
+                  texelRectInSourceForTarget.xMin,
+                  texelRectInSourceForTarget.yMin);
+
+                existingUvs[vertIndex + 1] = new Vector2(
+                  texelRectInSourceForTarget.xMax,
+                  texelRectInSourceForTarget.yMin);
+
+                existingUvs[vertIndex + 2] = new Vector2(
+                  texelRectInSourceForTarget.xMin,
+                  texelRectInSourceForTarget.yMax);
+
+                existingUvs[vertIndex + 3] = new Vector2(
+                  texelRectInSourceForTarget.xMax,
+                  texelRectInSourceForTarget.yMax);
+
+                existingColors[vertIndex + 0] = encodedValues;
+                existingColors[vertIndex + 1] = encodedValues;
+                existingColors[vertIndex + 2] = encodedValues;
+                existingColors[vertIndex + 3] = encodedValues;
+
+#if OVR_GPU_PACK_TANGENT_INFO
+                existingInfo[vertIndex + 0] = encodedInfo;
+                existingInfo[vertIndex + 1] = encodedInfo;
+                existingInfo[vertIndex + 2] = encodedInfo;
+                existingInfo[vertIndex + 3] = encodedInfo;
+#endif
+
+                texelRectInSourceForTarget.y += uvHeightForSingleTarget;
+            }
+
+            // Update mesh
+            existingMesh.vertices = existingVerts;
+            existingMesh.uv = existingUvs;
+
+#if OVR_GPU_PACK_TANGENT_INFO
+            existingMesh.tangents = existingInfo;
+#endif
+
+            existingMesh.colors = existingColors;
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrCombinedMorphTargetsQuads.cs.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrCombinedMorphTargetsQuads.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..f813ed28c7eaba3d5bb22f421f4d976a49903927
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrCombinedMorphTargetsQuads.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 44a0784efd543cf489abaa2171ba0627
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrComputeBufferPool.cs b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrComputeBufferPool.cs
new file mode 100644
index 0000000000000000000000000000000000000000..b8748e346ac16f60410b8317c4386418608db447
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrComputeBufferPool.cs
@@ -0,0 +1,161 @@
+using System;
+using System.Runtime.InteropServices;
+using Unity.Collections.LowLevel.Unsafe;
+
+using UnityEngine;
+
+// This holds a pool of compute buffers that can be used by many draw calls.
+// These buffers are unsynchronized, the memory returned is GPU memory(when possible).
+// Currently there are 2 pools, one for joints, and one for morph target weights.
+//
+// Joints: This buffer is structured(shader storage buffer). It will be read from main memory
+//         in the shader. Use setBuffer on material.
+// Weights: For morph target weights. This will be in constant(local) memory. Must be a multiple of 256 bytes
+//          and deal with std140. Use setContantBuffer on material.
+internal class OvrComputeBufferPool : System.IDisposable
+{
+    // max active avatars, plus 4 extras(can go over by a couple for 1-2 frames sometimes)
+    private const int BUFFER_SIZE = 36;
+    // 3 "should" be enough for VR(2 if using low latency mode).
+    // use 4 just in case, might be needed in Editor. Increase if seeing really strange behavior
+    // outside of VR. If not enough, you could be writing to memory that the GPU is actively using to render.
+    private const int NUM_BUFFERS = 4;
+
+    private const int VECTOR4_SIZE_BYTES = sizeof(float) * 4;
+    private const int BYTES_PER_MATRIX = sizeof(float) * 16;
+
+    // Our glb encodes joint indices in 8 bits currently.
+    // Currently we use 134 bones(8/3/2022). Its always creeping up though.
+    // Leave some extra for future. This number can't be increased beyond 254 though
+    // glb reserves some values, so only get 254 max in 8 bits.
+    internal const int MaxJoints = 160;
+
+    internal const int JointDataSize = 2 * BYTES_PER_MATRIX;
+    [StructLayout(LayoutKind.Explicit, Size = JointDataSize)]
+    internal struct JointData
+    {
+        [FieldOffset(0)] public Matrix4x4 transform;
+        [FieldOffset(BYTES_PER_MATRIX)] public Matrix4x4 normalTransform;
+    }
+
+    // Currently at 103(8/3/2022). some extra, round to next 256 byte alignment.
+    // Note, because of std140, access this as a vec4 in shader to avoid having to pad here.
+    internal const int MAX_WEIGHTS = 128;
+    internal const int WeightsSize = sizeof(float) * MAX_WEIGHTS;
+
+    private const int JOINTS_PER_BUFFER = BUFFER_SIZE * MaxJoints;
+    private const int WEIGHTS_PER_BUFFER = BUFFER_SIZE * MAX_WEIGHTS;
+
+    internal OvrComputeBufferPool()
+    {
+        _jointsBuffer = new ComputeBuffer(JOINTS_PER_BUFFER * NUM_BUFFERS, JointDataSize, ComputeBufferType.Structured, ComputeBufferMode.SubUpdates);
+        // TODO: really should be a constant buffer, so it can live in on chip memory instead of main memory. Doesn't work in Unity 2020 though. If it is working
+        // in a renderdoc capture you should see a call to glBindBufferRange instead of glBindBufferBase when using a constant buffer.
+        _weightsBuffer = new ComputeBuffer(WEIGHTS_PER_BUFFER * NUM_BUFFERS, sizeof(float), ComputeBufferType.Structured, ComputeBufferMode.SubUpdates);
+
+        _jointsBuffer.name = "OVR Avatar GPU-Skinning Joint Buffer";
+        _weightsBuffer.name = "OVR Avatar GPU-Skinning Weight Buffer";
+    }
+
+    public void Dispose()
+    {
+        Dispose(true);
+        GC.SuppressFinalize(this);
+    }
+
+    private void Dispose(bool isMainThread)
+    {
+        _jointsBuffer.Release();
+        _weightsBuffer.Release();
+    }
+
+    ~OvrComputeBufferPool()
+    {
+        Dispose(false);
+    }
+
+    public void StartFrame()
+    {
+        Debug.Assert(_currentBuffer < NUM_BUFFERS);
+        var dataJoints = _jointsBuffer.BeginWrite<JointData>(_currentBuffer * JOINTS_PER_BUFFER, JOINTS_PER_BUFFER);
+        var dataWeights = _weightsBuffer.BeginWrite<float>(_currentBuffer * WEIGHTS_PER_BUFFER, WEIGHTS_PER_BUFFER);
+        unsafe
+        {
+            _jointMappedData = NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(dataJoints);
+            _weightMappedData = NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(dataWeights);
+        }
+        _jointNumberWritten = 0;
+        _weightsNumberWritten = 0;
+    }
+
+    public void EndFrame()
+    {
+        _jointsBuffer.EndWrite<JointData>(_jointNumberWritten * MaxJoints);
+        _weightsBuffer.EndWrite<float>(_weightsNumberWritten * MAX_WEIGHTS);
+        _currentBuffer = (_currentBuffer + 1) % NUM_BUFFERS;
+    }
+
+    public struct EntryJoints
+    {
+        public IntPtr Data;
+        public int JointOffset;
+    }
+
+    public EntryJoints GetNextEntryJoints()
+    {
+        Debug.Assert(_jointNumberWritten < BUFFER_SIZE, "Too many joint entries requested. increase BUFFER_SIZE");
+        EntryJoints result;
+        result.JointOffset = _currentBuffer * JOINTS_PER_BUFFER + (_jointNumberWritten * MaxJoints);
+        unsafe
+        {
+            Debug.Assert(_jointMappedData != null, "Calling GetNextEntryJoints outside of a frame");
+            var jointSet = ((JointData*)_jointMappedData) + _jointNumberWritten * MaxJoints;
+            result.Data = (IntPtr)jointSet;
+        }
+        ++_jointNumberWritten;
+        return result;
+    }
+
+    public struct EntryWeights
+    {
+        public IntPtr Data;
+        public int Offset;
+    }
+
+    public EntryWeights GetNextEntryWeights(int numMorphTargets)
+    {
+        Debug.Assert(numMorphTargets <= MAX_WEIGHTS, "Too many morph targets, increase MAX_WEIGHTS");
+        Debug.Assert(_weightsNumberWritten < BUFFER_SIZE, "Too many weight entries requested. increase BUFFER_SIZE");
+        EntryWeights result;
+        result.Offset = _currentBuffer * WEIGHTS_PER_BUFFER + (_weightsNumberWritten * MAX_WEIGHTS);
+        unsafe
+        {
+            Debug.Assert(_weightMappedData != null, "Calling GetNextEntryWeights outside of a frame");
+            var weights = ((float*)_weightMappedData) + (_weightsNumberWritten * MAX_WEIGHTS);
+            result.Data = (IntPtr)weights;
+        }
+        ++_weightsNumberWritten;
+        return result;
+    }
+
+    internal ComputeBuffer GetJointBuffer()
+    {
+        return _jointsBuffer;
+    }
+
+    internal ComputeBuffer GetWeightsBuffer()
+    {
+        return _weightsBuffer;
+    }
+
+    private readonly ComputeBuffer _jointsBuffer;
+    private readonly ComputeBuffer _weightsBuffer;
+
+    //Note, these is only valid between StartFrame/EndFrame
+    private unsafe void* _jointMappedData;
+    private unsafe void* _weightMappedData;
+
+    int _currentBuffer = 0;
+    int _jointNumberWritten;
+    int _weightsNumberWritten;
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrComputeBufferPool.cs.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrComputeBufferPool.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..13c631aedf94a0070e5ac2ff6997ca4bb9b314bb
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrComputeBufferPool.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 2d5524268cada924e92121a59d098581
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrComputeMeshAnimator.cs b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrComputeMeshAnimator.cs
new file mode 100644
index 0000000000000000000000000000000000000000..7da39a4c4744ddde655f3b27cddb59a3c2a45292
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrComputeMeshAnimator.cs
@@ -0,0 +1,781 @@
+// Due to Unity bug (fixed in version 2021.2), copy to a native array then copy native array to ComputeBuffer in one chunk
+// (ComputeBuffer.SetData erases previously set data)
+// https://issuetracker.unity3d.com/issues/partial-updates-of-computebuffer-slash-graphicsbuffer-using-setdata-dont-preserve-existing-data-when-using-opengl-es
+#if UNITY_2021_2_OR_NEWER
+    #define COMPUTE_BUFFER_PARTIAL_UPDATE_ALLOWED
+#endif
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using Oculus.Avatar2;
+using Unity.Collections;
+using Unity.Collections.LowLevel.Unsafe;
+using UnityEngine;
+
+namespace Oculus.Skinning.GpuSkinning
+{
+    internal sealed class OvrComputeMeshAnimator : IDisposable
+    {
+        private const int BAG_OF_UINTS_STRIDE = 4; // in bytes
+
+        private const string LOG_SCOPE = "OvrComputeMeshAnimator";
+
+        private const int WEIGHTS_STRIDE_BYTES = 4; // 32-bit float per morph target
+        private const int JOINT_MATRIX_STRIDE_BYTES = 16 * 4; // 4x4 32-bit float matrices per joint matrix
+        private const int SLICE_OUTPUT_STRIDE_BYTES = 4; // a single 32-bit int
+
+        private const string KERNEL_NAME = "CSMain";
+        private const string HAS_TANGENTS_KERNEL_NAME = "CSMainWithTangents";
+        private const string DOUBLE_BUFFER_KERNEL_NAME = "CSMainDoubleBuffer";
+        private const string HAS_TANGENTS_DOUBLE_BUFFER_KERNEL_NAME = "CSMainDoubleBufferWithTangents";
+        private const string TRIPLE_BUFFER_KERNEL_NAME = "CSMainTripleBuffer";
+        private const string HAS_TANGENTS_TRIPLE_BUFFER_KERNEL_NAME = "CSMainTripleBufferWithTangents";
+
+        public OvrSkinningTypes.SkinningQuality SkinningQuality { get; set; }
+
+        public enum MaxOutputFrames
+        {
+            ONE = 1,
+            TWO = 2,
+            THREE = 3,
+        }
+
+        private int MaxJointsToSkin
+        {
+            get
+            {
+                switch (SkinningQuality)
+                {
+                    case OvrSkinningTypes.SkinningQuality.Bone4:
+                        return 4;
+                    case OvrSkinningTypes.SkinningQuality.Bone2:
+                        return 2;
+                    case OvrSkinningTypes.SkinningQuality.Bone1:
+                        return 1;
+                    default:
+                        return 0;
+                }
+            }
+        }
+
+        public OvrComputeMeshAnimator(
+            ComputeShader shader,
+            int numMeshVerts,
+            int numMeshMorphTargets,
+            int numMeshJoints,
+            OvrAvatarComputeSkinnedPrimitive gpuPrimitive,
+            bool hasTangents,
+            MaxOutputFrames maxOutputFrames)
+        {
+            Debug.Assert(shader != null);
+            Debug.Assert(gpuPrimitive != null);
+            Debug.Assert(gpuPrimitive.SourceMetaData != null);
+
+            CheckPropertyIdInit();
+
+            _shader = shader;
+
+            _numMorphedVertices = gpuPrimitive.SourceMetaData.numMorphedVerts;
+            _numPlainVerts = gpuPrimitive.SourceMetaData.numVertsNoJointsOrMorphs;
+            _numSkinningOnlyVerts = numMeshVerts - _numPlainVerts - _numMorphedVertices;
+            _maxOutputFrames = maxOutputFrames;
+            _hasMorphTargets = numMeshMorphTargets > 0 && _numMorphedVertices > 0;
+            _hasJoints = numMeshJoints > 0;
+
+            _numMorphTargetWeights = numMeshMorphTargets;
+            _numJointMatrices = numMeshJoints * 2; // * 2 due to interleaved normal matrices
+
+            // The primitive still "owns" the static compute shader
+            _staticBuffer = gpuPrimitive.StaticDataComputeBuffer;
+
+            _positionOutputBias = gpuPrimitive.SourceMetaData.positionOutputBias;
+            _positionOutputScale = gpuPrimitive.SourceMetaData.positionOutputScale;
+
+            _inputPositionPrecision = gpuPrimitive.SourceMetaData.inputPositionPrecision;
+            _morphDeltasPrecision = gpuPrimitive.SourceMetaData.morphDeltasPrecision;
+            _outputPositionPrecision = gpuPrimitive.SourceMetaData.outputPositionPrecision;
+            _jointIndicesPrecision = gpuPrimitive.SourceMetaData.jointIndicesPrecision;
+
+            int numOutputSlices = (int)maxOutputFrames;
+            CreateDynamicDataBuffer(numMeshVerts, numMeshMorphTargets, numMeshJoints, numOutputSlices);
+            CreateOutputBuffers(numMeshVerts, hasTangents, numOutputSlices);
+            GetShaderKernel(hasTangents, numOutputSlices);
+        }
+
+        public static MaxOutputFrames GetMaxOutputFramesForConfiguration(bool motionSmoothing, bool supportApplicationSpacewarp)
+        {
+            int numExtraSlicesForMotionSmoothing = motionSmoothing ? 1 : 0;
+            int numExtraSlicesForAppSpacewarp = supportApplicationSpacewarp ? 1 : 0;
+            return (MaxOutputFrames)(1 + numExtraSlicesForAppSpacewarp + numExtraSlicesForMotionSmoothing);
+        }
+
+        public ComputeBuffer GetPositionOutputBuffer()
+        {
+            return _positionOutputBuffer;
+        }
+
+        public ComputeBuffer GetFrenetOutputBuffer()
+        {
+            return _frenetOutputBuffer;
+        }
+
+        public Vector3 GetPositionOutputScale()
+        {
+            return _positionOutputScale;
+        }
+
+        public Vector3 GetPositionOutputBias()
+        {
+            return _positionOutputBias;
+        }
+
+        public void SetMorphTargetWeights(NativeArray<float> weights)
+        {
+            Debug.Assert(weights.Length == _numMorphTargetWeights);
+            if (_hasMorphTargets)
+            {
+                _dynamicBufferUpdater.SetMorphTargetWeights(weights);
+                _outputNeedsUpdating = true;
+            }
+        }
+
+        public void SetJointMatrices(NativeArray<Matrix4x4> matrices)
+        {
+            Debug.Assert(matrices.Length == _numJointMatrices);
+            if (_hasJoints )
+            {
+                _dynamicBufferUpdater.SetJointMatrices(matrices);
+                _outputNeedsUpdating = true;
+            }
+        }
+
+        public void DispatchAndUpdateOutputs()
+        {
+            if (!_outputNeedsUpdating) { return; }
+
+            SetShaderKeywordsAndProperties();
+            _dynamicBufferUpdater.UpdateComputeBufferBeforeDispatch();
+
+            // Potentially do three dispatches if there are morphs and skinning and neither
+            int startIndex = 0;
+            if (_hasMorphTargets)
+            {
+                const bool applyMorphs = true;
+                ComputeShaderDispatch(startIndex, _numMorphedVertices, applyMorphs, _hasJoints);
+                startIndex += _numMorphedVertices;
+            }
+
+            if (_hasJoints)
+            {
+                const bool applyMorphs = false;
+                const bool applyJoints = true;
+                ComputeShaderDispatch(startIndex, _numSkinningOnlyVerts, applyMorphs, applyJoints);
+                startIndex += _numSkinningOnlyVerts;
+            }
+
+            if (_numPlainVerts != 0)
+            {
+                const bool applyMorphs = false;
+                const bool applyJoints = false;
+                ComputeShaderDispatch(startIndex, _numPlainVerts, applyMorphs, applyJoints);
+            }
+
+            _outputNeedsUpdating = false;
+        }
+
+        public void SetWriteDestinationInDynamicBuffer(SkinningOutputFrame writeDestination)
+        {
+            Debug.Assert((int)writeDestination < (int)_maxOutputFrames);
+            var writeDestinationAsUint = (UInt32)SkinningOutputFrame.FrameZero;
+            _dynamicBufferUpdater.SetWriteDestinationInDynamicBuffer(writeDestinationAsUint);
+        }
+
+        private void ComputeShaderDispatch(int startIndex, int numVerts, bool applyMorphs, bool applyJoints)
+        {
+            int numWorkGroups = (numVerts + _threadsPerWorkGroup - 1) / _threadsPerWorkGroup;
+
+            int dispatchEndIndex = startIndex + numVerts - 1;
+            SetPerDispatchShaderProperties(startIndex, dispatchEndIndex, applyMorphs, applyJoints);
+            _shader.Dispatch(_shaderKernel, numWorkGroups, 1, 1);
+        }
+
+        private void CreateDynamicDataBuffer(
+            int numMeshVerts,
+            int numMeshMorphTargets,
+            int numMeshJoints,
+            int numOutputSlicesPerAttribute)
+        {
+            // Data layout for dynamic buffer is
+            // [numMeshVerts number of VertexInstanceData] ->
+            // a single MeshInstanceMetaData ->
+            // [numMorphTargets floats for morph target weights] ->
+            // [2 * numJoints float4x4 for joint matrices] ->
+            // a single uint for the "double buffer slice"
+
+            // Calculate the necessary offset/sizes
+            int vertexInstanceDataSizePerInstance = UnsafeUtility.SizeOf<VertexInstanceData>();
+            int totalVertexInstancesSize = vertexInstanceDataSizePerInstance * numMeshVerts;
+            int meshInstanceDataSize = UnsafeUtility.SizeOf<MeshInstanceMetaData>();
+            int morphTargetWeightsSize = WEIGHTS_STRIDE_BYTES * numMeshMorphTargets;
+            int jointMatricesSize = numMeshJoints * JOINT_MATRIX_STRIDE_BYTES * 2; // *2 because normal + model matrices
+            int sliceOutputSize = numOutputSlicesPerAttribute > 1 ? SLICE_OUTPUT_STRIDE_BYTES : 0;
+
+            int totalDynamicBufferSize =
+                totalVertexInstancesSize +
+                meshInstanceDataSize +
+                morphTargetWeightsSize +
+                jointMatricesSize +
+                sliceOutputSize;
+
+            const int meshStaticDataOffset = 0; // always 0 (no batching)
+            const int vertexInstanceArrayOffset = 0; // always 0 (no batching)
+            int meshInstanceDataOffset = totalVertexInstancesSize;
+            int morphTargetWeightsOffset = meshInstanceDataOffset + meshInstanceDataSize;
+            int jointMatricesOffset = morphTargetWeightsOffset + morphTargetWeightsSize;
+            int writeToSliceOffset = jointMatricesOffset + jointMatricesSize;
+
+            /////////////////////////////////////////////////////////////////////////////////////////////////
+            // First, create VertexInstanceData structs that will be at the beginning of dynamic data buffer
+            var vertexInstanceArray = new NativeArray<VertexInstanceData>(
+                numMeshVerts,
+                Allocator.Persistent,
+                NativeArrayOptions.UninitializedMemory);
+            NativeArray<MeshInstanceMetaData> singleMeshInstanceMetaData = default;
+
+            try
+            {
+                // Fill in vertex instance data
+                for (int i = 0; i < numMeshVerts; i++)
+                {
+                    vertexInstanceArray[i] = new VertexInstanceData
+                    {
+                        // meshInstanceDataOffsetBytes(x) vertexIndexInMesh(y)
+                        properties = new Vector4UInt
+                        {
+                            x = (uint)meshInstanceDataOffset,
+                            y = (uint)i
+                        }
+                    };
+                }
+
+                ////////////////////////////////////////////////////////////////////////////////////////////////////
+                // Now write out the single MeshInstanceMetaData
+                singleMeshInstanceMetaData = new NativeArray<MeshInstanceMetaData>(
+                    1,
+                    Allocator.Persistent,
+                    NativeArrayOptions.UninitializedMemory);
+
+                singleMeshInstanceMetaData[0] = new MeshInstanceMetaData
+                {
+                    // meshStaticDataOffsetBytes(x)
+                    // morphTargetWeightsOffsetBytes(y)
+                    // jointMatricesOffsetBytes(z)
+                    // writeToSecondSliceAddress (w) (if double buffered)
+                    properties = new Vector4UInt
+                    {
+                        x = meshStaticDataOffset,
+                        y = (uint)morphTargetWeightsOffset,
+                        z = (uint)jointMatricesOffset,
+                        w = (uint)writeToSliceOffset
+                    },
+                    outputFrenetOffsets = new Vector4UInt
+                    {
+                        // outputPositionBufferOffsetBytes(x)
+                        // outputFrenetBufferOffsetBytes(y)
+                        x = 0,
+                        y = 0
+                    }
+                };
+
+#if (COMPUTE_BUFFER_PARTIAL_UPDATE_ALLOWED)
+                    _dynamicBufferUpdater = new DynamicBufferDirectUpdater(
+                        totalDynamicBufferSize,
+                        vertexInstanceArray,
+                        vertexInstanceArrayOffset,
+                        singleMeshInstanceMetaData,
+                        meshInstanceDataOffset,
+                        morphTargetWeightsOffset,
+                        jointMatricesOffset,
+                        writeToSliceOffset);
+#else
+                    _dynamicBufferUpdater = new DynamicBufferUpdaterViaNativeArray(
+                        totalDynamicBufferSize,
+                        vertexInstanceArray,
+                        vertexInstanceArrayOffset,
+                        singleMeshInstanceMetaData,
+                        meshInstanceDataOffset,
+                        morphTargetWeightsOffset,
+                        jointMatricesOffset,
+                        writeToSliceOffset);
+#endif
+            }
+            finally
+            {
+                vertexInstanceArray.Dispose();
+                if (singleMeshInstanceMetaData.IsCreated)
+                {
+                    singleMeshInstanceMetaData.Dispose();
+                }
+            }
+        }
+
+        private static void SetComputeBufferDataFromNativeArray<T>(
+            ComputeBuffer computeBuffer,
+            NativeArray<T> nativeArr,
+            int byteOffsetInComputeBuffer) where T : struct
+        {
+            int stride = computeBuffer.stride;
+            var arrayOfUints = nativeArr.Reinterpret<uint>(UnsafeUtility.SizeOf<T>());
+            computeBuffer.SetData(
+                arrayOfUints,
+                0,
+                byteOffsetInComputeBuffer / stride,
+                arrayOfUints.Length);
+        }
+
+        private static void CopyNativeArrayToBackingBuffer<T>(
+            NativeArray<byte> backingBuffer,
+            NativeArray<T> nativeArr,
+            int byteOffset) where T : struct
+        {
+            var arrayOfBytes = nativeArr.Reinterpret<byte>(UnsafeUtility.SizeOf<T>());
+            var sourceSlice = arrayOfBytes.Slice();
+            var destSlice = backingBuffer.Slice(byteOffset, arrayOfBytes.Length);
+            destSlice.CopyFrom(sourceSlice);
+        }
+
+        private void CreateOutputBuffers(
+            int numMeshVerts,
+            bool hasTangents,
+            int numOutputSlices)
+        {
+            _positionOutputBuffer =
+                new ComputeBuffer(
+                    GetPositionOutputBufferSize(numMeshVerts, _outputPositionPrecision, numOutputSlices),
+                    BAG_OF_UINTS_STRIDE,
+                    ComputeBufferType.Structured);
+            _frenetOutputBuffer = new ComputeBuffer(
+                GetFrenetOutputBufferSize(numMeshVerts, hasTangents, numOutputSlices),
+                BAG_OF_UINTS_STRIDE,
+                ComputeBufferType.Structured);
+        }
+
+        private void GetShaderKernel(
+            bool hasTangents,
+            int numOutputSlices)
+        {
+            string kernelName = String.Empty;
+            switch (numOutputSlices)
+            {
+                case 1:
+                    kernelName = hasTangents ? HAS_TANGENTS_KERNEL_NAME : KERNEL_NAME;
+                    break;
+                case 2:
+                    kernelName = hasTangents ? HAS_TANGENTS_DOUBLE_BUFFER_KERNEL_NAME : DOUBLE_BUFFER_KERNEL_NAME;
+                    break;
+                case 3:
+                    kernelName = hasTangents ? HAS_TANGENTS_TRIPLE_BUFFER_KERNEL_NAME : TRIPLE_BUFFER_KERNEL_NAME;
+                    break;
+            }
+
+            if (_shader.HasKernel(kernelName))
+            {
+                _shaderKernel = _shader.FindKernel(kernelName);
+            }
+            else
+            {
+                // No kernel, just default to 0
+                OvrAvatarLog.LogWarning("Error finding compute shader kernel, using default compute kernel", LOG_SCOPE);
+                kernelName = KERNEL_NAME;
+
+                _shaderKernel = 0;
+            }
+
+            _shader.GetKernelThreadGroupSizes(_shaderKernel, out uint threadGroupSizeU, out _, out _);
+            _threadsPerWorkGroup = (int)threadGroupSizeU;
+        }
+
+        private void SetShaderKeywordsAndProperties()
+        {
+            // Unfortunately compute shader keywords aren't supported in Unity 2019, so do nothing
+            // in regards to keywords
+
+            // To prevent needing a copy of the compute shader, just set properties needed (saves CPU time?)
+            _shader.SetInt(_propertyIds.MaxJointsToSkinPropId, MaxJointsToSkin);
+
+            _shader.SetBuffer(_shaderKernel, _propertyIds.StaticBufferPropId, _staticBuffer);
+            _shader.SetBuffer(_shaderKernel, _propertyIds.DynamicBufferPropId, _dynamicBufferUpdater.DynamicBuffer);
+            _shader.SetBuffer(_shaderKernel, _propertyIds.PositionOutputBufferPropId, _positionOutputBuffer);
+            _shader.SetBuffer(_shaderKernel, _propertyIds.FrenetOutputBufferPropId, _frenetOutputBuffer);
+            _shader.SetInt(
+                _propertyIds.VertexPositionDataFormatPropId,
+                OvrComputeUtils.GetEncodingPrecisionShaderValue(_inputPositionPrecision));
+            _shader.SetInt(
+                _propertyIds.MorphTargetDeltasFormatPropId,
+                OvrComputeUtils.GetEncodingPrecisionShaderValue(_morphDeltasPrecision));
+            _shader.SetInt(
+                _propertyIds.PositionOutputDataFormatPropId,
+                OvrComputeUtils.GetEncodingPrecisionShaderValue(_outputPositionPrecision));
+            _shader.SetInt(
+                _propertyIds.JointIndicesDataFormatPropId,
+                OvrComputeUtils.GetEncodingPrecisionShaderValue(_jointIndicesPrecision));
+        }
+
+        private void SetPerDispatchShaderProperties(int dispatchStartVertIndex, int dispatchEndVertIndex, bool applyMorphs, bool applyJoints)
+        {
+            _shader.SetInt(_propertyIds.DispatchVertStartIndexPropId, dispatchStartVertIndex);
+            _shader.SetInt(_propertyIds.DispatchVertEndIndexPropId, dispatchEndVertIndex);
+
+            _shader.SetBool(_propertyIds.EnableMorphTargetsPropId, applyMorphs);
+            _shader.SetBool(_propertyIds.EnableSkinningPropId, applyJoints);
+        }
+
+        private static int GetPositionOutputBufferSize(
+            int numMeshVerts,
+            GpuSkinningConfiguration.TexturePrecision positionEncodingPrecision,
+            int numOutputSlices)
+        {
+            int positionStrideBytes = 0;
+            switch (positionEncodingPrecision)
+            {
+                case GpuSkinningConfiguration.TexturePrecision.Float:
+                    positionStrideBytes = 4 * 4; // 4 32-bit floats
+                    break;
+                case GpuSkinningConfiguration.TexturePrecision.Half:
+                case GpuSkinningConfiguration.TexturePrecision.Unorm16:
+                    positionStrideBytes = 4 * 2; // 4 16-bit halfs/unorms
+                    break;
+                default:
+                    // Unsupported output format, this should probably be handled
+                    // further up the chain, but catch here just in case
+                    OvrAvatarLog.LogError($"Unsupported format {positionEncodingPrecision}.");
+                    break;
+            }
+
+            int positionOutputSize = positionStrideBytes * numMeshVerts * numOutputSlices;
+
+            return positionOutputSize / BAG_OF_UINTS_STRIDE;
+        }
+
+        private static int GetFrenetOutputBufferSize(int numMeshVerts, bool hasTangents, int numOutputSlices)
+        {
+            const int FRENET_ATTRIBUTE_STRIDE_BYTES = 4; // only supporting 10-10-10-2 format for normal/tangents
+            int frenetOutputSize = FRENET_ATTRIBUTE_STRIDE_BYTES * numMeshVerts * (hasTangents ? 2 : 1) * numOutputSlices;
+
+            return frenetOutputSize / BAG_OF_UINTS_STRIDE;
+        }
+
+        [StructLayout(LayoutKind.Sequential)]
+        private struct Vector4UInt
+        {
+            public uint x, y, z, w;
+        }
+
+        [StructLayout(LayoutKind.Sequential)]
+        private struct MeshInstanceMetaData {
+            public Vector4UInt properties;
+            public Vector4UInt outputFrenetOffsets;
+        }
+
+        [StructLayout(LayoutKind.Sequential)]
+        private struct VertexInstanceData {
+            public Vector4UInt properties;
+        }
+
+        private readonly int _numMorphedVertices;
+        private readonly int _numSkinningOnlyVerts;
+        private readonly int _numPlainVerts; //no morphs or skinning
+
+        private readonly int _numMorphTargetWeights;
+        private readonly int _numJointMatrices;
+
+        private readonly MaxOutputFrames _maxOutputFrames;
+
+        private int _shaderKernel;
+        private int _threadsPerWorkGroup;
+        private ComputeShader _shader;
+
+        private readonly bool _hasMorphTargets;
+        private readonly bool _hasJoints;
+
+        private bool _outputNeedsUpdating;
+
+        private readonly CAPI.ovrGpuSkinningEncodingPrecision _inputPositionPrecision;
+        private readonly CAPI.ovrGpuSkinningEncodingPrecision _morphDeltasPrecision;
+        private readonly CAPI.ovrGpuSkinningEncodingPrecision _jointIndicesPrecision;
+        private readonly GpuSkinningConfiguration.TexturePrecision _outputPositionPrecision;
+
+        private readonly ComputeBuffer _staticBuffer;
+        private ComputeBuffer _positionOutputBuffer;
+        private ComputeBuffer _frenetOutputBuffer;
+
+        private DynamicBufferUpdaterBase _dynamicBufferUpdater;
+
+        private readonly Vector3 _positionOutputScale;
+        private readonly Vector3 _positionOutputBias;
+
+        private static ComputePropertyIds _propertyIds = default;
+        private static void CheckPropertyIdInit()
+        {
+            if (!_propertyIds.IsValid)
+            {
+                _propertyIds = new ComputePropertyIds(ComputePropertyIds.InitMethod.PropertyToId);
+            }
+        }
+
+        public void Dispose()
+        {
+            Dispose(true);
+            GC.SuppressFinalize(this);
+        }
+
+        private void Dispose(bool isDispose)
+        {
+            if (isDispose)
+            {
+                _dynamicBufferUpdater?.Dispose();
+                _positionOutputBuffer?.Dispose();
+                _frenetOutputBuffer?.Dispose();
+            }
+            else
+            {
+                if (_dynamicBufferUpdater != null || _positionOutputBuffer != null || _frenetOutputBuffer != null)
+                {
+                    OvrAvatarLog.LogError($"OvrComputeMeshAnimator was not disposed before being destroyed", LOG_SCOPE);
+                }
+            }
+
+            _dynamicBufferUpdater = null;
+            _positionOutputBuffer = null;
+            _frenetOutputBuffer = null;
+        }
+
+        ~OvrComputeMeshAnimator()
+        {
+            Dispose(false);
+        }
+
+        private readonly struct ComputePropertyIds
+        {
+            public readonly int StaticBufferPropId;
+            public readonly int DynamicBufferPropId;
+            public readonly int PositionOutputBufferPropId;
+            public readonly int FrenetOutputBufferPropId;
+
+            public readonly int DispatchVertStartIndexPropId;
+            public readonly int DispatchVertEndIndexPropId;
+
+            public readonly int EnableMorphTargetsPropId;
+            public readonly int EnableSkinningPropId;
+            public readonly int MaxJointsToSkinPropId;
+
+            public readonly int VertexPositionDataFormatPropId;
+            public readonly int MorphTargetDeltasFormatPropId;
+            public readonly int PositionOutputDataFormatPropId;
+            public readonly int JointIndicesDataFormatPropId;
+
+            // These will both be 0 if default initialized, otherwise they are guaranteed unique
+            public bool IsValid => StaticBufferPropId != DynamicBufferPropId;
+
+            public enum InitMethod { PropertyToId }
+            public ComputePropertyIds(InitMethod initMethod)
+            {
+                StaticBufferPropId = Shader.PropertyToID("_StaticDataBuffer");
+                DynamicBufferPropId = Shader.PropertyToID("_DynamicDataBuffer");
+                PositionOutputBufferPropId = Shader.PropertyToID("_PositionOutputBuffer");
+                FrenetOutputBufferPropId = Shader.PropertyToID("_FrenetOutputBuffer");
+
+                DispatchVertStartIndexPropId = Shader.PropertyToID("_DispatchStartVertIndex");
+                DispatchVertEndIndexPropId = Shader.PropertyToID("_DispatchEndVertIndex");
+
+                EnableMorphTargetsPropId = Shader.PropertyToID("_EnableMorphTargets");
+                EnableSkinningPropId = Shader.PropertyToID("_EnableSkinning");
+                MaxJointsToSkinPropId = Shader.PropertyToID("_MaxJointsToSkin");
+
+                VertexPositionDataFormatPropId = Shader.PropertyToID("_VertexPositionsDataFormat");
+                MorphTargetDeltasFormatPropId = Shader.PropertyToID("_MorphTargetDeltasDataFormat");
+                PositionOutputDataFormatPropId = Shader.PropertyToID("_PositionOutputBufferDataFormat");
+                JointIndicesDataFormatPropId = Shader.PropertyToID("_JointIndicesDataFormat");
+            }
+        }
+
+        private abstract class DynamicBufferUpdaterBase : IDisposable
+        {
+            private readonly ComputeBuffer _dynamicBuffer;
+
+            protected int _weightsOffsetBytes;
+            protected int _matricesOffsetBytes;
+            protected int _writeDestinationOffsetBytes;
+
+            public abstract void SetMorphTargetWeights(NativeArray<float> weights);
+            public abstract void SetJointMatrices(NativeArray<Matrix4x4> matrices);
+            public abstract void SetWriteDestinationInDynamicBuffer(uint writeDestination);
+
+            public abstract void UpdateComputeBufferBeforeDispatch();
+
+            public ComputeBuffer DynamicBuffer => _dynamicBuffer;
+
+            protected DynamicBufferUpdaterBase(
+                int dynamicBufferSizeBytes,
+                int weightsOffsetBytes,
+                int matricesOffsetBytes,
+                int writeDestinationOffsetBytes)
+            {
+                _weightsOffsetBytes = weightsOffsetBytes;
+                _matricesOffsetBytes = matricesOffsetBytes;
+                _writeDestinationOffsetBytes = writeDestinationOffsetBytes;
+
+                _dynamicBuffer = new ComputeBuffer(
+                    dynamicBufferSizeBytes / BAG_OF_UINTS_STRIDE,
+                    BAG_OF_UINTS_STRIDE,
+                    ComputeBufferType.Raw);
+            }
+            public virtual void Dispose()
+            {
+                _dynamicBuffer.Dispose();
+            }
+        }
+
+        private class DynamicBufferDirectUpdater : DynamicBufferUpdaterBase
+        {
+            private readonly List<uint> _writeDestinationList = new List<uint>(1);
+
+            public DynamicBufferDirectUpdater(
+                int dynamicBufferSizeBytes,
+                NativeArray<VertexInstanceData> vertexInstanceDataArray,
+                int vertexInstanceDataOffsetBytes,
+                NativeArray<MeshInstanceMetaData> meshInstancesMetaData,
+                int meshInstancesMetaDataOffsetBytes,
+                int weightsOffsetBytes,
+                int matricesOffsetBytes,
+                int writeDestinationOffsetBytes) :
+                base(
+                    dynamicBufferSizeBytes,
+                    weightsOffsetBytes,
+                    matricesOffsetBytes,
+                    writeDestinationOffsetBytes)
+            {
+                SetComputeBufferDataFromNativeArray(
+                    DynamicBuffer,
+                    vertexInstanceDataArray,
+                    vertexInstanceDataOffsetBytes);
+
+                SetComputeBufferDataFromNativeArray(
+                    DynamicBuffer,
+                    meshInstancesMetaData,
+                    meshInstancesMetaDataOffsetBytes);
+            }
+
+            public override void SetMorphTargetWeights(NativeArray<float> weights)
+            {
+                SetComputeBufferDataFromNativeArray(
+                    DynamicBuffer,
+                    weights,
+                    _weightsOffsetBytes);
+            }
+
+            public override void SetJointMatrices(NativeArray<Matrix4x4> matrices)
+            {
+                SetComputeBufferDataFromNativeArray(
+                    DynamicBuffer,
+                    matrices,
+                    _matricesOffsetBytes);
+            }
+
+            public override void SetWriteDestinationInDynamicBuffer(uint writeDestinationAsUint)
+            {
+                _writeDestinationList[0] = writeDestinationAsUint;
+
+                int stride = DynamicBuffer.stride;
+                DynamicBuffer.SetData(
+                    _writeDestinationList,
+                    0,
+                    _writeDestinationOffsetBytes / stride,
+                    _writeDestinationList.Count);
+            }
+
+            public override void UpdateComputeBufferBeforeDispatch()
+            {
+                // Intentionally empty
+            }
+        }
+
+        // Due to a Unity bug, have a version which updates a NativeArray and then copies
+        // that whole to the compute buffer, once per frame
+        private class DynamicBufferUpdaterViaNativeArray : DynamicBufferUpdaterBase
+        {
+            private NativeArray<byte> _backingBuffer;
+
+            public DynamicBufferUpdaterViaNativeArray(
+                int dynamicBufferSizeBytes,
+                NativeArray<VertexInstanceData> vertexInstanceDataArray,
+                int vertexInstanceDataOffsetBytes,
+                NativeArray<MeshInstanceMetaData> meshInstancesMetaData,
+                int meshInstancesMetaDataOffsetBytes,
+                int weightsOffsetBytes,
+                int matricesOffsetBytes,
+                int writeDestinationOffsetBytes) :
+                base(
+                    dynamicBufferSizeBytes,
+                    weightsOffsetBytes,
+                    matricesOffsetBytes,
+                    writeDestinationOffsetBytes)
+            {
+                // Declare a "backing" native array that is the same size
+                // as the compute buffer which is updated and then copied to compute buffer
+                _backingBuffer = new NativeArray<byte>(
+                    dynamicBufferSizeBytes,
+                    Allocator.Persistent,
+                    NativeArrayOptions.UninitializedMemory);
+
+                CopyNativeArrayToBackingBuffer(
+                    _backingBuffer,
+                    vertexInstanceDataArray,
+                    vertexInstanceDataOffsetBytes);
+                CopyNativeArrayToBackingBuffer(
+                    _backingBuffer,
+                    meshInstancesMetaData,
+                    meshInstancesMetaDataOffsetBytes);
+            }
+
+            public override void Dispose()
+            {
+                base.Dispose();
+
+                if (_backingBuffer.IsCreated)
+                {
+                    _backingBuffer.Dispose();
+                }
+            }
+
+            public override void SetMorphTargetWeights(NativeArray<float> weights)
+            {
+                CopyNativeArrayToBackingBuffer(
+                    _backingBuffer,
+                    weights,
+                    _weightsOffsetBytes);
+            }
+
+            public override void SetJointMatrices(NativeArray<Matrix4x4> matrices)
+            {
+                CopyNativeArrayToBackingBuffer(
+                    _backingBuffer,
+                    matrices,
+                    _matricesOffsetBytes);
+            }
+
+            public override void SetWriteDestinationInDynamicBuffer(uint writeDestinationAsUint)
+            {
+                _backingBuffer.ReinterpretStore(_writeDestinationOffsetBytes, writeDestinationAsUint);
+            }
+
+            public override void UpdateComputeBufferBeforeDispatch()
+            {
+                // Copy whole thing to compute buffer
+                SetComputeBufferDataFromNativeArray(DynamicBuffer, _backingBuffer, 0);
+            }
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrComputeMeshAnimator.cs.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrComputeMeshAnimator.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..43ee0ea59deda15e1c46702409984fdb2fd7ed25
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrComputeMeshAnimator.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 4311df0df2bc1974da385e80d974447e
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrComputeUtils.cs b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrComputeUtils.cs
new file mode 100644
index 0000000000000000000000000000000000000000..9684435a097e7b85c0d6bb09997f14259286c078
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrComputeUtils.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using Oculus.Avatar2;
+using Unity.Collections;
+using Unity.Collections.LowLevel.Unsafe;
+using UnityEngine;
+
+namespace Oculus.Skinning.GpuSkinning
+{
+    internal static class OvrComputeUtils
+    {
+        private const string LOG_SCOPE = nameof(OvrComputeUtils);
+        // These are pulled from the shader
+        private const int FORMAT_FLOAT_32 = 0;
+        private const int FORMAT_HALF_16  = 1;
+        private const int FORMAT_UNORM_16 = 2;
+        private const int FORMAT_UINT_16 = 3;
+        private const int FORMAT_SNORM_10_10_10_2 = 4;
+        // private const int FORMAT_UNORM_8  = 5;
+        private const int FORMAT_UINT_8 = 6;
+
+        public static int GetEncodingPrecisionShaderValue(CAPI.ovrGpuSkinningEncodingPrecision precision)
+        {
+            switch (precision)
+            {
+                case CAPI.ovrGpuSkinningEncodingPrecision.ENCODING_PRECISION_FLOAT:
+                    return FORMAT_FLOAT_32;
+                case CAPI.ovrGpuSkinningEncodingPrecision.ENCODING_PRECISION_HALF:
+                    return FORMAT_HALF_16;
+                case CAPI.ovrGpuSkinningEncodingPrecision.ENCODING_PRECISION_UINT16:
+                    return FORMAT_UINT_16;
+                case CAPI.ovrGpuSkinningEncodingPrecision.ENCODING_PRECISION_10_10_10_2:
+                    return FORMAT_SNORM_10_10_10_2;
+                case CAPI.ovrGpuSkinningEncodingPrecision.ENCODING_PRECISION_UINT8:
+                    return FORMAT_UINT_8;
+            }
+
+            OvrAvatarLog.LogError($"Unsupported format in compute shader {precision}.", LOG_SCOPE);
+            return FORMAT_FLOAT_32;
+        }
+
+        public static int GetEncodingPrecisionShaderValue(GpuSkinningConfiguration.TexturePrecision precision)
+        {
+            switch (precision)
+            {
+                case GpuSkinningConfiguration.TexturePrecision.Float:
+                    return FORMAT_FLOAT_32;
+                case GpuSkinningConfiguration.TexturePrecision.Half:
+                    return FORMAT_HALF_16;
+                case GpuSkinningConfiguration.TexturePrecision.Snorm10:
+                    return FORMAT_SNORM_10_10_10_2;
+                case GpuSkinningConfiguration.TexturePrecision.Unorm16:
+                    return FORMAT_UNORM_16;
+                case GpuSkinningConfiguration.TexturePrecision.Byte:
+                    return FORMAT_UINT_8;
+            }
+
+            OvrAvatarLog.LogError($"Unsupported format in compute shader {precision}.", LOG_SCOPE);
+            return FORMAT_FLOAT_32;
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrComputeUtils.cs.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrComputeUtils.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..44c6d3865b620627d2df56511202eced93e8ee00
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrComputeUtils.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 419823825e6ed9a45956f43be2b1f26e
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrExpandableTextureArray.cs b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrExpandableTextureArray.cs
new file mode 100644
index 0000000000000000000000000000000000000000..5ff56d974eabd5567a552000f844e73c2bcbc011
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrExpandableTextureArray.cs
@@ -0,0 +1,263 @@
+using System;
+
+using Oculus.Avatar2;
+
+using Unity.Collections;
+
+using UnityEngine;
+using UnityEngine.Experimental.Rendering;
+
+namespace Oculus.Skinning.GpuSkinning
+{
+    public class OvrExpandableTextureArray
+    {
+        private const string LOG_SCOPE = "OvrExpandableTextureArray";
+
+        // TODO: Sender -> OvrExpandableTextureArray
+        // Declare the delegate (if using non-generic pattern).
+        public delegate void ArrayGrowthEventHandler(OvrExpandableTextureArray sender, Texture2DArray newArray);
+
+        private event ArrayGrowthEventHandler _ArrayResized;
+        // Declare the event.
+        public event ArrayGrowthEventHandler ArrayResized
+        {
+            add
+            {
+                _ArrayResized += value;
+                if (HasTexArray)
+                {
+                    value(this, GetTexArray());
+                }
+            }
+            remove
+            {
+                _ArrayResized -= value;
+            }
+        }
+
+        public string name => _texArray.name;
+        // TODO: Remove, always true
+        public bool HasTexArray => _texArray != null;
+
+        public int Width => _texArray.width;
+        public int Height => _texArray.height;
+        public TextureFormat Format => _texArray.format;
+
+        public bool HasMips =>
+#if UNITY_2019_3_OR_NEWER
+            _texArray.mipmapCount
+#else
+            MIP_COUNT
+#endif
+         > 1;
+
+        private readonly GraphicsFormat _graphicsFormat;
+
+        public bool IsLinear => IS_LINEAR;
+
+        public bool CheckFit(in Vector2Int size)
+        {
+            return _texArray.CheckFit(size);
+        }
+        public OvrExpandableTextureArray(string name, Int32 width, Int32 height, GraphicsFormat texFormat)
+            : this(name, _ConvertDimension(width), _ConvertDimension(height), texFormat) { }
+
+        public OvrExpandableTextureArray(string name, UInt32 width, UInt32 height, GraphicsFormat texFormat)
+        {
+            Debug.Assert(width > 0 && width <= MAX_TEX_DIM);
+            Debug.Assert(height > 0 && height <= MAX_TEX_DIM);
+
+            _graphicsFormat = texFormat;
+
+            try
+            {
+#if UNITY_2019_3_OR_NEWER
+                _texArray = new Texture2DArray((Int32)width, (Int32)height, 1, texFormat, TextureCreationFlags.None, MIP_COUNT);
+#else
+                _texArray = new Texture2DArray((Int32)width, (Int32)height, 1, texFormat, MIP_COUNT > 1 ? TextureCreationFlags.MipChain : TextureCreationFlags.None);
+#endif
+                _texArray.name = name;
+
+                ConfigureTexArray(_texArray);
+
+                // We will only copy into this texture with GPU to GPU copies from temp textures. So just drop the cpu copy immediately.
+                // Wish we could create the texture without the cpu backing copy to begin with. or at least drop it without paying for
+                // a one time copy of garbage memory to GPU.
+                _texArray.Apply(false, true);
+            }
+            catch (Exception e)
+            {
+                OvrAvatarLog.LogError($"Texture2DArray {name} ({width}, {height}) allocation failure - {e.Message}", logScope);
+            }
+
+            _atlasPackerId = CAPI.OvrGpuSkinning_AtlasPackerCreate(
+                (uint)width,
+                (uint)height,
+                CAPI.AtlasPackerPackingAlgortihm.Runtime);
+        }
+
+
+        public void Destroy()
+        {
+            if (_atlasPackerId != CAPI.AtlasPackerId.Invalid)
+            {
+                CAPI.OvrGpuSkinning_AtlasPackerDestroy(_atlasPackerId);
+            }
+            if (_texArray != null)
+            {
+                Texture2DArray.Destroy(_texArray);
+            }
+        }
+
+        public OvrSkinningTypes.Handle AddEmptyBlock(in CAPI.ovrAvatar2Vector2u dims)
+        {
+            return AddEmptyBlock(dims.x, dims.y);
+        }
+        public OvrSkinningTypes.Handle AddEmptyBlock(UInt32 width, UInt32 height)
+        {
+            // Call out to atlas packer to get packing rectangle
+            if (_texArray == null || _atlasPackerId == CAPI.AtlasPackerId.Invalid
+                || width > _texArray.width || height > _texArray.height)
+            {
+                return OvrSkinningTypes.Handle.kInvalidHandle;
+            }
+
+            OvrSkinningTypes.Handle packerHandle = CAPI.OvrGpuSkinning_AtlasPackerAddBlock(
+                _atlasPackerId,
+                width,
+                height);
+
+            //CAPI.ovrTextureLayoutResult packResult = CAPI.ovrTextureLayoutResult.INVALID_LAYOUT;
+            var packResult = CAPI.OvrGpuSkinning_AtlasPackerResultsForBlock(
+                _atlasPackerId,
+                packerHandle);
+            OvrAvatarLog.AssertConstMessage(packResult.IsValid, "Invalid Layout");
+
+            // See if tex array needs to grow
+            // Tex slice is a 0 based index, where num tex array slices isn't
+            if (packResult.texSlice >= _texArray.depth)
+            {
+                GrowTexArray((int)(packResult.texSlice + 1));
+            }
+
+            return packerHandle;
+        }
+
+        public CAPI.ovrTextureLayoutResult GetLayout(OvrSkinningTypes.Handle handle)
+        {
+            //CAPI.ovrTextureLayoutResult layout = CAPI.ovrTextureLayoutResult.INVALID_LAYOUT;
+            return CAPI.OvrGpuSkinning_AtlasPackerResultsForBlock(_atlasPackerId, handle);
+        }
+
+        public void CopyFromTexture(CAPI.ovrTextureLayoutResult layout, Texture2D tempTex)
+        {
+#if UNITY_2022_2_OR_NEWER
+            if (QualitySettings.globalTextureMipmapLimit == 0) {
+#else
+            if (QualitySettings.masterTextureLimit == 0) {
+#endif
+                Graphics.CopyTexture(
+                    tempTex,  // src
+                    0,    // srcElement
+                    0,    // srcMip
+                    0,    // srcX
+                    0,    // srcY
+                    layout.w, // srcWidth
+                    layout.h, // srcHeight
+                    _texArray,    // dst
+                    (int)layout.texSlice, // dstElement
+                    0,    // dstMip
+                    layout.x, // dstX
+                    layout.y  // dstY
+                );
+            } else {
+                // the above call to CopyTexture fails during half-res or quarter-res Quality. See Unity bug: https://fogbugz.unity3d.com/default.asp?1312568_d9rfr8bdr8od1sj7
+                OvrAvatarLog.AssertConstMessage(layout.x == 0 && layout.y == 0 && layout.w == tempTex.width && layout.h == tempTex.height,
+                    "masterTextureLimit non-zero during GPU Skinning initialiation. This prohibits use of layout offsets. To use Avatar GPUSkinning, disable reduced texture resolution Project Quality Setttings.");
+
+                Graphics.CopyTexture(
+                    tempTex,    // src
+                    0,  // srcElement
+                    0,  // srcMip
+                    _texArray, // dst
+                    (int)layout.texSlice, // dstElement
+                    0   // dstMip
+                );
+            }
+
+            _texArray.IncrementUpdateCount();
+        }
+
+        public void RemoveBlock(OvrSkinningTypes.Handle handle)
+        {
+            CAPI.OvrGpuSkinning_AtlasPackerRemoveBlock(_atlasPackerId, handle);
+        }
+
+        public Texture2DArray GetTexArray()
+        {
+            return _texArray;
+        }
+
+        private void ConfigureTexArray(Texture2DArray newArray)
+        {
+            newArray.filterMode = FilterMode.Point;
+            newArray.anisoLevel = 0;
+            newArray.wrapMode = TextureWrapMode.Clamp;
+        }
+
+        private void GrowTexArray(int newDepth)
+        {
+            // Unfortunately the Unity API doesn't allow for expansion
+            // of the same texture 2D array, so will need to create a new one
+            // here and inform listeners of its creation
+
+            // Copy all slices to a temporary texture array while the original one expands
+            // TODO*: Have a pool/temporary allocation of texture arrays to not create new ones
+
+            int oldDepth = _texArray.depth;
+
+            // Create new expanded texture array
+            var newArray = new Texture2DArray(
+                _texArray.width,
+                _texArray.height,
+                newDepth,
+                _graphicsFormat,
+                MIP_COUNT > 1 ? TextureCreationFlags.MipChain : TextureCreationFlags.None
+#if UNITY_2019_3_OR_NEWER
+                , MIP_COUNT
+#endif
+                );
+
+            ConfigureTexArray(newArray);
+
+            var oldArray = _texArray;
+            for (int texSlice = 0; texSlice < oldDepth; texSlice++)
+            {
+                Graphics.CopyTexture(oldArray, texSlice, newArray, texSlice);
+            }
+
+            newArray.name = oldArray.name;
+            newArray.IncrementUpdateCount();
+            _texArray = newArray;
+            Texture2DArray.Destroy(oldArray);
+
+            // Inform listeners
+            _ArrayResized?.Invoke(this, _texArray);
+        }
+
+        private const string logScope = "OvrExpandableTextureArray";
+        private static readonly UInt32 MAX_TEX_DIM = (UInt32)SystemInfo.maxTextureSize;
+        private const int MIP_COUNT = 1;
+        private const bool IS_LINEAR = true;
+
+        private Texture2DArray _texArray;
+
+        private readonly CAPI.AtlasPackerId _atlasPackerId;
+        
+        private static UInt32 _ConvertDimension(Int32 dim)
+        {
+            Debug.Assert(dim >= 0);
+            return (UInt32)dim;
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrExpandableTextureArray.cs.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrExpandableTextureArray.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..46e08f040154ce922a676baeebe564a7fecb1ae3
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrExpandableTextureArray.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: d40eabd4b1d5a4f4cba51a9e7f9a8129
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrFreeListBufferTracker.cs b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrFreeListBufferTracker.cs
new file mode 100644
index 0000000000000000000000000000000000000000..7364701a2111cb5073f0e104f4d9195a8ede4942
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrFreeListBufferTracker.cs
@@ -0,0 +1,244 @@
+using System.Collections.Generic;
+
+namespace Oculus.Skinning.GpuSkinning
+{
+    public class OvrFreeListBufferTracker
+    {
+        public struct LayoutResult
+        {
+            public static readonly LayoutResult Invalid = new LayoutResult(int.MaxValue, 0);
+            public LayoutResult(int startIdx, int numAtIdx)
+            {
+                startIndex = startIdx;
+                count = numAtIdx;
+            }
+
+            public bool IsValid => startIndex < int.MaxValue;
+
+            public readonly int startIndex;
+            public readonly int count;
+        };
+
+        public OvrFreeListBufferTracker(int maxSize)
+        {
+            _nodes = new LinkedList<TrackerNode>();
+            _freeNodes = new List<LinkedListNode<TrackerNode>>();
+            _handleGenerator = new OvrHandleGenerator();
+            _handleToNode = new Dictionary<OvrSkinningTypes.Handle, LinkedListNode<TrackerNode>>();
+
+            _sizeNeeded = 0;
+            _maxSize = maxSize;
+        }
+
+        public OvrSkinningTypes.Handle TrackBlock(int numInBlock)
+        {
+            OvrSkinningTypes.Handle handle = _handleGenerator.GetHandle();
+
+            // Look to see if there are free nodes that fit
+            var listNodeThatCanFit = FindFreeNodeThatCanFit(numInBlock);
+            if (listNodeThatCanFit != null)
+            {
+                var nodeThatCanFit = listNodeThatCanFit.Value;
+
+                // Remove from free nodes and update underlying node
+                _freeNodes.Remove(listNodeThatCanFit);
+
+                // See if node needs to be "split" into another node
+                // to be put onto the free list
+                if (nodeThatCanFit.size != numInBlock)
+                {
+                    // Create new node
+                    // New "free node" has reduced size from previously free node
+                    var newNode = new TrackerNode
+                    {
+                        startIndex = nodeThatCanFit.startIndex + numInBlock,
+                        size = nodeThatCanFit.size - numInBlock,
+                        isFree = true,
+                    };
+
+                    // Insert into nodes linked list and into free nodes list
+                    var addedNode = _nodes.AddAfter(listNodeThatCanFit, newNode);
+                    _freeNodes.Add(addedNode);
+
+                    // Sort free nodes
+                    _freeNodes.Sort(sComparer);
+                }
+
+                // Update no longer free node to be of new size
+                nodeThatCanFit.isFree = false;
+                nodeThatCanFit.size = numInBlock;
+
+                _handleToNode[handle] = listNodeThatCanFit;
+                return handle;
+            } // end if found a free node that can fit
+
+            // No existing node fits, make new node
+            int newNodesStartIndex = 0;
+            if (_nodes.Count != 0)
+            {
+                var lastNode = _nodes.Last.Value;
+                newNodesStartIndex = lastNode.startIndex + lastNode.size;
+            }
+
+            var newListNode = _nodes.AddLast(new TrackerNode
+            {
+                startIndex = newNodesStartIndex,
+                isFree = false,
+                size = numInBlock,
+            });
+
+            _sizeNeeded += numInBlock;
+
+            // Add to mapping
+            _handleToNode[handle] = newListNode;
+            return handle;
+        }
+
+        public LayoutResult GetLayoutInBufferForBlock(OvrSkinningTypes.Handle handle)
+        {
+            return _handleToNode.TryGetValue(handle, out LinkedListNode<TrackerNode> listNode) ?
+                new LayoutResult(listNode.Value.startIndex, listNode.Value.size) :
+                LayoutResult.Invalid;
+        }
+
+        public void FreeBlock(OvrSkinningTypes.Handle handle)
+        {
+            // See if even found
+            if (!_handleToNode.TryGetValue(handle, out LinkedListNode<TrackerNode> listNode))
+            {
+                return;
+            }
+
+            // Remove node from mapping and add to free nodes, but also
+            // see if it any of the node's previous or next node (or both) can be joined
+            // together to form a larger free node
+
+            // Remove from mapping
+            TrackerNode nodeToFree = listNode.Value;
+            _handleToNode.Remove(handle);
+
+            // Don't actually remove the node, just move it to free list
+            nodeToFree.isFree = true;
+
+            bool keepNode = true;
+            // See if "next node" is free, if so, "absorb" into this one
+            LinkedListNode<TrackerNode> otherListNode = listNode.Next;
+            if (otherListNode != null)
+            {
+                TrackerNode otherNode = otherListNode.Value;
+                if (otherNode.isFree)
+                {
+                    // Absorb the other node into "node to free"
+                    nodeToFree.size += otherNode.size;
+
+                    // Remove "other node" from free nodes
+                    // and nodes list (removes node)
+                    _freeNodes.Remove(otherListNode);
+                    _nodes.Remove(otherListNode);
+                }
+            }
+
+            // See if "previous node" is free, if so, "absorb" node into the previous one
+            otherListNode = listNode.Previous;
+            if (otherListNode != null)
+            {
+                TrackerNode otherNode = otherListNode.Value;
+                if (otherNode.isFree)
+                {
+                    otherNode.size += nodeToFree.size;
+                    keepNode = false;
+                }
+            }
+
+            if (!keepNode)
+            {
+                // Remove node from _nodes, do not add to free nodes list
+                _nodes.Remove(listNode);
+            }
+            else
+            {
+                _freeNodes.Add(listNode);
+
+                // Sort free nodes
+                _freeNodes.Sort(sComparer);
+            }
+        }
+
+        public int BufferSizeNeeded()
+        {
+            return _sizeNeeded;
+        }
+
+        public bool CanFit(int size)
+        {
+            if (CanFitInMaximumSize(size))
+            {
+                return true;
+            }
+
+            LinkedListNode<TrackerNode> listNodeThatCanFit = FindFreeNodeThatCanFit(size);
+            return listNodeThatCanFit != null;
+        }
+
+        private bool CanFitInMaximumSize(int size)
+        {
+            return BufferSizeNeeded() + size < _maxSize;
+        }
+
+        private LinkedListNode<TrackerNode> FindFreeNodeThatCanFit(int size)
+        {
+            // See if any of the free nodes can fit by using binary search
+            if (_freeNodes.Count == 0)
+            {
+                return null;
+            }
+
+            // See if any of the free nodes can fit by using binary search
+            LinkedListNode<TrackerNode> comparisonNode = new LinkedListNode<TrackerNode>(new TrackerNode
+            {
+                size = size,
+            });
+            int nodeThatCanFitIndex = _freeNodes.BinarySearch(comparisonNode, sComparer);
+
+            // See if a node that can fit exact number was found
+            if (nodeThatCanFitIndex < 0)
+            {
+                // Binary search returns bitwise compliment of index that can fit
+                // or the bitwise compliment of Count
+                nodeThatCanFitIndex = ~nodeThatCanFitIndex;
+            }
+
+            return nodeThatCanFitIndex != _freeNodes.Count ? _freeNodes[nodeThatCanFitIndex] : null;
+        }
+
+        private struct TrackerNode
+        {
+            public int size;
+            public int startIndex;
+            public bool isFree;
+        };
+
+        private class TrackerNodeComparer : IComparer<LinkedListNode<TrackerNode>>
+        {
+            public int Compare(LinkedListNode<TrackerNode> x, LinkedListNode<TrackerNode> y)
+            {
+                if (x.Value.size > y.Value.size)
+                    return 1;
+                if (x.Value.size < y.Value.size)
+                    return -1;
+                return 0;
+
+            }
+        }
+
+        private readonly LinkedList<TrackerNode> _nodes;
+        private readonly List<LinkedListNode<TrackerNode>> _freeNodes;
+        private readonly OvrHandleGenerator _handleGenerator;
+        private readonly Dictionary<OvrSkinningTypes.Handle, LinkedListNode<TrackerNode>> _handleToNode;
+
+        private static readonly TrackerNodeComparer sComparer = new TrackerNodeComparer();
+
+        private int _sizeNeeded;
+        private readonly int _maxSize;
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrFreeListBufferTracker.cs.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrFreeListBufferTracker.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..6bfddc865d616ce170987e54b33ae370cb4102b8
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrFreeListBufferTracker.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: ffa987cb4881d7243b41f58501fa8ff3
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuCombinerDrawCall.cs b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuCombinerDrawCall.cs
new file mode 100644
index 0000000000000000000000000000000000000000..90d383a78a90a630f35f3ce3a1c1aad9d04584fe
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuCombinerDrawCall.cs
@@ -0,0 +1,231 @@
+// Check for differences in update vs current state and ignore if they match
+//#define OVR_GPUSKINNING_DIFFERENCE_CHECK
+
+using Oculus.Avatar2;
+
+using System;
+using System.Collections.Generic;
+
+using UnityEngine;
+using UnityEngine.Profiling;
+
+namespace Oculus.Skinning.GpuSkinning
+{
+    internal class OvrGpuCombinerDrawCall
+    {
+        private const string logScope = "OvrGpuCombinerDrawCall";
+        internal OvrGpuCombinerDrawCall(
+            Shader combineShader,
+            OvrExpandableTextureArray morphTargetsSourceTex,
+            Vector4[] ranges,
+            bool hasTangents,
+            bool useSNorm10)
+        {
+            _morphTargetsSourceTex = morphTargetsSourceTex;
+            _combineMaterial = new Material(combineShader);
+            if (hasTangents)
+            {
+                _combineMaterial.EnableKeyword(OVR_HAS_TANGENTS);
+            }
+            if (useSNorm10)
+            {
+                _combineMaterial.EnableKeyword(OVR_MORPH_10_10_10_2);
+            }
+            _combineMaterial.SetVectorArray(MORPH_TARGET_RANGES_PROP, ranges);
+
+            _combineMaterial.SetBuffer(MORPH_TARGET_WEIGHTS_PROP, OvrAvatarManager.Instance.GpuSkinningController.GetWeightsBuffer());
+
+            _mesh = new Mesh
+            {
+                vertices = Array.Empty<Vector3>(),
+                uv = Array.Empty<Vector2>(),
+                colors = Array.Empty<Color>(),
+                triangles = Array.Empty<int>()
+            };
+
+            _meshLayout = new OvrFreeListBufferTracker(MAX_QUADS);
+            _handleToBlockData = new Dictionary<OvrSkinningTypes.Handle, BlockData>();
+
+            _morphTargetsSourceTex.ArrayResized += MorphTargetsSourceTexArrayResized;
+        }
+
+        private void MorphTargetsSourceTexArrayResized(object sender, Texture2DArray newArray)
+        {
+            SetMorphSourceTexture(newArray);
+        }
+
+        internal void Destroy()
+        {
+            _morphTargetsSourceTex.ArrayResized -= MorphTargetsSourceTexArrayResized;
+
+            if (_combineMaterial)
+            {
+                Material.Destroy(_combineMaterial);
+            }
+        }
+
+        // The shapesRect is specified in texels
+        internal OvrSkinningTypes.Handle AddMorphTargetsToMesh(
+            RectInt texelRectInSource,
+            int sourceTexSlice,
+            int sourceTexWidth,
+            int sourceTexHeight,
+            RectInt texelRectInOutput,
+            int outputTexWidth,
+            int outputTexHeight,
+            int numMorphTargets)
+        {
+            OvrSkinningTypes.Handle layoutHandle = _meshLayout.TrackBlock(numMorphTargets);
+
+            if (!layoutHandle.IsValid())
+            {
+                return layoutHandle;
+            }
+
+            OvrFreeListBufferTracker.LayoutResult quadsLayout = _meshLayout.GetLayoutInBufferForBlock(layoutHandle);
+
+            // Create Quads if needed
+            int quadIndex = quadsLayout.startIndex;
+            int vertStartIndex = quadIndex * NUM_VERTS_PER_QUAD;
+            int blockIndex = layoutHandle.GetValue();
+
+            if (vertStartIndex >= _mesh.vertexCount)
+            {
+                OvrCombinedMorphTargetsQuads.ExpandMeshToFitQuads(_mesh, numMorphTargets);
+            }
+
+            OvrCombinedMorphTargetsQuads.UpdateQuadsInMesh(
+                vertStartIndex,
+                blockIndex,
+                quadIndex,
+                texelRectInOutput,
+                outputTexWidth,
+                outputTexHeight,
+                texelRectInSource,
+                sourceTexSlice,
+                sourceTexWidth,
+                sourceTexHeight,
+                numMorphTargets,
+                _mesh);
+
+            // Expand compute buffers and lists if needed
+            int newNumBlocks = blockIndex + 1;
+
+            _combineMaterial.SetFloat(BLOCK_ENABLED_PROP, 1.0f);
+
+            // Add new mapping of handle to block data
+            _handleToBlockData[layoutHandle] = new BlockData
+            {
+                blockIndex = blockIndex,
+                indexInWeightsBuffer = 0,
+                numMorphTargets = numMorphTargets
+            };
+
+            return layoutHandle;
+        }
+
+        internal void RemoveMorphTargetBlock(OvrSkinningTypes.Handle handle)
+        {
+            _meshLayout.FreeBlock(handle);
+        }
+
+        internal IntPtr GetMorphWeightsBuffer(OvrSkinningTypes.Handle handle)
+        {
+            if (_handleToBlockData.TryGetValue(handle, out BlockData blockData))
+            {
+                Debug.Assert(blockData.indexInWeightsBuffer == 0 && blockData.numMorphTargets < OvrComputeBufferPool.MAX_WEIGHTS);
+                var entry = OvrAvatarManager.Instance.GpuSkinningController.GetNextEntryWeights(blockData.numMorphTargets);
+                _combineMaterial.SetInt(MORPH_TARGET_WEIGHTS_OFFSET_PROP, entry.Offset);
+
+                return entry.Data;
+            }
+            return default;
+        }
+
+        internal bool MorphWeightsBufferUpdateComplete(OvrSkinningTypes.Handle handle)
+        {
+            bool drawUpdateNeeded = false;
+            if (_handleToBlockData.TryGetValue(handle, out BlockData dataForThisBlock))
+            {
+                MarkBlockUpdated(in dataForThisBlock);
+                drawUpdateNeeded = true;
+            }
+            return drawUpdateNeeded;
+        }
+
+        internal void Draw()
+        {
+            if (_areAnyBlocksEnabled)
+            {
+                ForceDraw();
+            }
+        }
+
+        internal void ForceDraw()
+        {
+            Profiler.BeginSample("OvrGpuCombinerDrawCall::ForceDraw");
+
+            // Don't care about matrices as the shader used should handle clip space
+            // conversions without matrices (due to how quads set up)
+            bool didSetPass = _combineMaterial.SetPass(0);
+            Debug.Assert(didSetPass);
+            Graphics.DrawMeshNow(_mesh, Matrix4x4.identity);
+
+            _combineMaterial.SetFloat(BLOCK_ENABLED_PROP, 0.0f);
+
+            // Reset booleans and mark all blocks as disabled for next frame
+            _areAnyBlocksEnabled = false;
+
+            Profiler.EndSample();
+        }
+
+        internal bool CanFit(int numMorphTargets)
+        {
+            return _meshLayout.CanFit(numMorphTargets);
+        }
+
+        private void SetMorphSourceTexture(Texture2DArray morphTargetsSourceTex)
+        {
+            _combineMaterial.SetTexture(MORPH_TARGETS_SOURCE_TEX_PROP, morphTargetsSourceTex);
+        }
+
+        private void MarkBlockUpdated(in BlockData block)
+        {
+            Debug.Assert(block.blockIndex == 0);
+
+            _combineMaterial.SetFloat(BLOCK_ENABLED_PROP, 1.0f);
+            _areAnyBlocksEnabled = true;
+        }
+
+        private readonly Mesh _mesh;
+        private readonly OvrFreeListBufferTracker _meshLayout;
+
+        private readonly Material _combineMaterial;
+
+        private bool _areAnyBlocksEnabled = false;
+
+        private struct BlockData
+        {
+            public int blockIndex;
+            public int indexInWeightsBuffer;
+            public int numMorphTargets;
+        }
+
+        private readonly Dictionary<OvrSkinningTypes.Handle, BlockData> _handleToBlockData;
+
+        private readonly OvrExpandableTextureArray _morphTargetsSourceTex;
+
+        private const int NUM_VERTS_PER_QUAD = 4;
+        private const int MAX_QUADS = ushort.MaxValue / NUM_VERTS_PER_QUAD;
+
+        private const string OVR_HAS_TANGENTS = "OVR_HAS_TANGENTS";
+        private const string OVR_MORPH_10_10_10_2 = "OVR_MORPH_10_10_10_2";
+
+        private static readonly int MORPH_TARGETS_SOURCE_TEX_PROP = Shader.PropertyToID("u_MorphTargetSourceTex");
+        private static readonly int MORPH_TARGET_WEIGHTS_PROP = Shader.PropertyToID("u_Weights");
+        private static readonly int MORPH_TARGET_WEIGHTS_OFFSET_PROP = Shader.PropertyToID("u_WeightOffset");
+        private static readonly int BLOCK_ENABLED_PROP = Shader.PropertyToID("u_BlockEnabled");
+        private static readonly int MORPH_TARGET_RANGES_PROP = Shader.PropertyToID("u_MorphTargetRanges");
+
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuCombinerDrawCall.cs.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuCombinerDrawCall.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..8098347e842809e6ec8bdfa5484bfec9edff423c
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuCombinerDrawCall.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 0bad23cb934232f40bfb012c7ae11613
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuMorphTargetsCombiner.cs b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuMorphTargetsCombiner.cs
new file mode 100644
index 0000000000000000000000000000000000000000..97952b5a63a70202cb9f7e4085c1e0019363bde0
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuMorphTargetsCombiner.cs
@@ -0,0 +1,393 @@
+using System;
+using System.Collections.Generic;
+
+using Oculus.Avatar2;
+
+using Unity.Collections;
+
+using UnityEngine;
+using UnityEngine.Experimental.Rendering;
+using UnityEngine.Profiling;
+
+namespace Oculus.Skinning.GpuSkinning
+{
+    public class OvrGpuMorphTargetsCombiner
+    {
+        private const string logScope = "OvrGpuMorphTargetsCombiner";
+        // Declare the delegate (if using non-generic pattern).
+        public delegate void ArrayGrowthEventHandler(OvrGpuMorphTargetsCombiner sender, RenderTexture newArray);
+
+        public string name => _outputTex.name;
+        public int Width => _outputTex.width;
+        public int Height => _outputTex.height;
+
+        public GraphicsFormat OutputTexFormat { get; }
+
+        // Declare the event.
+        public event ArrayGrowthEventHandler ArrayResized;
+
+        private const int NUM_INITIAL_SLICES = 1;
+
+        public OvrGpuMorphTargetsCombiner(
+            string name,
+            int width,        // The width is equivalent to the number of verts in the morph target, plus one.
+            int height,       // The height is based on the number of attributes, 2 for pos/norm, and 3 for pos/norm/tan
+            GraphicsFormat texFormat,  // float, half, or "SNorm10", but since morph targets deal in a LARGE number of SMALL displacements, "SNorm10" is preferred
+            OvrExpandableTextureArray morphTargetsSource,
+            Vector3 positionRange,
+            Vector3 normalRange,
+            Vector3 tangentRange,
+            bool hasTangents,
+            bool useSnorm10,
+            Shader combineMorphTargetsShader)
+        {
+            OutputTexFormat = texFormat;
+
+            _combineMorphTargetsShader = combineMorphTargetsShader;
+
+            GetRenderTexDescriptor(width, height, NUM_INITIAL_SLICES, texFormat, out var description);
+            _outputTex = new RenderTexture(description);
+            _outputTex.name = name;
+
+            ConfigureRenderTexture(_outputTex);
+
+            _atlasPackerId = CAPI.OvrGpuSkinning_AtlasPackerCreate(
+                (uint)width,
+                (uint)height,
+                CAPI.AtlasPackerPackingAlgortihm.Runtime);
+
+            _morphTargetsSourceTex = morphTargetsSource;
+
+            _ranges[0] = positionRange;
+            _ranges[1] = normalRange;
+            _ranges[2] = tangentRange;
+            _hasTangents = hasTangents;
+            _useSNorm10 = useSnorm10;
+
+            _drawCallsForSlice.Add(new List<OvrGpuCombinerDrawCall>(1));
+
+            // Compute unaffected verts tex coord
+            AddUnaffectedVertexBlock();
+        }
+
+        public void Destroy()
+        {
+            if (_outputTex != null)
+            {
+                _outputTex.Release();
+            }
+
+            foreach (List<OvrGpuCombinerDrawCall> drawCallsList in _drawCallsForSlice)
+            {
+                foreach (OvrGpuCombinerDrawCall drawCall in drawCallsList)
+                {
+                    drawCall.Destroy();
+                }
+            }
+
+            CAPI.OvrGpuSkinning_AtlasPackerDestroy(_atlasPackerId);
+        }
+
+        internal IntPtr GetMorphBuffer(OvrSkinningTypes.Handle handle)
+        {
+            return _handleToBlockData.TryGetValue(handle, out var blockData)
+                ? blockData.combinerDrawCall.GetMorphWeightsBuffer(blockData.handleInDrawCall)
+                : default;
+        }
+
+        internal bool FinishMorphUpdate(OvrSkinningTypes.Handle handle)
+        {
+            var hasBlock = _handleToBlockData.TryGetValue(handle, out var blockData);
+            if (hasBlock)
+            {
+                bool morphWeightsUpdated = blockData.combinerDrawCall.MorphWeightsBufferUpdateComplete(blockData.handleInDrawCall);
+                if (morphWeightsUpdated && parentController != null)
+                {
+                    parentController.AddActiveCombiner(this);
+                }
+            }
+            return hasBlock;
+        }
+
+        // The shapesRect is specified in texels
+        public OvrSkinningTypes.Handle AddMorphTargetBlock(
+            in RectInt texelRectInSource,
+            in Vector2Int outputSize,
+            int sourceTexSlice,
+            int numMorphTargets)
+        {
+            // TODO* Call out to atlas packer to get packing rectangle, but, for now
+            // assume a rectangle
+            if (!_morphTargetsSourceTex.CheckFit(texelRectInSource.size))
+            {
+                OvrAvatarLog.LogError($"Source texel dimensions ({texelRectInSource}) are too large for input texture " +
+                    $"{_morphTargetsSourceTex.name} ({_morphTargetsSourceTex.Width}, {_morphTargetsSourceTex.Height})");
+                return OvrSkinningTypes.Handle.kInvalidHandle;
+            }
+
+            if (!_outputTex.CheckFit(in outputSize))
+            {
+                OvrAvatarLog.LogError($"Output texel dimensions ({outputSize}) are too large for output texture " +
+                    $"{_outputTex.name} ({_outputTex.width}, {_outputTex.height})");
+                return OvrSkinningTypes.Handle.kInvalidHandle;
+            }
+
+            // TODO: These don't match due to expectedSize not including the "empty texel" row
+            // TODO: Probably would be better to add 1 to width instead of adding an entire row for 1 texel?
+            //var expectedSize = new Vector2Int(texelRectInSource.width, texelRectInSource.height / numMorphTargets);
+            //if (expectedSize != outputSize)
+            //{
+            //    OvrAvatarLog.LogError($"Unexpected output size, got {outputSize} but expected {expectedSize}");
+            //    return OvrSkinningTypes.Handle.kInvalidHandle;
+            //}
+
+            OvrSkinningTypes.Handle packerHandle = CAPI.OvrGpuSkinning_AtlasPackerAddBlock(
+                _atlasPackerId, (uint)outputSize.x, (uint)outputSize.y);
+
+            if (!packerHandle.IsValid())
+            {
+                OvrAvatarLog.LogError("Invalid packing handle returned by ovrGpuSkinning_AtlasPackerAddBlock");
+                return OvrSkinningTypes.Handle.kInvalidHandle;
+            }
+
+            var packResult = CAPI.OvrGpuSkinning_AtlasPackerResultsForBlock(_atlasPackerId, packerHandle);
+
+            // See if tex array needs to grow
+            // Tex slice is a 0 based index, where num tex array slices isn't
+            if (packResult.texSlice >= _outputTex.volumeDepth)
+            {
+                GrowRenderTexture((int)(packResult.texSlice + 1));
+            }
+
+            OvrGpuCombinerDrawCall drawCallThatCanFit = GetDrawCallThatCanFit((int)packResult.texSlice, numMorphTargets);
+
+            var drawCallHandle = drawCallThatCanFit.AddMorphTargetsToMesh(
+                texelRectInSource,
+                sourceTexSlice,
+                _morphTargetsSourceTex.Width,
+                _morphTargetsSourceTex.Height,
+                new RectInt(packResult.x, packResult.y, packResult.w, packResult.h),
+                _outputTex.width,
+                _outputTex.height,
+                numMorphTargets);
+
+            _handleToBlockData[packerHandle] = new BlockData
+            {
+                combinerDrawCall = drawCallThatCanFit,
+                handleInDrawCall = drawCallHandle,
+            };
+
+            return packerHandle;
+        }
+
+        public void RemoveMorphTargetBlock(OvrSkinningTypes.Handle handle)
+        {
+            if (_handleToBlockData.TryGetValue(handle, out BlockData dataForThisBlock))
+            {
+                dataForThisBlock.combinerDrawCall.RemoveMorphTargetBlock(dataForThisBlock.handleInDrawCall);
+                _handleToBlockData.Remove(handle);
+            }
+        }
+
+        public CAPI.ovrTextureLayoutResult GetLayoutInCombinedTex(OvrSkinningTypes.Handle handle)
+        {
+            return CAPI.OvrGpuSkinning_AtlasPackerResultsForBlock(_atlasPackerId, handle);
+        }
+
+        public Vector3 GetTexCoordForUnaffectedVertices()
+        {
+            return _unaffectedVertsTexCoords;
+        }
+
+        public void CombineMorphTargetWithCurrentWeights()
+        {
+            Profiler.BeginSample("OvrGpuMorphTargetsCombiner::CombineMorphTargetWithCurrentWeights");
+
+            Profiler.BeginSample("OvrGpuMorphTargetsCombiner.IterateSlices");
+            // Call out to draw calls to do combining
+            bool prevsRGB = GL.sRGBWrite;
+            RenderTexture oldRT = RenderTexture.active;
+
+            // For combining morph targets, don't care about previous render texture contents,
+            // so we can discard and clear here, but in practice this was found to be unneccesary
+            //_outputTex.DiscardContents();
+
+            GL.sRGBWrite = false;
+
+            int texSlice = 0;
+            foreach (List<OvrGpuCombinerDrawCall> drawCallsList in _drawCallsForSlice)
+            {
+                Profiler.BeginSample("OvrGpuMorphTargetsCombiner.SubmitDrawCall");
+                {
+                    Graphics.SetRenderTarget(_outputTex, MIP_LEVEL, CubemapFace.Unknown, texSlice);
+                    GL.Clear(false, true, Color.black); // no need to clear the depth buffer
+
+                    foreach (var drawCall in drawCallsList)
+                    {
+                        drawCall.ForceDraw();
+                    }
+                }
+                texSlice++;
+                Profiler.EndSample(); // "OvrGpuMorphTargetsCombiner.SubmitDrawCall"
+            }
+
+            GL.sRGBWrite = prevsRGB;
+            RenderTexture.active = oldRT;
+
+            _outputTex.IncrementUpdateCount();
+            Profiler.EndSample(); // "OvrGpuMorphTargetsCombiner.IterateSlices"
+            Profiler.EndSample(); // "OvrGpuMorphTargetsCombiner::CombineMorphTargetWithCurrentWeights"
+        }
+
+        public RenderTexture GetCombinedShapesTexArray()
+        {
+            return _outputTex;
+        }
+
+        // Note this function is used only to grow the texture by slices, not in the height and width directions (set in constructor).
+        // See comments in the constructor for how those are calculated.
+        private void GrowRenderTexture(int newNumSlices)
+        {
+            Debug.Assert(newNumSlices > _outputTex.volumeDepth);
+
+            var descriptor = _outputTex.descriptor;
+            descriptor.volumeDepth = newNumSlices;
+
+            var newRt = new RenderTexture(descriptor);
+            newRt.name = _outputTex.name;
+
+            ConfigureRenderTexture(newRt);
+
+            var oldTex = _outputTex;
+
+            for (int slice = 0; slice < oldTex.volumeDepth; slice++)
+            {
+                Graphics.CopyTexture(
+                    oldTex,    // src
+                    slice,   // srcElement
+                    MIP_LEVEL,  // srcMip
+                    newRt,  // dst
+                    slice,  // dstElement
+                    MIP_LEVEL   // dstMip
+                );
+            }
+            for (int i = oldTex.volumeDepth; i < newNumSlices; i++)
+            {
+                Graphics.SetRenderTarget(newRt, MIP_LEVEL, CubemapFace.Unknown, i);
+                GL.Clear(true, true, Color.black);
+            }
+
+            _outputTex = newRt;
+            oldTex.Release();
+
+            // Expand draw call list
+            for (int i = _drawCallsForSlice.Count; i < newNumSlices; i++)
+            {
+                _drawCallsForSlice.Add(new List<OvrGpuCombinerDrawCall>(1));
+            }
+
+            // Inform listeners
+            ArrayResized?.Invoke(this, _outputTex);
+        }
+
+        private OvrGpuCombinerDrawCall GetDrawCallThatCanFit(int texSlice, int numMorphTargets)
+        {
+            List<OvrGpuCombinerDrawCall> drawCallsList = _drawCallsForSlice[texSlice];
+
+            foreach (OvrGpuCombinerDrawCall drawCall in drawCallsList)
+            {
+                if (drawCall.CanFit(numMorphTargets))
+                {
+                    return drawCall;
+                }
+            }
+
+            // No existing draw call can fit, make a new one
+            OvrGpuCombinerDrawCall newDrawCall = new OvrGpuCombinerDrawCall(_combineMorphTargetsShader, _morphTargetsSourceTex, _ranges, _hasTangents, _useSNorm10);
+            drawCallsList.Add(newDrawCall);
+            return newDrawCall;
+        }
+
+        private const uint UnaffectedVertexBlockWidth = 1;
+        private const uint UnaffectedVertexBlockHeight = 1;
+
+        private void AddUnaffectedVertexBlock()
+        {
+            // Pack a single texel
+            OvrSkinningTypes.Handle handle = CAPI.OvrGpuSkinning_AtlasPackerAddBlock(_atlasPackerId,
+                UnaffectedVertexBlockWidth,
+                UnaffectedVertexBlockHeight);
+            Debug.Assert(handle.IsValid());
+
+            // Expect this to always fit (should be called in constructor)
+            var packResult = CAPI.OvrGpuSkinning_AtlasPackerResultsForBlock(_atlasPackerId, handle);
+
+            // Store off texture coordinate (at texel center)
+            _unaffectedVertsTexCoords = new Vector3(
+                (2.0f * packResult.x + 1.0f) / (2.0f * _outputTex.width),
+                (2.0f * packResult.y + 1.0f) / (2.0f * _outputTex.height),
+                packResult.texSlice);
+        }
+
+        private const int MIP_COUNT = 0;
+        private const int MIP_LEVEL = 0;
+        private const int DEPTH_BITS = 0; // no depth
+
+        private readonly Shader _combineMorphTargetsShader;
+        private RenderTexture _outputTex;
+        private Vector3 _unaffectedVertsTexCoords;
+
+        private readonly OvrExpandableTextureArray _morphTargetsSourceTex;
+        private readonly Vector4[] _ranges = new Vector4[3];
+        private readonly bool _hasTangents;
+        private readonly bool _useSNorm10;
+
+        private readonly List<List<OvrGpuCombinerDrawCall>> _drawCallsForSlice
+            = new List<List<OvrGpuCombinerDrawCall>>(1);
+
+        public OvrAvatarGpuSkinningController parentController = null;
+
+        private struct BlockData
+        {
+            public OvrSkinningTypes.Handle handleInDrawCall;
+            public OvrGpuCombinerDrawCall combinerDrawCall;
+        }
+
+        private readonly Dictionary<OvrSkinningTypes.Handle, BlockData> _handleToBlockData
+            = new Dictionary<OvrSkinningTypes.Handle, BlockData>();
+        private readonly CAPI.AtlasPackerId _atlasPackerId;
+
+
+        private static void GetRenderTexDescriptor(int width, int height, int slices, GraphicsFormat format, out RenderTextureDescriptor description)
+        {
+#if UNITY_2019_OR_NEWER
+            description = new RenderTextureDescriptor(width, height, format, DEPTH_BITS, MIP_COUNT);
+
+            description.stencilFormat = UnityEngine.Experimental.Rendering.GraphicsFormat.None;
+            description.useDynamicScale = false;
+#else
+            // TODO: Loss of info in GraphicsFormat->RenderTextureFormat conversion
+            description = new RenderTextureDescriptor(width, height, format.GetRenderTextureFormat(), DEPTH_BITS);
+#endif
+
+            description.sRGB = false;
+            description.msaaSamples = 1;
+            description.useMipMap = false;
+            description.autoGenerateMips = false;
+            description.vrUsage = VRTextureUsage.None;
+            description.shadowSamplingMode = UnityEngine.Rendering.ShadowSamplingMode.None;
+            description.dimension = UnityEngine.Rendering.TextureDimension.Tex2DArray;
+            description.volumeDepth = slices;
+        }
+        private static void ConfigureRenderTexture(RenderTexture renderTexture)
+        {
+            renderTexture.useDynamicScale = false;
+            renderTexture.filterMode = FilterMode.Point;
+            renderTexture.anisoLevel = 0;
+            renderTexture.wrapMode = TextureWrapMode.Clamp;
+
+            var didCreate = renderTexture.IsCreated() || renderTexture.Create();
+            Debug.Assert(didCreate);
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuMorphTargetsCombiner.cs.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuMorphTargetsCombiner.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..8a7b9c48e2c7f31e1b6e3ce1ecc368e8e69aa1ea
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuMorphTargetsCombiner.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 1cb980afc19a6e845af87fb9daf690b3
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinner.cs b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinner.cs
new file mode 100644
index 0000000000000000000000000000000000000000..084f805067ab40f5e17fe64b866ed728eb12662b
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinner.cs
@@ -0,0 +1,121 @@
+using System;
+
+using Oculus.Avatar2;
+
+using Unity.Collections;
+
+using UnityEngine;
+using UnityEngine.Experimental.Rendering;
+
+namespace Oculus.Skinning.GpuSkinning
+{
+    // Missing C++ template metaprogramming to avoiding needing
+    // separate classes for this
+    internal class OvrGpuSkinner : OvrGpuSkinnerBase<OvrGpuSkinnerDrawCall>, IOvrGpuJointSkinner
+    {
+        public OvrSkinningTypes.SkinningQuality Quality
+        {
+            get => _skinningQuality;
+            set
+            {
+                if (_skinningQuality != value)
+                {
+                    _skinningQuality = value;
+                    UpdateDrawCallQuality(value);
+                }
+            }
+        }
+
+        public OvrGpuSkinner(
+            int width,
+            int height,
+            GraphicsFormat texFormat,
+            FilterMode texFilterMode,
+            int depthTexelsPerSlice,
+            OvrExpandableTextureArray neutralPoseTexture,
+            OvrExpandableTextureArray jointsTexture,
+            OvrSkinningTypes.SkinningQuality quality,
+            OvrExpandableTextureArray indirectionTexture,
+            OvrGpuMorphTargetsCombiner combiner,
+            Shader skinningShader) : base(
+            $"morphJointSkinnerOutput({combiner.name}+{jointsTexture.name})",
+            width,
+            height,
+            texFormat,
+            texFilterMode,
+            depthTexelsPerSlice,
+            neutralPoseTexture,
+            skinningShader)
+
+        {
+            _jointsTex = jointsTexture;
+            _skinningQuality = quality;
+            _indirectionTex = indirectionTexture;
+            _combiner = combiner;
+        }
+
+        public OvrSkinningTypes.Handle AddBlock(
+            int widthInOutputTex,
+            int heightInOutputTex,
+            CAPI.ovrTextureLayoutResult layoutInNeutralPoseTex,
+            CAPI.ovrTextureLayoutResult layoutInJointsTex,
+            int numJoints,
+            CAPI.ovrTextureLayoutResult layoutInIndirectionTex)
+        {
+            OvrSkinningTypes.Handle packerHandle = PackBlockAndExpandOutputIfNeeded(widthInOutputTex, heightInOutputTex);
+            if (!packerHandle.IsValid())
+            {
+                return packerHandle;
+            }
+
+            var layoutInOutputTexture = GetLayoutInOutputTex(packerHandle);
+            OvrGpuSkinnerDrawCall drawCallThatCanFit = GetDrawCallThatCanFit(
+                (int)layoutInOutputTexture.texSlice,
+                drawCall => drawCall.CanAdditionalQuad() && drawCall.CanFitAdditionalJoints(numJoints),
+                () => new OvrGpuSkinnerDrawCall(
+                    _skinningShader,
+                    _outputScaleBias,
+                    _neutralPoseTex,
+                    _jointsTex,
+                    _skinningQuality,
+                    _combiner,
+                    _indirectionTex));
+
+            OvrSkinningTypes.Handle drawCallHandle = drawCallThatCanFit.AddBlock(
+                new RectInt(layoutInOutputTexture.x, layoutInOutputTexture.y, layoutInOutputTexture.w, layoutInOutputTexture.h),
+                Width,
+                Height,
+                layoutInNeutralPoseTex,
+                layoutInJointsTex,
+                numJoints,
+                layoutInIndirectionTex);
+
+            if (!drawCallHandle.IsValid())
+            {
+                RemoveBlock(packerHandle);
+                return OvrSkinningTypes.Handle.kInvalidHandle;
+            }
+
+            AddBlockDataForHandle(layoutInOutputTexture, packerHandle, drawCallThatCanFit, drawCallHandle);
+            return packerHandle;
+        }
+
+        public override IntPtr GetJointTransformMatricesArray(OvrSkinningTypes.Handle handle)
+        {
+            BlockData dataForBlock = GetBlockDataForHandle(handle);
+            if (dataForBlock != null)
+            {
+                return dataForBlock.skinnerDrawCall.GetJointTransformMatricesArray(dataForBlock.handleInDrawCall);
+            }
+            else { return IntPtr.Zero; }
+        }
+
+        public override bool HasJoints => true;
+
+        private readonly OvrExpandableTextureArray _jointsTex;
+        private readonly OvrExpandableTextureArray _indirectionTex;
+        private readonly OvrGpuMorphTargetsCombiner _combiner;
+
+        private OvrSkinningTypes.SkinningQuality _skinningQuality;
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinner.cs.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinner.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..3c6a09060f956dac22228577cf82b7a7cef7a8da
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinner.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 91d6233a1ec00714abb5aeb5af0a0044
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinnerBase.cs b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinnerBase.cs
new file mode 100644
index 0000000000000000000000000000000000000000..f566c1da0d0dc41ee5aa6809afc7409934efa0e5
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinnerBase.cs
@@ -0,0 +1,370 @@
+using System;
+using System.Collections.Generic;
+using Unity.Collections;
+using Oculus.Avatar2;
+using UnityEngine;
+using UnityEngine.Experimental.Rendering;
+using UnityEngine.Profiling;
+
+namespace Oculus.Skinning.GpuSkinning
+{
+    internal interface IOvrGpuJointSkinner
+    {
+        OvrSkinningTypes.SkinningQuality Quality { get; set; }
+    }
+
+    // Missing C++ template metaprogramming to avoiding needing
+    // separate classes for this
+    internal abstract class OvrGpuSkinnerBase<TDrawCallType> : IOvrGpuSkinner where TDrawCallType : IOvrGpuSkinnerDrawCall
+    {
+        private OvrAvatarGpuSkinningController _parentController = null;
+        public override OvrAvatarGpuSkinningController ParentController {
+            get { return _parentController; }
+            set { _parentController = value; }
+        }
+
+        // Declare the delegate (if using non-generic pattern).
+        public delegate void ArrayGrowthEventHandler(object sender, Texture newArray);
+
+        // Declare the event.
+        public event ArrayGrowthEventHandler ArrayResized;
+
+        public int Width => _outputTex.width;
+        public int Height => _outputTex.height;
+
+        public readonly GraphicsFormat outputFormat;
+
+        private const int INITIAL_SLICE_COUNT = 1;
+
+        // number of "output depth texels" per "atlas packer" slice
+        private readonly int _numDepthTexelsPerSlice = 1;
+
+        protected OvrGpuSkinnerBase(
+            string name,
+            int width,
+            int height,
+            GraphicsFormat texFormat,
+            FilterMode texFilterMode,
+            int depthTexelsPerSlice,
+            OvrExpandableTextureArray neutralPoseTexture,
+            Shader skinningShader)
+        {
+            OvrAvatarLog.Assert(skinningShader);
+
+            outputFormat = texFormat;
+
+            _neutralPoseTex = neutralPoseTexture;
+            _skinningShader = skinningShader;
+            _numDepthTexelsPerSlice = depthTexelsPerSlice;
+
+            if (texFormat == GraphicsFormat.R16G16B16A16_UNorm)
+            {
+                var scale = GpuSkinningConfiguration.Instance.SkinnerUnormScale;
+                _outputScaleBias = new Vector2(1.0f / (2.0f * scale), 0.5f);
+            }
+            else
+            {
+                _outputScaleBias = new Vector2(1.0f, 0.0f);
+            }
+            // TODO: Use RenderTextureDescriptor
+#if UNITY_2019_3_OR_NEWER
+            _outputTex = new RenderTexture(width, height, 0, texFormat, MIP_COUNT);
+#else
+            _outputTex = new RenderTexture(width, height, 0, texFormat);
+            _outputTex.useMipMap = MIP_COUNT > 1;
+#endif
+            _outputTex.name = name;
+
+            _atlasPackerId = CAPI.OvrGpuSkinning_AtlasPackerCreate(
+                (uint)width,
+                (uint)height,
+                CAPI.AtlasPackerPackingAlgortihm.Runtime);
+
+            // Add some initial draw calls for the initial slices
+            _drawCallsForSlice = new List<List<TDrawCallType>>(INITIAL_SLICE_COUNT);
+            for (int slice = 0; slice < INITIAL_SLICE_COUNT; slice++)
+            {
+                _drawCallsForSlice.Add(new List<TDrawCallType>());
+            }
+
+            _handleToBlockData = new Dictionary<OvrSkinningTypes.Handle, BlockData>();
+
+            _outputTex.filterMode = texFilterMode;
+            _outputTex.useMipMap = false;
+            _outputTex.autoGenerateMips = false;
+            _outputTex.dimension = UnityEngine.Rendering.TextureDimension.Tex3D;
+            _outputTex.volumeDepth = INITIAL_SLICE_COUNT * _numDepthTexelsPerSlice;
+        }
+
+        public override void Destroy()
+        {
+            if (_outputTex != null)
+            {
+                _outputTex.Release();
+                _outputTex = null;
+            }
+
+            if (_drawCallsForSlice.Count > 0)
+            {
+                foreach (List<TDrawCallType> drawCallsList in _drawCallsForSlice)
+                {
+                    foreach (TDrawCallType drawCall in drawCallsList)
+                    {
+                        drawCall.Destroy();
+                    }
+                    drawCallsList.Clear();
+                }
+                _drawCallsForSlice.Clear();
+            }
+        }
+
+        // The shapesRect is specified in texels
+        protected OvrSkinningTypes.Handle PackBlockAndExpandOutputIfNeeded(
+            int widthInOutputTex,
+            int heightInOutputTex)
+        {
+            // TODO* Call out to atlas packer to get packing rectangle, but, for now
+            // assume a rectangle
+            if (widthInOutputTex < _outputTex.width || heightInOutputTex < _outputTex.height)
+            {
+                return OvrSkinningTypes.Handle.kInvalidHandle;
+            }
+
+            OvrSkinningTypes.Handle packerHandle = CAPI.OvrGpuSkinning_AtlasPackerAddBlock(
+                _atlasPackerId,
+                (uint)widthInOutputTex,
+                (uint)heightInOutputTex);
+
+            var packResult = CAPI.OvrGpuSkinning_AtlasPackerResultsForBlock(
+                _atlasPackerId,
+                packerHandle);
+
+            // See if tex array needs to grow
+            // Tex slice is a 0 based index, where num tex array slices isn't
+            if (packResult.texSlice >= _outputTex.volumeDepth / _numDepthTexelsPerSlice)
+            {
+                GrowRenderTexture((int)(packResult.texSlice + 1));
+            }
+
+            return packerHandle;
+        }
+
+        protected void AddBlockDataForHandle(
+            CAPI.ovrTextureLayoutResult packResult,
+            OvrSkinningTypes.Handle packerHandle,
+            TDrawCallType drawCallThatCanFit,
+            OvrSkinningTypes.Handle drawCallHandle)
+        {
+            _handleToBlockData[packerHandle] = new BlockData
+            {
+                skinnerDrawCall = drawCallThatCanFit,
+                handleInDrawCall = drawCallHandle,
+            };
+        }
+
+        protected BlockData GetBlockDataForHandle(OvrSkinningTypes.Handle handle)
+        {
+            return _handleToBlockData.TryGetValue(handle, out BlockData dataForThisBlock) ? dataForThisBlock : null;
+        }
+
+        protected TDrawCallType GetDrawCallThatCanFit(
+            int texSlice,
+            Func<TDrawCallType, bool> canFitFunc,
+            Func<TDrawCallType> creatorFunc)
+        {
+            // TODO: This doesn't seem ideal?
+            while (texSlice >= _drawCallsForSlice.Count)
+            {
+                _drawCallsForSlice.Add(new List<TDrawCallType>());
+            }
+
+            List<TDrawCallType> drawCallsList = _drawCallsForSlice[texSlice];
+
+            foreach (TDrawCallType drawCall in drawCallsList)
+            {
+                if (canFitFunc.Invoke(drawCall))
+                {
+                    return drawCall;
+                }
+            }
+
+            // No existing draw call can fit, make a new one
+            TDrawCallType newDrawCall = creatorFunc();
+            drawCallsList.Add(newDrawCall);
+            return newDrawCall;
+        }
+
+        public void RemoveBlock(OvrSkinningTypes.Handle handle)
+        {
+            if (_handleToBlockData.TryGetValue(handle, out BlockData dataForThisBlock))
+            {
+                dataForThisBlock.skinnerDrawCall.RemoveBlock(dataForThisBlock.handleInDrawCall);
+                _handleToBlockData.Remove(handle);
+            }
+
+            // TODO* Remove from packer when one is available
+            CAPI.OvrGpuSkinning_AtlasPackerRemoveBlock(_atlasPackerId, handle);
+        }
+
+        public override CAPI.ovrTextureLayoutResult GetLayoutInOutputTex(OvrSkinningTypes.Handle handle)
+        {
+            return CAPI.OvrGpuSkinning_AtlasPackerResultsForBlock(_atlasPackerId, handle);
+        }
+
+        // public abstract NativeSlice<OvrJointsData.JointData>? GetJointTransformMatricesArray(
+        //     OvrSkinningTypes.Handle handle);
+
+        // public abstract void UpdateJointTransformMatrices(OvrSkinningTypes.Handle handle);
+
+        private readonly SkinningOutputFrame[] destinations = { SkinningOutputFrame.FrameZero, SkinningOutputFrame.FrameOne };
+
+        public override void UpdateOutputTexture()
+        {
+            Profiler.BeginSample("OvrGpuSkinnerBase.UpdateOutputTexture");
+            // Call out to draw calls to do combining
+            bool prevsRGB = GL.sRGBWrite;
+            RenderTexture oldRT = RenderTexture.active;
+
+            GL.sRGBWrite = false;
+
+            Profiler.BeginSample("OvrGpuSkinnerBase.IterateDrawCalls");
+            // For skinning, we do care about previous contents, keep them around by not clearing or discarding
+            // unless every block across all draw calls are being updated here
+            int depthSlice = 0;
+            foreach (List<TDrawCallType> drawCallsList in _drawCallsForSlice)
+            {
+                // For interpolation purposes, each draw call can potentially write to multiple slices
+                // in the output 3D texture (with different blocks enabled for the different slices)
+                int sliceOffset = 0;
+                foreach (var writeDestination in destinations)
+                {
+                    // we may not need this check and for loop if we have seperate frame 0 active list and frame 1 active list in the controller
+                    // we'd probably want to call the whole function for either destination 0 or 1 as a parameter and determine slice offset.
+                    if (CheckAnyNeedDraw(drawCallsList, writeDestination))
+                    {
+                        Profiler.BeginSample("OvrGpuSkinnerBase.OutputDrawCall");
+                        // Each draw call
+                        Graphics.SetRenderTarget(_outputTex, MIP_LEVEL, CubemapFace.Unknown, depthSlice + sliceOffset);
+
+                        foreach (TDrawCallType drawCall in drawCallsList)
+                        {
+                            drawCall.Draw(writeDestination);
+                        }
+
+                        Profiler.EndSample(); //"OvrGpuSkinnerBase.OutputDrawCall"
+                    }
+
+                    sliceOffset++;
+                }
+
+                depthSlice++;
+            }
+
+            Profiler.EndSample(); // "OvrGpuSkinnerBase.IterateDrawCalls"
+
+            GL.sRGBWrite = prevsRGB;
+            RenderTexture.active = oldRT;
+
+            Profiler.EndSample(); //"OvrGpuSkinnerBase.UpdateOutputTexture"
+        }
+
+        private bool CheckAnyNeedDraw(IList<TDrawCallType> drawCalls, SkinningOutputFrame outputFrame)
+        {
+            int callCount = drawCalls.Count;
+            for (int idx = 0; idx < callCount; idx++)
+            {
+                if (drawCalls[idx].NeedsDraw(outputFrame))
+                {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
+        public override RenderTexture GetOutputTex()
+        {
+            return _outputTex;
+        }
+
+        private void GrowRenderTexture(int newNumPackerSlices)
+        {
+            // Create new render texture of new size
+            var newRt = new RenderTexture(
+                _outputTex.width,
+                _outputTex.height,
+                DEPTH_BITS,
+                _outputTex.format,
+                MIP_COUNT);
+
+            newRt.name = _outputTex.name;
+            newRt.filterMode = _outputTex.filterMode;
+            newRt.useMipMap = _outputTex.useMipMap;
+            newRt.autoGenerateMips = _outputTex.autoGenerateMips;
+            newRt.dimension = _outputTex.dimension;
+            newRt.volumeDepth = newNumPackerSlices * _numDepthTexelsPerSlice;
+
+            // TODO*: Copy over content from previous render texture?
+            // When creating a new render texture, it starts empty, but to not "lose"
+            // data from the previous render texture, the previous contents should be copied over
+            // to the new texture
+            _outputTex.Release();
+            _outputTex = newRt;
+
+            // Expand draw call list
+            for (int i = _drawCallsForSlice.Count; i < newNumPackerSlices; i++)
+            {
+                _drawCallsForSlice.Add(new List<TDrawCallType>(1));
+            }
+
+            // Inform listeners
+            ArrayResized?.Invoke(this, _outputTex);
+        }
+        public override void EnableBlockToRender(OvrSkinningTypes.Handle handle, SkinningOutputFrame outputFrame)
+        {
+            BlockData dataForBlock = GetBlockDataForHandle(handle);
+            Debug.Assert(dataForBlock != null);
+            bool skinningUpdated = dataForBlock.skinnerDrawCall.EnableBlock(dataForBlock.handleInDrawCall, outputFrame);
+            if(skinningUpdated && _parentController != null) {
+                _parentController.AddActiveSkinner(this);
+            }
+        }
+
+        // Helper for Joint skinners
+        protected void UpdateDrawCallQuality(OvrSkinningTypes.SkinningQuality quality)
+        {
+            foreach (var sliceList in _drawCallsForSlice)
+            {
+                foreach (var drawCall in sliceList)
+                {
+                    if (drawCall is IOvrGpuJointSkinnerDrawCall jointDrawCall)
+                    {
+                        jointDrawCall.Quality = quality;
+                    }
+                }
+            }
+        }
+
+        public override GraphicsFormat GetOutputTexGraphicFormat() => outputFormat;
+
+        protected readonly Shader _skinningShader;
+        protected readonly OvrExpandableTextureArray _neutralPoseTex;
+        protected readonly Vector2 _outputScaleBias;
+
+        protected class BlockData
+        {
+            public OvrSkinningTypes.Handle handleInDrawCall;
+            public TDrawCallType skinnerDrawCall;
+        }
+
+        private const int MIP_COUNT = 0;
+        private const int MIP_LEVEL = 0;
+        private const int DEPTH_BITS = 0; // no depth
+
+        private RenderTexture _outputTex;
+        private readonly CAPI.AtlasPackerId _atlasPackerId;
+
+        private readonly List<List<TDrawCallType>> _drawCallsForSlice;
+        private readonly Dictionary<OvrSkinningTypes.Handle, BlockData> _handleToBlockData;
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinnerBase.cs.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinnerBase.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..00576a9b2e84fe706d30ddade9a452485410daee
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinnerBase.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: ca14ced0e1a86214fb159a20c2e17ae6
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinnerBaseDrawCall.cs b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinnerBaseDrawCall.cs
new file mode 100644
index 0000000000000000000000000000000000000000..07dd95d379bb2d314ec0e689694bb128f1bfa7c3
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinnerBaseDrawCall.cs
@@ -0,0 +1,359 @@
+using Oculus.Avatar2;
+using System;
+using Unity.Collections;
+using UnityEngine;
+
+namespace Oculus.Skinning.GpuSkinning
+{
+    internal interface IOvrGpuJointSkinnerDrawCall
+    {
+        OvrSkinningTypes.SkinningQuality Quality { get; set; }
+    }
+
+    internal abstract class OvrGpuSkinnerBaseDrawCall<TBlockData> : IOvrGpuSkinnerDrawCall
+        where TBlockData : struct
+    {
+        protected const int VECTOR3_SIZE_BYTES = sizeof(float) * 3;
+        protected const int VECTOR4_SIZE_BYTES = sizeof(float) * 4;
+
+        protected int NeutralPoseTexWidth => _neutralPoseTex.Width;
+        protected int NeutralPoseTexHeight => _neutralPoseTex.Height;
+
+        internal OvrGpuSkinnerBaseDrawCall(
+            Shader skinningShader,
+            Vector2 scaleBias,
+            string[] shaderKeywords,
+            OvrExpandableTextureArray neutralPoseTexture,
+            int blockDataStrideBytes)
+        {
+            OvrAvatarLog.Assert(skinningShader != null);
+
+            _skinningMaterial = new Material(skinningShader);
+            _neutralPoseTex = neutralPoseTexture;
+
+            _blockDataStrideBytes = blockDataStrideBytes;
+
+            _blockEnabledFrameZero = Array.Empty<float>();
+            _blockEnabledFrameOne = Array.Empty<float>();
+
+            foreach (var kw in shaderKeywords)
+            {
+                _skinningMaterial.EnableKeyword(kw);
+            }
+
+            _mesh = new Mesh();
+            _mesh.vertices = Array.Empty<Vector3>();
+            _mesh.uv = Array.Empty<Vector2>();
+            _mesh.colors = Array.Empty<Color>();
+            _mesh.triangles = Array.Empty<int>();
+
+            _meshLayout = new OvrFreeListBufferTracker(MAX_QUADS);
+
+            _areAnyBlocksEnabledFrameZero = false;
+            _areAnyBlocksEnabledFrameOne = false;
+
+            _neutralPoseTex.ArrayResized += NeutralPoseTexResized;
+
+            SetNeutralPoseTextureInMaterial(_neutralPoseTex.GetTexArray());
+            SetBuffersInMaterial();
+            SetScaleBiasInMaterial(scaleBias);
+        }
+
+        public virtual void Destroy()
+        {
+            _blockDataBuffer?.Release();
+
+            if (_mesh != null)
+            {
+                Mesh.Destroy(_mesh);
+            }
+
+            if (_skinningMaterial != null)
+            {
+                Material.Destroy(_skinningMaterial);
+            }
+
+            _neutralPoseTex.ArrayResized -= NeutralPoseTexResized;
+        }
+
+        protected void TransitionShaderKeywords(string[] oldKeywords, string[] newKeywords)
+        {
+            foreach (var kw in oldKeywords)
+            {
+                _skinningMaterial.DisableKeyword(kw);
+            }
+
+            foreach (var kw in newKeywords)
+            {
+                _skinningMaterial.EnableKeyword(kw);
+            }
+        }
+        protected void TransitionQualityKeywords(OvrSkinningTypes.SkinningQuality oldQuality, OvrSkinningTypes.SkinningQuality newQuality)
+        {
+            Debug.Assert(oldQuality != newQuality);
+            TransitionShaderKeywords(
+                OvrJointsData.ShaderKeywordsForJoints(oldQuality),
+                OvrJointsData.ShaderKeywordsForJoints(newQuality)
+            );
+        }
+
+        private void NeutralPoseTexResized(object sender, Texture2DArray newArray)
+        {
+            SetNeutralPoseTextureInMaterial(newArray);
+        }
+
+
+        public bool EnableBlock(OvrSkinningTypes.Handle layoutHandle, SkinningOutputFrame writeDest)
+        {
+            bool needsDrawUpdate = false;
+            if (layoutHandle.IsValid())
+            {
+                int blockIndex = layoutHandle.GetValue();
+
+                switch (writeDest)
+                {
+                    case SkinningOutputFrame.FrameZero:
+                        _areAnyBlocksEnabledFrameZero = true;
+                        _blockEnabledFrameZero[blockIndex] = 1.0f;
+                        needsDrawUpdate = true;
+
+                        break;
+                    case SkinningOutputFrame.FrameOne:
+                        _areAnyBlocksEnabledFrameOne = true;
+                        _blockEnabledFrameOne[blockIndex] = 1.0f;
+                        needsDrawUpdate = true;
+                        break;
+                }
+            }
+            return needsDrawUpdate;
+        }
+
+        protected OvrSkinningTypes.Handle AddBlockData(
+            RectInt texelRectInOutput,
+            int outputTexWidth,
+            int outputTexHeight,
+            TBlockData blockData)
+        {
+            OvrSkinningTypes.Handle layoutHandle = _meshLayout.TrackBlock(1);
+
+            if (!layoutHandle.IsValid())
+            {
+                return layoutHandle;
+            }
+
+            OvrFreeListBufferTracker.LayoutResult quadsLayout = _meshLayout.GetLayoutInBufferForBlock(layoutHandle);
+
+            // Create Quads if needed
+            int quadIndex = quadsLayout.startIndex;
+            int vertStartIndex = quadIndex * NUM_VERTS_PER_QUAD;
+            int blockIndex = layoutHandle.GetValue();
+
+            if (vertStartIndex >= _mesh.vertexCount)
+            {
+                OvrSkinningQuads.ExpandMeshToFitQuad(_mesh);
+            }
+
+            OvrSkinningQuads.UpdateQuadInMesh(
+                vertStartIndex,
+                blockIndex,
+                texelRectInOutput,
+                outputTexWidth,
+                outputTexHeight,
+                _mesh);
+
+            // Expand compute buffers and lists if needed
+            int newNumBlocks = blockIndex + 1;
+            if (newNumBlocks > _blockEnabledFrameZero.Length)
+            {
+                // Grab contents of block data buffer. Due to Unity API restrictions,
+                // there is no copying between buffers, so the only way to enlarge a buffer
+                // but not blow away contents is to read via GetData which causes a pipeline stall.
+                // This isn't expected to happen very frequently (only when a block causes the mesh
+                // to increase in size).
+                //
+                // TODO: if the data left in the front of the array is the same as before and we're
+                // just adding data to the back of the array, use:
+                // _blockDataBuffer.SetData(tempArray,oldNumBlocks,oldNumBlocks,newNumBlocks-oldNumBlocks);
+                //
+                // TODO* See how slow this is and potentially have a boolean dictating
+                // whether or not to use more memory to circumvent the stall
+                //
+                TBlockData[] tempArray = _blockDataBuffer == null ? null : new TBlockData[_blockDataBuffer.count];
+                _blockDataBuffer?.GetData(tempArray);
+
+                // Enlarge buffer and copy back contents
+                _blockDataBuffer?.Release();
+                _blockDataBuffer = new ComputeBuffer(newNumBlocks, _blockDataStrideBytes);
+                if (tempArray != null) { _blockDataBuffer.SetData(tempArray); }
+
+
+                Array.Resize(ref _blockEnabledFrameZero, newNumBlocks);
+                Array.Resize(ref _blockEnabledFrameOne, newNumBlocks);
+                FlushBlockEnabled(_blockEnabledFrameZero);
+                FlushBlockEnabled(_blockEnabledFrameOne);
+
+                SetBuffersInMaterial();
+            }
+
+            // going to wait to set this on the main thread, to borrow the pipeline barrier unity will set for this buffer.
+            _newBlockData = blockData;
+            _newBlockIndex = blockIndex;
+            _hasNewBlockData = true;
+
+            return layoutHandle;
+        }
+
+        public virtual void RemoveBlock(OvrSkinningTypes.Handle handle)
+        {
+            _meshLayout.FreeBlock(handle);
+        }
+
+        public void Draw(SkinningOutputFrame writeDest)
+        {
+            switch (writeDest)
+            {
+                case SkinningOutputFrame.FrameZero:
+                    DrawToFrame(ref _areAnyBlocksEnabledFrameZero, _blockEnabledFrameZero);
+                    break;
+                case SkinningOutputFrame.FrameOne:
+                    DrawToFrame(ref _areAnyBlocksEnabledFrameOne, _blockEnabledFrameOne);
+                    break;
+            }
+        }
+
+        private void DrawToFrame(ref bool areAnyBlocksEnabled, float[] blockEnabledArray)
+        {
+            // Early exit
+            if (!areAnyBlocksEnabled)
+            {
+                return;
+            }
+
+            // Copy from block enabled array to compute buffer
+            Debug.Assert(blockEnabledArray.Length == 0 || blockEnabledArray.Length == 1);
+            float blockEnabled = (blockEnabledArray.Length >= 1) ? blockEnabledArray[0] : 0.0f;
+            _skinningMaterial.SetFloat(BLOCK_ENABLED_PROP, blockEnabled);
+
+            // We are delaying setting the data for this block until here. Unity isn't
+            // properly putting a pipeline barrier for our neutral pose texture upload.
+            // but it does for this kind of buffer, so by waiting to here, the barrier
+            // for this buffer will also apply to the neutral pose texture, and we won't
+            // get a screen flash in vulkan.
+            if (_hasNewBlockData)
+            {
+                // only one new block of data has been introduced, so set it here:
+                var nativeWrapper = new NativeArray<TBlockData>(1, Allocator.Temp, NativeArrayOptions.UninitializedMemory);
+                {
+                    nativeWrapper[0] = _newBlockData;
+                    _blockDataBuffer.SetData(nativeWrapper, 0, _newBlockIndex, 1);
+                }
+                _hasNewBlockData = false;
+            }
+
+            // Don't care about matrices as the shader used should handle clip space
+            // conversions without matrices (due to how quads set up)
+            bool didSetPass = _skinningMaterial.SetPass(0);
+            Debug.Assert(didSetPass);
+            Graphics.DrawMeshNow(_mesh, Matrix4x4.identity);
+
+            // Reset booleans and mark all blocks as disabled for next frame
+            areAnyBlocksEnabled = false;
+            ClearBlockEnabled(blockEnabledArray);
+        }
+
+        // Ensure virtual method for interface,
+        bool IOvrGpuSkinnerDrawCall.NeedsDraw(SkinningOutputFrame dest)
+        {
+            switch (dest)
+            {
+                case SkinningOutputFrame.FrameZero:
+                    return _areAnyBlocksEnabledFrameZero;
+                case SkinningOutputFrame.FrameOne:
+                    return _areAnyBlocksEnabledFrameOne;
+            }
+
+            return false;
+        }
+
+        void IOvrGpuSkinnerDrawCall.Draw(SkinningOutputFrame outputFrame)
+        {
+            this.Draw(outputFrame);
+        }
+
+        internal bool CanAdditionalQuad()
+        {
+            return _meshLayout.CanFit(1);
+        }
+
+        private void SetBuffersInMaterial()
+        {
+            _skinningMaterial.SetBuffer(BLOCK_DATA_PROP, _blockDataBuffer);
+        }
+
+        private void SetNeutralPoseTextureInMaterial(Texture2DArray texture)
+        {
+            _skinningMaterial.SetTexture(NEUTRAL_POSE_TEX_PROP, texture);
+        }
+
+        private void SetScaleBiasInMaterial(Vector2 scaleBias)
+        {
+            if (!Mathf.Approximately(scaleBias.x, 1.0f) || !Mathf.Approximately(scaleBias.y, 0.0f))
+            {
+                _skinningMaterial.EnableKeyword("OVR_OUTPUT_SCALE_BIAS");
+            }
+            else
+            {
+                _skinningMaterial.DisableKeyword("OVR_OUTPUT_SCALE_BIAS");
+            }
+
+            _skinningMaterial.SetVector(OUTPUT_SCALE_BIAS_PROP, scaleBias);
+        }
+
+        private void ClearBlockEnabled(float[] blockEnabledArray)
+        {
+            Array.Clear(blockEnabledArray, 0, blockEnabledArray.Length);
+        }
+
+        private void FlushBlockEnabled(float[] blockEnabledArray)
+        {
+            for (int i = 0; i < blockEnabledArray.Length; i++)
+            {
+                blockEnabledArray[i] = 1.0f;
+            }
+        }
+
+        protected readonly Material _skinningMaterial;
+
+        private OvrExpandableTextureArray _neutralPoseTex;
+
+        private ComputeBuffer _blockDataBuffer = null;
+
+        private bool _hasNewBlockData = false;
+        private int _newBlockIndex;
+        private TBlockData _newBlockData;
+
+        private readonly int _blockDataStrideBytes;
+        // The Unity API for ComputeBuffer only allows setting via
+        // an array. The block enabled buffer will be changed completely every time
+        // Draw() is called, so in order to not have to make a new temporary array
+        // every Draw(), make a private field here
+        private float[] _blockEnabledFrameZero;
+        private float[] _blockEnabledFrameOne;
+
+        private readonly Mesh _mesh;
+        private readonly OvrFreeListBufferTracker _meshLayout;
+
+        private bool _areAnyBlocksEnabledFrameZero = false;
+        private bool _areAnyBlocksEnabledFrameOne = false;
+
+        private const int BYTES_PER_FLOAT = 4;
+        private const int NUM_VERTS_PER_QUAD = 4;
+        private const int MAX_QUADS = ushort.MaxValue / NUM_VERTS_PER_QUAD;
+
+        private static readonly int NEUTRAL_POSE_TEX_PROP = Shader.PropertyToID("u_NeutralPoseTex");
+        private static readonly int BLOCK_ENABLED_PROP = Shader.PropertyToID("u_BlockEnabled");
+        private static readonly int BLOCK_DATA_PROP = Shader.PropertyToID("u_BlockData");
+
+        private static readonly int OUTPUT_SCALE_BIAS_PROP = Shader.PropertyToID("u_OutputScaleBias");
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinnerBaseDrawCall.cs.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinnerBaseDrawCall.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..68b5ba948cd1d01ae421045c2319dc03088393b9
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinnerBaseDrawCall.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 28e58bca78c53d84e9044207edd7e0e0
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinnerDrawCall.cs b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinnerDrawCall.cs
new file mode 100644
index 0000000000000000000000000000000000000000..415e6f48ac9842d0867f54687c19ba2271bd78dd
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinnerDrawCall.cs
@@ -0,0 +1,169 @@
+using Oculus.Avatar2;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using UnityEngine;
+
+namespace Oculus.Skinning.GpuSkinning
+{
+    internal sealed class OvrGpuSkinnerDrawCall : OvrGpuSkinnerBaseDrawCall<OvrGpuSkinnerDrawCall.PerBlockData>, IOvrGpuJointSkinnerDrawCall
+    {
+        public OvrGpuSkinnerDrawCall(
+            Shader skinningShader,
+            Vector2 scaleBias,
+            OvrExpandableTextureArray neutralPoseTexture,
+            OvrExpandableTextureArray jointsTexture,
+            OvrSkinningTypes.SkinningQuality quality,
+            OvrGpuMorphTargetsCombiner combiner,
+            OvrExpandableTextureArray indirectionTexture) :
+            base(
+                skinningShader,
+                scaleBias,
+                OvrMorphTargetsData.ShaderKeywordsForMorphTargets().Concat(OvrJointsData.ShaderKeywordsForJoints(quality)).ToArray(),
+                neutralPoseTexture,
+                PerBlockData.STRIDE_BYTES)
+        {
+            _quality = quality;
+            _targetsData = new OvrMorphTargetsData(combiner, indirectionTexture, _skinningMaterial);
+            _jointsData = new OvrJointsData(jointsTexture, _skinningMaterial);
+            _meshHandleToJointsHandle = new Dictionary<OvrSkinningTypes.Handle, OvrSkinningTypes.Handle>();
+        }
+
+        public override void Destroy()
+        {
+            _targetsData.Destroy();
+            _jointsData.Destroy();
+
+            base.Destroy();
+        }
+
+        public OvrSkinningTypes.Handle AddBlock(
+            RectInt texelRectInOutput,
+            int outputTexWidth,
+            int outputTexHeight,
+            CAPI.ovrTextureLayoutResult layoutInNeutralPoseTex,
+            CAPI.ovrTextureLayoutResult layoutInJointsTex,
+            int numJoints,
+            CAPI.ovrTextureLayoutResult layoutInIndirectionTex)
+        {
+            OvrSkinningTypes.Handle jointsHandle = _jointsData.AddJoints(numJoints);
+            if (!jointsHandle.IsValid())
+            {
+                return jointsHandle;
+            }
+
+            OvrFreeListBufferTracker.LayoutResult jointsLayout = _jointsData.GetLayoutForJoints(jointsHandle);
+
+            PerBlockData blockData = new PerBlockData(
+                layoutInNeutralPoseTex,
+                NeutralPoseTexWidth,
+                NeutralPoseTexHeight,
+                jointsLayout.startIndex,
+                layoutInJointsTex,
+                _jointsData.JointsTexWidth,
+                _jointsData.JointsTexHeight,
+                layoutInIndirectionTex,
+                _targetsData.IndirectionTexWidth,
+                _targetsData.IndirectionTexHeight);
+
+            OvrSkinningTypes.Handle meshHandle = AddBlockData(texelRectInOutput, outputTexWidth, outputTexHeight, blockData);
+
+            _meshHandleToJointsHandle[meshHandle] = jointsHandle;
+
+            return meshHandle;
+        }
+
+        public override void RemoveBlock(OvrSkinningTypes.Handle handle)
+        {
+            base.RemoveBlock(handle);
+
+            if (_meshHandleToJointsHandle.TryGetValue(handle, out OvrSkinningTypes.Handle jointsHandle))
+            {
+                _jointsData.RemoveJoints(jointsHandle);
+            }
+        }
+
+        public bool CanFitAdditionalJoints(int numJoints)
+        {
+            return _jointsData.CanFitAdditionalJoints(numJoints);
+        }
+
+        public IntPtr GetJointTransformMatricesArray(OvrSkinningTypes.Handle handle)
+        {
+            return _jointsData.GetJointTransformMatricesArray(handle);
+        }
+
+        [StructLayout(LayoutKind.Explicit, Size = 64)]
+        internal struct PerBlockData
+        {
+            public PerBlockData(
+                CAPI.ovrTextureLayoutResult layoutInNeutralPoseTex,
+                int neutralPoseTexWidth,
+                int neutralPoseTexHeight,
+                int jointsStartIndex,
+                CAPI.ovrTextureLayoutResult layoutInJointsTex,
+                int jointsTexWidth,
+                int jointsTexHeight,
+                CAPI.ovrTextureLayoutResult layoutInIndirectionTex,
+                int indirectionTexWidth,
+                int indirectionTexHeight)
+            {
+                Vector2 invTexDim = new Vector2(1.0f / neutralPoseTexWidth, 1.0f / neutralPoseTexHeight);
+
+                _neutralPoseTexUvRect = new Vector4(
+                    layoutInNeutralPoseTex.x * invTexDim.x,
+                    layoutInNeutralPoseTex.y * invTexDim.y,
+                    layoutInNeutralPoseTex.w * invTexDim.x,
+                    layoutInNeutralPoseTex.h * invTexDim.y);
+
+                _indicesAndSlices = new Vector4(
+                    jointsStartIndex,
+                    layoutInNeutralPoseTex.texSlice,
+                    layoutInJointsTex.texSlice,
+                    layoutInIndirectionTex.texSlice);
+
+                invTexDim = new Vector2(1.0f / jointsTexWidth, 1.0f / jointsTexHeight);
+
+                _jointsTexUvRect = new Vector4(
+                    layoutInJointsTex.x * invTexDim.x,
+                    layoutInJointsTex.y * invTexDim.y,
+                    layoutInJointsTex.w * invTexDim.x,
+                    layoutInJointsTex.h * invTexDim.y);
+
+                invTexDim = new Vector2(1.0f / indirectionTexWidth, 1.0f / indirectionTexHeight);
+
+                _indirectionTexUvRect = new Vector4(
+                    layoutInIndirectionTex.x * invTexDim.x,
+                    layoutInIndirectionTex.y * invTexDim.y,
+                    layoutInIndirectionTex.w * invTexDim.x,
+                    layoutInIndirectionTex.h * invTexDim.y);
+            }
+
+            [FieldOffset(VECTOR4_SIZE_BYTES * 0)] private Vector4 _neutralPoseTexUvRect;
+            [FieldOffset(VECTOR4_SIZE_BYTES * 1)] private Vector4 _indicesAndSlices;
+            [FieldOffset(VECTOR4_SIZE_BYTES * 2)] private Vector4 _jointsTexUvRect;
+            [FieldOffset(VECTOR4_SIZE_BYTES * 3)] private Vector4 _indirectionTexUvRect;
+
+            public const int STRIDE_BYTES = VECTOR4_SIZE_BYTES * 4;
+        }
+
+        private readonly OvrMorphTargetsData _targetsData;
+        private readonly OvrJointsData _jointsData;
+        private readonly Dictionary<OvrSkinningTypes.Handle, OvrSkinningTypes.Handle> _meshHandleToJointsHandle;
+
+        private OvrSkinningTypes.SkinningQuality _quality = OvrSkinningTypes.SkinningQuality.Invalid;
+        OvrSkinningTypes.SkinningQuality IOvrGpuJointSkinnerDrawCall.Quality
+        {
+            get => _quality;
+            set
+            {
+                if (_quality != value)
+                {
+                    TransitionQualityKeywords(_quality, value);
+                    _quality = value;
+                }
+            }
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinnerDrawCall.cs.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinnerDrawCall.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..3490c7c88a9f76c42a3823f14abedd470a5d2dd0
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinnerDrawCall.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: a13061565e300a54791975cdb9feb89e
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinnerJointsOnly.cs b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinnerJointsOnly.cs
new file mode 100644
index 0000000000000000000000000000000000000000..e7ac786980ebe767bfd24446d58a714e05f0666e
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinnerJointsOnly.cs
@@ -0,0 +1,110 @@
+using System;
+
+using Oculus.Avatar2;
+
+using Unity.Collections;
+
+using UnityEngine;
+using UnityEngine.Experimental.Rendering;
+
+namespace Oculus.Skinning.GpuSkinning
+{
+    // Missing C++ template metaprogramming to avoiding needing
+    // separate classes for this
+    internal class OvrGpuSkinnerJointsOnly : OvrGpuSkinnerBase<OvrGpuSkinnerJointsOnlyDrawCall>, IOvrGpuJointSkinner
+    {
+        public OvrSkinningTypes.SkinningQuality Quality
+        {
+            get => _skinningQuality;
+            set
+            {
+                if (_skinningQuality != value)
+                {
+                    _skinningQuality = value;
+                    UpdateDrawCallQuality(value);
+                }
+            }
+        }
+
+        public OvrGpuSkinnerJointsOnly(
+            int width,
+            int height,
+            GraphicsFormat texFormat,
+            FilterMode texFilterMode,
+            int depthTexelsPerSlice,
+            OvrExpandableTextureArray neutralPoseTexture,
+            OvrExpandableTextureArray jointsTexture,
+            OvrSkinningTypes.SkinningQuality quality,
+            Shader skinningShader) : base(
+            $"jointSkinnerOutput({jointsTexture.name})",
+            width,
+            height,
+            texFormat,
+            texFilterMode,
+            depthTexelsPerSlice,
+            neutralPoseTexture,
+            skinningShader)
+        {
+            _jointsTex = jointsTexture;
+            _skinningQuality = quality;
+        }
+
+        public OvrSkinningTypes.Handle AddBlock(
+            int widthInOutputTex,
+            int heightInOutputTex,
+            CAPI.ovrTextureLayoutResult layoutInNeutralPoseTex,
+            CAPI.ovrTextureLayoutResult layoutInJointsTex,
+            int numJoints)
+        {
+            OvrSkinningTypes.Handle packerHandle = PackBlockAndExpandOutputIfNeeded(widthInOutputTex, heightInOutputTex);
+            if (!packerHandle.IsValid())
+            {
+                return packerHandle;
+            }
+
+            var layoutInOutputTexture = GetLayoutInOutputTex(packerHandle);
+            OvrGpuSkinnerJointsOnlyDrawCall drawCallThatCanFit = GetDrawCallThatCanFit(
+                (int)layoutInOutputTexture.texSlice,
+                drawCall => drawCall.CanAdditionalQuad() && drawCall.CanFitAdditionalJoints(numJoints),
+                () => new OvrGpuSkinnerJointsOnlyDrawCall(
+                    _skinningShader,
+                    _outputScaleBias,
+                    _neutralPoseTex,
+                    _jointsTex,
+                    _skinningQuality));
+
+            OvrSkinningTypes.Handle drawCallHandle = drawCallThatCanFit.AddBlock(
+                new RectInt(layoutInOutputTexture.x, layoutInOutputTexture.y, layoutInOutputTexture.w, layoutInOutputTexture.h),
+                Width,
+                Height,
+                layoutInNeutralPoseTex,
+                layoutInJointsTex,
+                numJoints);
+
+            if (!drawCallHandle.IsValid())
+            {
+                RemoveBlock(packerHandle);
+                return OvrSkinningTypes.Handle.kInvalidHandle;
+            }
+
+            AddBlockDataForHandle(layoutInOutputTexture, packerHandle, drawCallThatCanFit, drawCallHandle);
+            return packerHandle;
+        }
+
+        public override IntPtr GetJointTransformMatricesArray(OvrSkinningTypes.Handle handle)
+        {
+            BlockData dataForBlock = GetBlockDataForHandle(handle);
+            if (dataForBlock != null)
+            {
+                return dataForBlock.skinnerDrawCall.GetJointTransformMatricesArray(dataForBlock.handleInDrawCall);
+            }
+            else { return IntPtr.Zero; }
+        }
+
+        public override bool HasJoints => true;
+
+        private readonly OvrExpandableTextureArray _jointsTex;
+
+        private OvrSkinningTypes.SkinningQuality _skinningQuality;
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinnerJointsOnly.cs.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinnerJointsOnly.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..1cbcb307a6edd7dd3f626aee3de1dedb3fd866aa
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinnerJointsOnly.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: fc64b91aa53960a4c837c48f57b77884
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinnerJointsOnlyDrawCall.cs b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinnerJointsOnlyDrawCall.cs
new file mode 100644
index 0000000000000000000000000000000000000000..77c5cad78d87660b1ef1684839074a915721c933
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinnerJointsOnlyDrawCall.cs
@@ -0,0 +1,147 @@
+using Oculus.Avatar2;
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+using UnityEngine;
+
+namespace Oculus.Skinning.GpuSkinning
+{
+    internal sealed class OvrGpuSkinnerJointsOnlyDrawCall : OvrGpuSkinnerBaseDrawCall<OvrGpuSkinnerJointsOnlyDrawCall.PerBlockData>, IOvrGpuJointSkinnerDrawCall
+    {
+        public OvrGpuSkinnerJointsOnlyDrawCall(
+            Shader skinningShader,
+            Vector2 scaleBias,
+            OvrExpandableTextureArray neutralPoseTexture,
+            OvrExpandableTextureArray jointsTexture,
+            OvrSkinningTypes.SkinningQuality quality) :
+            base(
+                skinningShader,
+                scaleBias,
+                OvrJointsData.ShaderKeywordsForJoints(quality),
+                neutralPoseTexture,
+                PerBlockData.STRIDE_BYTES)
+        {
+            _quality = quality;
+            _jointsData = new OvrJointsData(jointsTexture, _skinningMaterial);
+            _meshHandleToJointsHandle = new Dictionary<OvrSkinningTypes.Handle, OvrSkinningTypes.Handle>();
+        }
+
+        public override void Destroy()
+        {
+            _jointsData.Destroy();
+            base.Destroy();
+        }
+
+        public OvrSkinningTypes.Handle AddBlock(
+            RectInt texelRectInOutput,
+            int outputTexWidth,
+            int outputTexHeight,
+            CAPI.ovrTextureLayoutResult layoutInNeutralPoseTex,
+            CAPI.ovrTextureLayoutResult layoutInJointsTex,
+            int numJoints)
+        {
+            OvrSkinningTypes.Handle jointsHandle = _jointsData.AddJoints(numJoints);
+
+            if (!jointsHandle.IsValid())
+            {
+                return jointsHandle;
+            }
+
+            OvrFreeListBufferTracker.LayoutResult jointsLayout = _jointsData.GetLayoutForJoints(jointsHandle);
+
+            PerBlockData blockData = new PerBlockData(
+                jointsLayout.startIndex,
+                layoutInNeutralPoseTex,
+                NeutralPoseTexWidth,
+                NeutralPoseTexHeight,
+                layoutInJointsTex,
+                _jointsData.JointsTexWidth,
+                _jointsData.JointsTexHeight);
+
+            OvrSkinningTypes.Handle meshHandle = AddBlockData(texelRectInOutput, outputTexWidth, outputTexHeight, blockData);
+
+            _meshHandleToJointsHandle[meshHandle] = jointsHandle;
+
+            return meshHandle;
+        }
+
+        public override void RemoveBlock(OvrSkinningTypes.Handle handle)
+        {
+            base.RemoveBlock(handle);
+
+            if (_meshHandleToJointsHandle.TryGetValue(handle, out OvrSkinningTypes.Handle jointsHandle))
+            {
+                _jointsData.RemoveJoints(jointsHandle);
+            }
+        }
+
+        public bool CanFitAdditionalJoints(int numJoints)
+        {
+            return _jointsData.CanFitAdditionalJoints(numJoints);
+        }
+
+        public IntPtr GetJointTransformMatricesArray(OvrSkinningTypes.Handle handle)
+        {
+            return _jointsData.GetJointTransformMatricesArray(handle);
+        }
+
+        [StructLayout(LayoutKind.Explicit, Size = VECTOR4_SIZE_BYTES * 3)]
+        internal struct PerBlockData
+        {
+            public PerBlockData(
+                int jointsStartIndex,
+                CAPI.ovrTextureLayoutResult layoutInNeutralPoseTex,
+                int neutralPoseTexWidth,
+                int neutralPoseTexHeight,
+                CAPI.ovrTextureLayoutResult layoutInJointsTex,
+                int jointsTexWidth,
+                int jointsTexHeight)
+            {
+                Vector2 invTexDim = new Vector2(1.0f / neutralPoseTexWidth, 1.0f / neutralPoseTexHeight);
+
+                _neutralPoseTexUvRect = new Vector4(
+                    layoutInNeutralPoseTex.x * invTexDim.x,
+                    layoutInNeutralPoseTex.y * invTexDim.y,
+                    layoutInNeutralPoseTex.w * invTexDim.x,
+                    layoutInNeutralPoseTex.h * invTexDim.y);
+
+                _indicesAndSlices = new Vector4(
+                    jointsStartIndex,
+                    layoutInNeutralPoseTex.texSlice,
+                    layoutInJointsTex.texSlice,
+                    0.0f);
+
+                invTexDim = new Vector2(1.0f / jointsTexWidth, 1.0f / jointsTexHeight);
+
+                _jointsTexUvRect = new Vector4(
+                    layoutInJointsTex.x * invTexDim.x,
+                    layoutInJointsTex.y * invTexDim.y,
+                    layoutInJointsTex.w * invTexDim.x,
+                    layoutInJointsTex.h * invTexDim.y);
+            }
+
+            [FieldOffset(VECTOR4_SIZE_BYTES * 0)] private Vector4 _neutralPoseTexUvRect;
+            [FieldOffset(VECTOR4_SIZE_BYTES * 1)] private Vector4 _indicesAndSlices;
+            [FieldOffset(VECTOR4_SIZE_BYTES * 2)] private Vector4 _jointsTexUvRect;
+
+            public const int STRIDE_BYTES = VECTOR4_SIZE_BYTES * 3;
+        }
+
+        private readonly OvrJointsData _jointsData;
+        private readonly Dictionary<OvrSkinningTypes.Handle, OvrSkinningTypes.Handle> _meshHandleToJointsHandle;
+
+        private OvrSkinningTypes.SkinningQuality _quality = OvrSkinningTypes.SkinningQuality.Invalid;
+        OvrSkinningTypes.SkinningQuality IOvrGpuJointSkinnerDrawCall.Quality
+        {
+            get => _quality;
+            set
+            {
+                if (_quality != value)
+                {
+                    TransitionQualityKeywords(_quality, value);
+                    _quality = value;
+                }
+            }
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinnerJointsOnlyDrawCall.cs.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinnerJointsOnlyDrawCall.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..9953ffe9b7236136b58c6392510d06c402a74241
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinnerJointsOnlyDrawCall.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 9f89a3499b054c24582b19c543ad3eb7
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinnerMorphTargetsOnly.cs b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinnerMorphTargetsOnly.cs
new file mode 100644
index 0000000000000000000000000000000000000000..9f91b0395895327ce7930b78cdb31236849abc0c
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinnerMorphTargetsOnly.cs
@@ -0,0 +1,90 @@
+using System;
+
+using Oculus.Avatar2;
+
+using Unity.Collections;
+
+using UnityEngine;
+using UnityEngine.Experimental.Rendering;
+
+namespace Oculus.Skinning.GpuSkinning
+{
+    // Missing C++ template metaprogramming to avoiding needing
+    // separate classes for this
+    internal class OvrGpuSkinnerMorphTargetsOnly : OvrGpuSkinnerBase<OvrGpuSkinnerMorphTargetsOnlyDrawCall>
+    {
+        public OvrGpuSkinnerMorphTargetsOnly(
+            int width,
+            int height,
+            GraphicsFormat texFormat,
+            FilterMode texFilterMode,
+            int depthTexelsPerSlice,
+            OvrExpandableTextureArray neutralPoseTexture,
+            OvrExpandableTextureArray indirectionTexture,
+            OvrGpuMorphTargetsCombiner combiner,
+            Shader skinningShader) : base(
+            $"morphSkinnerOutput({combiner.name})",
+            width,
+            height,
+            texFormat,
+            texFilterMode,
+            depthTexelsPerSlice,
+            neutralPoseTexture,
+            skinningShader)
+        {
+            _indirectionTex = indirectionTexture;
+            _combiner = combiner;
+        }
+
+        public OvrSkinningTypes.Handle AddBlock(
+            int widthInOutputTex,
+            int heightInOutputTex,
+            CAPI.ovrTextureLayoutResult layoutInNeutralPoseTex,
+            CAPI.ovrTextureLayoutResult layoutInIndirectionTex)
+        {
+            OvrSkinningTypes.Handle packerHandle = PackBlockAndExpandOutputIfNeeded(widthInOutputTex, heightInOutputTex);
+            if (!packerHandle.IsValid())
+            {
+                return packerHandle;
+            }
+
+            var layoutInOutputTexture = GetLayoutInOutputTex(packerHandle);
+            OvrGpuSkinnerMorphTargetsOnlyDrawCall drawCallThatCanFit = GetDrawCallThatCanFit(
+                (int)layoutInOutputTexture.texSlice,
+                drawCall => drawCall.CanAdditionalQuad(),
+                () => new OvrGpuSkinnerMorphTargetsOnlyDrawCall(
+                    _skinningShader,
+                    _outputScaleBias,
+                    _combiner,
+                    _neutralPoseTex,
+                    _indirectionTex));
+
+            OvrSkinningTypes.Handle drawCallHandle = drawCallThatCanFit.AddBlock(
+                new RectInt(layoutInOutputTexture.x, layoutInOutputTexture.y, layoutInOutputTexture.w, layoutInOutputTexture.h),
+                Width,
+                Height,
+                layoutInNeutralPoseTex,
+                layoutInIndirectionTex);
+
+            if (!drawCallHandle.IsValid())
+            {
+                RemoveBlock(packerHandle);
+                return OvrSkinningTypes.Handle.kInvalidHandle;
+            }
+
+            AddBlockDataForHandle(layoutInOutputTexture, packerHandle, drawCallThatCanFit, drawCallHandle);
+            return packerHandle;
+        }
+
+        public override IntPtr GetJointTransformMatricesArray(OvrSkinningTypes.Handle handle)
+        {
+            return IntPtr.Zero; // No-op
+        }
+
+        public override bool HasJoints => false;
+
+
+        private readonly OvrExpandableTextureArray _indirectionTex;
+        private readonly OvrGpuMorphTargetsCombiner _combiner;
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinnerMorphTargetsOnly.cs.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinnerMorphTargetsOnly.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..11c91ce53820e6565277a22aa7543c0b36110a08
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinnerMorphTargetsOnly.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 802a5e3fc4476504a89e41ee168b277d
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinnerMorphTargetsOnlyDrawCall.cs b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinnerMorphTargetsOnlyDrawCall.cs
new file mode 100644
index 0000000000000000000000000000000000000000..db060f57671765ac87c2f770608669168cfb1101
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinnerMorphTargetsOnlyDrawCall.cs
@@ -0,0 +1,94 @@
+using System;
+using System.Collections.Generic;
+using Oculus.Avatar2;
+using System.Runtime.InteropServices;
+using UnityEngine;
+
+namespace Oculus.Skinning.GpuSkinning
+{
+    internal class OvrGpuSkinnerMorphTargetsOnlyDrawCall : OvrGpuSkinnerBaseDrawCall<OvrGpuSkinnerMorphTargetsOnlyDrawCall.PerBlockData>
+    {
+        public OvrGpuSkinnerMorphTargetsOnlyDrawCall(
+            Shader skinningShader,
+            Vector2 scaleBias,
+            OvrGpuMorphTargetsCombiner combiner,
+            OvrExpandableTextureArray neutralPoseTexture,
+            OvrExpandableTextureArray indirectionTexture) :
+            base(
+                skinningShader,
+                scaleBias,
+                OvrMorphTargetsData.ShaderKeywordsForMorphTargets(),
+                neutralPoseTexture,
+                PerBlockData.STRIDE_BYTES)
+        {
+            _targetsData = new OvrMorphTargetsData(combiner, indirectionTexture, _skinningMaterial);
+        }
+
+        public override void Destroy()
+        {
+            base.Destroy();
+            _targetsData.Destroy();
+        }
+
+        public OvrSkinningTypes.Handle AddBlock(
+            RectInt texelRectInOutput,
+            int outputTexWidth,
+            int outputTexHeight,
+            CAPI.ovrTextureLayoutResult layoutInNeutralPoseTex,
+            CAPI.ovrTextureLayoutResult layoutInIndirectionTex)
+        {
+            PerBlockData blockData = new PerBlockData(
+                layoutInNeutralPoseTex,
+                NeutralPoseTexWidth,
+                NeutralPoseTexHeight,
+                layoutInIndirectionTex,
+                _targetsData.IndirectionTexWidth,
+                _targetsData.IndirectionTexHeight);
+
+            return AddBlockData(texelRectInOutput, outputTexWidth, outputTexHeight, blockData);
+        }
+
+        [StructLayout(LayoutKind.Explicit, Size = 48)]
+
+        internal struct PerBlockData
+        {
+            public PerBlockData(
+                CAPI.ovrTextureLayoutResult layoutInNeutralPoseTex,
+                int neutralPoseTexWidth,
+                int neutralPoseTexHeight,
+                CAPI.ovrTextureLayoutResult layoutInIndirectionTex,
+                int indirectionTexWidth,
+                int indirectionTexHeight)
+            {
+                Vector2 invTexDim = new Vector2(1.0f / neutralPoseTexWidth, 1.0f / neutralPoseTexHeight);
+
+                _neutralPoseTexUvRect = new Vector4(
+                    layoutInNeutralPoseTex.x * invTexDim.x,
+                    layoutInNeutralPoseTex.y * invTexDim.y,
+                    layoutInNeutralPoseTex.w * invTexDim.x,
+                    layoutInNeutralPoseTex.h * invTexDim.y);
+
+                _indicesAndSlices = new Vector4(
+                    0.0f,
+                    layoutInNeutralPoseTex.texSlice,
+                    0.0f,
+                    layoutInIndirectionTex.texSlice);
+
+                invTexDim = new Vector2(1.0f / indirectionTexWidth, 1.0f / indirectionTexHeight);
+                _indirectionTexUvRect = new Vector4(
+                    layoutInIndirectionTex.x * invTexDim.x,
+                    layoutInIndirectionTex.y * invTexDim.y,
+                    layoutInIndirectionTex.w * invTexDim.x,
+                    layoutInIndirectionTex.h * invTexDim.y);
+            }
+
+            [FieldOffset(VECTOR4_SIZE_BYTES * 0)] private Vector4 _neutralPoseTexUvRect;
+            [FieldOffset(VECTOR4_SIZE_BYTES * 1)] private Vector4 _indicesAndSlices;
+            [FieldOffset(VECTOR4_SIZE_BYTES * 2)] private Vector4 _indirectionTexUvRect;
+
+            public static int STRIDE_BYTES = VECTOR4_SIZE_BYTES * 3;
+        }
+
+        private readonly OvrMorphTargetsData _targetsData;
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinnerMorphTargetsOnlyDrawCall.cs.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinnerMorphTargetsOnlyDrawCall.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..4daa572eaa9c79edfe5dde63468f27b03e469c28
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinnerMorphTargetsOnlyDrawCall.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 02a982646f1c8354ab8fd56cd83d29c5
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinningUtils.cs b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinningUtils.cs
new file mode 100644
index 0000000000000000000000000000000000000000..a48458c99ffdca5366822287fe7032090888819b
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinningUtils.cs
@@ -0,0 +1,78 @@
+using System;
+using UnityEngine;
+
+namespace Oculus.Skinning.GpuSkinning
+{
+    public static class OvrGpuSkinningUtils
+    {
+        public static int getTextureHeightToFitVertexInfo(
+            int numVerts,
+            int rowsPerVert,
+            int texWidth)
+        {
+            int texHeight = numVerts / texWidth;
+
+            if (texWidth * texHeight < numVerts)
+            {
+                ++texHeight;
+            }
+
+            return texHeight * rowsPerVert;
+        }
+
+        public static Vector2Int findOptimalTextureDimensions(
+            int numVerts,
+            int rowsPerVert,
+            uint maxTexSize)
+        {
+            int texWidth = numVerts;
+            if (numVerts > maxTexSize)
+            {
+                // Divide by 2 until under maxWidth
+                bool originallyOdd = (numVerts & 1) != 0;
+                while (texWidth > maxTexSize)
+                {
+                    texWidth >>= 1;
+                }
+
+                // See if needs an additional texel if the original had odd number of verts, but
+                // check for edge max where that would spill over max width
+                if (texWidth == maxTexSize && originallyOdd)
+                {
+                    texWidth >>= 1;
+                }
+
+                texWidth += (originallyOdd ? 1 : 0);
+            }
+
+            return new Vector2Int(
+                texWidth, getTextureHeightToFitVertexInfo(numVerts, rowsPerVert, texWidth));
+        }
+
+        public static Vector2Int findIndirectionTextureSize(
+            int numVerts,
+            int numAttribs)
+        {
+            return findOptimalTextureDimensions(numVerts, numAttribs, MAX_TEXTURE_DIMENSION);
+        }
+
+        public static Vector2Int findMorphTargetCombinerSize(
+            int numAffectedVerts,
+            int numAttribs)
+        {
+            Vector2Int dimensions = findOptimalTextureDimensions(numAffectedVerts, 1, MAX_TEXTURE_DIMENSION);
+
+            // Make sure there is room for 1 "unaffected verts" texel. Since all blocks
+            // added are rectangular in their definition (even if all texels in the rectangle are not used).
+            // So the additional texel (1x1 rectangle) for the unaffected verts will need
+            // to be an extra row
+
+            // Take attribute count into affect and additional row if needed
+            dimensions.y = (dimensions.y * numAttribs) + 1;
+
+            return dimensions;
+        }
+
+        public static readonly uint MAX_TEXTURE_DIMENSION = Math.Min(512U, (uint)SystemInfo.maxTextureSize);
+    }
+} // namespace Oculus.Skinning.GpuSkinning
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinningUtils.cs.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinningUtils.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..6d78f9e8a0c76c24027a6490bac3f18fc10bf3b7
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrGpuSkinningUtils.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: c64a4d66cb9b5e94c834183ee0f74432
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrJointsData.cs b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrJointsData.cs
new file mode 100644
index 0000000000000000000000000000000000000000..d24d9c36caa40954ee31c41b5f3be32d899541aa
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrJointsData.cs
@@ -0,0 +1,129 @@
+// Check for differences in update vs current state and ignore if they match
+//#define OVR_GPUSKINNING_DIFFERENCE_CHECK
+
+using System;
+using System.Runtime.InteropServices;
+using Unity.Collections;
+using Unity.Collections.LowLevel.Unsafe;
+using UnityEngine;
+using UnityEngine.Profiling;
+
+using Oculus.Avatar2;
+
+namespace Oculus.Skinning.GpuSkinning
+{
+    internal class OvrJointsData
+    {
+        public int JointsTexWidth => _jointsTex.Width;
+        public int JointsTexHeight => _jointsTex.Height;
+
+        public static string[] ShaderKeywordsForJoints(OvrSkinningTypes.SkinningQuality quality)
+        {
+            var qualityIndex = (uint)quality;
+            var keywords = qualityIndex < _KeywordLookup.Length ? _KeywordLookup[qualityIndex] : null;
+            if (keywords == null)
+            {
+                throw new ArgumentOutOfRangeException(nameof(quality), quality, "Invalid SkinningQuality value");
+            }
+            return keywords;
+        }
+
+        public OvrJointsData(OvrExpandableTextureArray jointsTexture, Material skinningMaterial)
+        {
+            _jointsTex = jointsTexture;
+            _skinningMaterial = skinningMaterial;
+
+            _jointsBufferLayout = new OvrFreeListBufferTracker(OvrComputeBufferPool.MaxJoints);
+
+            _jointsTex.ArrayResized += JointsTexArrayResized;
+
+            SetJointsTextureInMaterial(jointsTexture.GetTexArray());
+            SetBuffersInMaterial();
+        }
+
+        public void Destroy()
+        {
+            _jointsTex.ArrayResized -= JointsTexArrayResized;
+        }
+
+        private void JointsTexArrayResized(object sender, Texture2DArray newArray)
+        {
+            SetJointsTextureInMaterial(newArray);
+        }
+
+        public OvrSkinningTypes.Handle AddJoints(int numJoints)
+        {
+            OvrSkinningTypes.Handle layoutHandle = _jointsBufferLayout.TrackBlock(numJoints);
+            if (!layoutHandle.IsValid())
+            {
+                return layoutHandle;
+            }
+
+            SetBuffersInMaterial();
+
+            return layoutHandle;
+        }
+
+        public OvrFreeListBufferTracker.LayoutResult GetLayoutForJoints(OvrSkinningTypes.Handle handle)
+        {
+            return _jointsBufferLayout.GetLayoutInBufferForBlock(handle);
+        }
+
+        public void RemoveJoints(OvrSkinningTypes.Handle handle)
+        {
+            _jointsBufferLayout.FreeBlock(handle);
+        }
+
+        public bool CanFitAdditionalJoints(int numJoints)
+        {
+            return _jointsBufferLayout.CanFit(numJoints);
+        }
+
+        public IntPtr GetJointTransformMatricesArray(OvrSkinningTypes.Handle handle)
+        {
+            var layout = _jointsBufferLayout.GetLayoutInBufferForBlock(handle);
+
+            if (!layout.IsValid)
+            {
+                return IntPtr.Zero;
+            }
+
+            var jointEntry = OvrAvatarManager.Instance.GpuSkinningController.GetNextEntryJoints();
+            _skinningMaterial.SetInt(JOINT_OFFSET_PROP, jointEntry.JointOffset);
+
+            return jointEntry.Data;
+        }
+
+        private void SetBuffersInMaterial()
+        {
+            _skinningMaterial.SetBuffer(JOINT_MATRICES_PROP, OvrAvatarManager.Instance.GpuSkinningController.GetJointBuffer());
+        }
+
+        private void SetJointsTextureInMaterial(Texture2DArray texture)
+        {
+            _skinningMaterial.SetTexture(JOINTS_TEX_PROP, texture);
+        }
+
+
+        private OvrExpandableTextureArray _jointsTex;
+        private Material _skinningMaterial;
+
+        private readonly OvrFreeListBufferTracker _jointsBufferLayout;
+
+        private const string OVR_FOUR_BONES_KEYWORD = "OVR_SKINNING_QUALITY_4_BONES";
+        private const string OVR_TWO_BONES_KEYWORD = "OVR_SKINNING_QUALITY_2_BONES";
+        private const string OVR_ONE_BONE_KEYWORD = "OVR_SKINNING_QUALITY_1_BONE";
+
+        private static readonly int JOINTS_TEX_PROP = Shader.PropertyToID("u_JointsTex");
+        private static readonly int JOINT_MATRICES_PROP = Shader.PropertyToID("u_JointMatrices");
+        private static readonly int JOINT_OFFSET_PROP = Shader.PropertyToID("u_JointOffset");
+
+        private static readonly string[][] _KeywordLookup = {
+                /* 0, INVALID */ null,
+                /* 1, Bone1 */ new[] { OVR_ONE_BONE_KEYWORD },
+                /* 2, Bone2 */ new[] { OVR_TWO_BONES_KEYWORD },
+                /* 3, Unsupported */ null,
+                /* 4, Bone4 */ new[] { OVR_FOUR_BONES_KEYWORD },
+        };
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrJointsData.cs.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrJointsData.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..2f1b59d4c1c6bf8f1960c408523cd398a193c7e4
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrJointsData.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 9fee38e5b95d77e47b4782d52a09033a
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrMorphTargetsData.cs b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrMorphTargetsData.cs
new file mode 100644
index 0000000000000000000000000000000000000000..c9e34d35c4e6f13a286ad0cc47db8918f2c6501b
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrMorphTargetsData.cs
@@ -0,0 +1,71 @@
+using UnityEngine;
+using UnityEngine.Rendering;
+
+namespace Oculus.Skinning.GpuSkinning
+{
+    internal class OvrMorphTargetsData
+    {
+        public static string[] ShaderKeywordsForMorphTargets(bool useIndirectionTexture = true)
+        {
+            return useIndirectionTexture ? _shaderIndirectionKeywords : _shaderKeywords;
+        }
+
+        public int IndirectionTexWidth => _indirectionTex.Width;
+        public int IndirectionTexHeight => _indirectionTex.Height;
+
+        public OvrMorphTargetsData(
+            OvrGpuMorphTargetsCombiner morphTargetsCombiner,
+            OvrExpandableTextureArray indirectionTexture,
+            Material skinningMaterial)
+        {
+            _combiner = morphTargetsCombiner;
+            _indirectionTex = indirectionTexture;
+            _skinningMaterial = skinningMaterial;
+
+            _combiner.ArrayResized += CombinerArrayResized;
+            _indirectionTex.ArrayResized += IndirectionTexArrayResized;
+
+            SetIndirectionTextureInMaterial(_indirectionTex.GetTexArray());
+            SetCombinedMorphTargetsTextureInMaterial(morphTargetsCombiner.GetCombinedShapesTexArray());
+        }
+
+        public void Destroy()
+        {
+            _combiner.ArrayResized -= CombinerArrayResized;
+            _indirectionTex.ArrayResized -= IndirectionTexArrayResized;
+        }
+
+        private void CombinerArrayResized(OvrGpuMorphTargetsCombiner sender, RenderTexture newArray)
+        {
+            SetCombinedMorphTargetsTextureInMaterial(newArray);
+        }
+
+        private void IndirectionTexArrayResized(OvrExpandableTextureArray sender, Texture2DArray newArray)
+        {
+            SetIndirectionTextureInMaterial(newArray);
+        }
+
+        private void SetIndirectionTextureInMaterial(Texture2DArray indirectionTex)
+        {
+            _skinningMaterial.SetTexture(INDIRECTION_TEX_PROP, indirectionTex);
+        }
+
+        private void SetCombinedMorphTargetsTextureInMaterial(RenderTexture combinedMorphTargetsTex)
+        {
+            _skinningMaterial.SetTexture(COMBINED_MORPH_TARGETS_TEX_PROP, combinedMorphTargetsTex, RenderTextureSubElement.Color);
+        }
+
+        private const string OVR_MORPH_TARGET_KEYWORD = "OVR_HAS_MORPH_TARGETS";
+        private const string OVR_MORPH_TARGET_INDIRECTION_KEYWORD = "OVR_HAS_MORPH_TARGETS_INDIRECTION_TEXTURE";
+
+        private static readonly string[] _shaderKeywords = { OVR_MORPH_TARGET_KEYWORD };
+        private static readonly string[] _shaderIndirectionKeywords = { OVR_MORPH_TARGET_INDIRECTION_KEYWORD };
+
+        private static readonly int COMBINED_MORPH_TARGETS_TEX_PROP = Shader.PropertyToID("u_CombinedMorphTargetsTex");
+        private static readonly int INDIRECTION_TEX_PROP = Shader.PropertyToID("u_IndirectionTex");
+
+        private Material _skinningMaterial;
+        private OvrGpuMorphTargetsCombiner _combiner;
+        private OvrExpandableTextureArray _indirectionTex;
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrMorphTargetsData.cs.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrMorphTargetsData.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..20f90d3612d5d2b4d44755faa6226aa5426f997a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrMorphTargetsData.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 9f1e4b631f04a2a4c97b5755e1a60dc7
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrSkinningQuads.cs b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrSkinningQuads.cs
new file mode 100644
index 0000000000000000000000000000000000000000..43d6cfbcd5e10222573ec1400c4d28975286b7f8
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrSkinningQuads.cs
@@ -0,0 +1,99 @@
+using System;
+using UnityEngine;
+
+namespace Oculus.Skinning.GpuSkinning
+{
+    public static class OvrSkinningQuads
+    {
+        private const int NUM_VERTS_PER_QUAD = 4; // 1 quad per
+        private const int NUM_INDICES_PER_QUAD = 6; // 1 quad per
+        private const float Z_POSITION = 0.75f;
+
+        public static void ExpandMeshToFitQuad(Mesh existingMesh)
+        {
+            Vector3[] verts = existingMesh.vertices;
+            Vector2[] uvs = existingMesh.uv;
+            Color[] colors = existingMesh.colors;
+            int[] indices = existingMesh.triangles;
+
+            int oldNumVerts = verts.Length;
+            int newNumVerts = verts.Length + NUM_VERTS_PER_QUAD;
+            int oldNumIndices = indices.Length;
+            int newNumIndices = indices.Length + NUM_INDICES_PER_QUAD;
+
+            Array.Resize(ref verts, newNumVerts);
+            Array.Resize(ref uvs, newNumVerts);
+            Array.Resize(ref colors, newNumVerts);
+            Array.Resize(ref indices, newNumIndices);
+
+            indices[oldNumIndices + 0] = oldNumVerts + 0;
+            indices[oldNumIndices + 1] = oldNumVerts + 2;
+            indices[oldNumIndices + 2] = oldNumVerts + 1;
+            indices[oldNumIndices + 3] = oldNumVerts + 2;
+            indices[oldNumIndices + 4] = oldNumVerts + 3;
+            indices[oldNumIndices + 5] = oldNumVerts + 1;
+
+            // Unity documentation says resizing the vertices will also resize colors, uvs, etc.
+            existingMesh.vertices = verts;
+            existingMesh.uv = uvs;
+            existingMesh.colors = colors;
+            existingMesh.triangles = indices;
+        }
+
+        public static void UpdateQuadInMesh(
+          int meshVertexStartIndex,
+          int blockIndex,
+          RectInt texelRectInOutputTex,
+          int outputTexWidth,
+          int outputTexHeight,
+          Mesh existingMesh)
+        {
+            float invTexWidth = 1.0f / outputTexWidth;
+            float invTexHeight = 1.0f / outputTexHeight;
+
+            // Transform the "row and column" into clip space [-1 to 1] for the rectangle origins
+            // 4 positions per quad rect change with blend shapes)
+            Vector3[] quadPositions = new Vector3[NUM_VERTS_PER_QUAD];
+
+            // Convert from "texels" to clip space
+            // origin
+            quadPositions[0] = new Vector3(texelRectInOutputTex.xMin * invTexWidth, texelRectInOutputTex.yMin * invTexHeight, Z_POSITION) * 2.0f - Vector3.one;
+            // "x corner"
+            quadPositions[1] = new Vector3(texelRectInOutputTex.xMax * invTexWidth, texelRectInOutputTex.yMin * invTexHeight, Z_POSITION) * 2.0f - Vector3.one;
+            // "y corner"
+            quadPositions[2] = new Vector3(texelRectInOutputTex.xMin * invTexWidth, texelRectInOutputTex.yMax * invTexHeight, Z_POSITION) * 2.0f - Vector3.one;
+            // "opposite corner"
+            quadPositions[3] = new Vector3(texelRectInOutputTex.xMax * invTexWidth, texelRectInOutputTex.yMax * invTexHeight, Z_POSITION) * 2.0f - Vector3.one;
+
+            Vector3[] existingVerts = existingMesh.vertices;
+            Vector2[] existingUvs = existingMesh.uv;
+            Color[] existingColors = existingMesh.colors;
+
+            // Now, add the quad
+
+            // Just encode block index, there is too much information to encode into 4 channel color,
+            // so instead the shader will just use a buffer to store the data
+            Color encodedValues = new Color(blockIndex, 0.0f, 0.0f, 0.0f);
+
+            existingVerts[meshVertexStartIndex + 0] = quadPositions[0];
+            existingVerts[meshVertexStartIndex + 1] = quadPositions[1];
+            existingVerts[meshVertexStartIndex + 2] = quadPositions[2];
+            existingVerts[meshVertexStartIndex + 3] = quadPositions[3];
+
+            existingUvs[meshVertexStartIndex + 0] = new Vector2(0.0f, 0.0f);
+            existingUvs[meshVertexStartIndex + 1] = new Vector2(1.0f, 0.0f);
+            existingUvs[meshVertexStartIndex + 2] = new Vector2(0.0f, 1.0f);
+            existingUvs[meshVertexStartIndex + 3] = new Vector2(1.0f, 1.0f);
+
+            existingColors[meshVertexStartIndex + 0] = encodedValues;
+            existingColors[meshVertexStartIndex + 1] = encodedValues;
+            existingColors[meshVertexStartIndex + 2] = encodedValues;
+            existingColors[meshVertexStartIndex + 3] = encodedValues;
+
+            // Update mesh
+            existingMesh.vertices = existingVerts;
+            existingMesh.uv = existingUvs;
+            existingMesh.colors = existingColors;
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrSkinningQuads.cs.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrSkinningQuads.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..64d670242466d735fd1f2d4a74e07c4a9c210ac3
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/OvrSkinningQuads.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 70d1e916f9882564d98886bee94b82a6
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders.meta
new file mode 100644
index 0000000000000000000000000000000000000000..1d78ced1ecbe87e8c5ecf955181909e24cee940e
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: db3abd283e220794d835fdc650ed60bb
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/DisplayArray.shader b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/DisplayArray.shader
new file mode 100644
index 0000000000000000000000000000000000000000..aac84e09c7a45377504ca6987612395a684501df
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/DisplayArray.shader
@@ -0,0 +1,51 @@
+// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
+
+Shader "Avatar/DisplayArray"
+{
+    Properties
+    {
+        _MyArr ("Tex", 2DArray) = "" {}
+        _SliceRange ("Slices", Range(0,16)) = 6
+        _UVScale ("UVScale", Float) = 1.0
+    }
+    SubShader
+    {
+        Pass
+        {
+            CGPROGRAM
+            #pragma vertex vert
+            #pragma fragment frag
+            // texture arrays are not available everywhere,
+            // only compile shader on platforms where they are
+            #pragma require 2darray
+
+            #include "UnityCG.cginc"
+
+            struct v2f
+            {
+                float3 uv : TEXCOORD0;
+                float4 vertex : SV_POSITION;
+            };
+
+            float _SliceRange;
+            float _UVScale;
+
+            v2f vert (float4 vertex : POSITION)
+            {
+                v2f o;
+                o.vertex = UnityObjectToClipPos(vertex);
+                o.uv.xy = (vertex.xy + 0.5) * _UVScale;
+                o.uv.z = (vertex.z + 0.5) * _SliceRange;
+                return o;
+            }
+
+            UNITY_DECLARE_TEX2DARRAY(_MyArr);
+
+            half4 frag (v2f i) : SV_Target
+            {
+                return UNITY_SAMPLE_TEX2DARRAY(_MyArr, i.uv);
+            }
+            ENDCG
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/DisplayArray.shader.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/DisplayArray.shader.meta
new file mode 100644
index 0000000000000000000000000000000000000000..0b93dc56c4f345e2d8ce0cf9677f4f36f4138c17
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/DisplayArray.shader.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 49b8e7d320c7e69479485c6933035310
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/OvrApplyMorphsAndSkinning.compute b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/OvrApplyMorphsAndSkinning.compute
new file mode 100644
index 0000000000000000000000000000000000000000..091584dcd9619e9d81dd843b6d71f8ea13ad52e8
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/OvrApplyMorphsAndSkinning.compute
@@ -0,0 +1,422 @@
+// Each #kernel tells which function to compile; you can have many kernels
+
+// Until Unity 2020, setting keywords is not supported. Instead have to have different kernels
+// with the different keywords defined like this
+#pragma kernel CSMain
+#pragma kernel CSMainWithTangents OVR_HAS_TANGENTS
+#pragma kernel CSMainDoubleBuffer OVR_DOUBLE_BUFFER_OUTPUT
+#pragma kernel CSMainDoubleBufferWithTangents OVR_HAS_TANGENTS OVR_DOUBLE_BUFFER_OUTPUT
+#pragma kernel CSMainTripleBuffer OVR_TRIPLE_BUFFER_OUTPUT
+#pragma kernel CSMainTripleBufferWithTangents OVR_HAS_TANGENTS OVR_TRIPLE_BUFFER_OUTPUT
+
+#include "OvrVertexCompute.cginc"
+#include "OvrOutputBufferCompute.cginc"
+#include "OvrMorphsCompute.cginc"
+#include "OvrSkinningCompute.cginc"
+
+////////////////////////////////////////////////////////////////////////////
+// Data Layout(s)
+////////////////////////////////////////////////////////////////////
+///
+/// There are 4 buffers
+///
+/// "StaticDataBuffer" - All the 'static data' for any number of "mesh entries".
+/// There can be multiple "mesh entries" in the buffer to facilitate batching setups.
+///
+/// {
+///   [static_mesh_meta_data] -> Array of "headers" which holds byte offsets and other metadata about the static mesh data
+///   [neutral_positions] -> An array of "neutral pose positions"
+///   [neutral_normals] -> An array of "neutral pose normals"
+///   [neutral_tangents] -> An array (might be if no tangents) of "neutral pose tangents"
+///   [joint_weights] -> An array of joint weights
+///   [joint_indices] -> An array of joint indices
+///   [output_indices] -> An array of output indices per vertex. Serves as a mapping from input vertex order to output vertex order
+///   [morph_target_deltas] -> Arrays of morph target related information
+/// }
+///
+///  "Dynamic Buffer" - Stores all of the "dynamic" (can change frequently) data for a mesh.
+///  {
+///     [vertex_instance_data] -> An array of all the "vertex instance data" potentially across multiple "mesh instances"
+///     [mesh_instance_meta_data] -> An array of "mesh instance meta data" for the different "mesh instances"
+///     [joint_matrices] -> An array joint matrices for all mesh instances
+///     [joint_weights] -> An array of joint weights for all mesh instances
+///  }
+///
+///  "Position output buffer" - Stores only the positions of the final output.
+///  // Might be interleaved "double buffered" output for same vertex next to each other
+///  [positions] -> An array of output positions
+///
+///  "Frenet output buffer" - Stores interleaved normal + tangents (if applicable) information
+///  // Might be interleaved with "double buffered" output
+///  [normal, tangents] -> An array of interleaved normals and tangents (if applicable)
+
+
+////////////////////////////////
+// Data Structures
+////////////////////////////////
+struct StaticMeshMetaData {
+  // Static data offsets
+
+  // Offset to an array of positions(x), normals(y), tangents(z), joint weights (w)
+  uint4 attributeAndJointWeightsOffsetBytes;
+
+  // Morph target deltas offset bytes(x), numMorphs(y), numMorphedVertices(z), joint indices (w)
+  uint4 morphTargetInfoAndJointIndicesOffsetBytes;
+
+  // Offset of an array of output indices (x) (yzw) - unused
+  uint4 outputIndexOffset;
+
+  float4 vertexInputPositionBias; // Float4s for alignment, w unused (is this needed?)
+  float4 vertexInputPositionScale;
+  float4 vertexOutputPositionBias; // Float4s for alignment, w unused (is this needed?)
+  float4 vertexOutputPositionScale;
+
+  float4 morphTargetsPosRange;
+  float4 morphTargetsNormRange;
+  float4 morphTargetsTanRange;
+
+  // TODO* Padding/Alignment?
+};
+
+struct MeshInstanceMetaData {
+  // meshStaticDataOffsetBytes(x) morphTargetWeightsOffsetBytes(y) jointMatricesOffsetBytes(z) writeToSecondSliceAddress (w)
+  uint4 properties;
+
+  // outputPositionBufferOffsetBytes(x) outputBufferOffsetBytes(y)
+  uint4 outputOffsets;
+
+  // TODO* Padding/Alignment?
+};
+
+struct VertexInstanceData {
+  // meshInstanceDataOffsetBytes(x) vertexIndexInMesh(y)
+  uint4 properties;
+
+  // TODO* Padding/Alignment?
+};
+
+////////////////////////////////////////////
+/// Uniforms and Buffers
+////////////////////////////////////////////
+
+// TODO*: Layout diagram
+ByteAddressBuffer _StaticDataBuffer; // "Bag of Bytes" (really bag of dwords)
+ByteAddressBuffer _DynamicDataBuffer; // "Bag of Bytes" (really bag of dwords)
+RWByteAddressBuffer _PositionOutputBuffer; // "Bag of Bytes" (really bag of dwords)
+RWByteAddressBuffer _FrenetOutputBuffer; // "Bag of Bytes" (really bag of dwords)
+
+int _DispatchStartVertIndex;
+int _DispatchEndVertIndex;
+
+// Using these below instead of #ifdefs to decrease variants (at the cost of
+// a "static" branch) at least until Unity 2020. There would be too large of
+// an explosion of variants to manually track
+bool _EnableMorphTargets; // 0 = false, 1 = true
+bool _EnableSkinning; // 0 = false, 1 = true
+int _MaxJointsToSkin;
+int _VertexPositionsDataFormat;
+int _MorphTargetDeltasDataFormat;
+int _JointIndicesDataFormat;
+int _PositionOutputBufferDataFormat;
+
+////////////////////////////////////////////////////////
+// Helper Functions
+////////////////////////////////////////////////////////
+
+uint GetMorphTargetDeltasOffsetBytes(StaticMeshMetaData static_mesh_meta_data) {
+  // Morph target deltas offset bytes(x)
+  return static_mesh_meta_data.morphTargetInfoAndJointIndicesOffsetBytes.x;
+}
+
+uint GetNumMorphs(StaticMeshMetaData static_mesh_meta_data) {
+  // numMorphs(y)
+  return static_mesh_meta_data.morphTargetInfoAndJointIndicesOffsetBytes.y;
+}
+
+uint GetNumMorphedVertices(StaticMeshMetaData static_mesh_meta_data) {
+  // numMorphedVertices(z)
+  return static_mesh_meta_data.morphTargetInfoAndJointIndicesOffsetBytes.z;
+}
+
+uint GetJointIndicesOffsetBytes(StaticMeshMetaData static_mesh_meta_data) {
+  return static_mesh_meta_data.morphTargetInfoAndJointIndicesOffsetBytes.w;
+}
+
+uint GetOutputPositionBufferOffsetBytes(MeshInstanceMetaData mesh_instance_meta_data) {
+  // outputPositionBufferOffsetBytes(x)
+  return mesh_instance_meta_data.outputOffsets.x;
+}
+
+uint GetOutputBufferOffsetBytes(MeshInstanceMetaData mesh_instance_meta_data) {
+  // outputPositionBufferOffsetBytes(x)
+  return mesh_instance_meta_data.outputOffsets.y;
+}
+
+uint GetMeshStaticDataOffsetBytes(MeshInstanceMetaData mesh_instance_meta_data) {
+  return mesh_instance_meta_data.properties.x;
+}
+
+uint GetMorphTargetWeightsOffetBytes(MeshInstanceMetaData mesh_instance_meta_data) {
+  return mesh_instance_meta_data.properties.y;
+}
+
+uint GetJointMatricesOffsetBytes(MeshInstanceMetaData mesh_instance_meta_data) {
+  // jointMatricesOffsetBytes(z)
+  return mesh_instance_meta_data.properties.z;
+}
+
+// "Slice" here meaning which of the double/triple buffer entries to write to
+uint GetOutputSlice(MeshInstanceMetaData mesh_instance_meta_data, ByteAddressBuffer data_buffer) {
+  // Grab address to read from from the mesh instance meta data
+  const int address = mesh_instance_meta_data.properties.w;
+
+  return data_buffer.Load(address);
+}
+
+uint GetMeshInstanceDataOffsetBytes(VertexInstanceData vertex_instance_data) {
+  return vertex_instance_data.properties.x;
+}
+
+uint GetVertexIndexWithinMesh(VertexInstanceData vertex_instance_data) {
+  return vertex_instance_data.properties.y;
+}
+
+uint GetInputPositionOffsetBytes(StaticMeshMetaData static_mesh_meta_data) {
+  return static_mesh_meta_data.attributeAndJointWeightsOffsetBytes.x;
+}
+
+uint GetInputNormalOffsetBytes(StaticMeshMetaData static_mesh_meta_data) {
+  return static_mesh_meta_data.attributeAndJointWeightsOffsetBytes.y;
+}
+
+uint GetInputTangentOffsetBytes(StaticMeshMetaData static_mesh_meta_data) {
+  return static_mesh_meta_data.attributeAndJointWeightsOffsetBytes.z;
+}
+
+uint GetJointWeightsOffsetBytes(StaticMeshMetaData static_mesh_meta_data) {
+  return static_mesh_meta_data.attributeAndJointWeightsOffsetBytes.w;
+}
+
+uint GetOutputIndexOffsetBytes(StaticMeshMetaData static_mesh_meta_data) {
+  return static_mesh_meta_data.outputIndexOffset.x;
+}
+
+StaticMeshMetaData GetStaticMeshMetaData(ByteAddressBuffer data_buffer, MeshInstanceMetaData mesh_instance) {
+  StaticMeshMetaData meta_data;
+
+  // meshStaticDataHeaderOffsetBytes(y)
+  const uint address = GetMeshStaticDataOffsetBytes(mesh_instance); // in bytes
+
+  // NOTE!!!!!!
+  // Order here is very important and must match struct declaration
+  // Until Shader Model 6.2 is supported by Unity, this will remain in all its fragile glory
+
+  // 4 byte stride per uint
+  // 16 byte stride per float4/uint4
+  meta_data.attributeAndJointWeightsOffsetBytes = OvrLoadUint4(data_buffer, address);
+  meta_data.morphTargetInfoAndJointIndicesOffsetBytes = OvrLoadUint4(data_buffer, address + 16u);
+  meta_data.outputIndexOffset = OvrLoadUint4(data_buffer, address + 32u);
+  meta_data.vertexInputPositionBias = OvrUnpackFloat4x32(data_buffer, address + 48u);
+  meta_data.vertexInputPositionScale = OvrUnpackFloat4x32(data_buffer, address + 64u);
+  meta_data.vertexOutputPositionBias = OvrUnpackFloat4x32(data_buffer, address + 80u);
+  meta_data.vertexOutputPositionScale = OvrUnpackFloat4x32(data_buffer, address + 96u);
+  meta_data.morphTargetsPosRange = OvrUnpackFloat4x32(data_buffer, address + 112u);
+  meta_data.morphTargetsNormRange = OvrUnpackFloat4x32(data_buffer, address + 128u);
+  meta_data.morphTargetsTanRange = OvrUnpackFloat4x32(data_buffer, address + 144u);
+
+  return meta_data;
+}
+
+MeshInstanceMetaData GetMeshInstanceMetaData(ByteAddressBuffer data_buffer, VertexInstanceData vert_instance) {
+  MeshInstanceMetaData mesh_instance_data;
+
+  const uint address = GetMeshInstanceDataOffsetBytes(vert_instance);
+
+  // NOTE!!!!!!
+  // Order here is very important
+  // Until Shader Model 6.2 is supported by Unity, this will remain in all its fragile glory
+  mesh_instance_data.properties = OvrLoadUint4(data_buffer, address);
+  mesh_instance_data.outputOffsets = OvrLoadUint4(data_buffer, address + 16u);
+
+  return mesh_instance_data;
+}
+
+VertexInstanceData GetVertexInstanceData(ByteAddressBuffer data_buffer, uint address) {
+  VertexInstanceData vertex_instance_data;
+
+  // NOTE!!!!!!
+  // Order here is very important
+  // Until Shader Model 6.2 is supported by Unity, this will remain in all its fragile glory
+  vertex_instance_data.properties = OvrLoadUint4(data_buffer, address);
+
+  return vertex_instance_data;
+}
+
+Vertex GetVertexStaticData(
+  StaticMeshMetaData static_mesh_meta_data,
+  uint vertex_index)
+{
+  return GetVertexStaticData(
+    _StaticDataBuffer,
+    GetInputPositionOffsetBytes(static_mesh_meta_data),
+    GetInputNormalOffsetBytes(static_mesh_meta_data),
+    GetJointWeightsOffsetBytes(static_mesh_meta_data),
+    OVR_FORMAT_FLOAT_32,
+    GetJointIndicesOffsetBytes(static_mesh_meta_data),
+    _JointIndicesDataFormat,
+    GetOutputIndexOffsetBytes(static_mesh_meta_data),
+    _VertexPositionsDataFormat,
+    static_mesh_meta_data.vertexInputPositionBias.xyz,
+    static_mesh_meta_data.vertexInputPositionScale.xyz,
+    vertex_index);
+}
+
+VertexInstanceData GetVertexInstanceData(uint vert_instances_index) {
+  // ASSUMPTION: The vertex instance data is the first data in the dynamic buffer
+  static const uint STRIDE = 4u * 4u; // 4 32-bit uints
+  const int address = vert_instances_index * STRIDE;
+
+  VertexInstanceData vertex_instance_data;
+  vertex_instance_data.properties = OvrLoadUint4(_DynamicDataBuffer, address);
+
+  return vertex_instance_data;
+}
+
+////////////////////////////////////////
+// Main
+////////////////////////////////////////
+
+void ApplyMorphsAndSkinning(uint vert_instances_index) {
+  // Benchmark if this or just having some "empty" data is faster (though more memory)
+  if (vert_instances_index > (uint)_DispatchEndVertIndex) {
+    return;
+  }
+
+  // ASSUMPTION: The vertex instance data is the first data in the dynamic buffer
+  const VertexInstanceData vertex_instance = GetVertexInstanceData(vert_instances_index);
+
+  // From that data, grab the mesh instance data and mesh static data
+  const MeshInstanceMetaData mesh_instance_meta_data = GetMeshInstanceMetaData(
+    _DynamicDataBuffer,
+    vertex_instance);
+
+  const StaticMeshMetaData static_mesh_meta_data = GetStaticMeshMetaData(
+    _StaticDataBuffer,
+    mesh_instance_meta_data);
+
+  // Get the vertex id "within the mesh"
+  const uint vertex_index = GetVertexIndexWithinMesh(vertex_instance);
+
+  // Grab "vertex static data" out of the static data
+  Vertex vertex = GetVertexStaticData(static_mesh_meta_data, vertex_index);
+
+  // Compiler should hopefully optimize out any potential branches due to static const bool values,
+  // and otherwise, branches should be based on uniform parameters passed in which
+  // should make their just the branch and not cause diverging branches across workgroups
+  // Compiler should also optimize out unused parameters
+#if defined(OVR_HAS_TANGENTS)
+  static const bool has_tangents = true;
+#else
+  static const bool has_tangents = false;
+#endif
+
+#if defined(OVR_DOUBLE_BUFFER_OUTPUT)
+  static const uint num_slices_per_attribute = 2;
+  const uint output_slice = GetOutputSlice(mesh_instance_meta_data, _DynamicDataBuffer);
+#elif defined(OVR_TRIPLE_BUFFER_OUTPUT)
+  static const uint num_slices_per_attribute = 3;
+  const uint output_slice = GetOutputSlice(mesh_instance_meta_data, _DynamicDataBuffer);
+#else
+  static const uint num_slices_per_attribute = 1;
+  static const uint output_slice = 0;
+#endif
+
+
+  float4 vertex_tangent = float4(0.0, 0.0, 1.0, 1.0);
+  if (has_tangents) {
+    uint tangents_start_address = GetInputTangentOffsetBytes(static_mesh_meta_data);
+    vertex_tangent = GetNeutralPoseTangent(_StaticDataBuffer, tangents_start_address, vertex_index);
+  }
+
+  // Apply morphs
+  [branch]
+  if (_EnableMorphTargets) {
+    ApplyRectangularMorphs(
+      _StaticDataBuffer,
+      _DynamicDataBuffer,
+      GetMorphTargetDeltasOffsetBytes(static_mesh_meta_data),
+      GetMorphTargetWeightsOffetBytes(mesh_instance_meta_data),
+      GetNumMorphs(static_mesh_meta_data),
+      GetNumMorphedVertices(static_mesh_meta_data),
+      _MorphTargetDeltasDataFormat,
+      vertex.position,
+      static_mesh_meta_data.morphTargetsPosRange.xyz,
+      vertex.normal,
+      static_mesh_meta_data.morphTargetsNormRange.xyz,
+      vertex_tangent,
+      static_mesh_meta_data.morphTargetsTanRange.xyz,
+      vertex.vertexIndex,
+      has_tangents);
+  }
+
+  // Apply skinning
+  [branch]
+  if (_EnableSkinning && _MaxJointsToSkin > 0) {
+    ApplySkinning(
+      _MaxJointsToSkin,
+      _DynamicDataBuffer,
+      GetJointMatricesOffsetBytes(mesh_instance_meta_data),
+      vertex.position,
+      vertex.normal,
+      vertex_tangent.xyz,
+      vertex.jointWeights,
+      vertex.jointIndices,
+      has_tangents);
+  }
+
+  StoreVertexOutput(
+    _PositionOutputBuffer,
+    _FrenetOutputBuffer,
+    GetOutputPositionBufferOffsetBytes(mesh_instance_meta_data),
+    GetOutputBufferOffsetBytes(mesh_instance_meta_data),
+    static_mesh_meta_data.vertexOutputPositionBias.xyz,
+    1.0 / static_mesh_meta_data.vertexOutputPositionScale.xyz,
+    _PositionOutputBufferDataFormat,
+    vertex.position,
+    vertex.normal,
+    vertex_tangent,
+    vertex.outputIndex,
+    has_tangents,
+    num_slices_per_attribute,
+    output_slice);
+}
+
+[numthreads(64,1,1)]
+void CSMain(uint3 id : SV_DispatchThreadID) {
+  ApplyMorphsAndSkinning((uint)_DispatchStartVertIndex + id.x);
+}
+
+[numthreads(64,1,1)]
+void CSMainWithTangents(uint3 id : SV_DispatchThreadID) {
+  ApplyMorphsAndSkinning((uint)_DispatchStartVertIndex + id.x);
+}
+
+[numthreads(64,1,1)]
+void CSMainDoubleBuffer(uint3 id : SV_DispatchThreadID) {
+  ApplyMorphsAndSkinning((uint)_DispatchStartVertIndex + id.x);
+}
+
+[numthreads(64,1,1)]
+void CSMainDoubleBufferWithTangents(uint3 id : SV_DispatchThreadID) {
+  ApplyMorphsAndSkinning((uint)_DispatchStartVertIndex + id.x);
+}
+
+[numthreads(64,1,1)]
+void CSMainTripleBuffer(uint3 id : SV_DispatchThreadID) {
+  ApplyMorphsAndSkinning((uint)_DispatchStartVertIndex + id.x);
+}
+
+[numthreads(64,1,1)]
+void CSMainTripleBufferWithTangents(uint3 id : SV_DispatchThreadID) {
+  ApplyMorphsAndSkinning((uint)_DispatchStartVertIndex + id.x);
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/OvrApplyMorphsAndSkinning.compute.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/OvrApplyMorphsAndSkinning.compute.meta
new file mode 100644
index 0000000000000000000000000000000000000000..a256999fb04b79beeaefd4409ae6a941ff06abfc
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/OvrApplyMorphsAndSkinning.compute.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 6593496738c8e8045badc49f0ef2917a
+ComputeShaderImporter:
+  externalObjects: {}
+  currentAPIMask: 2052
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/OvrMorphsCompute.cginc b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/OvrMorphsCompute.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..694b91cd7c2e5af756b085eac36230aa49734123
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/OvrMorphsCompute.cginc
@@ -0,0 +1,248 @@
+#ifndef OVR_MORPHS_COMPUTE_INCLUDED
+#define OVR_MORPHS_COMPUTE_INCLUDED
+
+#include "../../../ShaderUtils/OvrDecodeUtils.cginc"
+#include "../../../ShaderUtils/OvrDecodeFormats.cginc"
+
+float GetMorphTargetWeight(
+  in ByteAddressBuffer data_buffer,
+  uint morph_target_weights_start_address,
+  uint morph_target_index)
+{
+  static const uint STRIDE = 4u; // single 32-bit float
+  const uint address = mad(STRIDE, morph_target_index, morph_target_weights_start_address);
+  return OvrUnpackFloat1x32(data_buffer, address);
+}
+
+// Yes, macros. Was too much copy and paste otherwise
+
+// ASSUMPTION: Assumes some variable names to limit number of parameters to the macro
+// so, if stuff fails to compile, it might be do to bad assumed variable names
+#define OVR_APPLY_RECTANGULAR_MORPHS_BODY(unpack_func) \
+  for (mt_idx = 0; mt_idx < num_morphs; mt_idx++) { \
+    const float weight = GetMorphTargetWeight( \
+      dynamic_data_buffer, \
+      morph_target_weights_start_address, \
+      mt_idx); \
+\
+    pos_sum += weight * unpack_func(static_data_buffer, address); \
+\
+    address += attribute_row_stride; \
+    norm_sum += weight * unpack_func(static_data_buffer, address); \
+\
+    address += attribute_row_stride; \
+  }
+
+// ASSUMPTION: Assumes some variable names to limit number of parameters to the macro
+// so, if stuff fails to compile, it might be do to bad assumed variable names
+#define OVR_APPLY_RECTANGULAR_MORPHS_BODY_WITH_TANGENTS(unpack_func) \
+  for (mt_idx = 0; mt_idx < num_morphs; mt_idx++) { \
+    const float weight = GetMorphTargetWeight( \
+      dynamic_data_buffer, \
+      morph_target_weights_start_address, \
+      mt_idx); \
+\
+    pos_sum += weight * unpack_func(static_data_buffer, address); \
+\
+    address += attribute_row_stride; \
+    norm_sum += weight * unpack_func(static_data_buffer, address); \
+\
+    address += attribute_row_stride; \
+    tan_sum += weight * unpack_func(static_data_buffer, address); \
+\
+    address += attribute_row_stride; \
+}
+
+// Compiler should hopefully optimize out any potential branches due to static const bool values.
+// Compiler should also optimize out unused parameters
+void ApplyRectangularMorphsWithTangents(
+    in ByteAddressBuffer static_data_buffer,
+    in ByteAddressBuffer dynamic_data_buffer,
+    uint morph_target_deltas_start_address,
+    uint morph_target_weights_start_address,
+    uint num_morphs,
+    uint num_morphed_vertices,
+    int morph_target_deltas_format,
+    inout float4 position,
+    float3 pos_scale,
+    inout float3 normal,
+    float3 norm_scale,
+    inout float4 tangent,
+    float3 tan_scale,
+    uint vertex_index)
+{
+  static const uint STRIDE_32 = 4u * 4u;
+  static const uint STRIDE_16 = 2u * 4u;
+  static const uint STRIDE_10_10_10_2 = 4u;
+  static const uint STRIDE_8 = 4u;
+
+  // ASSUMPTION: Data for a given morph target is arranged
+  // with all position deltas, then all normal deltas
+  uint address = 0;
+  uint attribute_row_stride = 0;
+
+  float3 pos_sum = 0.0;
+  float3 norm_sum = 0.0;
+  float3 tan_sum = 0.0;
+
+  uint mt_idx = 0;
+
+  [branch] switch(morph_target_deltas_format)
+  {
+    case OVR_FORMAT_FLOAT_32:
+      address = mad(STRIDE_32, vertex_index, morph_target_deltas_start_address);
+      attribute_row_stride = num_morphed_vertices * STRIDE_32;
+      OVR_APPLY_RECTANGULAR_MORPHS_BODY_WITH_TANGENTS(OvrUnpackFloat3x32)
+    break;
+    case OVR_FORMAT_HALF_16:
+      address = mad(STRIDE_16, vertex_index, morph_target_deltas_start_address);
+      attribute_row_stride = num_morphed_vertices * STRIDE_16;
+      OVR_APPLY_RECTANGULAR_MORPHS_BODY_WITH_TANGENTS(OvrUnpackHalf3x16)
+    break;
+    case OVR_FORMAT_UNORM_16:
+      address = mad(STRIDE_16, vertex_index, morph_target_deltas_start_address);
+      attribute_row_stride = num_morphed_vertices * STRIDE_16;
+      OVR_APPLY_RECTANGULAR_MORPHS_BODY_WITH_TANGENTS(OvrUnpackUnorm3x16)
+    break;
+    case OVR_FORMAT_SNORM_10_10_10_2:
+      address = mad(STRIDE_10_10_10_2, vertex_index, morph_target_deltas_start_address);
+      attribute_row_stride = num_morphed_vertices * STRIDE_10_10_10_2;
+      OVR_APPLY_RECTANGULAR_MORPHS_BODY_WITH_TANGENTS(OvrUnpackVector_10_10_10_2)
+    break;
+    case OVR_FORMAT_UNORM_8:
+      address = mad(STRIDE_8, vertex_index, morph_target_deltas_start_address);
+      attribute_row_stride = num_morphed_vertices * STRIDE_8;
+      OVR_APPLY_RECTANGULAR_MORPHS_BODY_WITH_TANGENTS(OvrUnpackUnorm3x8)
+    break;
+    default:
+      // error?
+    break;
+  }
+
+  position.xyz += pos_scale * pos_sum;
+  normal += norm_scale * norm_sum;
+  tangent.xyz += tan_scale * tan_sum;
+}
+
+// Compiler should hopefully optimize out any potential branches due to static const bool values.
+// Compiler should also optimize out unused parameters
+void ApplyRectangularMorphsNoTangents(
+    in ByteAddressBuffer static_data_buffer,
+    in ByteAddressBuffer dynamic_data_buffer,
+    uint morph_target_deltas_start_address,
+    uint morph_target_weights_start_address,
+    uint num_morphs,
+    uint num_morphed_vertices,
+    uint morph_target_deltas_format,
+    inout float4 position,
+    float3 pos_scale,
+    inout float3 normal,
+    float3 norm_scale,
+    uint vertex_index)
+{
+  static const int STRIDE_32 = 4u * 4u; // In memory as 4 component vectors for alignment purposes (needed?)
+  static const int STRIDE_16 = 2u * 4u;
+  static const int STRIDE_10_10_10_2 = 4u;
+  static const int STRIDE_8 = 4u;
+
+  // ASSUMPTION: Data for a given morph target is arranged
+  // with all position deltas, then all normal deltas
+  uint address = 0;
+  uint attribute_row_stride = 0;
+
+  float3 pos_sum = 0.0;
+  float3 norm_sum = 0.0;
+
+  uint mt_idx = 0;
+
+  [branch] switch(morph_target_deltas_format)
+  {
+    case OVR_FORMAT_FLOAT_32:
+      address = mad(STRIDE_32, vertex_index, morph_target_deltas_start_address);
+      attribute_row_stride = num_morphed_vertices * STRIDE_32;
+      OVR_APPLY_RECTANGULAR_MORPHS_BODY(OvrUnpackFloat3x32)
+    break;
+    case OVR_FORMAT_HALF_16:
+      address = mad(STRIDE_16, vertex_index, morph_target_deltas_start_address);
+      attribute_row_stride = num_morphed_vertices * STRIDE_16;
+      OVR_APPLY_RECTANGULAR_MORPHS_BODY(OvrUnpackHalf3x16)
+    break;
+    case OVR_FORMAT_UNORM_16:
+      address = mad(STRIDE_16, vertex_index, morph_target_deltas_start_address);
+      attribute_row_stride = num_morphed_vertices * STRIDE_16;
+      OVR_APPLY_RECTANGULAR_MORPHS_BODY(OvrUnpackUnorm3x16)
+    break;
+    case OVR_FORMAT_SNORM_10_10_10_2:
+      address = mad(STRIDE_10_10_10_2, vertex_index, morph_target_deltas_start_address);
+      attribute_row_stride = num_morphed_vertices * STRIDE_10_10_10_2;
+      OVR_APPLY_RECTANGULAR_MORPHS_BODY(OvrUnpackVector_10_10_10_2)
+    break;
+    case OVR_FORMAT_UNORM_8:
+      address = mad(STRIDE_8, vertex_index, morph_target_deltas_start_address);
+      attribute_row_stride = num_morphed_vertices * STRIDE_8;
+      OVR_APPLY_RECTANGULAR_MORPHS_BODY(OvrUnpackUnorm3x8)
+    break;
+    default:
+      // error?
+    break;
+  }
+
+  position.xyz += pos_scale * pos_sum;
+  normal += norm_scale * norm_sum;
+}
+
+// Compiler should hopefully optimize out any potential branches due to static const bool values.
+// Compiler should also optimize out unused parameters
+void ApplyRectangularMorphs(
+    in ByteAddressBuffer static_data_buffer,
+    in ByteAddressBuffer dynamic_data_buffer,
+    uint morph_target_deltas_start_address,
+    uint morph_target_weights_start_address,
+    uint num_morphs,
+    uint num_morphed_vertices,
+    uint morph_target_deltas_format,
+    inout float4 position,
+    float3 pos_scale,
+    inout float3 normal,
+    float3 norm_scale,
+    inout float4 tangent,
+    float3 tan_scale,
+    uint vertex_index,
+    bool has_tangents)
+{
+  if (has_tangents) {
+    ApplyRectangularMorphsWithTangents(
+      static_data_buffer,
+      dynamic_data_buffer,
+      morph_target_deltas_start_address,
+      morph_target_weights_start_address,
+      num_morphs,
+      num_morphed_vertices,
+      morph_target_deltas_format,
+      position,
+      pos_scale,
+      normal,
+      norm_scale,
+      tangent,
+      tan_scale,
+      vertex_index);
+  }
+  else
+  {
+    ApplyRectangularMorphsNoTangents(
+       static_data_buffer,
+       dynamic_data_buffer,
+       morph_target_deltas_start_address,
+       morph_target_weights_start_address,
+       num_morphs,
+       num_morphed_vertices,
+       morph_target_deltas_format,
+       position,
+       pos_scale,
+       normal,
+       norm_scale,
+       vertex_index);
+  }
+}
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/OvrMorphsCompute.cginc.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/OvrMorphsCompute.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..8aaf167fca35f666d328f7454abf0d651a7e8615
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/OvrMorphsCompute.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 965a7342d0a3d754ebd6dffb92b93258
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/OvrOutputBufferCompute.cginc b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/OvrOutputBufferCompute.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..206d579f4942b02d40492d14686d5451717d65f7
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/OvrOutputBufferCompute.cginc
@@ -0,0 +1,222 @@
+#ifndef OVR_OUTPUT_BUFFER_COMPUTE_INCLUDED
+#define OVR_OUTPUT_BUFFER_COMPUTE_INCLUDED
+
+#include "../../../ShaderUtils/OvrDecodeUtils.cginc"
+#include "../../../ShaderUtils/OvrDecodeFormats.cginc"
+
+//////////////////////////////////////////////////////
+// Output
+//////////////////////////////////////////////////////
+
+void StoreVertexNormal(
+  inout RWByteAddressBuffer output_buffer,
+  uint output_buffer_start_address,
+  in float3 normal,
+  uint output_index)
+{
+  static const uint STRIDE = 4u; // 1 32-bit uint for 10_10_10_2
+
+  // Normalize on store
+  const uint address = mad(output_index, STRIDE, output_buffer_start_address);
+  OvrStoreUint(output_buffer, address, OvrPackSnorm4x10_10_10_2(float4(normalize(normal.xyz), 0.0)));
+}
+
+void StoreVertexTangent(
+  inout RWByteAddressBuffer output_buffer,
+  uint output_buffer_start_address,
+  in float4 tangent,
+  uint output_index)
+{
+  static const uint STRIDE = 4u; // 1 32-bit uint for 10_10_10_2
+
+  // Normalize on store
+  const uint address = mad(output_index, STRIDE, output_buffer_start_address);
+  OvrStoreUint(output_buffer, address, OvrPackSnorm4x10_10_10_2(float4(normalize(tangent.xyz), tangent.w)));
+}
+
+void StoreVertexPositionFloat4x32(
+  inout RWByteAddressBuffer output_buffer,
+  uint output_buffer_start_address,
+  in float4 position,
+  uint output_index)
+{
+  static const int POS_STRIDE = 4u * 4u; // 4 32-bit uints for 4 32-bit floats
+  const uint address = mad(output_index, POS_STRIDE, output_buffer_start_address);
+  const uint4 packed_data = asuint(position);
+
+  OvrStoreUint4(output_buffer, address, packed_data);
+}
+
+void StoreVertexPositionHalf4x16(
+  inout RWByteAddressBuffer output_buffer,
+  uint output_buffer_start_address,
+  in float4 position,
+  uint output_index)
+{
+  static const uint STRIDE = 4u * 2u; // 2 32-bit uints for 4 16-bit halfs
+  const uint address = mad(output_index, STRIDE, output_buffer_start_address);
+  const uint2 packed_data = uint2(OvrPackHalf2x16(position.xy), OvrPackHalf2x16(position.zw));
+
+  OvrStoreUint2(output_buffer, address, packed_data);
+}
+
+void StoreVertexPositionUnorm4x16(
+  inout RWByteAddressBuffer output_buffer,
+  uint output_buffer_start_address,
+  in float4 position,
+  in float3 position_bias,
+  in float3 inv_position_scale,
+  uint output_index)
+{
+  static const uint STRIDE = 4u * 2u; // 2 32-bit uints for 4 16-bit unorms
+  const uint address = mad(output_index, STRIDE, output_buffer_start_address);
+
+  // Normalize to 0 -> 1 but given the bias and scale
+  // ASSUMPTION: Assuming the position_bias and position_scale will be large enough
+  // to place in the range 0 -> 1
+  float4 normalized = float4((position.xyz - position_bias) * inv_position_scale, position.w);
+  const uint2 packed_data = uint2(OvrPackUnorm2x16(normalized.xy), OvrPackUnorm2x16(normalized.zw));
+
+  OvrStoreUint2(output_buffer, address, packed_data);
+}
+
+void StoreVertexPositionUnorm4x8(
+  inout RWByteAddressBuffer output_buffer,
+  uint output_buffer_start_address,
+  in float4 position,
+  in float3 position_bias,
+  in float3 inv_position_scale,
+  uint output_index)
+{
+  static const uint STRIDE = 4u; // 1 32-bit uints for 4 8-bit unorms
+  const uint address = mad(output_index, STRIDE, output_buffer_start_address);
+
+  // Normalize to 0 -> 1 but given the offset and scale
+  // ASSUMPTION: Assuming the position_offset and position_scale will be large enough
+  // to place in the range 0 -> 1
+  const float4 normalized = float4((position.xyz - position_bias) * inv_position_scale, position.w);
+  const uint packed_data = OvrPackUnorm4x8(normalized);
+
+  OvrStoreUint(output_buffer, address, packed_data);
+}
+
+uint CalculatePositionOutputIndex(
+  uint vertex_output_index,
+  uint num_slices_per_attribute,
+  uint output_slice)
+{
+  return vertex_output_index * num_slices_per_attribute + output_slice;
+}
+
+uint CalculateNormalOutputIndex(
+  uint vertex_output_index,
+  uint num_slices_per_attribute,
+  uint output_slice,
+  bool has_tangents)
+{
+  // *2 if interleaving tangent
+  return (has_tangents ? 2u : 1u) * vertex_output_index * num_slices_per_attribute + output_slice;
+}
+
+void StoreVertexPosition(
+  inout RWByteAddressBuffer output_buffer,
+  uint output_buffer_start_address,
+  int format,
+  float3 position_bias,
+  float3 inv_position_scale,
+  in float4 position,
+  uint output_index)
+{
+  [branch] switch(format)
+  {
+    case OVR_FORMAT_FLOAT_32:
+      StoreVertexPositionFloat4x32(
+        output_buffer,
+        output_buffer_start_address,
+        position,
+        output_index);
+    break;
+    case OVR_FORMAT_HALF_16:
+      StoreVertexPositionHalf4x16(
+        output_buffer,
+        output_buffer_start_address,
+        position,
+        output_index);
+    break;
+    case OVR_FORMAT_UNORM_16:
+      StoreVertexPositionUnorm4x16(
+        output_buffer,
+        output_buffer_start_address,
+        position,
+        position_bias,
+        inv_position_scale,
+        output_index);
+    break;
+    case OVR_FORMAT_UNORM_8:
+      StoreVertexPositionUnorm4x8(
+        output_buffer,
+        output_buffer_start_address,
+        position,
+        position_bias,
+        inv_position_scale,
+        output_index);
+    break;
+    default:
+      break;
+  }
+}
+
+// Compiler should hopefully optimize out any potential branches due to static const bool values,
+// and otherwise, branches should be based on uniform parameters passed in which
+// should make their just the branch and not cause diverging branches across workgroups
+// Compiler should also optimize out unused parameters
+void StoreVertexOutput(
+  inout RWByteAddressBuffer position_output_buffer,
+  inout RWByteAddressBuffer output_buffer,
+  uint position_output_buffer_start_address,
+  uint output_buffer_start_address,
+  float3 position_bias,
+  float3 inv_position_scale,
+  int position_format,
+  in float4 position,
+  in float3 normal,
+  in float4 tangent,
+  uint vertex_output_index,
+  bool has_tangents,
+  uint num_slices_per_attribute,
+  uint output_slice)
+{
+  // * 2 due to double buffering, then maybe +1 if writing to second slice
+  const uint pos_output_index = CalculatePositionOutputIndex(
+    vertex_output_index,
+    num_slices_per_attribute,
+    output_slice);
+  const uint norm_output_index = CalculateNormalOutputIndex(
+    vertex_output_index,
+    num_slices_per_attribute,
+    output_slice,
+    has_tangents);
+
+  StoreVertexPosition(
+    position_output_buffer,
+    position_output_buffer_start_address,
+    position_format,
+    position_bias,
+    inv_position_scale,
+    position,
+    pos_output_index);
+
+  StoreVertexNormal(
+    output_buffer,
+    output_buffer_start_address,
+    normal,
+    norm_output_index);
+
+  if (has_tangents) {
+    const int tangent_output_index = norm_output_index + num_slices_per_attribute;
+
+    StoreVertexTangent(output_buffer, output_buffer_start_address, tangent, tangent_output_index);
+  }
+}
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/OvrOutputBufferCompute.cginc.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/OvrOutputBufferCompute.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..28f68a9898cb5c25ad529a4435f728364985e3f1
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/OvrOutputBufferCompute.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 90a35115571c4b648908d914aa13c40e
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/OvrSkinningCompute.cginc b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/OvrSkinningCompute.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..c0fc2ff13681535034533fe46f302b229f5f3401
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/OvrSkinningCompute.cginc
@@ -0,0 +1,68 @@
+#ifndef OVR_SKINNING_COMPUTE_INCLUDED
+#define OVR_SKINNING_COMPUTE_INCLUDED
+
+#include "../../../ShaderUtils/OvrDecodeUtils.cginc"
+
+float4x4 GetJointMatrix(
+  in ByteAddressBuffer data_buffer,
+  uint joint_matrices_start_address,
+  uint joint_index)
+{
+  // Load 4x float4s, store in matrix, return
+  static const uint STRIDE = 16u * 4u; // 16 floats in matrix. Each float is 4 bytes
+
+  const uint matrix_data_start_address = mad(joint_index, STRIDE, joint_matrices_start_address);
+  return OvrUnpackFloat16x32(data_buffer, matrix_data_start_address);
+}
+
+void ApplySkinning(
+  int max_joints_to_skin,
+  in ByteAddressBuffer dynamic_data_buffer,
+  uint joint_matrices_start_address,
+  inout float4 position,
+  inout float3 normal,
+  inout float3 tangent,
+  in float4 joint_weights,
+  in uint4 joint_indices,
+  bool has_tangents)
+{
+  // The weights used should all sum up to 1.0 to prevent distortion
+  // In cases where the encoded data encodes up to 4 weights, but
+  // less are used due to a runtime setting, the weights may have to be recalculated
+  float sum_of_weights = joint_weights.x;
+  float4x4 blended_model_matrix = GetJointMatrix(dynamic_data_buffer, joint_matrices_start_address, joint_indices.x) * joint_weights.x;
+  float4x4 blended_normal_matrix = GetJointMatrix(dynamic_data_buffer, joint_matrices_start_address, joint_indices.x + 1u) * joint_weights.x;
+
+  [branch]
+  if (max_joints_to_skin > 1 && joint_weights.y > 0) {
+    blended_model_matrix += GetJointMatrix(dynamic_data_buffer, joint_matrices_start_address, joint_indices.y) * joint_weights.y;
+    blended_normal_matrix += GetJointMatrix(dynamic_data_buffer, joint_matrices_start_address, joint_indices.y + 1u) * joint_weights.y;
+    sum_of_weights += joint_weights.y;
+
+    [branch]
+    if (max_joints_to_skin > 2 && joint_weights.z > 0) {
+      blended_model_matrix += GetJointMatrix(dynamic_data_buffer, joint_matrices_start_address, joint_indices.z) * joint_weights.z;
+      blended_normal_matrix += GetJointMatrix(dynamic_data_buffer, joint_matrices_start_address, joint_indices.z + 1u) * joint_weights.z;
+      sum_of_weights += joint_weights.z;
+
+      [branch]
+      if (max_joints_to_skin > 3 && joint_weights.w > 0) {
+        blended_model_matrix += GetJointMatrix(dynamic_data_buffer, joint_matrices_start_address, joint_indices.w) * joint_weights.w;
+        blended_normal_matrix += GetJointMatrix(dynamic_data_buffer, joint_matrices_start_address, joint_indices.w + 1u) * joint_weights.w;
+        sum_of_weights += joint_weights.w;
+      } // end if max_joints_to_skin > 3
+    } // end if max_joints_to_skin > 2
+  } // end if max_joints_to_skin > 1
+
+  blended_model_matrix = blended_model_matrix / sum_of_weights;
+  blended_normal_matrix = blended_normal_matrix / sum_of_weights;
+
+  position.xyz = mul(blended_model_matrix, position).xyz;
+  normal = mul(blended_normal_matrix, float4(normal, 0.0)).xyz;
+
+  if (has_tangents) {
+    tangent.xyz = mul(blended_model_matrix, float4(tangent.xyz, 0.0)).xyz;
+  }
+}
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/OvrSkinningCompute.cginc.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/OvrSkinningCompute.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..aa14a5f84ec409996eeeb1970463b6737aba438d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/OvrSkinningCompute.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 00fb2f9f922796c4fa43f5a0c8b46b54
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/OvrVertexCompute.cginc b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/OvrVertexCompute.cginc
new file mode 100644
index 0000000000000000000000000000000000000000..6d6d2a7d98c16b1e7bc4c442f685f8a3e2aae29c
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/OvrVertexCompute.cginc
@@ -0,0 +1,270 @@
+#ifndef OVR_VERTEX_COMPUTE_INCLUDED
+#define OVR_VERTEX_COMPUTE_INCLUDED
+
+#include "../../../ShaderUtils/OvrDecodeUtils.cginc"
+#include "../../../ShaderUtils/OvrDecodeFormats.cginc"
+
+struct Vertex {
+  float4 position;
+  float3 normal;
+  uint vertexIndex;
+  uint4 jointIndices;
+  float4 jointWeights;
+  uint outputIndex;
+};
+
+///////////////////////////////////////////////////
+// Neutral Pose
+///////////////////////////////////////////////////
+
+float4 GetNeutralPosePosition(
+  ByteAddressBuffer data_buffer,
+  uint positions_start_address,
+  float3 bias,
+  float3 scale,
+  uint vertex_index,
+  int format)
+{
+  static const uint STRIDE_X32 = 4u * 4u; // 4 32-bit uints for 4 32-bit values
+  static const uint STRIDE_X16 = 2u * 4u; // 2 32-bit uints for 4 16-bit values
+  static const uint STRIDE_X8 = 1u * 4u; // 1 32-bit uint for 4 8-bit values
+
+  float4 position = float4(0.0, 0.0, 0.0, 1.0);
+
+  [branch] switch(format) {
+    case OVR_FORMAT_FLOAT_32:
+      // 4 32-bit uints for 4 32-bit floats
+      position.xyz = OvrUnpackFloat3x32(
+        data_buffer,
+        mad(vertex_index, STRIDE_X32, positions_start_address));
+    break;
+    case OVR_FORMAT_HALF_16:
+      // 2 32-bit uints for 4 16 bit halfs
+      position.xyz = OvrUnpackHalf3x16(
+        data_buffer,
+        mad(vertex_index, STRIDE_X16, positions_start_address));
+    break;
+    case OVR_FORMAT_UNORM_16:
+      position.xyz = OvrUnpackUnorm3x16(
+        data_buffer,
+        mad(vertex_index, STRIDE_X16, positions_start_address));
+    break;
+    case OVR_FORMAT_SNORM_10_10_10_2:
+      position.xyz = OvrUnpackVector_10_10_10_2(
+        data_buffer,
+        mad(vertex_index, STRIDE_X8, positions_start_address));
+    break;
+    case OVR_FORMAT_UNORM_8:
+      position.xyz = OvrUnpackUnorm3x8(
+        data_buffer,
+        mad(vertex_index, STRIDE_X8, positions_start_address));
+    break;
+    default:
+      break;
+  }
+
+  // Apply scale and bias
+  position.xyz = mad(position.xyz, scale, bias);
+
+  return position;
+}
+
+float3 GetNeutralPoseNormal(
+  ByteAddressBuffer data_buffer,
+  uint normals_start_address,
+  uint vertex_index)
+{
+  // Only supporting 10-10-10-2 snorm at the moment
+  static const uint STRIDE = 1u * 4u; // 1 32-bit uint for 3 10-bit SNorm and 1 2-bit extra
+  return normalize(OvrUnpackSnorm3x10_10_10_2(
+    data_buffer,
+    mad(vertex_index, STRIDE, normals_start_address)));
+}
+
+float4 GetNeutralPoseTangent(
+  ByteAddressBuffer data_buffer,
+  uint tangents_start_address,
+  uint vertex_index)
+{
+  // Only supporting full floats for positions at the moment
+  static const uint STRIDE = 1u * 4u; // 1 32-bit uint for 3 10-bit snorm and 1 2bit extra
+  float4 tangent = OvrUnpackSnorm4x10_10_10_2(
+    data_buffer,
+    mad(vertex_index, STRIDE, tangents_start_address));
+
+  tangent.xyz = normalize(tangent.xyz);
+
+  return tangent;
+}
+
+float4 GetJointWeights(
+  in ByteAddressBuffer data_buffer,
+  uint joint_weights_address,
+  uint vertex_index,
+  uint format)
+{
+  static const uint STRIDE_X32 = 4u * 4u; // 4 32-bit uints for 4 32-bit values
+  static const uint STRIDE_X16 = 2u * 4u; // 2 32-bit uints for 4 16-bit values
+  static const uint STRIDE_X8 = 1u * 4u; // 1 32-bit uint for 4 8-bit values
+
+  float4 weights = float4(0.0, 0.0, 0.0, 0.0);
+
+  [branch] switch(format) {
+    case OVR_FORMAT_FLOAT_32:
+      // 4 32-bit uints for 4 32-bit floats
+      weights = OvrUnpackFloat4x32(
+        data_buffer,
+        mad(vertex_index, STRIDE_X32, joint_weights_address));
+    break;
+    case OVR_FORMAT_HALF_16:
+      // 2 32-bit uints for 4 16 bit halfs
+      weights = OvrUnpackHalf4x16(
+        data_buffer,
+        mad(vertex_index, STRIDE_X16, joint_weights_address));
+    break;
+    case OVR_FORMAT_UNORM_16:
+      weights = OvrUnpackUnorm4x16(
+        data_buffer,
+        mad(vertex_index, STRIDE_X16, joint_weights_address));
+    break;
+    case OVR_FORMAT_UNORM_8:
+      weights = OvrUnpackUnorm4x8(
+        data_buffer,
+        mad(vertex_index, STRIDE_X8, joint_weights_address));
+    break;
+    default:
+      break;
+  }
+
+  return weights;
+}
+
+uint4 GetJointIndices(
+  in ByteAddressBuffer data_buffer,
+  uint joint_indices_address,
+  uint vertex_index,
+  uint format)
+{
+  static const uint STRIDE_X16 = 2u * 4u; // 2 32-bit uints for 4 16-bit values
+  static const uint STRIDE_X8 = 1u * 4u; // 1 32-bit uint for 4 8-bit values
+
+  uint4 indices = uint4(0u, 0u, 0u, 0u);
+
+  [branch] switch(format) {
+    case OVR_FORMAT_UINT_16:
+      indices = OvrUnpackUint4x16(
+        data_buffer,
+        mad(vertex_index, STRIDE_X16, joint_indices_address));
+    break;
+    case OVR_FORMAT_UINT_8:
+      indices = OvrUnpackUint4x8(
+        data_buffer,
+        mad(vertex_index, STRIDE_X8, joint_indices_address));
+    break;
+    default:
+      break;
+  }
+
+  return indices;
+}
+
+uint GetOutputIndex(
+  in ByteAddressBuffer data_buffer,
+  uint output_indices_offset_bytes,
+  uint vertex_index)
+{
+  // ASSUMPTION: Data stored for the output index is an array of Uint16s
+
+  // Somewhat tricky here as we need to calculate the address, but that needs to be a multiple of 4
+  // (ByteAddressBuffer = bag of uints)
+  // So, this function needs to load a particular uint and then separate out the uint16s
+  static const uint STRIDE = 2u; // 1 16-bit unsigned int, stride is in bytes
+  uint address = mad(vertex_index, STRIDE, output_indices_offset_bytes); // in bytes
+
+  // Address not guaranteed to be multiple of 4 here,
+  // so round down to multiple of 4
+  address = address / SIZE_OF_UINT * SIZE_OF_UINT;
+
+  // Convert from bytes to uint offset
+  uint2 unpacked = OvrUnpackUint2x16(data_buffer, address);
+
+  // Now decide which one to pull
+  return vertex_index % 2u == 0u ? unpacked.x : unpacked.y;
+}
+
+void PopulateVertexNoTangents(
+  in ByteAddressBuffer static_data_buffer,
+  uint positions_offset_bytes,
+  uint normals_offset_bytes,
+  uint joint_weights_offset_bytes,
+  uint joint_weights_format,
+  uint joint_indices_offset_bytes,
+  uint joint_indices_format,
+  uint output_index_mapping_offset_bytes,
+  uint position_format,
+  float3 position_bias,
+  float3 position_scale,
+  uint vertex_index,
+  inout Vertex vertex)
+{
+  vertex.position = GetNeutralPosePosition(
+    static_data_buffer,
+    positions_offset_bytes,
+    position_bias.xyz,
+    position_scale.xyz,
+    vertex_index,
+    position_format);
+  vertex.normal = GetNeutralPoseNormal(static_data_buffer, normals_offset_bytes, vertex_index);
+
+  vertex.outputIndex = GetOutputIndex(static_data_buffer, output_index_mapping_offset_bytes, vertex_index);
+
+  const uint4 joint_indices = GetJointIndices(
+    static_data_buffer,
+    joint_indices_offset_bytes,
+    vertex_index,
+    joint_indices_format);
+
+  vertex.jointWeights = GetJointWeights(
+    static_data_buffer,
+    joint_weights_offset_bytes,
+    vertex_index,
+    joint_weights_format);
+  vertex.jointIndices = joint_indices * 2u; // * 2 because of interleaved matrices, so 2x per joint
+}
+
+Vertex GetVertexStaticData(
+  in ByteAddressBuffer static_data_buffer,
+  uint positions_offset_bytes,
+  uint normals_offset_bytes,
+  uint joint_weights_offset_bytes,
+  int joint_weights_format,
+  uint joint_indices_offset_bytes,
+  int joint_indices_format,
+  uint output_index_mapping_offset_bytes,
+  int position_format,
+  float3 position_bias,
+  float3 position_scale,
+  uint vertex_index)
+{
+  Vertex result = (Vertex)0;
+
+  PopulateVertexNoTangents(
+    static_data_buffer,
+    positions_offset_bytes,
+    normals_offset_bytes,
+    joint_weights_offset_bytes,
+    joint_weights_format,
+    joint_indices_offset_bytes,
+    joint_indices_format,
+    output_index_mapping_offset_bytes,
+    position_format,
+    position_bias,
+    position_scale,
+    vertex_index,
+    result);
+
+  result.vertexIndex = vertex_index;
+  return result;
+}
+
+#endif
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/OvrVertexCompute.cginc.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/OvrVertexCompute.cginc.meta
new file mode 100644
index 0000000000000000000000000000000000000000..cc532951ec6882c6be840eb55892f1a31945c782
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/OvrVertexCompute.cginc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 7a5088ea3ed743349b50a50ea125332d
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/combine-morph-targets.shader b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/combine-morph-targets.shader
new file mode 100644
index 0000000000000000000000000000000000000000..be06fe62689a8000a7d9abeaa7dfcea542172c80
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/combine-morph-targets.shader
@@ -0,0 +1,176 @@
+// CombineMorphTargets shader:
+// - combines partial morphs into a blended shape based on the weights given
+
+Shader "Avatar/CombineMorphTargets" {
+  Properties {
+    [NoScaleOffset] u_MorphTargetSourceTex("MorphTargets Source Texture", 2DArray) = "black" {}
+  }
+  SubShader {
+    Tags{
+        "RenderType" =
+            "Transparent"
+            "Queue" = "Transparent"} LOD 100
+
+        Pass {
+      Cull Off Blend One One ZTest Off ZWrite Off ZClip Off
+
+          CGPROGRAM
+#pragma vertex VertShader
+#pragma fragment FragShader
+
+#pragma target 4.5
+
+#pragma multi_compile ___ OVR_MORPH_10_10_10_2
+#pragma multi_compile ___ OVR_HAS_TANGENTS
+
+#include "UnityCG.cginc"
+
+      // TODO: Really this should be a constant buffer. But thats not working in Unity 2020
+      uniform StructuredBuffer<float> u_Weights;
+      uniform int u_WeightOffset;
+
+      uniform float u_BlockEnabled;
+
+      uniform float4 u_MorphTargetRanges[3];
+
+      #define OVR_TEXTURE_PRECISION_FLOAT // hard coding this for now to test perf
+
+      // NOTE: According to Unity documentation here https://docs.unity3d.com/Manual/SL-DataTypesAndPrecision.html
+      // The standard declaration of Texture2DArray yields the following
+      // "For mobile platforms, these translate into “low precision samplers”, i.e. the textures are expected to
+      // have low precision data in them."
+      // Upon shader inspection, the declarations become "uniform mediump sampler2DArray" which
+      // is 16-bit precision. This is not desired as some of the data in the textures is
+      // expected to have 32-bit precision. So, for mobile platforms, make an option for explicitly
+      // setting 32-bit precision
+      #if defined(SHADER_API_MOBILE) && defined(OVR_TEXTURE_PRECISION_FLOAT)
+          #define OVR_DECLARE_TEX2DARRAY(tex) Texture2DArray_float tex; SamplerState sampler##tex
+      #else
+          #define OVR_DECLARE_TEX2DARRAY(tex) UNITY_DECLARE_TEX2DARRAY(tex)
+      #endif
+
+      OVR_DECLARE_TEX2DARRAY(u_MorphTargetSourceTex);
+
+      struct appdata {
+        float4 a_Position : POSITION;
+        float4 a_Color : COLOR;
+        float2 a_UV1 : TEXCOORD0;
+      };
+
+      struct v2F {
+        float4 pos : SV_POSITION;
+        float3 v_UVCoord1 : TEXCOORD0;
+
+        nointerpolation float v_Weight : TEXCOORD1;
+      };
+
+      int OvrBitfieldExtract10(int value, int offset) {
+        value = value >> offset;
+        value &= 0x03ff;
+        if ((value & 0x0200) != 0) {
+          value |= 0xfffffc00;
+        }
+        return value;
+      }
+
+      float3 Unpack_10_10_10_2(int packedValue, float3 scale) {
+        // bonus scale is still a unorm, if I convert it to an snorm, I loose one value.
+        // that does mean I can't use the hardware to convert this format though, it has
+        // to be unpacked by hand. If you do have hardware 10_10_10_2 conversion, it may
+        // be better to just sample twice? once as unorm, once as snorm.
+        uint bonusScaleIndex = uint(packedValue >> 30 & 0x03);
+
+        const float bonusScaleLookup[4] = {1.0f, 0.5f, 0.25f, 0.125f};
+        float bonusScale = bonusScaleLookup[bonusScaleIndex];
+
+        int3 unpackedInt;
+        unpackedInt.x = OvrBitfieldExtract10(packedValue, 0);
+        unpackedInt.y = OvrBitfieldExtract10(packedValue, 10);
+        unpackedInt.z = OvrBitfieldExtract10(packedValue, 20);
+
+        float3 unpacked = float3(unpackedInt);
+        // convert all to -1 to 1
+        const float inv511 = 1.0 / 511.0;
+        unpacked *= float3(inv511, inv511, inv511);
+
+        unpacked = unpacked * scale * bonusScale;
+
+        return unpacked;
+      }
+
+      v2F VertShader(appdata input) {
+        v2F output;
+
+        float4 pos = float4(input.a_Position.xyz, 1.0);
+#if UNITY_UV_STARTS_AT_TOP
+        // Unity is trying to be "helpful" and unify DX v OGL coordinates,
+        // Unfortunately it does a very poor job and just makes things worse
+        // Because our quad center is (0,0) this effectively flips them vertically
+        pos.y = -input.a_Position.y;
+#endif
+
+        output.v_UVCoord1.xy = input.a_UV1;
+
+        // Pull tex slice from color, store as z in UV
+        output.v_UVCoord1.z = input.a_Color.w;
+
+        // Grab blend weight array index and enabled array index
+        uint weightIndex = uint(round(input.a_Color.y));
+
+        float weight = u_Weights[u_WeightOffset + weightIndex];
+        output.v_Weight = weight;
+
+        float isWeightNonZero = float(weight > 0.0001);
+
+        // Generate degnerate triangle if weight or enabled are 0
+        output.pos = pos * (u_BlockEnabled * isWeightNonZero);
+
+        return output;
+      }
+
+      float4 FragShader(v2F input) : SV_Target {
+        float4 output;
+
+#if defined(OVR_MORPH_10_10_10_2)
+        uint2 texSize;
+        uint elements;
+        uint levels;
+        u_MorphTargetSourceTex.GetDimensions(0, texSize.x, texSize.y, elements, levels);
+        uint rangeSelection = uint(float(texSize.y) * input.v_UVCoord1.y);
+#if defined(OVR_HAS_TANGENTS)
+        rangeSelection %= 3;
+#else
+        rangeSelection %= 2;
+#endif
+
+        // Unity wont let you set the texture format to something more useful like R32_SInt, or
+        // A2B10G10R10_UIntPack32. It will fail to create the texture array. Maybe would have
+        // more luck with a regular 2d texture? At any rate currently have to take a lot more pain
+        // in decoding the data. First taking R8G8B8A8_UNorm data, and converting it to a single 32
+        // bit integer, then decoding the 10_10_10_2 format that it really is.
+        float4 texSampleUNorms =
+            UNITY_SAMPLE_TEX2DARRAY(u_MorphTargetSourceTex, input.v_UVCoord1.xyz);
+        texSampleUNorms = round(texSampleUNorms * float4(255.0, 255.0, 255.0, 255.0));
+        int4 texSampleBytes = texSampleUNorms;
+        int texSample = texSampleBytes.x | (texSampleBytes.y << 8) | (texSampleBytes.z << 16) |
+            (texSampleBytes.w << 24);
+        float3 unpacked = Unpack_10_10_10_2(texSample, u_MorphTargetRanges[rangeSelection]);
+
+        output = float4(unpacked * input.v_Weight, 1.0);
+#else
+
+        float4 texSample = UNITY_SAMPLE_TEX2DARRAY(u_MorphTargetSourceTex, input.v_UVCoord1.xyz);
+
+        output.rgb = (texSample.rgb * input.v_Weight);
+        output.a = 1.0;
+#endif
+
+        return output;
+      }
+
+      ENDCG
+    } // Pass
+
+  } // SubShader
+
+} // Shader
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/combine-morph-targets.shader.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/combine-morph-targets.shader.meta
new file mode 100644
index 0000000000000000000000000000000000000000..e2cecd5654a5d660a4d6ae0545fd7255dc5aadc3
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/combine-morph-targets.shader.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: bef2973cf0137a04fbdd59ed0bab09bc
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/skin-to-texture.shader b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/skin-to-texture.shader
new file mode 100644
index 0000000000000000000000000000000000000000..d25ee76edbc366fe73cba9ed223e1fe238d5a115
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/skin-to-texture.shader
@@ -0,0 +1,287 @@
+// SkinToTexture shader:
+// - skins default model verts to a skeleton and encodes pos/norm into textures
+
+Shader "Avatar/SkinToTexture"
+{
+    Properties
+    {
+        [ShowIfKeyword(OVR_SKINNING_QUALITY_1_BONE, OVR_SKINNING_QUALITY_2_BONES, OVR_SKINNING_QUALITY_4_BONES)]
+        [NoScaleOffset] u_JointsTex("Joints Source Texture", 2DArray) = "black" {}
+
+        [ShowIfKeyword(OVR_HAS_MORPH_TARGETS, OVR_HAS_MORPH_TARGETS_INDIRECTION_TEXTURE)]
+        [NoScaleOffset] u_CombinedMorphTargetsTex("MorphTargets Combined Texture", 2DArray) = "black" {}
+
+        [ShowIfKeyword(OVR_HAS_MORPH_TARGETS_INDIRECTION_TEXTURE)]
+        [NoScaleOffset] u_IndirectionTex("Vert Indirection Texture", 2DArray) = "black" {}
+
+        [NoScaleOffset] u_NeutralPoseTex("Neutral Pose Texture", 2DArray) = "black" {}
+
+        // Will set "HAS_JOINTS" shader keyword when set.
+        [Toggle(OVR_HAS_MORPH_TARGETS_INDIRECTION_TEXTURE)] _HAS_MORPH_TARGETS ("Has MT?", Float) = 0
+    }
+    SubShader
+    {
+        Tags { "RenderType"="Opaque" "RenderQueue"="Geometry" }
+        LOD 100
+
+        Pass
+        {
+            Cull Off
+            Blend One Zero
+            ZTest Off
+            ZWrite Off
+            ZClip Off
+
+            CGPROGRAM
+            #pragma vertex VertShader
+            #pragma fragment FragShader
+
+            #pragma require compute
+
+            #pragma multi_compile ___ OVR_HAS_MORPH_TARGETS OVR_HAS_MORPH_TARGETS_INDIRECTION_TEXTURE
+            #pragma multi_compile ___ OVR_SKINNING_QUALITY_1_BONE OVR_SKINNING_QUALITY_2_BONES OVR_SKINNING_QUALITY_4_BONES
+            #pragma multi_compile ___ OVR_OUTPUT_SCALE_BIAS
+
+            #include "UnityCG.cginc"
+
+            #define OVR_HAS_JOINTS (defined(OVR_SKINNING_QUALITY_1_BONE) || defined(OVR_SKINNING_QUALITY_2_BONES) || defined(OVR_SKINNING_QUALITY_4_BONES))
+
+            #define OVR_TEXTURE_PRECISION_FLOAT // hard coding this for now to test perf
+
+            // NOTE: According to Unity documentation here https://docs.unity3d.com/Manual/SL-DataTypesAndPrecision.html
+            // The standard declaration of Texture2DArray yields the following
+            // "For mobile platforms, these translate into “low precision samplers”, i.e. the textures are expected to
+            // have low precision data in them."
+            // Upon shader inspection, the declarations become "uniform mediump sampler2DArray" which
+            // is 16-bit precision. This is not desired as some of the data in the textures is
+            // expected to have 32-bit precision. So, for mobile platforms, make an option for explicitly
+            // setting 32-bit precision
+            #if defined(SHADER_API_MOBILE) && defined(OVR_TEXTURE_PRECISION_FLOAT)
+                #define OVR_DECLARE_TEX2DARRAY(tex) Texture2DArray_float tex; SamplerState sampler##tex
+            #else
+                #define OVR_DECLARE_TEX2DARRAY(tex) UNITY_DECLARE_TEX2DARRAY(tex)
+            #endif
+
+            struct appdata {
+                float4 a_Position : POSITION;
+                float2 a_UV1 : TEXCOORD0;
+                float4 a_Color : COLOR;
+            };
+
+            struct v2f {
+                float4 pos : SV_POSITION;
+                float3 v_NeutralPoseTexUv : TEXCOORD0;
+#if defined(OVR_HAS_JOINTS)
+                float3 v_JointsTexUv : TEXCOORD1;
+                nointerpolation uint f_JointsStartIndex : TEXCOORD2;
+#endif
+
+#if defined(OVR_HAS_MORPH_TARGETS_INDIRECTION_TEXTURE)
+                // w is index
+                float3 v_IndirectionTexUv : TEXCOORD3;
+#elif defined(OVR_HAS_MORPH_TARGETS)
+                float3 v_CombinedMorphTargetsUv: TEXCOORD3;
+#endif
+            };
+
+            struct PerBlockData {
+              float4 neutralPoseTexUvRect;
+              float4 indicesAndSlices;
+
+#if defined(OVR_HAS_JOINTS)
+              float4 jointsTexUvRect;
+#endif
+
+#if defined(OVR_HAS_MORPH_TARGETS_INDIRECTION_TEXTURE)
+              float4 indirectionTexUvRect;
+#elif defined(OVR_HAS_MORPH_TARGETS)
+              float4 combinedMorphTargetsUvRect;
+#endif
+            };
+
+            struct JointData
+            {
+                float4x4 transform;
+                float4x4 normalTransform;
+            };
+
+            // Textures
+            OVR_DECLARE_TEX2DARRAY( u_NeutralPoseTex );
+
+#if defined(OVR_HAS_JOINTS)
+            OVR_DECLARE_TEX2DARRAY( u_JointsTex );
+#endif
+
+#if defined(OVR_HAS_MORPH_TARGETS) || defined(OVR_HAS_MORPH_TARGETS_INDIRECTION_TEXTURE)
+            OVR_DECLARE_TEX2DARRAY( u_CombinedMorphTargetsTex );
+#if defined(OVR_HAS_MORPH_TARGETS_INDIRECTION_TEXTURE)
+            OVR_DECLARE_TEX2DARRAY( u_IndirectionTex );
+#endif
+#endif
+
+            uniform StructuredBuffer<PerBlockData> u_BlockData;
+            uniform StructuredBuffer<JointData> u_JointMatrices;
+
+            uniform float u_BlockEnabled;
+
+#if OVR_OUTPUT_SCALE_BIAS
+            uniform float2 u_OutputScaleBias;
+#endif
+
+            uniform int u_JointOffset;
+
+            float3 getUvForTexture(float4 uvRect, float texSlice, float2 a_UV1)
+            {
+              float3 result;
+
+              // Use the attribute UV as a interpolation value
+              // between the min and max
+              float minU = uvRect.x;
+              float rangeU = uvRect.z;
+              float minV = uvRect.y;
+              float rangeV = uvRect.w;
+
+              result.x = minU + (rangeU * a_UV1.x);
+              result.y = minV + (rangeV * a_UV1.y);
+              result.z = texSlice;
+
+              return result;
+            }
+
+            v2f VertShader(appdata vIn)
+            {
+              v2f output;
+
+              float4 pos = float4(vIn.a_Position.xyz, 1.0);
+#if UNITY_UV_STARTS_AT_TOP
+              // Unity is trying to be "helpful" and unify DX v OGL coordinates,
+              // Unfortunately it does a very poor job and just makes things worse
+              // Because our quad center is (0,0) this effectively flips them vertically
+              pos.y = -pos.y;
+#endif
+
+              int blockIndex = int(vIn.a_Color.r);
+              // Generate degnerate triangle if not enabled
+              output.pos = pos * step(0.0, u_BlockEnabled);
+
+              // Grab blockdata from array by index
+              PerBlockData blockData = u_BlockData[blockIndex];
+
+              // Create UV for neutral pose tex
+              output.v_NeutralPoseTexUv = getUvForTexture(blockData.neutralPoseTexUvRect, blockData.indicesAndSlices.g, vIn.a_UV1).xyz;
+
+#if defined(OVR_HAS_JOINTS)
+              // Create UV for joints texture
+              output.v_JointsTexUv = getUvForTexture(blockData.jointsTexUvRect, blockData.indicesAndSlices.b, vIn.a_UV1).xyz;
+
+              // Pass a float (non interpolated) joints start index
+              output.f_JointsStartIndex = uint(blockData.indicesAndSlices.r);
+#endif
+
+#if defined(OVR_HAS_MORPH_TARGETS_INDIRECTION_TEXTURE)
+              // Shader model doesn't allow vertex texture fetch - boo
+              output.v_IndirectionTexUv = getUvForTexture(blockData.indirectionTexUvRect, blockData.indicesAndSlices.a, vIn.a_UV1);
+              //output.v_IndirectionTexUv.w = blockData.indicesAndSlices.a;
+#elif defined(OVR_HAS_MORPH_TARGETS)
+              output.v_CombinedMorphTargetsUv = getUvForTexture(blockData.combinedMorphTargetsUvRect, blockData.indicesAndSlices.a, vIn.a_UV1);
+#endif
+
+              return output;
+            }
+
+            float4x4 GetJointMatrixForAttribute(uint jointIndex, bool isAttributeNormal) {
+              uint jIndex = jointIndex + u_JointOffset;
+              return isAttributeNormal ? u_JointMatrices[jIndex].normalTransform
+                                       : u_JointMatrices[jIndex].transform;
+            }
+
+            float4 FragShader(v2f input) : SV_Target
+            {
+                // Sample from neutral pose tex to get the "base line"
+                float4 attributeData = UNITY_SAMPLE_TEX2DARRAY(u_NeutralPoseTex, input.v_NeutralPoseTexUv.xyz);
+                bool isPosition = attributeData.a == 1.0;
+                bool isNormal = attributeData.a == 0.0;
+                bool isTangent = !isPosition && !isNormal;
+
+                // Tangents have either a 0.25 or 0.75 stored in alpha, so convert that
+                // to -1 or 1
+                float tangentOutputAlpha = step(0.5, attributeData.a) * 2.0 - 1.0;
+                attributeData.a = isPosition ? 1.0 : 0.0;
+
+                float outputAlpha = isTangent ? tangentOutputAlpha : attributeData.a;
+
+                // If morph targets are available, apply morph targets, normalizing for non
+                // position attributes
+#if defined(OVR_HAS_MORPH_TARGETS) || defined(OVR_HAS_MORPH_TARGETS_INDIRECTION_TEXTURE)
+
+  #if defined(OVR_HAS_MORPH_TARGETS_INDIRECTION_TEXTURE)
+                float3 morphTargetUv = UNITY_SAMPLE_TEX2DARRAY(u_IndirectionTex, input.v_IndirectionTexUv.xyz).xyz;
+  #elif defined(OVR_HAS_MORPH_TARGETS)
+                float3 morphTargetUv = input.v_CombinedMorphTargetsUv;
+  #endif // OVR_HAS_MORPH_TARGETS
+
+                float3 morphTargetDelta = UNITY_SAMPLE_TEX2DARRAY(u_CombinedMorphTargetsTex, morphTargetUv.xyz).xyz;
+
+                attributeData.xyz += morphTargetDelta;
+                // Apply delta and normalize if needed
+                attributeData.xyz = isPosition ? attributeData.xyz : normalize(attributeData.xyz);
+#endif // defined(OVR_HAS_MORPH_TARGETS) || defined(OVR_HAS_MORPH_TARGETS_INDIRECTION_TEXTURE)
+
+                // If joints are available multiply by matrices
+                #if defined(OVR_HAS_JOINTS)
+                    float4 skinningData = UNITY_SAMPLE_TEX2DARRAY(u_JointsTex, input.v_JointsTexUv.xyz);
+                    float4 boneIndices = floor(skinningData);
+                    float4 boneWeights = (skinningData - boneIndices) * 2.0; // * 2 here because the weights are stored in range of 0 -> 0.5
+
+                    boneIndices = boneIndices + input.f_JointsStartIndex;
+
+                    // The weights used should all sum up to 1.0 to prevent distortion
+                    // In cases where the encoded data encodes up to 4 weights, but
+                    // less are used, the weights may have to be recalculated
+                    float sumOfWeights = boneWeights.x;
+                    #if defined(OVR_SKINNING_QUALITY_2_BONES) || defined(OVR_SKINNING_QUALITY_4_BONES)
+                        sumOfWeights += boneWeights.y;
+                        #if defined(OVR_SKINNING_QUALITY_4_BONES)
+                            sumOfWeights += boneWeights.z + boneWeights.w;
+                        #endif // defined(OVR_SKINNING_QUALITY_4_BONES)
+                    #endif // defined(OVR_SKINNING_QUALITY_2_BONES) || defined(OVR_SKINNING_QUALITY_4_BONES)
+
+                    boneWeights /= sumOfWeights;
+
+                    float4x4 blendedMatrix = GetJointMatrixForAttribute(boneIndices.x, isNormal) * boneWeights.x;
+                    #if defined(OVR_SKINNING_QUALITY_2_BONES) || defined(OVR_SKINNING_QUALITY_4_BONES)
+                        [branch]
+                        if(boneWeights.y > 0) {
+                            blendedMatrix += GetJointMatrixForAttribute(boneIndices.y, isNormal) * boneWeights.y;
+                        }
+                        #if defined(OVR_SKINNING_QUALITY_4_BONES)
+                            [branch]
+                            if(boneWeights.z > 0) {
+                                blendedMatrix += GetJointMatrixForAttribute(boneIndices.z, isNormal) * boneWeights.z;
+                            }
+
+                            [branch]
+                            if(boneWeights.w > 0) {
+                                blendedMatrix += GetJointMatrixForAttribute(boneIndices.w, isNormal) * boneWeights.w;
+                            }
+                        #endif // OVR_SKINNING_QUALITY_4_BONES
+                    #endif // defined(OVR_SKINNING_QUALITY_2_BONES) || defined(OVR_SKINNING_QUALITY_4_BONES)
+
+                    attributeData = mul(blendedMatrix, attributeData);
+                #endif // OVR_HAS_JOINTS
+
+                float4 output;
+                output.rgb = attributeData.rgb;
+                output.a = outputAlpha;
+
+                #if OVR_OUTPUT_SCALE_BIAS
+                    output = output * u_OutputScaleBias.x + u_OutputScaleBias.y;
+                #endif
+
+                return output;
+            }
+
+            ENDCG
+        } // Pass
+    } // SubShader
+} // Shader
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/skin-to-texture.shader.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/skin-to-texture.shader.meta
new file mode 100644
index 0000000000000000000000000000000000000000..6c4e5e222c012f5fcec6a9c41e728e5ea9faf82a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinning/Shaders/skin-to-texture.shader.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: ee1f1406edf162945868fafc408691aa
+ShaderImporter:
+  externalObjects: {}
+  defaultTextures: []
+  nonModifiableTextures: []
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinningConfiguration.cs b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinningConfiguration.cs
new file mode 100644
index 0000000000000000000000000000000000000000..721e85ba90abd41b69d53c7e2376a87e21c0485d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinningConfiguration.cs
@@ -0,0 +1,274 @@
+// #define OVR_AVATAR_CLAMP_SKINNING_QUALITY
+
+using System;
+
+using Oculus.Skinning;
+using Oculus.Skinning.GpuSkinning;
+
+using UnityEngine;
+using UnityEngine.Experimental.Rendering;
+
+#if UNITY_2019_3_OR_NEWER
+using UnitySkinningQuality = UnityEngine.SkinWeights;
+#else
+using UnitySkinningQuality = UnityEngine.BlendWeights;
+#endif
+
+using SkinningConfig = Oculus.Avatar2.OvrAvatarEntity.SkinningConfig;
+
+/// @file GpuSkinningConfiguration.cs
+
+namespace Oculus.Avatar2
+{
+    // TODO: Convert to ScriptableObject
+    ///
+    /// Contains configuration options for GPU skinning.
+    /// You can specify the skinning quality (number of bones per vertex),
+    /// the numeric precision (float, half) and what type of skinning
+    /// implementation to use.
+    /// @see OvrAvatarSkinnedRenderable
+    ///
+    public class GpuSkinningConfiguration : OvrSingletonBehaviour<GpuSkinningConfiguration>
+    {
+        private const string logScope = "GpuSkinningConfiguration";
+
+        // Avoid skinning more avatars than MaxSkinnedAvatarsPerFrame per frame
+        public const uint MaxSkinnedAvatarsPerFrame = OvrAvatarGpuSkinningController.MaxSkinnedAvatarsPerFrame;
+
+        internal enum TexturePrecision { Float = 0, Half = 1, Unorm16 = 2, Snorm10 = 3, Byte = 4, Nibble = 5 }
+
+        /// Maximum allowed skinning quality (bones per vertex).
+        // Helper to query Unity skinWeights/boneWeights configuration as SkinningQuality enum
+        public OvrSkinningTypes.SkinningQuality MaxAllowedSkinningQuality
+        {
+            get
+            {
+#if OVR_AVATAR_CLAMP_SKINNING_QUALITY
+                // It'd be nice to cache this, but it can be changed by script at runtime
+#if UNITY_2019_3_OR_NEWER
+                switch (QualitySettings.skinWeights)
+#else // 2018
+                switch (QualitySettings.blendWeights)
+#endif
+                {
+                    case UnitySkinningQuality.OneBone:
+                        return OvrSkinningTypes.SkinningQuality.Bone1;
+                    case UnitySkinningQuality.TwoBones:
+                        return OvrSkinningTypes.SkinningQuality.Bone2;
+                    case UnitySkinningQuality.FourBones:
+                        return OvrSkinningTypes.SkinningQuality.Bone4;
+                }
+                return OvrSkinningTypes.SkinningQuality.Invalid;
+#else
+                return OvrSkinningTypes.SkinningQuality.Bone4;
+#endif
+            }
+        }
+
+        /// Default skinning implementation to use.
+        [SerializeField]
+        [Tooltip("SkinningConfig used when OvrAvatarEntity is set to `DEFAULT`")]
+        private SkinningConfig DefaultSkinningType = SkinningConfig.DEFAULT;
+
+        /// Fallback skinning type (when skinning implementation is not specified).
+        // Used if Entity does not specify and DefaultSkinningType == SkinningConfig.DEFAULT
+        private SkinningConfig FallbackSkinningType = SkinningConfig.OVR_UNITY_GPU_FULL;
+
+        /// Skinning quality (bones per vertex) used for each level of detail.
+        [SerializeField]
+        [Tooltip("SkinningQuality (number of bone influences per vert) used for each LOD")]
+        private OvrSkinningTypes.SkinningQuality[] QualityPerLOD = Array.Empty<OvrSkinningTypes.SkinningQuality>();
+
+        /// Precision of source morph textures.
+        [Header("Texture Settings (Advanced)")]
+        [Tooltip("Morph texture should work well as snorm10")]
+        [SerializeField]
+        internal TexturePrecision SourceMorphFormat = TexturePrecision.Float;
+
+        /// Precision of combined morph textures.
+        [Tooltip("Morph texture should work well as half")]
+        [SerializeField]
+        internal TexturePrecision CombinedMorphFormat = TexturePrecision.Float;
+
+        /// Precision of skinning output textures.
+        [Tooltip("Skinner Output likely needs to be float")]
+        [SerializeField]
+        internal TexturePrecision SkinnerOutputFormat = TexturePrecision.Float;
+
+        /// Scale used to calculate scale and bias used when using unorm formats.
+        [Tooltip("Scale used to calculate scale and bias used when using unorm formats")]
+        [SerializeField]
+        internal float SkinnerUnormScale = 4.0f;
+
+        /// Format of neutral pose texture.
+        [SerializeField]
+        readonly internal GraphicsFormat NeutralPoseFormat = GraphicsFormat.R32G32B32A32_SFloat;
+
+        /// Format of joints texture.
+        [SerializeField]
+        readonly internal GraphicsFormat JointsFormat = GraphicsFormat.R32G32B32A32_SFloat;
+
+        /// Format of indirection texture.
+        [SerializeField]
+        readonly internal GraphicsFormat IndirectionFormat = GraphicsFormat.R32G32B32A32_SFloat;
+
+        [Header("Performance Optimizations (Advanced)")]
+        [Tooltip("Enables Support for Application Space Warp. Applications still need to enable ASW.")]
+        [SerializeField]
+        internal bool SupportApplicationSpaceWarp;
+
+        [Header("Experimental Settings (Advanced, Unsupported)")]
+
+        /// Enable motion smoothing for GPU skinning.
+        [Tooltip("Smooths Motion Between Animation Updates but Introduces Latency. Ignored for Unity skinning types.")]
+        [SerializeField]
+        internal bool MotionSmoothing = false;
+
+        [Header("Shader Settings (Advanced)")]
+
+        // Assigned via editor
+        // TODO: Remove suppresion of unused variable warning once auto-recompile is landed
+#pragma warning disable CS0649
+        [SerializeField]
+        private Shader _CombineMorphTargetsShader;
+        [SerializeField]
+        private Shader _SkinToTextureShader;
+#pragma warning restore CS0649 //  is never assigned to
+
+        /// Shader to use for combining morph targets.
+        public Shader CombineMorphTargetsShader => _CombineMorphTargetsShader;
+
+        // Shader to use for skinning.
+        public Shader SkinToTextureShader => _SkinToTextureShader;
+
+        protected override void Initialize()
+        {
+            ValidateTexturePrecision(ref SourceMorphFormat, FormatUsage.Linear);
+            ValidateTexturePrecision(ref CombinedMorphFormat, FormatUsage.Blend);
+            ValidateTexturePrecision(ref SkinnerOutputFormat, FormatUsage.Render);
+        }
+
+        internal static void HandleDefaultConfig(ref SkinningConfig config)
+        {
+            Debug.Assert(hasInstance);
+            if (config == SkinningConfig.DEFAULT)
+            {
+                var defaultConfig = Instance.DefaultSkinningType;
+                config = defaultConfig != SkinningConfig.DEFAULT ? defaultConfig : Instance.FallbackSkinningType;
+            }
+        }
+
+        internal OvrSkinningTypes.SkinningQuality GetQualityForLOD(uint lodIndex)
+        {
+            Debug.Assert(lodIndex < QualityPerLOD.Length);
+            var configValue = QualityPerLOD[lodIndex];
+#if OVR_AVATAR_CLAMP_SKINNING_QUALITY
+            configValue = (OvrSkinningTypes.SkinningQuality)Math.Min((int)configValue, (int)MaxAllowedSkinningQuality);
+#endif
+            return configValue;
+        }
+
+        internal void ValidateFallbackSkinner(bool unitySkinnerSupported, bool gpuSkinnerSupported)
+        {
+            if (!gpuSkinnerSupported && FallbackSkinningType == SkinningConfig.OVR_UNITY_GPU_FULL)
+            {
+                FallbackSkinningType = unitySkinnerSupported ? SkinningConfig.UNITY : SkinningConfig.NONE;
+            }
+        }
+
+        private void ValidateTexturePrecision(ref TexturePrecision precision, FormatUsage usage)
+        {
+            var configPrecision = precision;
+
+            // Float is "lowest common denominator" - if the system doesn't support that it can't gpu skin
+            // TODO: Trigger fallback to UnitySMR if float isn't supported? Should be caught by ShaderModel check
+            while (precision > TexturePrecision.Float)
+            {
+                var graphicsFormat = precision.GetGraphicsFormat();
+                if (SystemInfo.IsFormatSupported(graphicsFormat, usage))
+                {
+                    // precision is supported - use it
+                    break;
+                }
+
+                // precision is not supported - drop down to next simpler/more-compatible format
+                precision--;
+            }
+
+            if (precision != configPrecision)
+            {
+                OvrAvatarLog.LogWarning(
+                    $"Configured precision {configPrecision} unsupported for usage {usage}"
+                    + $" - falling back to {precision} for compatibility"
+                    , logScope, this);
+            }
+        }
+
+#if UNITY_EDITOR
+        protected virtual void OnValidate()
+        {
+            EnforceValidFormatMorph(ref SourceMorphFormat);
+            EnforceValidFormat(ref CombinedMorphFormat);
+            EnforceValidFormat(ref SkinnerOutputFormat);
+
+            ValidateQualityPerLOD();
+
+            CheckDefaultShader(ref _CombineMorphTargetsShader, "Avatar/CombineMorphTargets");
+            CheckDefaultShader(ref _SkinToTextureShader, "Avatar/SkinToTexture");
+        }
+
+        private void EnforceValidFormat(ref TexturePrecision precision)
+        {
+            if (precision == TexturePrecision.Byte || precision == TexturePrecision.Nibble || precision == TexturePrecision.Snorm10)
+            {
+                RevertFormat(ref precision, TexturePrecision.Half);
+            }
+        }
+
+        private void EnforceValidFormatMorph(ref TexturePrecision precision)
+        {
+            if (precision == TexturePrecision.Byte || precision == TexturePrecision.Nibble || precision == TexturePrecision.Unorm16)
+            {
+                RevertFormat(ref precision, TexturePrecision.Snorm10);
+            }
+        }
+
+        private void RevertFormat<T>(ref T textureFormat, T correctFormat) where T : Enum
+        {
+            OvrAvatarLog.LogError($"Unsupported format {textureFormat}, reverted to {correctFormat}");
+            Debug.LogWarning("Bad GpuSkinningConfig setting", this);
+            textureFormat = correctFormat;
+        }
+
+        private void ValidateQualityPerLOD()
+        {
+            int oldLength = QualityPerLOD?.Length ?? 0;
+            for (int idx = 0; idx < oldLength; idx++)
+            {
+                ref var lod = ref QualityPerLOD[idx];
+                // TODO: Does `Invalid` have any useful meaning as a configuration here?
+                lod = (OvrSkinningTypes.SkinningQuality)
+                    Mathf.Clamp((int)lod, (int)OvrSkinningTypes.SkinningQuality.Bone1, (int)OvrSkinningTypes.SkinningQuality.Bone4);
+            }
+            if (oldLength != CAPI.ovrAvatar2EntityLODFlagsCount)
+            {
+                Array.Resize(ref QualityPerLOD, (int)CAPI.ovrAvatar2EntityLODFlagsCount);
+
+                var fillValue = oldLength > 0 && oldLength <= QualityPerLOD.Length ? QualityPerLOD[oldLength - 1] : OvrSkinningTypes.SkinningQuality.Bone4;
+                for (int newIdx = oldLength; newIdx < CAPI.ovrAvatar2EntityLODFlagsCount; newIdx++)
+                {
+                    QualityPerLOD[newIdx] = fillValue;
+                }
+            }
+        }
+
+        private void CheckDefaultShader(ref Shader shaderProperty, string defaultShaderName)
+        {
+            if (shaderProperty == null)
+            {
+                shaderProperty = Shader.Find(defaultShaderName);
+            }
+        }
+#endif
+    }
+} // namespace Oculus.Avatar
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinningConfiguration.cs.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinningConfiguration.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..1cd519748990499147b52395ccc89da4c751578d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/GpuSkinningConfiguration.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: ecd1062ecb075db4a83f03b03ba8100d
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/OvrAvatarUnitySkinnedRenderable.cs b/Assets/Oculus/Avatar2/Scripts/Skinning/OvrAvatarUnitySkinnedRenderable.cs
new file mode 100644
index 0000000000000000000000000000000000000000..fbe7370a95cc342cf0b6c101e801b177ac7438e2
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/OvrAvatarUnitySkinnedRenderable.cs
@@ -0,0 +1,211 @@
+using System;
+using System.Runtime.InteropServices;
+
+using Oculus.Avatar2;
+
+using UnityEngine;
+
+/// @file OvrAvatarUnitySkinnedRenderable.cs
+
+namespace Oculus.Skinning
+{
+    /**
+     * Component that encapsulates the meshes of a skinned avatar.
+     * This component implements skinning using Unity.
+     * It is used when the skinning configuration is set
+     * to *SkinningConfig.UNITY*.
+     *
+     * @see OvrAvatarSkinnedRenderable
+     * @see OvrAvatarGpuSkinnedRenderable
+     * @see OvrAvatarEntity.SkinningConfig
+     */
+    public class OvrAvatarUnitySkinnedRenderable : OvrAvatarSkinnedRenderable
+    {
+        [SerializeField]
+        [Tooltip("Configuration to override SkinQuality, otherwise indicates which Quality was selected for this LOD")]
+        private SkinQuality _skinQuality = SkinQuality.Auto;
+
+        public SkinQuality SkinQuality
+        {
+            get => _skinQuality;
+            set
+            {
+                if (_skinQuality != value)
+                {
+                    _skinQuality = value;
+                    UpdateSkinQuality();
+                }
+            }
+        }
+        private void UpdateSkinQuality()
+        {
+            if (_skinnedRenderer != null)
+            {
+                _skinnedRenderer.quality = _skinQuality;
+            }
+        }
+
+        private SkinnedMeshRenderer _skinnedRenderer;
+        private DummySkinningBufferPropertySetter _dummyBufferSetter;
+
+        private float[] _morphBuffer;
+        private uint _morphCount;
+
+        private float[] MorphBuffer
+        {
+            get
+            {
+                return _morphBuffer ??
+                   (_morphBuffer = _morphCount > 0
+                        ? new float[(int)_morphCount]
+                        : Array.Empty<float>());
+            }
+        }
+
+        protected override void Awake()
+        {
+            base.Awake();
+
+            _dummyBufferSetter = new DummySkinningBufferPropertySetter();
+        }
+
+        protected virtual void OnDisable()
+        {
+            _morphBuffer = null;
+
+            _bufferHandle.Dispose();
+        }
+
+        protected override void AddDefaultRenderer()
+        {
+            _skinnedRenderer = AddRenderer<SkinnedMeshRenderer>();
+        }
+
+        protected internal override void ApplyMeshPrimitive(OvrAvatarPrimitive primitive)
+        {
+            base.ApplyMeshPrimitive(primitive);
+
+            _skinnedRenderer.sharedMesh = MyMesh;
+
+            if (_skinQuality == SkinQuality.Auto)
+            {
+                _skinQuality = QualityForLODIndex(primitive.HighestQualityLODIndex);
+            }
+            _skinnedRenderer.quality = _skinQuality;
+
+            _morphCount = primitive.morphTargetCount;
+
+            rendererComponent.GetPropertyBlock(MatBlock);
+            _dummyBufferSetter.SetComputeSkinningBuffersInMatBlock(MatBlock);
+            rendererComponent.SetPropertyBlock(MatBlock);
+        }
+
+        public override void ApplySkeleton(Transform[] bones)
+        {
+            if (_skinnedRenderer.sharedMesh)
+            {
+                _skinnedRenderer.rootBone = transform;
+                _skinnedRenderer.bones = bones;
+
+                // This must be set after SkinnedMeshRenderer.bones to prevent a "Bones do not match bindpose" error
+                _skinnedRenderer.localBounds = AppliedPrimitive.hasBounds ? AppliedPrimitive.mesh.bounds : FixedBounds;
+            }
+            else
+            {
+                OvrAvatarLog.LogError("Had no shared mesh to apply skeleton to!");
+            }
+        }
+
+        public override IDisposableBuffer CheckoutMorphTargetBuffer(uint morphCount)
+        {
+            _bufferHandle.SetMorphBuffer(MorphBuffer);
+            return _bufferHandle;
+        }
+
+        public override void MorphTargetBufferUpdated(IDisposableBuffer buffer)
+        {
+            Debug.Assert(_bufferHandle.BufferPtr == buffer.BufferPtr);
+            for (int morphTargetIndex = 0; morphTargetIndex < _morphBuffer.Length; ++morphTargetIndex)
+            {
+                _skinnedRenderer.SetBlendShapeWeight(morphTargetIndex, _morphBuffer[morphTargetIndex]);
+            }
+        }
+
+        public override bool UpdateJointMatrices(CAPI.ovrAvatar2EntityId entityId, OvrAvatarPrimitive primitive, CAPI.ovrAvatar2PrimitiveRenderInstanceID primitiveInstanceId)
+        {
+            // No-op
+            // TODO: Update transforms here
+            return false;
+        }
+
+        protected override void OnAnimationEnabledChanged(bool isNowEnabled)
+        {
+            // Intentionally empty
+        }
+
+        internal override void AnimationFrameUpdate()
+        {
+            // Intentionally empty
+        }
+
+        internal override void RenderFrameUpdate()
+        {
+            // Intentionally empty
+        }
+
+        internal override bool IsAnimationDataCompletelyValid => true;
+
+        private static SkinQuality QualityForLODIndex(uint lodIndex)
+        {
+            return OvrAvatarManager.Instance.GetUnitySkinQualityForLODIndex(lodIndex);
+        }
+
+        protected override void Dispose(bool isMainThread)
+        {
+            _skinnedRenderer = null;
+            _morphBuffer = null;
+            _bufferHandle.Dispose();
+            _dummyBufferSetter?.Dispose();
+
+            base.Dispose(isMainThread);
+        }
+
+        // TODO: This is disposed via the `Cleanup` method
+#pragma warning disable CA2213 // Disposable fields should be disposed
+        private readonly BufferHandle _bufferHandle = new BufferHandle();
+#pragma warning restore CA2213 // Disposable fields should be disposed
+
+        private sealed class BufferHandle : IDisposableBuffer
+        {
+            private GCHandle _morphHandle;
+
+            public void SetMorphBuffer(float[] buffer)
+            {
+                Debug.Assert(!_morphHandle.IsAllocated);
+
+                _morphHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
+            }
+
+            public IntPtr BufferPtr => _morphHandle.AddrOfPinnedObject();
+
+            public void Dispose()
+            {
+                if (_morphHandle.IsAllocated)
+                {
+                    _morphHandle.Free();
+                }
+            }
+        }
+
+        // TODO: FixedBounds should definitely be removed ASAP
+        private static Bounds FixedBounds
+            => new Bounds(new Vector3(0f, 0.5f, 0.0f), new Vector3(2.0f, 2.0f, 2.0f));
+
+#if UNITY_EDITOR
+        protected virtual void OnValidate()
+        {
+            UpdateSkinQuality();
+        }
+#endif
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/OvrAvatarUnitySkinnedRenderable.cs.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/OvrAvatarUnitySkinnedRenderable.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..8d39a89e1a287a2740c78bad9f06559ccf5efc74
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/OvrAvatarUnitySkinnedRenderable.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 702e9f86f02189a40bfc3e2af446deb5
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/OvrGpuSkinningExtensions.cs b/Assets/Oculus/Avatar2/Scripts/Skinning/OvrGpuSkinningExtensions.cs
new file mode 100644
index 0000000000000000000000000000000000000000..db43febbe13343367b1b8c00da69ff73a9d5ed27
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/OvrGpuSkinningExtensions.cs
@@ -0,0 +1,118 @@
+using Oculus.Avatar2;
+
+using System;
+
+using UnityEngine;
+using UnityEngine.Experimental.Rendering;
+
+using TexturePrecision = Oculus.Avatar2.GpuSkinningConfiguration.TexturePrecision;
+
+namespace Oculus.Skinning
+{
+    internal static class OvrGpuSkinningExtensions
+    {
+        internal static GraphicsFormat GetGraphicsFormat(this TexturePrecision precision)
+        {
+            switch (precision)
+            {
+                case TexturePrecision.Float:
+                    return GraphicsFormat.R32G32B32A32_SFloat;
+                case TexturePrecision.Half:
+                    return GraphicsFormat.R16G16B16A16_SFloat;
+                case TexturePrecision.Unorm16:
+                    return GraphicsFormat.R16G16B16A16_UNorm;
+                case TexturePrecision.Snorm10:
+                    return GraphicsFormat.R8G8B8A8_UNorm;
+                case TexturePrecision.Byte:
+                    return GraphicsFormat.R8G8B8A8_UNorm;
+                case TexturePrecision.Nibble:
+                    return GraphicsFormat.R4G4B4A4_UNormPack16;
+                default:
+                    throw new NotImplementedException($"Unhanded TexturePrecision {precision}.");
+            }
+        }
+
+        internal static CAPI.ovrGpuSkinningEncodingPrecision GetOvrPrecision(this TexturePrecision precision)
+        {
+            // TODO: This should be flushed out further, but this is all we support for now
+            switch (precision)
+            {
+                case TexturePrecision.Float:
+                    return CAPI.ovrGpuSkinningEncodingPrecision.ENCODING_PRECISION_FLOAT;
+                case TexturePrecision.Half:
+                    return CAPI.ovrGpuSkinningEncodingPrecision.ENCODING_PRECISION_HALF;
+                case TexturePrecision.Unorm16:
+                    throw new NotImplementedException($"Unhanded TexturePrecision {precision}.");
+                case TexturePrecision.Snorm10:
+                    return CAPI.ovrGpuSkinningEncodingPrecision.ENCODING_PRECISION_10_10_10_2;
+                case TexturePrecision.Byte:
+                    throw new NotImplementedException($"Unhanded TexturePrecision {precision}.");
+                case TexturePrecision.Nibble:
+                    throw new NotImplementedException($"Unhanded TexturePrecision {precision}.");
+                default:
+                    throw new NotImplementedException($"Unhanded TexturePrecision {precision}.");
+            }
+        }
+
+        public static RenderTextureFormat GetRenderTextureFormat(this GraphicsFormat graphicsFormat)
+        {
+            switch (graphicsFormat)
+            {
+                case GraphicsFormat.R16G16B16A16_SFloat:
+                    return RenderTextureFormat.ARGBHalf;
+                case GraphicsFormat.R32G32B32A32_SFloat:
+                    return RenderTextureFormat.ARGBFloat;
+                default:
+                    throw new NotImplementedException($"Unhanded GraphicsFormat {graphicsFormat}.");
+            }
+        }
+
+        public static bool CheckFit(this Texture tex, in Vector2Int size)
+        {
+            return tex.width >= size.x && tex.height >= size.y;
+        }
+
+        public static RectInt ToRectInt(in this CAPI.ovrTextureLayoutResult layoutResult)
+        {
+            return new RectInt(layoutResult.x, layoutResult.y, layoutResult.w, layoutResult.h);
+        }
+
+        public static bool IsSuccess(this CAPI.ovrGpuSkinningResult result)
+        {
+            return result == CAPI.ovrGpuSkinningResult.Success;
+        }
+        public static bool IsFailure(this CAPI.ovrGpuSkinningResult result)
+        {
+            return result != CAPI.ovrGpuSkinningResult.Success;
+        }
+
+        private const string logScope = "OvrAvatarGpuSkinning";
+        private const string DefaultContext = "no context";
+        public static bool EnsureSuccess(this CAPI.ovrGpuSkinningResult result, string messageContext = DefaultContext, UnityEngine.Object unityContext = null)
+        {
+            bool success = result.IsSuccess();
+            result.LogErrors(messageContext, unityContext);
+            return success;
+        }
+
+        public static void LogErrors(this CAPI.ovrGpuSkinningResult result, string messageContext = DefaultContext, UnityEngine.Object unityContext = null)
+        {
+            if (result.IsFailure())
+            {
+                string errors = string.Empty;
+                string separator = string.Empty;
+                int remainingResults = (int)result;
+                do
+                {
+                    var thisResult = remainingResults & -remainingResults;
+                    remainingResults ^= thisResult;
+
+                    errors += separator + ((CAPI.ovrGpuSkinningResult)thisResult).ToString();
+                    separator = ", ";
+                } while (remainingResults != 0);
+
+                OvrAvatarLog.LogError($"Operation ({messageContext}) failed with errors ({errors})", logScope, unityContext);
+            }
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/OvrGpuSkinningExtensions.cs.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/OvrGpuSkinningExtensions.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..a1062603e5e6d9ca2797b3f9a049dcef9e24acf2
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/OvrGpuSkinningExtensions.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 668179efb3e851e4f997a88f2e4c4927
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/OvrHandleGenerator.cs b/Assets/Oculus/Avatar2/Scripts/Skinning/OvrHandleGenerator.cs
new file mode 100644
index 0000000000000000000000000000000000000000..09a971d6dc174602ded8e7b98ad360a21d210fbb
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/OvrHandleGenerator.cs
@@ -0,0 +1,33 @@
+using System.Collections.Generic;
+using System.Linq;
+using UnityEngine;
+
+namespace Oculus.Skinning
+{
+    public class OvrHandleGenerator
+    {
+        public OvrHandleGenerator()
+        {
+            _freeHandles = new HashSet<OvrSkinningTypes.Handle>();
+            _maxHandleValSeen = -1;
+        }
+
+        public OvrSkinningTypes.Handle GetHandle()
+        {
+            if (_freeHandles.Count == 0)
+            {
+                return new OvrSkinningTypes.Handle(++_maxHandleValSeen);
+            }
+
+            return _freeHandles.First();
+        }
+
+        public void ReleaseHandle(OvrSkinningTypes.Handle handle)
+        {
+            _freeHandles.Add(handle);
+        }
+
+        private readonly HashSet<OvrSkinningTypes.Handle> _freeHandles;
+        private int _maxHandleValSeen;
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/OvrHandleGenerator.cs.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/OvrHandleGenerator.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..bdf2b6def16224fb6839c8ed3faa1bbe1b7d132f
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/OvrHandleGenerator.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 02bd0b79664693348905cd4db0151c23
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/OvrSkinningTypes.cs b/Assets/Oculus/Avatar2/Scripts/Skinning/OvrSkinningTypes.cs
new file mode 100644
index 0000000000000000000000000000000000000000..a73fb4d0eb85b2579e07e7245e595df991ad9c97
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/OvrSkinningTypes.cs
@@ -0,0 +1,80 @@
+using System;
+
+using Oculus.Avatar2;
+
+/// @#file OvrSkinningTypes.cs
+
+namespace Oculus.Skinning
+{
+    ///
+    /// Types used for skinning.
+    ///
+    public static class OvrSkinningTypes
+    {
+        public class Handle : IEquatable<Handle>
+        {
+            private const int kInvalidValue = -1;
+
+            public static readonly Handle kInvalidHandle = new Handle();
+
+            private Handle()
+            {
+                _val = kInvalidValue;
+            }
+
+            public Handle(int val)
+            {
+                _val = val;
+            }
+
+            public int GetValue()
+            {
+                return _val;
+            }
+
+            public bool IsValid()
+            {
+                return _val > kInvalidValue;
+            }
+
+            public bool Equals(Handle other)
+            {
+                if (other is null)
+                    return false;
+
+                return _val == other._val;
+            }
+
+            // TODO: Should be explicit
+            public static implicit operator Handle(CAPI.ovrGpuSkinningHandle h) => new Handle((int)h);
+            public static implicit operator CAPI.ovrGpuSkinningHandle(Handle h) => (CAPI.ovrGpuSkinningHandle)h.GetValue();
+
+            public override bool Equals(object obj) => Equals(obj as Handle);
+            public override int GetHashCode() => _val;
+
+            public override string ToString() => $"Handle with value of {_val.ToString()}";
+
+            private readonly int _val;
+        }
+
+        ///
+        /// Specifies the skinning quality.
+        /// This is the number of bones used to influence
+        /// each vertex in the skin.
+        ///
+        public enum SkinningQuality
+        {
+            /// Error code
+            Invalid = 0,
+
+            ///Use 1 bone to deform a single vertex. (The most important bones will be used).
+            Bone1 = 1,
+
+            /// Use 2 bones to deform a single vertex. (The most important bones will be used).
+            Bone2 = 2,
+
+            /// Use 4 bones to deform a single vertex.
+            Bone4 = 4,
+        }
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Skinning/OvrSkinningTypes.cs.meta b/Assets/Oculus/Avatar2/Scripts/Skinning/OvrSkinningTypes.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..3ed3aecd2449601dc11749e5fd61de4d439872da
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Skinning/OvrSkinningTypes.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: e2a6dd1043de21a42af37be5148bf322
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Utility.meta b/Assets/Oculus/Avatar2/Scripts/Utility.meta
new file mode 100644
index 0000000000000000000000000000000000000000..f61434a1ffdae04f71f1322c198d43f0730c9a8e
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Utility.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 99710e77816ca8e449f178d8359ea157
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Utility/EnumMaskAttribute.cs b/Assets/Oculus/Avatar2/Scripts/Utility/EnumMaskAttribute.cs
new file mode 100644
index 0000000000000000000000000000000000000000..76d59ac884a210b16518701d0e7fd18412636325
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Utility/EnumMaskAttribute.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace Oculus.Avatar2
+{
+public class EnumMaskAttribute : PropertyAttribute
+{
+
+    public EnumMaskAttribute() { }
+}
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Utility/EnumMaskAttribute.cs.meta b/Assets/Oculus/Avatar2/Scripts/Utility/EnumMaskAttribute.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..98278dde02f913e6e801afad32f7fb7912108d4d
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Utility/EnumMaskAttribute.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 433ed54e49a07e6468df90d129cc1c5d
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Utility/IblSetupForEnvironment.cs b/Assets/Oculus/Avatar2/Scripts/Utility/IblSetupForEnvironment.cs
new file mode 100644
index 0000000000000000000000000000000000000000..46e62729f12bf11ca003b12a1496eaa237c09f37
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Utility/IblSetupForEnvironment.cs
@@ -0,0 +1,112 @@
+// NOTE: In a shipping apllication it is inefficient to do this since the
+// Update() dispatch is an unnecessary expense. It will often be the case
+// that a scene will be loaded with an IBL setup, and this will not change
+// until the next scene is loaded.
+#if UNITY_EDITOR
+#define ALLOW_DYNAMIC_IBL_SWTICH
+#endif
+
+
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace Oculus.Avatar2
+{
+
+    public class IblSetupForEnvironment : MonoBehaviour
+    {
+        [Tooltip("This value should affect all tone mapped and non tone mapped lighting exposures.")]
+        public float CurrentExposure = 1.0f;
+        private float _previousExposure;
+        [Tooltip("Exposure shader parameter name.")]
+        public string ExposureShaderParameterName = "u_Exposure";
+
+        [Tooltip("A prefiltered single mip texture for diffuse influences.")]
+        public Texture DiffuseEnvironmentCubeMap;
+        private Texture _previousDiffuseEnvironmentCubeMap;
+        [Tooltip("Diffuse environment sampler shader parameter name.")]
+        public string[] DiffuseEnvironmentShaderParameterNames = new string[] { "u_DiffuseEnvSampler", "u_LambertianEnvSampler" };
+
+        [Tooltip("Ambient specular reflections on a multiple mip texture, chosen via the roughness of materials.")]
+        public Texture SpecularEnvironmentCubeMap;
+        private Texture _previousSpecularEnvironmentCubeMap;
+        [Tooltip("Specular environment sampler shader parameter name.")]
+        [SerializeField]
+        private string[] SpecularEnvironmentShaderParameterNames = new string[] { "u_SpecularEnvSampler", "u_GGXEnvSampler" };
+        [Tooltip("Represents the number of mips in the specular texture map. This is automatically set when setting the texture.")]
+        public string SpecularMapMipCountShaderPameterName = "u_MipCount";
+
+        [Tooltip("A 2 channel look up textrure to represent the BRDF function.")]
+        public Texture BrdfLutMap;
+        private Texture _previousBrdfLutMap;
+        [Tooltip("BRDF Lut sammpler shader parameter name.")]
+        public string[] BrdfLutShaderParameterNames = new string[] { "u_brdfLUT", "u_GGXLUT"};
+
+        [Tooltip("Optional world object cubemap material, the specular environment cube above will be set into it.")]
+        public Material CubeMapMaterial;
+        [Tooltip("Name of the texture parameter in the world object cubemap shader.")]
+        public string CubeMapShaderParameterName = "_Tex";
+
+
+        private void SetExposureScopeParm() {
+            Shader.SetGlobalFloat(ExposureShaderParameterName, CurrentExposure);
+            _previousExposure = CurrentExposure;
+        }
+
+        private void SetAllIblGlobalScopeParams() {
+            foreach (var parameterName in DiffuseEnvironmentShaderParameterNames)
+            {
+                Shader.SetGlobalTexture(parameterName, DiffuseEnvironmentCubeMap);
+            }
+            foreach (var parameterName in SpecularEnvironmentShaderParameterNames)
+            {
+                Shader.SetGlobalTexture(parameterName, SpecularEnvironmentCubeMap);
+            }
+            foreach (var parameterName in BrdfLutShaderParameterNames)
+            {
+                Shader.SetGlobalTexture(parameterName, BrdfLutMap);
+            }
+#if UNITY_2018
+            Shader.SetGlobalInt(SpecularMapMipCountShaderPameterName, 10);
+#else
+            Shader.SetGlobalInt(SpecularMapMipCountShaderPameterName, SpecularEnvironmentCubeMap.mipmapCount);
+#endif
+            if (CubeMapMaterial != null)
+            {
+                CubeMapMaterial.SetTexture(CubeMapShaderParameterName, SpecularEnvironmentCubeMap);
+            }
+
+
+            _previousDiffuseEnvironmentCubeMap = DiffuseEnvironmentCubeMap;
+            _previousSpecularEnvironmentCubeMap = SpecularEnvironmentCubeMap;
+            _previousBrdfLutMap = BrdfLutMap;
+        }
+
+        protected virtual void OnEnable()
+        {
+            SetExposureScopeParm();
+            SetAllIblGlobalScopeParams();
+        }
+
+        protected virtual void OnDisable()
+        {
+
+        }
+
+#if ALLOW_DYNAMIC_IBL_SWTICH
+        protected virtual void Update()
+        {
+            if(CurrentExposure != _previousExposure) {
+                SetExposureScopeParm();
+            }
+            if ( _previousDiffuseEnvironmentCubeMap != DiffuseEnvironmentCubeMap ||
+                _previousSpecularEnvironmentCubeMap != SpecularEnvironmentCubeMap ||
+                _previousBrdfLutMap != BrdfLutMap ) 
+            {
+                SetAllIblGlobalScopeParams();
+            }
+        }
+#endif
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Utility/IblSetupForEnvironment.cs.meta b/Assets/Oculus/Avatar2/Scripts/Utility/IblSetupForEnvironment.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..08468aaeb6b1b38e81f9394d487f15d0c9168a31
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Utility/IblSetupForEnvironment.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: cc8b2bc8875ed714f874f31c2183c84b
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Utility/OvrAvatarHelperExtensions.cs b/Assets/Oculus/Avatar2/Scripts/Utility/OvrAvatarHelperExtensions.cs
new file mode 100644
index 0000000000000000000000000000000000000000..36c556adfe13df5ff2a8a9854cb2b73c246721ba
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Utility/OvrAvatarHelperExtensions.cs
@@ -0,0 +1,570 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+
+using Unity.Collections;
+using Unity.Collections.LowLevel.Unsafe;
+
+using UnityEngine;
+
+namespace Oculus.Avatar2
+{
+    public static class OvrAvatarHelperExtensions
+    {
+        public static bool IsSuccess(this CAPI.ovrAvatar2Result result)
+        {
+            return result == CAPI.ovrAvatar2Result.Success;
+        }
+        public static bool IsFailure(this CAPI.ovrAvatar2Result result)
+        {
+            return result != CAPI.ovrAvatar2Result.Success;
+        }
+
+        #region Containers
+
+        // HashSet<T> Extensions
+
+        public static T[] NullSafeToArray<T>(this HashSet<T> set)
+        {
+            return set != null ? set.ToArray() : Array.Empty<T>();
+        }
+
+        public static T[] ToArray<T>(this HashSet<T> set)
+        {
+            var copyArray = set.Count > 0 ? new T[set.Count] : Array.Empty<T>();
+            set.CopyTo(copyArray);
+            return copyArray;
+        }
+
+        // Unity.NativeArray<T> Extensions
+
+        public static UInt32 GetBufferSize<T>(in this NativeArray<T> array) where T : struct
+        {
+            return array.GetBufferSize(UnsafeUtility.SizeOf<T>());
+        }
+        public static UInt32 GetBufferSize<T>(in this NativeArray<T> array, UInt32 elementSize) where T : struct
+        {
+            Debug.Assert(elementSize != 0);
+            Debug.Assert(elementSize == UnsafeUtility.SizeOf<T>());
+            return (UInt32)(array.Length * elementSize);
+        }
+        public static UInt32 GetBufferSize<T>(in this NativeArray<T> array, int elementSize) where T : struct
+        {
+            Debug.Assert(elementSize > 0);
+            return array.GetBufferSize((UInt32)elementSize);
+        }
+        public static UInt32 GetBufferSize(in this NativeArray<byte> array)
+        {
+            return (UInt32)array.Length;
+        }
+
+        public static UInt32 GetEnumBufferSize<T>(in this NativeArray<T> array) where T : struct, Enum
+        {
+            var underlyingType = Enum.GetUnderlyingType(typeof(T));
+            return array.GetEnumBufferSize((uint)Marshal.SizeOf(underlyingType));
+        }
+
+        public static UInt32 GetEnumBufferSize<T>(in this NativeArray<T> array, UInt32 explicitSize) where T : struct, Enum
+        {
+            Debug.Assert(Marshal.SizeOf(Enum.GetUnderlyingType(typeof(T))) == explicitSize);
+            return (UInt32)(array.Length * explicitSize);
+        }
+
+        /* Lengths must match */
+        // NOTE: ref is invalid for `array` when used in `using` statement, eat 1 copy :(
+        public static void CopyFrom<T>(this NativeArray<T> array, HashSet<T> hashSet) where T : struct
+        {
+            OvrAvatarLog.Assert(array.Length == hashSet.Count);
+            int copyIdx = 0;
+            foreach (var value in hashSet)
+            {
+                array[copyIdx++] = value;
+            }
+        }
+
+        public static IntPtr GetIntPtr<T>(in this NativeArray<T> array) where T : struct
+        {
+            unsafe
+            {
+                return (IntPtr)array.GetUnsafePtr();
+            }
+        }
+
+        public static unsafe T* GetPtr<T>(in this NativeArray<T> array) where T : unmanaged
+        {
+            return (T*)array.GetUnsafePtr();
+        }
+
+        public static unsafe CastT* CastPtr<T, CastT>(in this NativeArray<T> array)
+            where T : unmanaged
+            where CastT : unmanaged
+        {
+            Debug.Assert(UnsafeUtility.SizeOf<CastT>() == UnsafeUtility.SizeOf<T>());
+            return (CastT*)array.GetUnsafePtr();
+        }
+
+        public static unsafe CAPI.ovrAvatar2Vector3f* CastOvrPtr(in this NativeArray<Vector3> array)
+        {
+            return array.CastPtr<Vector3, CAPI.ovrAvatar2Vector3f>();
+        }
+
+        public static unsafe T* GetReadonlyPtr<T>(in this NativeArray<T> array) where T : unmanaged
+        {
+            return (T*)array.GetUnsafeReadOnlyPtr();
+        }
+
+        /* If the target array `IsCreated`, `Dispose` it and reset the reference to default */
+        public static void Reset<T>(ref this NativeArray<T> array) where T : struct
+        {
+            if (array.IsCreated)
+            {
+                array.Dispose();
+                array = default;
+            }
+        }
+
+        /* Calling dispose on an unallocated NativeArray throws an exception
+         * This makes code handling optional NativeArrays rather clunky
+         * This wrapper simply adds an `IsCreated` check before forwarding the `Dispose` call*/
+        public struct NativeArrayDisposeWrapper<T> : IDisposable, IEnumerable<T>, System.Collections.IEnumerable where T : struct
+        {
+            public NativeArrayDisposeWrapper(in NativeArray<T> wrappedArray)
+                => array = wrappedArray;
+
+            public NativeArray<T> array;
+            public bool IsCreated => array.IsCreated;
+
+            public int Length => array.Length;
+
+            public void Dispose()
+            {
+                if (array.IsCreated)
+                {
+                    array.Dispose();
+                }
+            }
+
+            public T[] ToArray() => array.IsCreated ? array.ToArray() : Array.Empty<T>();
+
+            public static implicit operator NativeArrayDisposeWrapper<T>(in NativeArray<T> natArr)
+                => natArr.GetDisposeSafe();
+
+            // C# is truly a flawless language...
+            public NativeArray<T>.Enumerator GetEnumerator() => array.GetEnumerator();
+            IEnumerator<T> IEnumerable<T>.GetEnumerator() => array.GetEnumerator();
+            System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
+                => array.GetEnumerator();
+        }
+
+        public static NativeArrayDisposeWrapper<T> GetDisposeSafe<T>(this in NativeArray<T> array) where T : struct
+        {
+            return new NativeArrayDisposeWrapper<T>(in array);
+        }
+
+        // NativeSlice<T> Extensions
+
+        public static unsafe T* GetPtr<T>(in this NativeSlice<T> slice) where T : unmanaged
+        {
+            return (T*)slice.GetUnsafePtr();
+        }
+
+        public static UInt32 GetBufferSize<T>(in this NativeSlice<T> slice) where T : struct
+        {
+            unchecked { return (UInt32)slice.Length * (UInt32)UnsafeUtility.SizeOf<T>(); }
+        }
+        public static UInt32 GetBufferSize(in this NativeSlice<byte> array)
+        {
+            return (UInt32)array.Length;
+        }
+
+        // Dictionary<K, V> Extensions
+
+        public static Dictionary<K, V> Copy<K, V>(this Dictionary<K, V> dict)
+        {
+            return new Dictionary<K, V>(dict);
+        }
+
+        public static void CopyFrom<K, V>(this Dictionary<K, V> dest, Dictionary<K, V> source)
+        {
+            dest.Clear();
+            foreach (var kvp in source)
+            {
+                dest.Add(kvp.Key, kvp.Value);
+            }
+        }
+
+        public static void CopyFrom<K, V>(this Dictionary<K, V[]> dest, Dictionary<K, List<V>> source)
+        {
+            dest.Clear();
+            foreach (var kvp in source)
+            {
+                dest.Add(kvp.Key, kvp.Value.ToArray());
+            }
+        }
+
+        // T[] Extensions
+
+        /* Concatenate newElement onto the target array, returning the new array -
+         *  Previous array target should be considered invalid */
+        public static T[] Concat<T>(this T[] array, in T newElement)
+        {
+            var startLen = array.Length;
+            Array.Resize(ref array, startLen + 1);
+            array[startLen] = newElement;
+            return array;
+        }
+
+        /* Remove `elementToRemove` from the target array and return the resulting array, preserving order
+         *  Shifts all elements after the removed index
+         *  O(n) [find index] + O(n) [shift elements]
+         *  Previous array instance should be considered invalid */
+        public static T[] SliceOut<T>(this T[] array, in T elementToRemove)
+        {
+            int idx = array.IndexOf(in elementToRemove);
+            if (idx >= 0)
+            {
+                array = array.SliceOutIndex(idx);
+            }
+            return array;
+        }
+
+        /* Remove `elementToRemove` from the target array and return the resulting array, preserving order
+         *  Shifts all elements after the removed index
+         *  O(n) [find index] + O(n) [shift elements]
+         *  Previous array instance should be considered invalid */
+        public static T[] SliceOutIndex<T>(this T[] array, int indexToRemove)
+        {
+            var newLength = array.Length - 1;
+            while (indexToRemove < newLength)
+            {
+                array[indexToRemove] = array[++indexToRemove];
+            }
+            Array.Resize(ref array, newLength);
+            return array;
+        }
+
+        /* Remove `elementToRemove` from the target array and return the resulting array, changes order
+         *  Swaps the removed index with the last element in the array, then resizes
+         *  O(n) [find index] + O(1) [swap elements]
+         *  Previous array instance should be considered invalid */
+        public static T[] SwapOut<T>(this T[] array, in T elementToRemove)
+        {
+            int idx = array.IndexOf(in elementToRemove);
+            if (idx >= 0)
+            {
+                array = array.SwapOutIndex(idx);
+            }
+            return array;
+        }
+
+        /* Remove `indexToRemove` from the target array and return the resulting array, changes order
+         *  Swaps the removed index with the last element in the array, then resizes
+         *  O(n) [find index] + O(1) [swap elements]
+         *  Previous array instance should be considered invalid */
+        public static T[] SwapOutIndex<T>(this T[] array, int indexToRemove)
+        {
+            var newLength = array.Length - 1;
+            array[indexToRemove] = array[newLength];
+            Array.Resize(ref array, newLength);
+            return array;
+        }
+
+        /* Returns the index of `value` in `array`, or -1 if `value` was not found
+         *  O(n) */
+        public static int IndexOf<T>(this T[] array, in T value)
+        {
+            return Array.IndexOf(array, value);
+        }
+
+        /* Returns the index of `value` in `array` if found; otherwise, a negative number.
+         * - If value is not found and value is less than one or more elements in array,
+         *   the negative number returned is the bitwise complement of the index of the first element that is larger than value.
+         * - If value is not found and value is greater than all elements in array,
+         *   the negative number returned is the bitwise complement of (the index of the last element plus 1).
+         * - If this method is called with a non-sorted array,
+         *   the return value can be incorrect and a negative number could be returned, even if value is present in array.
+         * Array must be sorted in increasing order via T's IComparable implementation
+         *  O(logn) - performs a binary search */
+        public static int BinarySearch<T>(this T[] array, in T value)
+        {
+            return Array.BinarySearch(array, value);
+        }
+
+        /* Returns the index of `value` in `array`, if found; otherwise, a negative number.
+         * - If value is not found and value is less than one or more elements in array,
+         *   the negative number returned is the bitwise complement of the index of the first element that is larger than value.
+         * - If value is not found and value is greater than all elements in array,
+         *   the negative number returned is the bitwise complement of (the index of the last element plus 1).
+         * - If this method is called with a non-sorted array,
+         *   the return value can be incorrect and a negative number could be returned, even if value is present in array.
+         * Array must be sorted in increasing order per `comparer`
+         *  O(logn) - performs a binary search using `comparer` */
+        public static int BinarySearch<T>(this T[] array, in T value, IComparer<T> comparer)
+        {
+            return Array.BinarySearch(array, value, comparer);
+        }
+        // TODO: BinarySearch backed variant of Contains?
+
+        /* Returns `true` if `value` in `array`, `false` otherwise
+         *  O(n) */
+        public static bool Contains<T>(this T[] array, in T value)
+        {
+            return array.IndexOf(in value) >= 0;
+        }
+
+        public static bool Contains<T>(this IReadOnlyList<T> list, in T value, int searchLength) where T : class
+        {
+            for (int idx = 0; idx < searchLength; ++idx)
+            {
+                if (list[idx] == value)
+                {
+                    return true;
+                }
+            }
+
+            return false;
+        }
+
+        public static bool Contains(this Array array, in object value)
+        {
+            Int32 index = Array.IndexOf(array, value);
+            // Yay .NET https://docs.microsoft.com/en-us/dotnet/api/system.array.indexof?view=netcore-3.1
+            return array.GetLowerBound(0) <= index && index < Int32.MaxValue;
+        }
+
+        // List<T> Extensions
+
+        // Helpers for swapping List in to replace LinkedList (which makes lots of GC.Allocs)
+        public static T? First<T>(this List<T> list) where T : struct
+        {
+            return list.Count > 0 ? list[0] : (T?)null;
+        }
+
+        public static void AddFirst<T>(this List<T> list, T element)
+        {
+            list.Insert(0, element);
+        }
+
+        public static void AddLast<T>(this List<T> list, T element)
+        {
+            list.Add(element);
+        }
+
+        public static void RemoveFirst<T>(this List<T> list)
+        {
+            list.RemoveAt(0);
+        }
+
+        #endregion
+
+        #region Logging
+
+        private const string DefaultContext = "no context";
+        private const string DefaultScope = "ovrAvatar2.CAPI";
+        public static bool EnsureSuccess(this CAPI.ovrAvatar2Result result
+            , string msgContext = DefaultContext
+            , string logScope = DefaultScope
+            , UnityEngine.Object unityContext = null)
+        {
+            bool wasSuccess = result.IsSuccess();
+            if (!wasSuccess)
+            {
+                result.LogError(msgContext, logScope, unityContext);
+            }
+            return wasSuccess;
+        }
+
+        // TODO: Expand `EnsureSuccessOrLog` so it can handle `EnsureSuccessOrWarning` as expected
+        // May be easier to just update every call to `EnsureSuccessOrWarning`
+        // to include `to resolve this warning` text
+        private const string DefaultWarningSuggestion = "verify call";
+        internal static bool EnsureSuccessOrWarning(this CAPI.ovrAvatar2Result result
+            , CAPI.ovrAvatar2Result succeedWithWarningResult
+            , string warningSuggestion = DefaultWarningSuggestion
+            , string msgContext = DefaultContext
+            , string logScope = DefaultScope
+            , UnityEngine.Object unityContext = null)
+        {
+            bool wasSuccessOrWarn = result.IsSuccess();
+            if (!wasSuccessOrWarn)
+            {
+                wasSuccessOrWarn = result == succeedWithWarningResult;
+                if (wasSuccessOrWarn)
+                {
+                    OvrAvatarLog.LogWarning(
+                        $"Operation ({msgContext}) succeeded with warning ({result})\n - {warningSuggestion} to resolve this warning"
+                        , logScope, unityContext);
+                }
+                else
+                {
+                    result.LogError(msgContext, logScope, unityContext);
+                }
+            }
+            return wasSuccessOrWarn;
+        }
+
+        internal static bool EnsureSuccessOrWarning(this CAPI.ovrAvatar2Result result
+            , CAPI.ovrAvatar2Result succeedWithWarningResult0
+            , CAPI.ovrAvatar2Result succeedWithWarningResult1
+            , string warningSuggestion = DefaultWarningSuggestion
+            , string msgContext = DefaultContext
+            , string logScope = DefaultScope
+            , UnityEngine.Object unityContext = null)
+        {
+            bool wasSuccessOrWarn = result.IsSuccess();
+            if (!wasSuccessOrWarn)
+            {
+                wasSuccessOrWarn = result == succeedWithWarningResult0 || result == succeedWithWarningResult1;
+                if (wasSuccessOrWarn)
+                {
+                    OvrAvatarLog.LogWarning(
+                        $"Operation ({msgContext}) succeeded with warning ({result})\n - {warningSuggestion} to resolve this warning"
+                        , logScope, unityContext);
+                }
+                else
+                {
+                    result.LogError(msgContext, logScope, unityContext);
+                }
+            }
+            return wasSuccessOrWarn;
+        }
+
+        private const string DefaultLogVerboseContext = "no action required";
+        // TODO: System.Diagnostic.Conditional
+        internal static bool EnsureSuccessOrLogVerbose(this CAPI.ovrAvatar2Result result
+            , CAPI.ovrAvatar2Result succeedWithLogVerboseResult
+            , string logDebugContext = DefaultLogVerboseContext
+            , string msgContext = DefaultContext
+            , string logScope = DefaultScope
+            , UnityEngine.Object unityContext = null)
+        {
+            return EnsureSuccessOrLog(result, succeedWithLogVerboseResult,
+                logDebugContext, msgContext, logScope, unityContext,
+                OvrAvatarLog.ELogLevel.Verbose);
+        }
+        internal static bool EnsureSuccessOrLogVerbose(this CAPI.ovrAvatar2Result result
+            , CAPI.ovrAvatar2Result succeedWithLogVerboseResult0
+            , CAPI.ovrAvatar2Result succeedWithLogVerboseResult1
+            , string logDebugContext = DefaultLogVerboseContext
+            , string msgContext = DefaultContext
+            , string logScope = DefaultScope
+            , UnityEngine.Object unityContext = null)
+        {
+            return EnsureSuccessOrLog(
+                result, succeedWithLogVerboseResult0, succeedWithLogVerboseResult1,
+                logDebugContext, msgContext, logScope, unityContext,
+                OvrAvatarLog.ELogLevel.Verbose);
+        }
+
+        private const string DefaultLogDebugContext = "this should be a transient issue";
+        // TODO: System.Diagnostic.Conditional
+        internal static bool EnsureSuccessOrLogDebug(this CAPI.ovrAvatar2Result result
+            , CAPI.ovrAvatar2Result succeedWithLogDebugResult
+            , string logDebugContext = DefaultLogDebugContext
+            , string msgContext = DefaultContext
+            , string logScope = DefaultScope
+            , UnityEngine.Object unityContext = null)
+        {
+            return EnsureSuccessOrLog(result, succeedWithLogDebugResult,
+                logDebugContext, msgContext, logScope, unityContext,
+                OvrAvatarLog.ELogLevel.Debug);
+        }
+
+        private const string DefaultLogInfoContext = "verify no unwanted side effects";
+        // TODO: System.Diagnostic.Conditional
+        internal static bool EnsureSuccessOrLogInfo(this CAPI.ovrAvatar2Result result
+            , CAPI.ovrAvatar2Result succeedWithLogDebugResult
+            , string logDebugContext = DefaultLogInfoContext
+            , string msgContext = DefaultContext
+            , string logScope = DefaultScope
+            , UnityEngine.Object unityContext = null)
+        {
+            return EnsureSuccessOrLog(result, succeedWithLogDebugResult,
+                logDebugContext, msgContext, logScope, unityContext,
+                OvrAvatarLog.ELogLevel.Info);
+        }
+
+        // TODO: System.Diagnostic.Conditional
+        public static void LogError(this CAPI.ovrAvatar2Result result
+            , string msgContext = DefaultContext
+            , string logScope = DefaultScope
+            , UnityEngine.Object unityContext = null)
+        {
+            OvrAvatarLog.Assert(!result.IsSuccess());
+            OvrAvatarLog.LogError($"Operation ({msgContext}) failed with result ({result})"
+                , logScope, unityContext);
+        }
+
+        // TODO: System.Diagnostic.Conditional
+        public static void LogAssert(this CAPI.ovrAvatar2Result result
+            , string msgContext = DefaultContext
+            , string logScope = DefaultScope
+            , UnityEngine.Object unityContext = null)
+        {
+            OvrAvatarLog.AssertTwoParams(result.IsSuccess()
+                , msgContext, in result
+                , _CachedLogAssertBuilder
+                , logScope, unityContext);
+        }
+        // This ensures Mono doesn't helpfully allocate a wrapper every time this is called...
+        private static readonly OvrAvatarLog.AssertMessageBuilder<string, CAPI.ovrAvatar2Result>
+            _CachedLogAssertBuilder = _LogAssertBuilder;
+        private static string _LogAssertBuilder(in string msgCtx, in CAPI.ovrAvatar2Result r)
+            => $"{msgCtx} failed with {r}";
+
+
+        private static bool EnsureSuccessOrLog(this CAPI.ovrAvatar2Result result
+            , CAPI.ovrAvatar2Result succeedWithLogResult
+            , string logDebugContext
+            , string msgContext
+            , string logScope
+            , UnityEngine.Object unityContext
+            , OvrAvatarLog.ELogLevel logLevel)
+        {
+            bool wasSuccessOrLog = result.IsSuccess();
+            if (!wasSuccessOrLog)
+            {
+                wasSuccessOrLog = result == succeedWithLogResult;
+                if (wasSuccessOrLog)
+                {
+                    OvrAvatarLog.Log(logLevel
+                        , $"Operation ({msgContext}) succeeded with log ({result})\n - {logDebugContext}"
+                        , logScope, unityContext);
+                }
+                else
+                {
+                    result.LogError(msgContext, logScope, unityContext);
+                }
+            }
+            return wasSuccessOrLog;
+        }
+
+        private static bool EnsureSuccessOrLog(this CAPI.ovrAvatar2Result result
+            , CAPI.ovrAvatar2Result succeedWithLogResult0
+            , CAPI.ovrAvatar2Result succeedWithLogResult1
+            , string logDebugContext
+            , string msgContext
+            , string logScope
+            , UnityEngine.Object unityContext
+            , OvrAvatarLog.ELogLevel logLevel)
+        {
+            bool wasSuccessOrLog = result.IsSuccess();
+            if (!wasSuccessOrLog)
+            {
+                wasSuccessOrLog = result == succeedWithLogResult0 || result == succeedWithLogResult1;
+                if (wasSuccessOrLog)
+                {
+                    OvrAvatarLog.Log(logLevel
+                        , $"Operation ({msgContext}) succeeded with log ({result})\n - {logDebugContext}"
+                        , logScope, unityContext);
+                }
+                else
+                {
+                    result.LogError(msgContext, logScope, unityContext);
+                }
+            }
+            return wasSuccessOrLog;
+        }
+        #endregion //Logging
+    }
+} // namespace Oculus.Avatar2
diff --git a/Assets/Oculus/Avatar2/Scripts/Utility/OvrAvatarHelperExtensions.cs.meta b/Assets/Oculus/Avatar2/Scripts/Utility/OvrAvatarHelperExtensions.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..37f10e787a9623409b97e2b436a588d7b6f712cd
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Utility/OvrAvatarHelperExtensions.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 45913874766a03f4bab8a67c1d8a970d
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/Scripts/Utility/PlatformHelperUtil.cs b/Assets/Oculus/Avatar2/Scripts/Utility/PlatformHelperUtil.cs
new file mode 100644
index 0000000000000000000000000000000000000000..c0a0abd8d96bbb148d41dcb00332b07b34393ec5
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Utility/PlatformHelperUtil.cs
@@ -0,0 +1,50 @@
+using System;
+using UnityEngine;
+
+namespace Oculus.Avatar2
+{
+    public static class PlatformHelperUtils
+    {
+        private const string DefaultScope = "ovrAvatar2.platformUtils";
+
+        public static class AndroidSysProperties
+        {
+            public static readonly string ExperimentalFeatures = "persist.avatar.perf_test.expfeatures";
+        }
+
+        public static string GetAndroidSysProp(string propName, string logScope = DefaultScope)
+        {
+#if UNITY_ANDROID && !UNITY_EDITOR
+            try
+            {
+                var sysprops = new AndroidJavaClass("android.os.SystemProperties");
+                var val = sysprops.CallStatic<string>("get", propName);
+                OvrAvatarLog.LogInfo($"System property {propName} = {val}", logScope);
+                return val;
+            }
+            catch (System.Exception e)
+            {
+                OvrAvatarLog.LogError($"An exception occured while reading system property: {e}", logScope);
+            }
+#endif
+            return string.Empty;
+        }
+
+        public static int GetAndroidIntSysProp(string propName, int defaultValue, string logScope = DefaultScope)
+        {
+            var value = defaultValue;
+#if UNITY_ANDROID && !UNITY_EDITOR
+            try
+            {
+                value = int.Parse(GetAndroidSysProp(propName, logScope));
+            }
+            catch (System.FormatException)
+            {
+                OvrAvatarLog.LogError($"Unable to parse {propName}", logScope);
+            }
+#endif
+            return value;
+        }
+
+    }
+}
diff --git a/Assets/Oculus/Avatar2/Scripts/Utility/PlatformHelperUtil.cs.meta b/Assets/Oculus/Avatar2/Scripts/Utility/PlatformHelperUtil.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..f12f727f3cd9b285785cca7c97d6dc3fde2d1084
--- /dev/null
+++ b/Assets/Oculus/Avatar2/Scripts/Utility/PlatformHelperUtil.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 1e144a1f819ee094093cadb80d1615bd
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/StreamingAssets.meta b/Assets/Oculus/Avatar2/StreamingAssets.meta
new file mode 100644
index 0000000000000000000000000000000000000000..eaae0622beb382f7a183e1e2c37f204afaf5e753
--- /dev/null
+++ b/Assets/Oculus/Avatar2/StreamingAssets.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 42d6c2e9e6ed7e84eacb0e807cd79321
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/StreamingAssets/Oculus.meta b/Assets/Oculus/Avatar2/StreamingAssets/Oculus.meta
new file mode 100644
index 0000000000000000000000000000000000000000..fabc596a01b82eeeab7c2d4a577e027934a5888b
--- /dev/null
+++ b/Assets/Oculus/Avatar2/StreamingAssets/Oculus.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: f8f6311389eeeb2488848293c1b6de2a
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/StreamingAssets/Oculus/OvrAvatar2Assets.zip b/Assets/Oculus/Avatar2/StreamingAssets/Oculus/OvrAvatar2Assets.zip
new file mode 100644
index 0000000000000000000000000000000000000000..b97c677b4084580a477aec6b8ebb201ef5609201
Binary files /dev/null and b/Assets/Oculus/Avatar2/StreamingAssets/Oculus/OvrAvatar2Assets.zip differ
diff --git a/Assets/Oculus/Avatar2/StreamingAssets/Oculus/OvrAvatar2Assets.zip.meta b/Assets/Oculus/Avatar2/StreamingAssets/Oculus/OvrAvatar2Assets.zip.meta
new file mode 100644
index 0000000000000000000000000000000000000000..37718f2adec6c79f5a4b3b710353f9f1176330df
--- /dev/null
+++ b/Assets/Oculus/Avatar2/StreamingAssets/Oculus/OvrAvatar2Assets.zip.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 89958a450e4b43f49add2003e36181e0
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/StreamingAssets/SampleAssets.meta b/Assets/Oculus/Avatar2/StreamingAssets/SampleAssets.meta
new file mode 100644
index 0000000000000000000000000000000000000000..5e03dbdb0f1a783cf006e1b82462de2251e96477
--- /dev/null
+++ b/Assets/Oculus/Avatar2/StreamingAssets/SampleAssets.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 2dfb81cf72cfee14aad8a26fe9f76ece
+folderAsset: yes
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/StreamingAssets/SampleAssets/PresetAvatars_Fastload.zip b/Assets/Oculus/Avatar2/StreamingAssets/SampleAssets/PresetAvatars_Fastload.zip
new file mode 100644
index 0000000000000000000000000000000000000000..0eb8c15ae40bb36b704fa93bc70c4f67bc5b1a1d
Binary files /dev/null and b/Assets/Oculus/Avatar2/StreamingAssets/SampleAssets/PresetAvatars_Fastload.zip differ
diff --git a/Assets/Oculus/Avatar2/StreamingAssets/SampleAssets/PresetAvatars_Fastload.zip.meta b/Assets/Oculus/Avatar2/StreamingAssets/SampleAssets/PresetAvatars_Fastload.zip.meta
new file mode 100644
index 0000000000000000000000000000000000000000..0fdf0d6342d827fbf7abc14c2429a5a4015e31e4
--- /dev/null
+++ b/Assets/Oculus/Avatar2/StreamingAssets/SampleAssets/PresetAvatars_Fastload.zip.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: df32f4814cb27c4478b53a8dc4364e98
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/StreamingAssets/SampleAssets/PresetAvatars_Quest.zip b/Assets/Oculus/Avatar2/StreamingAssets/SampleAssets/PresetAvatars_Quest.zip
new file mode 100644
index 0000000000000000000000000000000000000000..307075873b974baf20412219f2e279751bfbf2a9
Binary files /dev/null and b/Assets/Oculus/Avatar2/StreamingAssets/SampleAssets/PresetAvatars_Quest.zip differ
diff --git a/Assets/Oculus/Avatar2/StreamingAssets/SampleAssets/PresetAvatars_Quest.zip.meta b/Assets/Oculus/Avatar2/StreamingAssets/SampleAssets/PresetAvatars_Quest.zip.meta
new file mode 100644
index 0000000000000000000000000000000000000000..b7c37766e8d5b9cead4bf0aa98c9d56489c9d37a
--- /dev/null
+++ b/Assets/Oculus/Avatar2/StreamingAssets/SampleAssets/PresetAvatars_Quest.zip.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 3b23a5451ec553f4bb22a9a51a41b8bb
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/StreamingAssets/SampleAssets/PresetAvatars_Rift.zip b/Assets/Oculus/Avatar2/StreamingAssets/SampleAssets/PresetAvatars_Rift.zip
new file mode 100644
index 0000000000000000000000000000000000000000..c3cf9ede7af0dc63f0bce0eac26760c009b291d6
Binary files /dev/null and b/Assets/Oculus/Avatar2/StreamingAssets/SampleAssets/PresetAvatars_Rift.zip differ
diff --git a/Assets/Oculus/Avatar2/StreamingAssets/SampleAssets/PresetAvatars_Rift.zip.meta b/Assets/Oculus/Avatar2/StreamingAssets/SampleAssets/PresetAvatars_Rift.zip.meta
new file mode 100644
index 0000000000000000000000000000000000000000..7eaa7aff61a08a1d62d273291335c6e17ef70e98
--- /dev/null
+++ b/Assets/Oculus/Avatar2/StreamingAssets/SampleAssets/PresetAvatars_Rift.zip.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 298c16c941bf88f4c80c7ecd8e40aa4f
+DefaultImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/THIRD_PARTY_NOTICES.html b/Assets/Oculus/Avatar2/THIRD_PARTY_NOTICES.html
new file mode 100644
index 0000000000000000000000000000000000000000..07b64cde100f9c99c06b14b614f77863823536d7
--- /dev/null
+++ b/Assets/Oculus/Avatar2/THIRD_PARTY_NOTICES.html
@@ -0,0 +1,1356 @@
+<html>
+<head>
+<title>Third Party Notices</title>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<style type="text/css" >      div.bodyStyle {padding: 20px; background-color: white; font: 14px Helvetica;}      h1.noticeTitle {margin: 0 0 16px 0; font-size: 28px; font-weight: bold; text-align: center;}      h1.attributionItemTitle {margin: 32px 0 16px 0; font-size: 24px; font-weight: bold;}      h1.packageCustomTextTitle {margin: 32px 0 16px 0; font-size: 24px; font-weight: bold;}      ul.packageList {list-style-type: disc; margin: 0px 10px;}      ul.copyrightList {margin: 4px 0;}      li.packageIdentifier {font-size: 14px;}      span.packageTitle {font-weight: bold;}      span.packageCopyright {font-style: italic;}      p.generalText {margin: 0 0 16px 0;}      p.disclaimer {margin: 0 0 24px 0;}      pre.rawContent {white-space: pre-wrap;}      </style>
+</head>
+<body>
+<div class="bodyStyle">
+<h1 class="noticeTitle">Third Party Notices</h1>
+<p class="disclaimer">THE FOLLOWING SETS FORTH ATTRIBUTION NOTICES FOR THIRD PARTY SOFTWARE THAT MAY BE CONTAINED IN PORTIONS OF THIS FACEBOOK PRODUCT.</p>
+<h1 class="attributionItemTitle">Apache License 2.0</h1>
+<p class="generalText">The following component are licensed under the Apache License 2.0 license reproduced below.</p>
+<ul class="packageList">
+<li class="packageIdentifier">
+<span class="packageTitle">ChangeOfBasis</span>
+<span class="packageCopyright">, Copyright 2014 Scott M. Johnson</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">KTX-Software</span>
+<span class="packageCopyright">, Copyright 2017-2020 The Khronos Group, Inc.</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">caffe</span>
+<span class="packageCopyright">, All contributions by the University of California: Copyright (c) 2014, 2015, The Regents of the University of California (Regents) All rights reserved. All other contributions: Copyright (c) 2014, 2015, the respective contributors All rights reserved.</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">cpu_features</span>
+<span class="packageCopyright">, Copyright (C) 2010 The Android Open Source Project * All rights reserved.</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">fast_float main</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">flatbuffers 1.5.0</span>
+<span class="packageCopyright">, Copyright 2014 Google Inc.</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">folly</span>
+<span class="packageCopyright">, Copyright (c) Facebook, Inc. and its affiliates</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">gemmlowp</span>
+<span class="packageCopyright">, Copyright 2015 The Gemmlowp Authors</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">perfetto</span>
+<span class="packageCopyright">, Copyright (C) 2019 The Android Open Source Project</span>
+</li>
+</ul>
+<pre class="rawContent">Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      &quot;License&quot; shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      &quot;Licensor&quot; shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      &quot;Legal Entity&quot; shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      &quot;control&quot; means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      &quot;You&quot; (or &quot;Your&quot;) shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      &quot;Source&quot; form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      &quot;Object&quot; form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      &quot;Work&quot; shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      &quot;Derivative Works&quot; shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      &quot;Contribution&quot; shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, &quot;submitted&quot;
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as &quot;Not a Contribution.&quot;
+
+      &quot;Contributor&quot; shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a &quot;NOTICE&quot; text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an &quot;AS IS&quot; BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets &quot;[]&quot;
+      replaced with your own identifying information. (Don&#039;t include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same &quot;printed page&quot; as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://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 &quot;AS IS&quot; 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.</pre>
+<h1 class="attributionItemTitle">Apache-2.0 WITH LLVM-exception</h1>
+<p class="generalText">The following component are licensed under the Apache-2.0 WITH LLVM-exception license reproduced below.</p>
+<ul class="packageList">
+<li class="packageIdentifier">
+<span class="packageTitle">llvm-project</span>
+</li>
+</ul>
+<pre class="rawContent">==============================================================================
+The LLVM Project is under the Apache License v2.0 with LLVM Exceptions:
+==============================================================================
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+    TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+    1. Definitions.
+
+      &quot;License&quot; shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      &quot;Licensor&quot; shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      &quot;Legal Entity&quot; shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      &quot;control&quot; means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      &quot;You&quot; (or &quot;Your&quot;) shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      &quot;Source&quot; form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      &quot;Object&quot; form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      &quot;Work&quot; shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      &quot;Derivative Works&quot; shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      &quot;Contribution&quot; shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, &quot;submitted&quot;
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as &quot;Not a Contribution.&quot;
+
+      &quot;Contributor&quot; shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+    2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+    3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+    4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a &quot;NOTICE&quot; text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+    5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+    6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+    7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an &quot;AS IS&quot; BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+    8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+    9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+    END OF TERMS AND CONDITIONS
+
+    APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets &quot;[]&quot;
+      replaced with your own identifying information. (Don&#039;t include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same &quot;printed page&quot; as the copyright notice for easier
+      identification within third-party archives.
+
+    Copyright [yyyy] [name of copyright owner]
+
+    Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);
+    you may not use this file except in compliance with the License.
+    You may obtain a copy of the License at
+
+       http://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 &quot;AS IS&quot; 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.
+
+
+---- LLVM Exceptions to the Apache 2.0 License ----
+
+As an exception, if, as a result of your compiling your source code, portions
+of this Software are embedded into an Object form of such source code, you
+may redistribute such embedded portions in such Object form without complying
+with the conditions of Sections 4(a), 4(b) and 4(d) of the License.
+
+In addition, if you combine or link compiled forms of this Software with
+software that is licensed under the GPLv2 (&quot;Combined Software&quot;) and if a
+court of competent jurisdiction determines that the patent provision (Section
+3), the indemnity provision (Section 9) or other Section of the License
+conflicts with the conditions of the GPLv2, you may retroactively and
+prospectively choose to deem waived or otherwise exclude such Section(s) of
+the License, but only in their entirety and only with respect to the Combined
+Software.
+
+==============================================================================
+Software from third parties included in the LLVM Project:
+==============================================================================
+The LLVM Project contains third party software which is under different license
+terms. All such code will be identified clearly using at least one of two
+mechanisms:
+1) It will be in a separate directory tree with its own `LICENSE.txt` or
+   `LICENSE` file at the top containing the specific license and restrictions
+   which apply to that software, or
+2) It will contain specific license and restriction terms at the top of every
+   file.
+
+==============================================================================
+Legacy LLVM License (https://llvm.org/docs/DeveloperPolicy.html#legacy):
+==============================================================================
+University of Illinois/NCSA
+Open Source License
+
+Copyright (c) 2003-2019 University of Illinois at Urbana-Champaign.
+All rights reserved.
+
+Developed by:
+
+    LLVM Team
+
+    University of Illinois at Urbana-Champaign
+
+    http://llvm.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the &quot;Software&quot;), to deal with
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+    * Redistributions of source code must retain the above copyright notice,
+      this list of conditions and the following disclaimers.
+
+    * Redistributions in binary form must reproduce the above copyright notice,
+      this list of conditions and the following disclaimers in the
+      documentation and/or other materials provided with the distribution.
+
+    * Neither the names of the LLVM Team, University of Illinois at
+      Urbana-Champaign, nor the names of its contributors may be used to
+      endorse or promote products derived from this Software without specific
+      prior written permission.
+
+THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE
+SOFTWARE.</pre>
+<h1 class="attributionItemTitle">BSD 2-Clause &quot;Simplified&quot; License</h1>
+<p class="generalText">The following component are licensed under the BSD 2-Clause &quot;Simplified&quot; License license reproduced below.</p>
+<ul class="packageList">
+<li class="packageIdentifier">
+<span class="packageTitle">ARM_NEON_2_x86_SSE 0</span>
+<span class="packageCopyright">, Copyright (C) 2012-2020 Intel Corporation. All rights reserved.</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">MPSC node-based queue Algorithm by Dmitry Vyukov</span>
+<span class="packageCopyright">, Copyright (c) 2010-2011 Dmitry Vyukov</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">NNPACK 0</span>
+<span class="packageCopyright">, Copyright (c) 2017 Facebook Inc. Copyright (c) 2015-2017, Georgia Institute of Technology All rights reserved.</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">clog 0</span>
+<span class="packageCopyright">, Copyright (c) 2019 Google LLC Copyright (c) 2017-2018 Facebook Inc. Copyright (C) 2012-2017 Georgia Institute of Technology Copyright (C) 2010-2012 Marat Dukhan All rights reserved.</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">concurrentqueue</span>
+<span class="packageCopyright">, Copyright (c) 2013-2016, Cameron Desrochers. All rights reserved.</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">cpuinfo</span>
+<span class="packageCopyright">, Copyright (c) 2019 Google LLC Copyright (c) 2017-2018 Facebook Inc. Copyright (C) 2012-2017 Georgia Institute of Technology Copyright (C) 2010-2012 Marat Dukhan All rights reserved.</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">pthreadpool 0</span>
+<span class="packageCopyright">, Copyright 2019 Google LLC Copyright (c) 2017 Facebook Inc. Copyright (c) 2015-2017 Georgia Institute of Technology All rights reserved.</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">xxHash</span>
+<span class="packageCopyright">, Copyright (c) 2012-2021 Yann Collet All rights reserved.</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">zstdmt</span>
+<span class="packageCopyright">, Copyright (c) 2016 - 2020, Tino Reichardt, All rights reserved.</span>
+</li>
+</ul>
+<pre class="rawContent">Copyright (c) &lt;year&gt; &lt;owner&gt;. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation
+and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS &quot;AS IS&quot;
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</pre>
+<h1 class="attributionItemTitle">BSD 3-Clause &quot;New&quot; or &quot;Revised&quot; License</h1>
+<p class="generalText">The following component are licensed under the BSD 3-Clause &quot;New&quot; or &quot;Revised&quot; License license reproduced below.</p>
+<ul class="packageList">
+<li class="packageIdentifier">
+<span class="packageTitle">IlmBase</span>
+<span class="packageCopyright">, Copyright (c) 2002-2012, Industrial Light &amp; Magic</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">Pytorch</span>
+<span class="packageCopyright">, From PyTorch: Copyright (c) 2016- Facebook, Inc (Adam Paszke) Copyright (c) 2014- Facebook, Inc (Soumith Chintala) Copyright (c) 2011-2014 Idiap Research Institute (Ronan Collobert) Copyright (c) 2012-2014 Deepmind Technologies (Koray Kavukcuoglu) Copyright (c) 2011-2012 NEC Laboratories America (Koray Kavukcuoglu) Copyright (c) 2011-2013 NYU (Clement Farabet) Copyright (c) 2006-2010 NEC Laboratories America (Ronan Collobert, Leon Bottou, Iain Melvin, Jason Weston) Copyright (c) 2006 Idiap Research Institute (Samy Bengio) Copyright (c) 2001-2004 Idiap Research Institute (Ronan Collobert, Samy Bengio, Johnny Mariethoz) From Caffe2: Copyright (c) 2016-present, Facebook Inc. All rights reserved. All contributions by Facebook: Copyright (c) 2016 Facebook Inc. All contributions by Google: Copyright (c) 2015 Google Inc. All rights reserved. All contributions by Yangqing Jia: Copyright (c) 2015 Yangqing Jia All rights reserved. All contributions by Kakao Brain: Copyright 2019-2020 Kakao Brain All contributions from Caffe: Copyright(c) 2013, 2014, 2015, the respective contributors All rights reserved. All other contributions: Copyright(c) 2015, 2016 the respective contributors All rights reserved.</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">QNNPACK</span>
+<span class="packageCopyright">, Copyright (c) Facebook, Inc. and its affiliates. All rights reserved.</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">boost::optional by Steve Hickman</span>
+<span class="packageCopyright">, Copyright (c) 2014, Steve Hickman All rights reserved.</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">cereal 1.2.2</span>
+<span class="packageCopyright">, Copyright (c) 2014, Randolph Voorhies, Shane Grant All rights reserved.</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">double-conversion 3.1.4</span>
+<span class="packageCopyright">, Copyright 2006-2011, the V8 project authors. All rights reserved.</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">gflags</span>
+<span class="packageCopyright">, Copyright (c) 2006, Google Inc. All rights reserved.</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">glog 0.3.4</span>
+<span class="packageCopyright">, Copyright (c) 2008, Google Inc. All rights reserved.</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">googletest</span>
+<span class="packageCopyright">, Copyright 2015, Google Inc. All rights reserved.</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">hptt 1.0.4</span>
+<span class="packageCopyright">, Copyright (c) 2017 Paul Springer (springer&#064;aices.rwth-aachen.de)</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">jinja2</span>
+<span class="packageCopyright">, Copyright (c) 2009 by the Jinja Team</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">libevent 1.4.14b-stable</span>
+<span class="packageCopyright">, Copyright (c) 2007-2012 Niels Provos and Nick Mathewson</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">markupsafe</span>
+<span class="packageCopyright">, Copyright 2010 Pallets</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">protobuf 3.7.1</span>
+<span class="packageCopyright">, Copyright 2008 Google Inc. All rights reserved.</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">smallsha1</span>
+<span class="packageCopyright">, Copyright (c) 2011, Micael Hildenborg All rights reserved. /* Contributors: Gustav Several members in the gamedev.se forum. Gregory Petrosyan */</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">websocketpp</span>
+<span class="packageCopyright">, Copyright (c) 2014, Peter Thorson. All rights reserved.</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">ze_oss</span>
+<span class="packageCopyright">, Copyright (c) 2015-2016, ETH Zurich, Wyss Zurich, Zurich Eye</span>
+</li>
+</ul>
+<pre class="rawContent">Copyright (c) &lt;year&gt; &lt;owner&gt;. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation
+and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors
+may be used to endorse or promote products derived from this software without
+specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS &quot;AS IS&quot;
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</pre>
+<h1 class="attributionItemTitle">BSD-2-Clause or GPL-2.0</h1>
+<p class="generalText">The following component are licensed under the BSD-2-Clause or GPL-2.0 license reproduced below.</p>
+<ul class="packageList">
+<li class="packageIdentifier">
+<span class="packageTitle">FiniteStateEntropy</span>
+<span class="packageCopyright">, Copyright (c) 2013, Yann Collet All rights reserved.</span>
+</li>
+</ul>
+<pre class="rawContent">This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+
+---
+
+ * You may select, at your option, one of the above-listed licenses.
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions are
+   met:
+
+       * Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+       * Redistributions in binary form must reproduce the above
+   copyright notice, this list of conditions and the following disclaimer
+   in the documentation and/or other materials provided with the
+   distribution.
+
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   &quot;AS IS&quot; AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+   OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+   OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</pre>
+<h1 class="attributionItemTitle">Boost Software License 1.0</h1>
+<p class="generalText">The following component are licensed under the Boost Software License 1.0 license reproduced below.</p>
+<ul class="packageList">
+<li class="packageIdentifier">
+<span class="packageTitle">Asio</span>
+<span class="packageCopyright">, Copyright (c) 2003-2020 Christopher M. Kohlhoff</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">Optional</span>
+<span class="packageCopyright">, Copyright (c) 2011 - 2012 Andrzej Krzemienski</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">boost 1.69.0</span>
+<span class="packageCopyright">, Copyright (c) Boost contributors</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">flat_hash_map</span>
+<span class="packageCopyright">, Copyright Malte Skarupke 2017</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">variant</span>
+<span class="packageCopyright">, Copyright Michael Park, 2015-2017</span>
+</li>
+</ul>
+<pre class="rawContent">Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the &quot;Software&quot;) to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor.
+
+THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</pre>
+<h1 class="attributionItemTitle">Creative Commons Zero v1.0 Universal</h1>
+<p class="generalText">The following component are licensed under the Creative Commons Zero v1.0 Universal license reproduced below.</p>
+<ul class="packageList">
+<li class="packageIdentifier">
+<span class="packageTitle">hedley</span>
+</li>
+</ul>
+<pre class="rawContent">Creative Commons Legal Code
+
+CC0 1.0 Universal
+
+    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+    LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
+    ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+    INFORMATION ON AN &quot;AS-IS&quot; BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+    REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
+    PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
+    THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
+    HEREUNDER.
+
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer
+exclusive Copyright and Related Rights (defined below) upon the creator
+and subsequent owner(s) (each and all, an &quot;owner&quot;) of an original work of
+authorship and/or a database (each, a &quot;Work&quot;).
+
+Certain owners wish to permanently relinquish those rights to a Work for
+the purpose of contributing to a commons of creative, cultural and
+scientific works (&quot;Commons&quot;) that the public can reliably and without fear
+of later claims of infringement build upon, modify, incorporate in other
+works, reuse and redistribute as freely as possible in any form whatsoever
+and for any purposes, including without limitation commercial purposes.
+These owners may contribute to the Commons to promote the ideal of a free
+culture and the further production of creative, cultural and scientific
+works, or to gain reputation or greater distribution for their Work in
+part through the use and efforts of others.
+
+For these and/or other purposes and motivations, and without any
+expectation of additional consideration or compensation, the person
+associating CC0 with a Work (the &quot;Affirmer&quot;), to the extent that he or she
+is an owner of Copyright and Related Rights in the Work, voluntarily
+elects to apply CC0 to the Work and publicly distribute the Work under its
+terms, with knowledge of his or her Copyright and Related Rights in the
+Work and the meaning and intended legal effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be
+protected by copyright and related or neighboring rights (&quot;Copyright and
+Related Rights&quot;). Copyright and Related Rights include, but are not
+limited to, the following:
+
+  i. the right to reproduce, adapt, distribute, perform, display,
+     communicate, and translate a Work;
+ ii. moral rights retained by the original author(s) and/or performer(s);
+iii. publicity and privacy rights pertaining to a person&#039;s image or
+     likeness depicted in a Work;
+ iv. rights protecting against unfair competition in regards to a Work,
+     subject to the limitations in paragraph 4(a), below;
+  v. rights protecting the extraction, dissemination, use and reuse of data
+     in a Work;
+ vi. database rights (such as those arising under Directive 96/9/EC of the
+     European Parliament and of the Council of 11 March 1996 on the legal
+     protection of databases, and under any national implementation
+     thereof, including any amended or successor version of such
+     directive); and
+vii. other similar, equivalent or corresponding rights throughout the
+     world based on applicable law or treaty, and any national
+     implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention
+of, applicable law, Affirmer hereby overtly, fully, permanently,
+irrevocably and unconditionally waives, abandons, and surrenders all of
+Affirmer&#039;s Copyright and Related Rights and associated claims and causes
+of action, whether now known or unknown (including existing as well as
+future claims and causes of action), in the Work (i) in all territories
+worldwide, (ii) for the maximum duration provided by applicable law or
+treaty (including future time extensions), (iii) in any current or future
+medium and for any number of copies, and (iv) for any purpose whatsoever,
+including without limitation commercial, advertising or promotional
+purposes (the &quot;Waiver&quot;). Affirmer makes the Waiver for the benefit of each
+member of the public at large and to the detriment of Affirmer&#039;s heirs and
+successors, fully intending that such Waiver shall not be subject to
+revocation, rescission, cancellation, termination, or any other legal or
+equitable action to disrupt the quiet enjoyment of the Work by the public
+as contemplated by Affirmer&#039;s express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason
+be judged legally invalid or ineffective under applicable law, then the
+Waiver shall be preserved to the maximum extent permitted taking into
+account Affirmer&#039;s express Statement of Purpose. In addition, to the
+extent the Waiver is so judged Affirmer hereby grants to each affected
+person a royalty-free, non transferable, non sublicensable, non exclusive,
+irrevocable and unconditional license to exercise Affirmer&#039;s Copyright and
+Related Rights in the Work (i) in all territories worldwide, (ii) for the
+maximum duration provided by applicable law or treaty (including future
+time extensions), (iii) in any current or future medium and for any number
+of copies, and (iv) for any purpose whatsoever, including without
+limitation commercial, advertising or promotional purposes (the
+&quot;License&quot;). The License shall be deemed effective as of the date CC0 was
+applied by Affirmer to the Work. Should any part of the License for any
+reason be judged legally invalid or ineffective under applicable law, such
+partial invalidity or ineffectiveness shall not invalidate the remainder
+of the License, and in such case Affirmer hereby affirms that he or she
+will not (i) exercise any of his or her remaining Copyright and Related
+Rights in the Work or (ii) assert any associated claims and causes of
+action with respect to the Work, in either case contrary to Affirmer&#039;s
+express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
+    surrendered, licensed or otherwise affected by this document.
+ b. Affirmer offers the Work as-is and makes no representations or
+    warranties of any kind concerning the Work, express, implied,
+    statutory or otherwise, including without limitation warranties of
+    title, merchantability, fitness for a particular purpose, non
+    infringement, or the absence of latent or other defects, accuracy, or
+    the present or absence of errors, whether or not discoverable, all to
+    the greatest extent permissible under applicable law.
+ c. Affirmer disclaims responsibility for clearing rights of other persons
+    that may apply to the Work or any use thereof, including without
+    limitation any person&#039;s Copyright and Related Rights in the Work.
+    Further, Affirmer disclaims responsibility for obtaining any necessary
+    consents, permissions or other rights required for any use of the
+    Work.
+ d. Affirmer understands and acknowledges that Creative Commons is not a
+    party to this document and has no duty or obligation with respect to
+    this CC0 or use of the Work.</pre>
+<h1 class="attributionItemTitle">GPL-2.0 or BSD-3-Clause</h1>
+<p class="generalText">The following component are licensed under the GPL-2.0 or BSD-3-Clause license reproduced below.</p>
+<ul class="packageList">
+<li class="packageIdentifier">
+<span class="packageTitle">zstd 1.4.3</span>
+<span class="packageCopyright">, Copyright (c) 2016-2020, Yann Collet, Facebook, Inc.</span>
+</li>
+</ul>
+<pre class="rawContent">* This source code is licensed under both the BSD-style license (found in the
+ * LICENSE file in the root directory of this source tree) and the GPLv2 (found
+ * in the COPYING file in the root directory of this source tree).
+ * You may select, at your option, one of the above-listed licenses.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS &quot;AS IS&quot; AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</pre>
+<h1 class="attributionItemTitle">IJG and BSD-3-Clause and Zlib</h1>
+<p class="generalText">The following component are licensed under the IJG and BSD-3-Clause and Zlib license reproduced below.</p>
+<ul class="packageList">
+<li class="packageIdentifier">
+<span class="packageTitle">libjpeg-turbo 1.5.x</span>
+<span class="packageCopyright">, Copyright (c) 1994-1996, Thomas G. Lane Copyright 2009 Pierre Ossman &lt;ossman&#064;cendio.se&gt; for Cendio AB Copyright (c) 2009, 2011, 2014-2015, D. R. Commander Copyright (c) 2013, Linaro Limited</span>
+</li>
+</ul>
+<pre class="rawContent">libjpeg-turbo Licenses
+libjpeg-turbo is covered by three compatible BSD-style open source licenses:
+
+The IJG (Independent JPEG Group) License, which is listed in README.ijg
+
+This license applies to the libjpeg API library and associated programs (any code inherited from libjpeg, and any modifications to that code.)
+
+The Modified (3-clause) BSD License, which is listed below
+
+This license covers the TurboJPEG API library and associated programs, as well as the build system.
+
+The zlib License
+
+This license is a subset of the other two, and it covers the libjpeg-turbo SIMD extensions.
+
+Complying with the libjpeg-turbo Licenses
+This section provides a roll-up of the libjpeg-turbo licensing terms, to the best of our understanding.
+
+If you are distributing a modified version of the libjpeg-turbo source, then:
+
+You cannot alter or remove any existing copyright or license notices from the source.
+
+Origin
+
+Clause 1 of the IJG License
+Clause 1 of the Modified BSD License
+Clauses 1 and 3 of the zlib License
+You must add your own copyright notice to the header of each source file you modified, so others can tell that you modified that file (if there is not an existing copyright header in that file, then you can simply add a notice stating that you modified the file.)
+
+Origin
+
+Clause 1 of the IJG License
+Clause 2 of the zlib License
+You must include the IJG README file, and you must not alter any of the copyright or license text in that file.
+
+Origin
+
+Clause 1 of the IJG License
+If you are distributing only libjpeg-turbo binaries without the source, or if you are distributing an application that statically links with libjpeg-turbo, then:
+
+Your product documentation must include a message stating:
+
+This software is based in part on the work of the Independent JPEG Group.
+
+Origin
+
+Clause 2 of the IJG license
+If your binary distribution includes or uses the TurboJPEG API, then your product documentation must include the text of the Modified BSD License (see below.)
+
+Origin
+
+Clause 2 of the Modified BSD License
+You cannot use the name of the IJG or The libjpeg-turbo Project or the contributors thereof in advertising, publicity, etc.
+
+Origin
+
+IJG License
+Clause 3 of the Modified BSD License
+The IJG and The libjpeg-turbo Project do not warrant libjpeg-turbo to be free of defects, nor do we accept any liability for undesirable consequences resulting from your use of the software.
+
+Origin
+
+IJG License
+Modified BSD License
+zlib License
+The Modified (3-clause) BSD License
+Copyright (C)2009-2021 D. R. Commander. All Rights Reserved.
+Copyright (C)2015 Viktor Szathmáry. All Rights Reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+Neither the name of the libjpeg-turbo Project nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS &quot;AS IS&quot;, AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.ibution.</pre>
+<h1 class="attributionItemTitle">MIT License</h1>
+<p class="generalText">The following component are licensed under the MIT License license reproduced below.</p>
+<ul class="packageList">
+<li class="packageIdentifier">
+<span class="packageTitle">FP16 0</span>
+<span class="packageCopyright">, Copyright (c) 2017 Facebook Inc. Copyright (c) 2017 Georgia Institute of Technology Copyright 2019 Google LLC</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">FXdiv 0</span>
+<span class="packageCopyright">, Copyright (c) 2017 Facebook Inc. Copyright (c) 2016-2017 Marat Dukhan</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">GLM 0.9.9</span>
+<span class="packageCopyright">, Copyright (c) 2005 - G-Truc Creation</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">GSL</span>
+<span class="packageCopyright">, Copyright (c) 2015 Microsoft Corporation. All rights reserved.</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">Nlohmann-JSON 3.9.1</span>
+<span class="packageCopyright">, Copyright (c) 2013-2021 Niels Lohmann</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">RapidJson 1.1.0</span>
+<span class="packageCopyright">, Copyright (C) 2015 THL A29 Limited, a Tencent company, and Milo Yip. All rights reserved.</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">cJSON</span>
+<span class="packageCopyright">, Copyright (c) 2009-2017 Dave Gamble and cJSON contributors</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">crc32 by Eric Biggers</span>
+<span class="packageCopyright">, Copyright 2016 Eric Biggers</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">fmt 7.0.3</span>
+<span class="packageCopyright">, Copyright (c) 2012 - present, Victor Zverovich</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">fx-gltf</span>
+<span class="packageCopyright">, Copyright (c) 2018-2021 Jesse Yurkovich</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">gulrak_filesystem 1.5.0</span>
+<span class="packageCopyright">, Copyright (c) 2018, Steffen Schümann &lt;s.schuemann&#064;pobox.com&gt;</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">jansson 2.9</span>
+<span class="packageCopyright">, Copyright (c) 2009-2020 Petri Lehtinen &lt;petri&#064;digip.org&gt;</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">nghttp2</span>
+<span class="packageCopyright">, Copyright © 2021 - Tatsuhiro Tsujikawa</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">psimd</span>
+<span class="packageCopyright">, Copyright (c) 2017 Facebook Inc. Copyright (c) 2014-2017 Georgia Institute of Technology Copyright 2019 Google LLC</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">stb</span>
+<span class="packageCopyright">, Copyright (c) 2017 Sean Barrett</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">tinygltf</span>
+<span class="packageCopyright">, Copyright (c) 2017 Syoyo Fujita, Aurélien Chatelain and many contributors</span>
+</li>
+</ul>
+<pre class="rawContent">MIT License Copyright (c) &lt;year&gt; &lt;copyright holders&gt;
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the &quot;Software&quot;), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice (including the next
+paragraph) shall be included in all copies or substantial portions of the
+Software.
+
+THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
+OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
+OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</pre>
+<h1 class="attributionItemTitle">MIT or BSL-1.0</h1>
+<p class="generalText">The following component are licensed under the MIT or BSL-1.0 license reproduced below.</p>
+<ul class="packageList">
+<li class="packageIdentifier">
+<span class="packageTitle">RapidXML</span>
+<span class="packageCopyright">, Copyright (c) 2006, 2009 Marcin Kalicinski</span>
+</li>
+</ul>
+<pre class="rawContent">Use of this software is granted under one of the following two licenses,
+to be chosen freely by the user.
+
+1. Boost Software License - Version 1.0 - August 17th, 2003
+===============================================================================
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the &quot;Software&quot;) to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+2. The MIT License
+===============================================================================
+
+Permission is hereby granted, free of charge, to any person obtaining a copy 
+of this software and associated documentation files (the &quot;Software&quot;), to deal 
+in the Software without restriction, including without limitation the rights 
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
+of the Software, and to permit persons to whom the Software is furnished to do so, 
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all 
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
+THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 
+IN THE SOFTWARE.</pre>
+<h1 class="attributionItemTitle">OpenSSL License</h1>
+<p class="generalText">The following component are licensed under the OpenSSL License license reproduced below.</p>
+<ul class="packageList">
+<li class="packageIdentifier">
+<span class="packageTitle">OpenSSL</span>
+<span class="packageCopyright">, Copyright (c) 1998-2021 The OpenSSL Project Copyright (c) 1995-1998 Eric A. Young, Tim J. Hudson All rights reserved.</span>
+</li>
+</ul>
+<pre class="rawContent">OpenSSL License
+
+Copyright (c) 1998-2008 The OpenSSL Project. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+3. All advertising materials mentioning features or use of this software must display the following acknowledgment: &quot;This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit. (http://www.openssl.org/)&quot;
+
+4. The names &quot;OpenSSL Toolkit&quot; and &quot;OpenSSL Project&quot; must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact openssl-core&#064;openssl.org.
+
+5. Products derived from this software may not be called &quot;OpenSSL&quot; nor may &quot;OpenSSL&quot; appear in their names without prior written permission of the OpenSSL Project.
+
+6. Redistributions of any form whatsoever must retain the following acknowledgment: &quot;This product includes software developed by the OpenSSL Project for use in the OpenSSL Toolkit (http://www.openssl.org/)&quot;
+
+THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS&#039;&#039; AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+This product includes cryptographic software written by Eric Young (eay&#064;cryptsoft.com). This product includes software written by Tim Hudson (tjh&#064;cryptsoft.com).
+
+
+Original SSLeay License
+
+Copyright (C) 1995-1998 Eric Young (eay&#064;cryptsoft.com) All rights reserved.
+
+This package is an SSL implementation written by Eric Young (eay&#064;cryptsoft.com). The implementation was written so as to conform with Netscapes SSL.
+
+This library is free for commercial and non-commercial use as long as the following conditions are aheared to. The following conditions apply to all code found in this distribution, be it the RC4, RSA, lhash, DES, etc., code; not just the SSL code. The SSL documentation included with this distribution is covered by the same copyright terms except that the holder is Tim Hudson (tjh&#064;cryptsoft.com).
+
+Copyright remains Eric Young&#039;s, and as such any Copyright notices in the code are not to be removed. If this package is used in a product, Eric Young should be given attribution as the author of the parts of the library used. This can be in the form of a textual message at program startup or in documentation (online or textual) provided with the package.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the copyright notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+
+3. All advertising materials mentioning features or use of this software must display the following acknowledgement:
+&quot;This product includes cryptographic software written by Eric Young (eay&#064;cryptsoft.com)&quot;
+The word &#039;cryptographic&#039; can be left out if the rouines from the library being used are not cryptographic related :-).
+
+4. If you include any Windows specific code (or a derivative thereof) from the apps directory (application code) you must include an acknowledgement: &quot;This product includes software written by Tim Hudson (tjh&#064;cryptsoft.com)&quot;
+
+THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS&#039;&#039; AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The licence and distribution terms for any publically available version or derivative of this code cannot be changed. i.e. this code cannot simply be copied and put under another distribution licence [including the GNU Public Licence.]</pre>
+<h1 class="attributionItemTitle">Public Domain</h1>
+<p class="generalText">The following component are licensed under the Public Domain license reproduced below.</p>
+<ul class="packageList">
+<li class="packageIdentifier">
+<span class="packageTitle">xoshiro256** 1.0</span>
+<span class="packageCopyright">, Written in 2018 by David Blackman and Sebastiano Vigna (vigna&#064;acm.org)</span>
+</li>
+</ul>
+<pre class="rawContent">To the extent possible under law, the author has dedicated all copyright
+and related and neighboring rights to this software to the public domain
+worldwide. This software is distributed without any warranty.</pre>
+<h1 class="attributionItemTitle">Public Domain</h1>
+<p class="generalText">The following component are licensed under the Public Domain license reproduced below.</p>
+<ul class="packageList">
+<li class="packageIdentifier">
+<span class="packageTitle">MurmurHash3</span>
+<span class="packageCopyright">, written by Austin Appleby</span>
+</li>
+</ul>
+<pre class="rawContent">// MurmurHash3 was written by Austin Appleby, and is placed in the public
+// domain. The author hereby disclaims copyright to this source code.</pre>
+<h1 class="attributionItemTitle">curl License</h1>
+<p class="generalText">The following component are licensed under the curl License license reproduced below.</p>
+<ul class="packageList">
+<li class="packageIdentifier">
+<span class="packageTitle">curl 7.70.0</span>
+<span class="packageCopyright">, Copyright (c) 1996 - 2021, Daniel Stenberg, &lt;daniel&#064;haxx.se&gt;, and many contributors, All rights reserved.</span>
+</li>
+</ul>
+<pre class="rawContent">COPYRIGHT AND PERMISSION NOTICE
+
+Copyright (c) 1996 - 2015, Daniel Stenberg, &lt;daniel&#064;haxx.se&gt;.
+All rights reserved.
+
+Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
+
+THE SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization of the copyright holder.</pre>
+<h1 class="attributionItemTitle">libpng License</h1>
+<p class="generalText">The following component are licensed under the libpng License license reproduced below.</p>
+<ul class="packageList">
+<li class="packageIdentifier">
+<span class="packageTitle">libpng 1.6.37</span>
+<span class="packageCopyright">, Copyright (c) 1995-2020 The PNG Reference Library Authors. * Copyright (c) 2018-2020 Cosmin Truta. * Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson. * Copyright (c) 1996-1997 Andreas Dilger. * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc.</span>
+</li>
+</ul>
+<pre class="rawContent">This copy of the libpng notices is provided for your convenience.  In case of any discrepancy between this copy and the notices in the file png.h that is included in the libpng distribution, the latter shall prevail.
+
+COPYRIGHT NOTICE, DISCLAIMER, and LICENSE:
+
+If you modify libpng you may insert additional notices immediately following this sentence.
+
+This code is released under the libpng license.
+
+libpng versions 1.2.6, August 15, 2004, through 1.4.5, December 9, 2010, are Copyright (c) 2004, 2006-2010 Glenn Randers-Pehrson, and are distributed according to the same disclaimer and license as libpng-1.2.5 with the following individual added to the list of Contributing Authors
+
+     Cosmin Truta
+
+libpng versions 1.0.7, July 1, 2000, through 1.2.5 - October 3, 2002, are
+Copyright (c) 2000-2002 Glenn Randers-Pehrson, and are distributed according to the same disclaimer and license as libpng-1.0.6 with the following individuals added to the list of Contributing Authors
+
+     Simon-Pierre Cadieux
+     Eric S. Raymond
+     Gilles Vollant
+
+and with the following additions to the disclaimer:
+
+     There is no warranty against interference with your enjoyment of the library or against infringement.  There is no warranty that our efforts or the library will fulfill any of your particular purposes or needs.  This library is provided with all faults, and the entire risk of satisfactory quality, performance, accuracy, and effort is with the user.
+
+libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are
+Copyright (c) 1998, 1999 Glenn Randers-Pehrson, and are distributed according to the same disclaimer and license as libpng-0.96, with the following individuals added to the list of Contributing Authors:
+
+     Tom Lane
+     Glenn Randers-Pehrson
+     Willem van Schaik
+
+libpng versions 0.89, June 1996, through 0.96, May 1997, are
+Copyright (c) 1996, 1997 Andreas Digger
+Distributed according to the same disclaimer and license as libpng-0.88, with the following individuals added to the list of Contributing Authors:
+
+     John Bowler
+     Kevin Bracey
+     Sam Bushell
+     Magnus Holmgren
+     Greg Roelofs
+     Tom Tanner
+
+libpng versions 0.5, May 1995, through 0.88, January 1996, are
+Copyright (c) 1995, 1996 Guy Eric Schalnat, Group 42, Inc.
+
+For the purposes of this copyright and license, &quot;Contributing Authors&quot; is defined as the following set of individuals:
+
+     Andreas Dilger
+     Dave Martindale
+     Guy Eric Schalnat
+     Paul Schmidt
+     Tim Wegner
+
+The PNG Reference Library is supplied &quot;AS IS&quot;.  The Contributing Authors and Group 42, Inc. disclaim all warranties, expressed or implied, including, without limitation, the warranties of merchantability and of fitness for any purpose.  The Contributing Authors and Group 42, Inc. assume no liability for direct, indirect, incidental, special, exemplary, or consequential damages, which may result from the use of the PNG Reference Library, even if advised of the possibility of such damage.
+
+Permission is hereby granted to use, copy, modify, and distribute this source code, or portions hereof, for any purpose, without fee, subject to the following restrictions:
+
+1. The origin of this source code must not be misrepresented.
+
+2. Altered versions must be plainly marked as such and must not be misrepresented as being the original source.
+
+3. This Copyright notice may not be removed or altered from any source or altered source distribution.
+
+The Contributing Authors and Group 42, Inc. specifically permit, without fee, and encourage the use of this source code as a component to supporting the PNG file format in commercial products.  If you use this source code in a product, acknowledgment is not required but would be appreciated.
+
+
+A &quot;png_get_copyright&quot; function is available, for convenient use in &quot;about&quot; boxes and the like:
+
+     printf(&quot;%s&quot;,png_get_copyright(NULL));
+
+Also, the PNG logo (in PNG format, of course) is supplied in the files &quot;pngbar.png&quot; and &quot;pngbar.jpg (88x31) and &quot;pngnow.png&quot; (98x31).
+
+Libpng is OSI Certified Open Source Software.  OSI Certified Open Source is a certification mark of the Open Source Initiative.
+
+Glenn Randers-Pehrson
+glennrp at users.sourceforge.net
+December 9, 2010</pre>
+<h1 class="attributionItemTitle">zlib License</h1>
+<p class="generalText">The following component are licensed under the zlib License license reproduced below.</p>
+<ul class="packageList">
+<li class="packageIdentifier">
+<span class="packageTitle">Code from htot crc32</span>
+<span class="packageCopyright">, Copyright 2016 Ferry Toth, Exalon Delft BV</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">base64 by Rene Nyffenegger</span>
+<span class="packageCopyright">, copyright 2004-2017 Rene Nyffenegger</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">crc by Stephan Brumme</span>
+<span class="packageCopyright">, Copyright (c) 2011-2019 Stephan Brumme</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">debug_assert 1.3</span>
+<span class="packageCopyright">, Copyright (C) 2016-2018 Jonathan Müller &lt;jonathanmueller.dev&#064;gmail.com&gt;</span>
+</li>
+<li class="packageIdentifier">
+<span class="packageTitle">zlib</span>
+<span class="packageCopyright">, Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler</span>
+</li>
+</ul>
+<pre class="rawContent">zlib License
+
+This software is provided &#039;as-is&#039;, without any express or implied warranty.  In no event will the authors be held liable for any damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
+
+     1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
+
+     2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
+
+     3. This notice may not be removed or altered from any source distribution.</pre>
+</div>
+</body>
+</html>
\ No newline at end of file
diff --git a/Assets/Oculus/Avatar2/THIRD_PARTY_NOTICES.html.meta b/Assets/Oculus/Avatar2/THIRD_PARTY_NOTICES.html.meta
new file mode 100644
index 0000000000000000000000000000000000000000..02139058ed504e776c50136bbfa159fd3bf469ce
--- /dev/null
+++ b/Assets/Oculus/Avatar2/THIRD_PARTY_NOTICES.html.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 764c866301566e940b3cd060560013bc
+TextScriptImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Oculus/Avatar2/build_number.txt b/Assets/Oculus/Avatar2/build_number.txt
new file mode 100644
index 0000000000000000000000000000000000000000..476d01a89d16a84b7a0acd6529d1561a3fdd10d5
Binary files /dev/null and b/Assets/Oculus/Avatar2/build_number.txt differ
diff --git a/Assets/Oculus/Avatar2/build_number.txt.meta b/Assets/Oculus/Avatar2/build_number.txt.meta
new file mode 100644
index 0000000000000000000000000000000000000000..138748cb1b40b5af2e96e198c681f904b804edb2
--- /dev/null
+++ b/Assets/Oculus/Avatar2/build_number.txt.meta
@@ -0,0 +1,7 @@
+fileFormatVersion: 2
+guid: 2118f9af82451754eaeb636aa802a93e
+TextScriptImporter:
+  externalObjects: {}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Resources/OculusPlatformSettings.asset b/Assets/Resources/OculusPlatformSettings.asset
new file mode 100644
index 0000000000000000000000000000000000000000..dc57b6a888759172bd280d4cb396980b825598df
--- /dev/null
+++ b/Assets/Resources/OculusPlatformSettings.asset
@@ -0,0 +1,17 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+  m_ObjectHideFlags: 0
+  m_CorrespondingSourceObject: {fileID: 0}
+  m_PrefabInstance: {fileID: 0}
+  m_PrefabAsset: {fileID: 0}
+  m_GameObject: {fileID: 0}
+  m_Enabled: 1
+  m_EditorHideFlags: 0
+  m_Script: {fileID: 11500000, guid: b3d881e962d099f4a8eb492ef7e9a8c0, type: 3}
+  m_Name: OculusPlatformSettings
+  m_EditorClassIdentifier: 
+  ovrAppID: 
+  ovrMobileAppID: 
+  ovrUseStandalonePlatform: 0
diff --git a/Assets/Resources/OculusPlatformSettings.asset.meta b/Assets/Resources/OculusPlatformSettings.asset.meta
new file mode 100644
index 0000000000000000000000000000000000000000..6950c68b92be1d94fe60e6870a72c8e92f086b63
--- /dev/null
+++ b/Assets/Resources/OculusPlatformSettings.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 337a4d95076a0704990cafcca28527f2
+NativeFormatImporter:
+  externalObjects: {}
+  mainObjectFileID: 11400000
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: