Exemple #1
0
        // tests SceneObjects and Bounds
        public bool FindSceneRayIntersection(Ray ray, out AnyRayHit hit, Func <SceneObject, bool> sofilter = null)
        {
            hit = null;

            SORayHit         bestSOHit     = null;
            GameObjectRayHit bestBoundsHit = null;

            bool bHitSO     = FindSORayIntersection(ray, out bestSOHit, sofilter);
            bool bHitBounds = FindWorldBoundsHit(ray, out bestBoundsHit);

            if (bHitSO && bHitBounds)
            {
                if (bestSOHit.fHitDist < bestBoundsHit.fHitDist)
                {
                    hit = new AnyRayHit(bestSOHit);
                }
                else
                {
                    hit = new AnyRayHit(bestBoundsHit, HitType.BoundsObjectHit);
                }
            }
            else if (bHitSO)
            {
                hit = new AnyRayHit(bestSOHit);
            }
            else if (bHitBounds)
            {
                hit = new AnyRayHit(bestBoundsHit, HitType.BoundsObjectHit);
            }
            return(hit != null);
        }
Exemple #2
0
        // rayhit-test a GameObject, handling collider enable/disable
        public static bool FindGORayIntersection(Ray ray, GameObject go, out GameObjectRayHit hit)
        {
            hit = null;
            Collider collider = go.GetComponent <Collider>();

            if (collider == null)
            {
                return(false);
            }

            bool bIsEnabled = collider.enabled;

            collider.enabled = true;
            RaycastHit hitInfo;

            if (collider.Raycast(ray, out hitInfo, Mathf.Infinity))
            {
                hit           = new GameObjectRayHit();
                hit.fHitDist  = hitInfo.distance;
                hit.hitPos    = hitInfo.point;
                hit.hitNormal = hitInfo.normal;
                hit.hitGO     = go;
            }
            collider.enabled = bIsEnabled;

            return(hit != null);
        }
        override public bool FindRayIntersection(Ray3f ray, out SORayHit hit)
        {
            hit = null;
            GameObjectRayHit hitg = null;

            if (FindGORayIntersection(ray, out hitg))
            {
                if (hitg.hitGO != null)
                {
                    hit = new SORayHit(hitg, this);

                    // compute analytic normal on cylinder body
                    if (hitg.hitGO == body)
                    {
                        Vector3 up = this.RootGameObject.transform.rotation * Vector3.up;
                        Vector3 l  = hitg.hitPos - this.RootGameObject.transform.position;
                        l -= Vector3.Dot(l, up) * up;
                        l.Normalize();
                        hit.hitNormal = l;
                    }

                    return(true);
                }
            }
            return(false);
        }
Exemple #4
0
        // override and return true to get hover events
        public virtual bool FindHoverRayIntersection(Ray3f ray, out UIRayHit hit)
        {
            hit = null;
            if (Enabled == false)
            {
                return(false);
            }

            if (EnableHover == false)
            {
                return(false);
            }

            GameObjectRayHit hitg = null;

            if (FindGORayIntersection(ray, out hitg))
            {
                if (hitg.hitGO != null)
                {
                    hit = new UIRayHit(hitg, this);
                    return(true);
                }
            }
            return(false);
        }
Exemple #5
0
 public AnyRayHit(GameObjectRayHit init, SceneUIElement ui)
 {
     hitPos   = init.hitPos;
     fHitDist = init.fHitDist;
     hitGO    = init.hitGO;
     eType    = HitType.SceneUIElementHit;
     hitUI    = ui;
 }
Exemple #6
0
 public UIRayHit(GameObjectRayHit init, SceneUIElement ui)
 {
     hitPos    = init.hitPos;
     hitNormal = init.hitNormal;
     fHitDist  = init.fHitDist;
     hitGO     = init.hitGO;
     hitUI     = ui;
 }
Exemple #7
0
 public SORayHit(GameObjectRayHit init, SceneObject so)
 {
     hitPos    = init.hitPos;
     hitNormal = init.hitNormal;
     fHitDist  = init.fHitDist;
     hitGO     = init.hitGO;
     hitSO     = so;
 }
Exemple #8
0
 public AnyRayHit(GameObjectRayHit init, SceneObject so)
 {
     hitPos   = init.hitPos;
     fHitDist = init.fHitDist;
     hitGO    = init.hitGO;
     eType    = HitType.SceneObjectHit;
     hitSO    = so;
 }
Exemple #9
0
 public AnyRayHit(GameObjectRayHit init, HitType eType)
 {
     Debug.Assert(eType == HitType.BoundsObjectHit);
     hitPos     = init.hitPos;
     hitNormal  = init.hitNormal;
     fHitDist   = init.fHitDist;
     hitGO      = init.hitGO;
     this.eType = eType;
 }
Exemple #10
0
 public AnyRayHit(GameObjectRayHit init, HitType eType)
 {
     Util.gDevAssert(eType == HitType.BoundsObjectHit);
     hitPos     = init.hitPos;
     hitNormal  = init.hitNormal;
     hitIndex   = init.hitIndex;
     fHitDist   = init.fHitDist;
     hitGO      = init.hitGO;
     this.eType = eType;
 }
        virtual public bool FindRayIntersection(Ray3f ray, out UIRayHit hit)
        {
            hit = null;
            GameObjectRayHit hitg = null;

            if (is_interactive && FindGORayIntersection(ray, out hitg))
            {
                if (hitg.hitGO != null)
                {
                    hit = new UIRayHit(hitg, this);
                    return(true);
                }
            }
            return(false);
        }
Exemple #12
0
        public bool FindRayIntersection(Ray ray, out SORayHit hit)
        {
            hit = null;
            GameObjectRayHit hitg = null;

            if (FindGORayIntersection(ray, out hitg))
            {
                if (hitg.hitGO != null)
                {
                    hit = new SORayHit(hitg, this);
                    return(true);
                }
            }
            return(false);
        }
        public virtual bool FindRayIntersection(UnityEngine.Ray ray, out UIRayHit hit)
        {
            hit = null;
            GameObjectRayHit hitg = null;

            if (FindGORayIntersection(ray, out hitg))
            {
                if (hitg.hitGO != null)
                {
                    hit = new UIRayHit(hitg, this);
                    return(true);
                }
            }
            return(false);
        }
Exemple #14
0
 public bool FindWorldBoundsHit(Ray3f ray, out GameObjectRayHit hit)
 {
     hit = null;
     foreach (var go in this.vBoundsObjects)
     {
         GameObjectRayHit myHit = null;
         if (UnityUtil.FindGORayIntersection(ray, go, out myHit))
         {
             if (hit == null || myHit.fHitDist < hit.fHitDist)
             {
                 hit = myHit;
             }
         }
     }
     return(hit != null);
 }
 protected void standard_begin_capture(InputEvent e, GameObjectRayHit hit)
 {
     if (handleGO.IsSameOrChild(hit.hitGO))
     {
         onHandlePress(e, hit.hitPos);
         eInterMode    = InteractionMode.InHandleDrag;
         vStartHitW    = hit.hitPos;
         vHandleStartW = handleGO.GetWorldFrame();
     }
     else if (backgroundGO.IsSameOrChild(hit.hitGO))
     {
         onSliderbarPress(e, hit.hitPos);
         eInterMode    = InteractionMode.InPressDrag;
         vStartHitW    = hit.hitPos;
         vHandleStartW = handleGO.GetWorldFrame();
     }
 }
Exemple #16
0
        override public bool FindRayIntersection(Ray3f ray, out SORayHit hit)
        {
            hit = null;
            GameObjectRayHit hitg = null;

            if (FindGORayIntersection(ray, out hitg))
            {
                if (hitg.hitGO != null)
                {
                    hit = new SORayHit(hitg, this);

                    // compute analytic normal
                    hit.hitNormal = (hit.hitPos - this.RootGameObject.GetPosition()).Normalized;

                    return(true);
                }
            }
            return(false);
        }
        public virtual bool FindGORayIntersection(Ray ray, out GameObjectRayHit hit, Func <GameObject, bool> FilterF = null)
        {
            hit = new GameObjectRayHit();
            RaycastHit hitInfo;

            // [RMS] this keeps popping up...why??
            //if (Mathf.Abs(ray.direction.sqrMagnitude - 1.0f) > 0.001) {
            //    DebugUtil.Log(2, "FindGORayIntersection: ray direction is not normalized! {0} {1}", ray.direction, ray.direction.magnitude);
            //    return false;
            //}

            foreach (var go in vObjects)
            {
                if (FilterF != null && FilterF(go) == false)
                {
                    continue;
                }
                if (go.IsVisible() == false)
                {
                    continue;
                }

                Collider collider = go.GetComponent <Collider>();
                if (collider)
                {
                    go.EnableCollider();
                    if (collider.Raycast(ray, out hitInfo, Mathf.Infinity))
                    {
                        if (hitInfo.distance < hit.fHitDist)
                        {
                            hit.fHitDist  = hitInfo.distance;
                            hit.hitPos    = hitInfo.point;
                            hit.hitNormal = hitInfo.normal;
                            hit.hitGO     = go;
                        }
                    }
                    go.DisableCollider();
                }
            }

            return(hit.hitGO != null);
        }
Exemple #18
0
        // rayhit-test a GameObject, handling collider enable/disable
        public static bool FindGORayIntersection(Ray ray, GameObject go, out GameObjectRayHit hit)
        {
            hit = null;

            bool bIsEnabled = go.GetComponent <MeshCollider> ().enabled;

            go.GetComponent <MeshCollider> ().enabled = true;
            RaycastHit hitInfo;

            if (go.GetComponent <MeshCollider> ().Raycast(ray, out hitInfo, Mathf.Infinity))
            {
                hit          = new GameObjectRayHit();
                hit.fHitDist = hitInfo.distance;
                hit.hitPos   = hitInfo.point;
                hit.hitGO    = go;
            }
            go.GetComponent <MeshCollider> ().enabled = bIsEnabled;

            return(hit != null);
        }
        public virtual bool FindGORayIntersection(Ray ray, out GameObjectRayHit hit)
        {
            hit = new GameObjectRayHit();
            RaycastHit hitInfo;

            foreach (var go in vObjects)
            {
                go.GetComponent <MeshCollider> ().enabled = true;
                if (go.GetComponent <MeshCollider> ().Raycast(ray, out hitInfo, Mathf.Infinity))
                {
                    if (hitInfo.distance < hit.fHitDist)
                    {
                        hit.fHitDist = hitInfo.distance;
                        hit.hitPos   = hitInfo.point;
                        hit.hitGO    = go;
                    }
                }
                go.GetComponent <MeshCollider> ().enabled = false;
            }

            return(hit.hitGO != null);
        }
Exemple #20
0
        // FixedUpdate is called before any Update
        public void Update()
        {
            // if we are in capture we freeze the cursor plane
            if (Scene.InCapture == false)
            {
                Vector3 camPos  = camera.gameObject.transform.position;
                Vector3 forward = camera.gameObject.transform.forward;

                // orient Y-up plane so that it is in front of eye, perp to camera direction
                xformObject.transform.position = camPos + 10 * forward;
                xformObject.transform.LookAt(camera.gameObject.transform);
                xformObject.transform.RotateAround(xformObject.transform.position, xformObject.transform.right, 90);

                // that plane is the plane the mouse cursor moves on
                this.vCursorPlaneOrigin  = xformObject.transform.position;
                this.vCursorPlaneRight   = xformObject.transform.right;
                this.vCursorPlaneForward = xformObject.transform.forward;
                this.vRaySourcePosition  = camera.transform.position;
            }

            Vector3 curPos = new Vector3(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y"), 0);

            dx -= 0.3f * curPos.x;
            dy -= 0.3f * curPos.y;

            vPlaneCursorPos =
                vCursorPlaneOrigin + dx * vCursorPlaneRight + dy * vCursorPlaneForward;
            vSceneCursorPos = vPlaneCursorPos;


            bool bHit = false;

            if (Scene != null)
            {
                Ray       r   = new Ray(camera.transform.position, (vPlaneCursorPos - camera.transform.position).normalized);
                AnyRayHit hit = null;
                if (Scene.FindAnyRayIntersection(r, out hit))
                {
                    vSceneCursorPos = hit.hitPos;
                    bHit            = true;
                }
                else
                {
                    GameObjectRayHit ghit = null;
                    if (Scene.GetScene().FindWorldBoundsHit(r, out ghit))
                    {
                        vSceneCursorPos = ghit.hitPos;
                    }
                }
            }

            this.CurrentCursorPosWorld       = vPlaneCursorPos;
            this.CurrentCursorRaySourceWorld = this.vRaySourcePosition;

            Cursor.transform.position = vSceneCursorPos;
            if (Scene.InCapture)
            {
                Cursor.GetComponent <MeshRenderer> ().material = CursorCapturingMaterial;
            }
            else if (bHit)
            {
                Cursor.GetComponent <MeshRenderer> ().material = CursorHitMaterial;
            }
            else
            {
                Cursor.GetComponent <MeshRenderer> ().material = CursorDefaultMaterial;
            }
            Cursor.layer = (bHit || Scene.InCapture) ? LayerMask.NameToLayer(SceneGraphConfig.WidgetOverlayLayerName) : 0;

            // maintain a consistent visual size for 3D cursor sphere
            float fScaling = MathUtil.GetVRRadiusForVisualAngle(vSceneCursorPos, camera.transform.position, CursorVisualAngleInDegrees);

            Cursor.transform.localScale = new Vector3(fScaling, fScaling, fScaling);
        }
        // FixedUpdate is called before any Update
        public void Update()
        {
            if (bFreezeCursor)
            {
                return;
            }

            // if we are in capture we freeze the cursor plane
            if (context.InCaptureMouse == false)
            {
                Vector3 camPos  = camera.gameObject.transform.position;
                Vector3 forward = camera.gameObject.transform.forward;

                // orient Y-up plane so that it is in front of eye, perp to camera direction
                float fCursorDepth = 10.0f;
                fCursorSpeedNormalization = 1.0f;
                if (context.ActiveCockpit != null && context.ActiveCockpit.DefaultCursorDepth > 0)
                {
                    fCursorDepth = context.ActiveCockpit.DefaultCursorDepth;
                    // cursor speed will change depending on cursor plane distance, unless we normalize
                    fCursorSpeedNormalization *= (fCursorDepth / 10.0f);
                }
                xformObject.transform.position = camPos + fCursorDepth * forward;
                xformObject.transform.LookAt(camera.gameObject.transform);
                xformObject.transform.RotateAround(xformObject.transform.position, xformObject.transform.right, 90);

                // that plane is the plane the mouse cursor moves on
                this.vCursorPlaneOrigin = xformObject.transform.position;
                this.vCursorPlaneRight  = xformObject.transform.right;
                this.vCursorPlaneUp     = xformObject.transform.forward;                    // because we rotated? weird...
                this.vRaySourcePosition = camera.transform.position;

                // if we were capturing, then plane was frozen and when we stop capturing, if
                //  head moved, the cursor will pop to a new position (because it is stored in
                //  local plane coords). So raycast through old cursor to hit new plane and figure
                //  out new local coords (fCurPlaneX, fCurPlaneY)
                if (bWasInCaptureFreeze)
                {
                    Frame3f newF      = new Frame3f(vCursorPlaneOrigin, this.camera.transform.forward);
                    Vector3 vPlaneHit = newF.RayPlaneIntersection(this.vRaySourcePosition,
                                                                  (vPlaneCursorPos - vRaySourcePosition).normalized, 2);
                    fCurPlaneX          = Vector3.Dot((vPlaneHit - vCursorPlaneOrigin), vCursorPlaneRight);
                    fCurPlaneY          = Vector3.Dot((vPlaneHit - vCursorPlaneOrigin), vCursorPlaneUp);
                    bWasInCaptureFreeze = false;
                }
            }
            else
            {
                bWasInCaptureFreeze = true;
            }

            Vector2f mousePos  = InputExtension.Get.Mouse.PositionDelta;
            Vector2f leftStick = InputExtension.Get.GamepadLeftStick.Position;
            float    fX        = mousePos.x + leftStick.x;
            float    fY        = mousePos.y + leftStick.y;

            // auto-hide cursor if it doesn't move for a while
            if (fX == 0 && fY == 0 && SceneGraphConfig.MouseCursorHideTimeout > 0)
            {
                if ((FPlatform.RealTime() - lastMouseEventTime) > SceneGraphConfig.MouseCursorHideTimeout)
                {
                    Cursor.SetVisible(false);
                    mouseInactiveState = true;
                }
                if (mouseInactiveState)
                {
                    return;
                }
            }
            else
            {
                lastMouseEventTime = FPlatform.RealTime();
                if (mouseInactiveState)
                {
                    Cursor.SetVisible(true);
                }
                mouseInactiveState = false;
            }

            // update cursor location
            fCurPlaneX     -= 0.3f * fX * fCursorSpeedNormalization;
            fCurPlaneY     -= 0.3f * fY * fCursorSpeedNormalization;
            vPlaneCursorPos =
                vCursorPlaneOrigin + fCurPlaneX * vCursorPlaneRight + fCurPlaneY * vCursorPlaneUp;
            vSceneCursorPos = vPlaneCursorPos;

            // if cursor gets outside of viewpoint it is almost impossible to get it back.
            // So, if it goes too far out of view (45 deg here), we snap it back to the origin
            if (context.InCameraManipulation == false && context.InCaptureMouse == false)
            {
                float fAngle = Vector3.Angle((vPlaneCursorPos - camera.transform.position).normalized, camera.transform.forward);
                if (fAngle > 50.0f)
                {
                    fCurPlaneX      = fCurPlaneY = 0;
                    vPlaneCursorPos =
                        vCursorPlaneOrigin + fCurPlaneX * vCursorPlaneRight + fCurPlaneY * vCursorPlaneUp;
                    vSceneCursorPos = vPlaneCursorPos;
                }
            }


            bool bHit = false;

            // [RMS] boundsHit cursor orientation could be useful for things where you are picking a point
            //   on the ground plane (eg like drawing contours). Not sure how to toggle that though.
            //   Just disabling for now...
            //bool bIsBoundsHit = false;
            if (context != null)
            {
                Ray       r   = new Ray(camera.transform.position, (vPlaneCursorPos - camera.transform.position).normalized);
                AnyRayHit hit = null;
                if (context.FindAnyRayIntersection(r, out hit))
                {
                    vSceneCursorPos = hit.hitPos;
                    bHit            = true;
                }
                else
                {
                    GameObjectRayHit ghit = null;
                    if (context.GetScene().FindWorldBoundsHit(r, out ghit))
                    {
                        vSceneCursorPos = ghit.hitPos;
                        //bIsBoundsHit = true;
                    }
                }
            }

            this.CurrentCursorPosWorld       = vPlaneCursorPos;
            this.CurrentCursorRaySourceWorld = this.vRaySourcePosition;

            Vector3 vEyeToPos = (vPlaneCursorPos - camera.transform.position).normalized;
            //if (bIsBoundsHit) {
            //    Vector3 rotAxis = (vEyeToPos + camera.transform.right).normalized;
            //    Cursor.transform.localRotation = Quaternion.AngleAxis(180.0f-45.0f, rotAxis);
            //} else {
            Quaternion rotAlignUp = Quaternion.FromToRotation(Vector3.up, camera.transform.up);
            Vector3    rotAxis    = (vEyeToPos + camera.transform.right).normalized;

            Cursor.transform.localRotation = Quaternion.AngleAxis(45.0f, rotAxis) * rotAlignUp;
            //}

            Cursor.transform.position = vSceneCursorPos;
            if (context.InCaptureMouse)
            {
                Cursor.GetComponent <MeshRenderer> ().material = CursorCapturingMaterial;
            }
            else if (bHit)
            {
                Cursor.GetComponent <MeshRenderer> ().material = CursorHitMaterial;
            }
            else
            {
                Cursor.GetComponent <MeshRenderer> ().material = CursorDefaultMaterial;
            }

            Cursor.SetLayer(FPlatform.CursorLayer);

            // maintain a consistent visual size for 3D cursor sphere
            float fScaling = VRUtil.GetVRRadiusForVisualAngle(vSceneCursorPos, camera.transform.position, CursorVisualAngleInDegrees);

            Cursor.transform.localScale = new Vector3(fScaling, fScaling, fScaling);

            // update cursor
            Mesh useMesh = context.ToolManager.HasActiveTool(ToolSide.Right) ? activeToolCursorMesh : standardCursorMesh;

            if (Cursor.GetSharedMesh() != useMesh)
            {
                Cursor.SetSharedMesh(useMesh);
            }
        }
Exemple #22
0
        // FixedUpdate is called before any Update
        public void Update()
        {
            if (CheckForSpatialInputActive() == false)
            {
                return;
            }

            Vector3 rootPos = spatialCamRig.transform.position;

            SpatialDevice[] hands = { Left, Right };
            for (int i = 0; i < 2; ++i)
            {
                SpatialDevice h = hands[i];

                h.CursorActive = VRPlatform.IsSpatialDeviceTracked(i);
                if (h.CursorActive)
                {
                    h.Hand.Show();
                    h.Cursor.Show();

                    Vector3    handPos = VRPlatform.GetLocalControllerPosition(i);
                    Quaternion handRot = VRPlatform.GetLocalControllerRotation(i);

                    h.AbsoluteHandFrame = new Frame3f(rootPos + handPos, handRot);

                    float fPositionT = 0.2f;
                    float fRotationT = 0.2f;
                    //float fPositionT = 1.0f;
                    //float fRotationT = 1.0f;

                    if (h.SmoothedHandFrame.Origin != Vector3f.Zero)
                    {
                        Vector3 new_origin =
                            Vector3.Lerp(h.SmoothedHandFrame.Origin, h.AbsoluteHandFrame.Origin, fPositionT);
                        Quaternion new_rotation =
                            Quaternion.Slerp(h.SmoothedHandFrame.Rotation, h.AbsoluteHandFrame.Rotation, fRotationT);
                        h.SmoothedHandFrame = new Frame3f(new_origin, new_rotation);
                    }
                    else
                    {
                        h.SmoothedHandFrame = h.AbsoluteHandFrame;
                    }

                    h.Hand.transform.position = h.SmoothedHandFrame.Origin;
                    h.Hand.transform.rotation = h.SmoothedHandFrame.Rotation * (Quaternionf)handGeomRotation;

                    h.CursorRay = new Ray(h.SmoothedHandFrame.Origin,
                                          (h.SmoothedHandFrame.Rotation * Vector3.forward).Normalized);

                    if (Mathf.Abs(h.CursorRay.direction.sqrMagnitude - 1.0f) > 0.001f)
                    {
                        DebugUtil.Log(2, "SpatialInputController.Update - invlaid cursor ray! rotation was {0}", h.SmoothedHandFrame.Rotation);
                        h.CursorRay = new Ray(h.SmoothedHandFrame.Origin, Vector3.up);
                    }

                    // raycast into scene to see if we hit object, UI, bounds, etc.
                    bool bHit = false;
                    if (context != null)
                    {
                        // want to hit-test active gizmo first, because that has hit-priority
                        if (context.TransformManager.HaveActiveGizmo)
                        {
                            UIRayHit uiHit = null;
                            if (context.TransformManager.ActiveGizmo.FindRayIntersection(h.CursorRay, out uiHit))
                            {
                                h.RayHitPos = uiHit.hitPos;
                                bHit        = true;
                            }
                        }
                        // next we tested scene
                        if (bHit == false)
                        {
                            AnyRayHit hit = null;
                            if (context.FindAnyRayIntersection(h.CursorRay, out hit))
                            {
                                h.RayHitPos = hit.hitPos;
                                bHit        = true;
                            }
                        }
                        // finally test worldbounds
                        if (bHit == false)
                        {
                            GameObjectRayHit ghit = null;
                            if (context.GetScene().FindWorldBoundsHit(h.CursorRay, out ghit))
                            {
                                h.RayHitPos = ghit.hitPos;
                            }
                        }
                    }

                    // if not, plane cursor on view-perp plane centered at last hit pos,
                    // otherwise it will be stuck/disappear
                    if (bHit == false)
                    {
                        Frame3f f = new Frame3f(h.RayHitPos, camera.transform.forward);
                        h.RayHitPos = f.RayPlaneIntersection(h.CursorRay.origin, h.CursorRay.direction, 2);
                    }

                    h.Cursor.transform.position = h.RayHitPos;
                    //if (scene.InCapture)
                    //    MaterialUtil.SetMaterial(h.Cursor, h.CursorCapturingMaterial);
                    //else
                    if (bHit)
                    {
                        MaterialUtil.SetMaterial(h.Cursor, h.CursorHitMaterial);
                    }
                    else
                    {
                        MaterialUtil.SetMaterial(h.Cursor, h.CursorDefaultMaterial);
                    }

                    // maintain a consistent visual size for 3D cursor sphere
                    float fScaling = VRUtil.GetVRRadiusForVisualAngle(h.RayHitPos, camera.transform.position, CursorVisualAngleInDegrees);
                    h.Cursor.transform.localScale = fScaling * Vector3.one;

                    // orient cursor so it is tilted like a 2D cursor, but per-hand
                    Vector3 cursor_right = Vector3.Cross(camera.transform.up, h.CursorRay.direction);
                    Vector3 cursor_fw    = Vector3.Cross(cursor_right, camera.transform.up);
                    float   rotSign      = (h == Right) ? 1.0f : -1.0f;
                    Vector3 pointDir     = (camera.transform.up + cursor_fw - 0.5f * rotSign * cursor_right).normalized;
                    h.Cursor.transform.localRotation = Quaternion.FromToRotation(Vector3.up, pointDir);

                    // update laser line
                    if (h.Laser != null)
                    {
                        float   hDist = (h.RayHitPos - h.CursorRay.origin).magnitude;
                        Vector3 p0    = h.RayHitPos - 0.9f * hDist * h.CursorRay.direction;
                        Vector3 p1    = h.RayHitPos + 100.0f * h.CursorRay.direction;
                        float   r0    = VRUtil.GetVRRadiusForVisualAngle(p0, camera.transform.position, 0.5f);
                        h.LaserRen.SetPosition(0, p0);
                        h.LaserRen.SetPosition(1, p1);
                        h.LaserRen.startWidth = h.LaserRen.endWidth = r0;
                    }

                    // udpate cursor
                    Mesh useMesh = context.ToolManager.HasActiveTool(i) ? activeToolCursorMesh : standardCursorMesh;
                    if (h.Cursor.GetSharedMesh() != useMesh)
                    {
                        h.Cursor.SetSharedMesh(useMesh);
                    }
                }
                else
                {
                    h.Hand.Hide();
                    h.Cursor.Hide();
                }
            }
        }
 // subclass can implement these to add custom behaviors
 virtual public bool custom_begin_capture(InputEvent e, GameObjectRayHit hit)
 {
     return(false);
 }