Container for handle references.
        /// <inheritdoc/>
        public void OnPointerDown(MixedRealityPointerEventData eventData)
        {
            if (eventData.EventHandled)
            {
                return;
            }

            if (this.currentCursor == null)
            {
                if (this.helpers.TryGetValue(eventData.CurrentTarget, out var handle))
                {
                    this.currentHandle = handle;

                    this.ApplyMaterialToHandle(this.currentHandle, h => h.GrabbedMaterial);

                    this.currentCursor         = eventData.Cursor;
                    this.grabbedEntityPosition = this.transform.Position;
                    this.grabbedCursorPosition = eventData.Position;

                    this.FireManipulationEvent(handle.Type, started: true);

                    eventData.SetHandled();
                }
            }
        }
        private void ApplyMaterialToHandle(AxisManipulationHelper handle, Func <AxisManipulationHelper, Material> materialGetter)
        {
            this.ApplyMaterialToAllComponents(handle.MaterialComponents, materialGetter(handle));

            if (this.PlaneHandlesActivateAxisHandles)
            {
                for (int i = 0; i < handle.RelatedHandles.Length; i++)
                {
                    var h = handle.RelatedHandles[i];
                    this.ApplyMaterialToAllComponents(h.MaterialComponents, materialGetter(h));
                }
            }
        }
        /// <inheritdoc/>
        public void OnPointerUp(MixedRealityPointerEventData eventData)
        {
            if (eventData.EventHandled)
            {
                return;
            }

            if (this.currentCursor == eventData.Cursor)
            {
                this.FireManipulationEvent(this.currentHandle.Type, started: false);

                this.ApplyMaterialToHandle(this.currentHandle, h => h.IdleMaterial);

                this.currentCursor = null;
                this.currentHandle = null;

                eventData.SetHandled();
            }
        }
        private AxisManipulationHelper CreateHandle(AxisManipulationHelperType amhType, AxisType axisType, Prefab prefab, Material idleMaterial, Material grabbedMaterial, Material focusedMaterial, Quaternion orientation, AxisManipulationHelper[] relatedHandlers)
        {
            // Entity name suffix
            var suffix = $"{amhType}_{axisType}";

            // Handle root
            var handle = new Entity($"handle_{suffix}")
                         .AddComponent(new Transform3D()
            {
                LocalScale = Vector3.One * this.HandleScale,
            });

            this.rigRootEntity.AddChild(handle);

            if (prefab != null)
            {
                // Instantiate prefab
                var prefabInstance = prefab.Instantiate();

                var prefabTransform = prefabInstance.FindComponent <Transform3D>();
                prefabTransform.LocalOrientation = orientation;

                handle.AddChild(prefabInstance);
            }
            else
            {
                // Generate default look for the handle
                Vector3   position = Vector3.Zero;
                Vector3   size     = Vector3.One;
                Component mesh     = null;
                Component collider = null;

                switch (amhType)
                {
                case AxisManipulationHelperType.Center:
                    var sphereDiameter = 1f;

                    mesh = new SphereMesh()
                    {
                        Diameter = sphereDiameter,
                    };
                    collider = new SphereCollider3D()
                    {
                        Margin = 0.0001f,
                        Radius = sphereDiameter,
                    };
                    break;

                case AxisManipulationHelperType.Axis:
                    var axisLength    = 4f;
                    var axisThickness = 0.5f;

                    size = new Vector3(axisLength, axisThickness, axisThickness);

                    mesh     = new CubeMesh();
                    collider = new BoxCollider3D()
                    {
                        Margin = 0.0001f,
                        Size   = size + Vector3.One,
                        Offset = Vector3.UnitX,
                    };

                    position = 0.5f * Vector3.UnitX * (axisLength + 2f);
                    break;

                case AxisManipulationHelperType.Plane:
                    var planeLength    = 2f;
                    var planeThickness = 0.25f;

                    size = new Vector3(planeLength, planeThickness, planeLength);

                    mesh     = new CubeMesh();
                    collider = new BoxCollider3D()
                    {
                        Margin = 0.0001f,
                        Size   = size + Vector3.One,
                        Offset = Vector3.UnitX + Vector3.UnitZ,
                    };

                    position = 0.5f * Vector3.Normalize(Vector3.UnitX + Vector3.UnitZ) * (planeLength + 2f);
                    break;
                }

                // Collider entity
                var handleCollider = new Entity($"collider_{suffix}")
                                     .AddComponent(new Transform3D()
                {
                    LocalPosition    = Vector3.Transform(position, orientation),
                    LocalOrientation = orientation,
                })
                                     .AddComponent(collider)
                                     .AddComponent(new StaticBody3D()
                {
                    CollisionCategories = this.CollisionCategory,
                    IsSensor            = true,
                })
                                     .AddComponent(new NearInteractionGrabbable());

                // Visual entity
                var handleVisual = new Entity($"visuals_{suffix}")
                                   .AddComponent(new Transform3D()
                {
                    LocalScale = size,
                })
                                   .AddComponent(mesh)
                                   .AddComponent(new MeshRenderer())
                                   .AddComponent(new MaterialComponent());

                // Build hierarchy
                handle.AddChild(handleCollider);
                handleCollider.AddChild(handleVisual);
            }

            // Apply material
            var materialComponents = handle.FindComponentsInChildren <MaterialComponent>().ToArray();

            this.ApplyMaterialToAllComponents(materialComponents, idleMaterial);

            // Register helper object
            var helperTargetEntity = handle.FindComponentInChildren <NearInteractionGrabbable>()?.Owner;

            if (helperTargetEntity == null)
            {
                throw new Exception($"The handle entity needs to have a {nameof(NearInteractionGrabbable)} component.");
            }

            var handleHelper = new AxisManipulationHelper()
            {
                Type               = amhType,
                AxisType           = axisType,
                BaseEntity         = handle,
                MaterialComponents = materialComponents,
                IdleMaterial       = idleMaterial,
                GrabbedMaterial    = grabbedMaterial,
                FocusedMaterial    = focusedMaterial,
                RelatedHandles     = relatedHandlers,
            };

            this.helpers.Add(helperTargetEntity, handleHelper);

            return(handleHelper);
        }