/// <summary>
        /// Reset the third person camera "head" angles.
        /// </summary>
        /// <param name="headAngle">new head angle</param>
        public bool ResetViewerAngle(Vector2?headAngle)
        {
            if (!headAngle.HasValue)
            {
                return(false);
            }

            Game.Entities.IMyControllableEntity controlledEntity = MySession.Static.ControlledEntity;
            if (controlledEntity == null)
            {
                return(false);
            }

            controlledEntity.HeadLocalXAngle = headAngle.Value.X;
            controlledEntity.HeadLocalYAngle = headAngle.Value.Y;
            return(true);
        }
        public void RecalibrateCameraPosition(bool isCharacter = false)
        {
            // if (m_lookAt != Vector3D.Zero)
            //    return;

            IMyCameraController cameraController = MySession.Static.CameraController;

            if (!(cameraController is MyEntity))  // also checks for not null
            {
                return;
            }

            Game.Entities.IMyControllableEntity controlledEntity = MySession.Static.ControlledEntity;
            if (controlledEntity == null)
            {
                return;
            }

            // get latest head matrix
            if (!isCharacter)
            {
                var headMatrix = controlledEntity.GetHeadMatrix(true);
                m_targetOrientation = headMatrix.GetOrientation();
                m_target            = headMatrix.Translation;
            }

            // parent of hierarchy
            MyEntity topControlledEntity = ((MyEntity)cameraController).GetTopMostParent();

            if (topControlledEntity.Closed)
            {
                return;
            }

            // calculate controlled object coordinates in parent space
            var      worldToLocal       = topControlledEntity.PositionComp.WorldMatrixNormalizedInv;
            Vector3D targetInLocal      = Vector3D.Transform(m_target, worldToLocal);
            MatrixD  orientationInLocal = m_targetOrientation * worldToLocal;
            var      localAABBHr        = topControlledEntity.PositionComp.LocalAABBHr;
            Vector3D centerToTarget     = targetInLocal - localAABBHr.Center;
            Vector3D backVec            = Vector3D.Normalize(orientationInLocal.Backward);

            // calculate offset for the
            double projectedCenterToTarget = Vector3D.Dot(centerToTarget, backVec);
            double projectedHalfExtent     = Math.Abs(Vector3D.Dot(localAABBHr.HalfExtents, backVec));
            double finalLength             = projectedHalfExtent - projectedCenterToTarget;

            //Vector3D targetWithOffset = centerToTarget + (finalLength * backVec);

            double width = m_lookAtDefaultLength; // some default value

            if (Math.Abs(backVec.Z) > 0.0001)
            {
                width = localAABBHr.HalfExtents.X * 1.5f;
            }
            else if (Math.Abs(backVec.X) > 0.0001)
            {
                width = localAABBHr.HalfExtents.Z * 1.5f;
            }

            // calculate complete offset for controlled object
            double halfFovTan = Math.Tan(MySector.MainCamera.FieldOfView * 0.5);
            double offset     = width / (2 * halfFovTan);

            offset += finalLength;

            double clampDist = MathHelper.Clamp(offset, MIN_VIEWER_DISTANCE, MAX_VIEWER_DISTANCE);

            Vector3D lookAt = m_lookAtDirection * clampDist;

            SetPositionAndLookAt(lookAt);
        }
        // Updates spectator position (spring connected to desired position)
        public override void UpdateAfterSimulation()
        {
            Game.Entities.IMyControllableEntity genericControlledEntity = MySession.Static.ControlledEntity;
            if (genericControlledEntity == null)
            {
                return;
            }
            var remotelyControlledEntity = genericControlledEntity as MyRemoteControl;
            var controlledEntity         = remotelyControlledEntity != null ? remotelyControlledEntity.Pilot : genericControlledEntity.Entity;

            while (controlledEntity.Parent is MyCockpit)
            {
                controlledEntity = controlledEntity.Parent;
            }
            if (controlledEntity != null && controlledEntity.PositionComp != null)
            {
                var      positionComp = controlledEntity.PositionComp;
                float    localY       = positionComp.LocalAABB.Max.Y - positionComp.LocalAABB.Min.Y;
                Vector3D lastTarget   = m_target;
                var      headMatrix   = remotelyControlledEntity == null?genericControlledEntity.GetHeadMatrix(true) : remotelyControlledEntity.Pilot.GetHeadMatrix(true);

                m_target            = controlledEntity is MyCharacter ? ((positionComp.GetPosition() + (localY + m_lookAtOffsetY) * positionComp.WorldMatrix.Up)) : headMatrix.Translation;
                m_targetOrientation = headMatrix.GetOrientation();
                m_targetUpVec       = m_positionCurrentIsSafe ? (Vector3D)m_targetOrientation.Up : positionComp.WorldMatrix.Up;
                m_transformedLookAt = Vector3D.Transform(m_clampedlookAt, m_targetOrientation);
                m_desiredPosition   = m_target + m_transformedLookAt;

                m_position += m_target - lastTarget; // compensate character movement

                //m_position = m_desiredPosition;
            }
            else
            {
                var headMatrix = genericControlledEntity.GetHeadMatrix(true);
                m_target            = headMatrix.Translation;
                m_targetOrientation = headMatrix.GetOrientation();
                m_targetUpVec       = m_targetOrientation.Up;

                m_transformedLookAt = Vector3D.Transform(m_clampedlookAt, m_targetOrientation);
                m_desiredPosition   = m_target + m_transformedLookAt;
                m_position          = m_desiredPosition;
            }

            Vector3D stretch = m_position - m_desiredPosition;

            Vector3D force = -m_currentSpring.Stiffness * stretch - m_currentSpring.Dampening * m_velocity;

            force.AssertIsValid();

            // Apply acceleration
            Vector3 acceleration = (Vector3)force / m_currentSpring.Mass;

            m_velocity += acceleration * MyEngineConstants.UPDATE_STEP_SIZE_IN_SECONDS;
            m_velocity.AssertIsValid();

            // Apply velocity
            m_position += m_velocity * MyEngineConstants.UPDATE_STEP_SIZE_IN_SECONDS;
            m_position.AssertIsValid();

            if (m_disableSpringThisFrame)
            {
                double smoothCoeff = 0.8;
                m_position = Vector3D.Lerp(m_position, m_desiredPosition, smoothCoeff);
                m_disableSpringThisFrame = false;
            }

            // Limit backward distance from target
            double backward = Vector3D.Dot(m_targetOrientation.Backward, (m_target - m_position));

            if (backward > -m_backwardCutoff)
            {
                m_position += (Vector3D)m_targetOrientation.Backward * (backward + m_backwardCutoff);
            }

            // -------- raycast, prevent camera being inside things ------
            if (controlledEntity != null)
            {
                if (!controlledEntity.Closed)
                {
                    HandleIntersection(controlledEntity);
                }
                else
                {
                    m_positionCurrentIsSafe = false;
                }
            }

            // ------- save current settings -------
            if (m_saveSettings)
            {
                MySession.Static.SaveControlledEntityCameraSettings(false);
                m_saveSettings = false;
            }
        }