// returns frame at ray-intersection point, with normal pointing *outwards* public static Frame3f GetSphereFrame(float fHUDRadius, float fHorzAngleDeg, float fVertAngleDeg) { Ray r = VRUtil.MakeRayFromSphereCenter(fHorzAngleDeg, fVertAngleDeg); float fRayT = 0.0f; RayIntersection.Sphere(r.origin, r.direction, Vector3.zero, fHUDRadius, out fRayT); Vector3 v = r.origin + fRayT * r.direction; return(new Frame3f(v, v.normalized)); }
// returns frame at ray-intersection point, with normal pointing *outwards* public static Frame3f GetCylinderFrameFromAngles(float fHUDRadius, float fHorzAngleDeg, float fVertAngleDeg) { Ray3f r = VRUtil.MakeRayFromSphereCenter(fHorzAngleDeg, fVertAngleDeg); float fRayT = 0.0f; RayIntersection.InfiniteCylinder(r.Origin, r.Direction, Vector3f.Zero, Vector3f.AxisY, fHUDRadius, out fRayT); Vector3f v = r.Origin + fRayT * r.Direction; Vector3f n = new Vector3f(v[0], 0, v[2]).Normalized; return(new Frame3f(v, n)); }
virtual public Frame3f From2DCoords(Vector2f pos, float fNormalOffset) { // todo simplify this, use radians only float circ = 2 * MathUtil.PIf * Radius; float fAngleX = (pos.x / circ) * 360.0f; float fAngleY = (pos.y / circ) * 360.0f; Vector3f normal = VRUtil.DirectionFromSphereCenter(fAngleX, fAngleY); Vector3f pos3 = (Radius + fNormalOffset) * normal + Origin; return(new Frame3f(pos3, normal)); }
public override void PreRender() { if (MaintainConsistentViewSize) { float fScaling = VRUtil.GetVRRadiusForVisualAngle( pivotGO.GetPosition(), parentScene.ActiveCamera.GetPosition(), SceneGraphConfig.DefaultPivotVisualDegrees); fScaling /= parentScene.GetSceneScale(); pivotGO.SetLocalScale(new Vector3f(fScaling, fScaling, fScaling)); } }
public static Frame3f GetCylinderFrameFromAngleHeight(float fHUDRadius, float fHorzAngleDeg, float fVertHeight) { Ray r = VRUtil.MakeRayFromSphereCenter(fHorzAngleDeg, 0); r.direction = fHUDRadius * r.direction + fVertHeight * Vector3.up; r.direction.Normalize(); float fRayT = 0.0f; RayIntersection.InfiniteCylinder(r.origin, r.direction, Vector3f.Zero, Vector3f.AxisY, fHUDRadius, out fRayT); Vector3 v = r.origin + fRayT * r.direction; Vector3 n = new Vector3(v[0], 0, v[2]).normalized; return(new Frame3f(v, n)); }
// called on per-frame Update() override public void PreRender() { gizmo.Show(); foreach (var v in Widgets) { float fScaling = VRUtil.GetVRRadiusForVisualAngle( v.Key.GetPosition(), parentScene.ActiveCamera.GetPosition(), SceneGraphConfig.DefaultPivotVisualDegrees); fScaling /= parentScene.GetSceneScale(); v.Key.SetLocalScale(fScaling * WidgetScale); } }
// called on per-frame Update() virtual public void PreRender() { gizmo.Show(); float fScaling = VRUtil.GetVRRadiusForVisualAngle( gizmo.transform.position, parentScene.ActiveCamera.GetPosition(), SceneGraphConfig.DefaultAxisGizmoVisualDegrees); fScaling /= parentScene.GetSceneScale(); float fGeomDim = gizmoGeomBounds.size.magnitude; fScaling /= fGeomDim; gizmo.transform.localScale = new Vector3(fScaling, fScaling, fScaling); }
public void UpdateSetWorldScale(Ray3f left, Ray3f right) { Vector3f hit1 = hitFrame.RayPlaneIntersection(left.Origin, left.Direction, 2); Vector3f hit2 = hitFrame.RayPlaneIntersection(right.Origin, right.Direction, 2); Vector3f avg = (hit1 + hit2) * 0.5f; float r0 = (hit1 - (Vector3f)go.transform.position).Length; float r1 = (hit2 - (Vector3f)go.transform.position).Length; float r = (r0 + r1) * 0.5f; float min_r = VRUtil.GetVRRadiusForVisualAngle(avg, camState.camPosition, 2.0f); r = (float)Math.Max(r, min_r); go.transform.localScale = r * Vector3f.One; go.GetComponent <Renderer>().material = (r > deadzone_r) ? mYes : mNo; }
public void PreRender(Vector3f cameraPosW) { float fSceneScale = parentSO.GetScene().GetSceneScale(); foreach (var i in vObjects) { Vector3f vPos = i.go.transform.position; float fR = VRUtil.GetVRRadiusForVisualAngle(vPos, cameraPosW, i.fVisualRadiusDeg); // have to compensate for scaling of parent SO float fParentSOScale = parentSO.GetLocalScale()[0]; fR = fR / fParentSOScale / fSceneScale; i.go.transform.localScale = fR * Vector3f.One; } }
void update_geometry(FScene s) { if (bUpdatePending == false && curve_timestamp == curve.Timestamp) { return; } if (curve.VertexCount < 2) { return; } update_vertices(s); verticesf.Clear(); buffer.Clear(); for (int i = 0; i < curve.VertexCount; ++i) { buffer.Add(curve[i % curve.VertexCount]); } if (CurveProcessorF != null) { CurveProcessorF(buffer); } int Nmod = buffer.Count; int N = (curve.Closed) ? buffer.Count + 1 : buffer.Count; for (int i = 0; i < N; ++i) { verticesf.Add((Vector3f)buffer[i % Nmod]); } curveObject.SetVertices(verticesf); float fWidth = VRUtil.EstimateStableCurveWidth(s, Frame3f.Identity, curve, SceneGraphConfig.DefaultSceneCurveVisualDegrees); if (fWidth > 0) { curveObject.SetLineWidth(fWidth); } bUpdatePending = false; curve_timestamp = curve.Timestamp; }
// called on per-frame Update() virtual public void PreRender() { root.Show(); if (MaintainConsistentViewSize) { float useDegrees = (GizmoVisualDegrees > 0) ? GizmoVisualDegrees : SceneGraphConfig.DefaultAxisGizmoVisualDegrees; float fWorldSize = VRUtil.GetRadiusForVisualAngle( root.GetPosition(), parentScene.ActiveCamera.GetPosition(), useDegrees); float fSceneSize = fWorldSize / parentScene.GetSceneScale(); float fGeomScale = fSceneSize / initialGizmoRadius; root.SetLocalScale(new Vector3f(fGeomScale)); foreach (var widget in Widgets) { widget.Value.UpdateGizmoWorldSize(fWorldSize); } } else if (DynamicScaleFactor > 0) { float fWorldSize = DynamicScaleFactor; float fSceneSize = fWorldSize / parentScene.GetSceneScale(); float fGeomScale = fSceneSize / initialGizmoRadius; root.SetLocalScale(new Vector3f(fGeomScale)); foreach (var widget in Widgets) { widget.Value.UpdateGizmoWorldSize(fWorldSize); } } if (DynamicVisibilityFiltering && targetWrapper != null) { Frame3f frameW = targetWrapper.GetLocalFrame(CoordSpace.WorldCoords); Vector3d camPosW = parentScene.ActiveCamera.GetPosition(); foreach (var go in enabledWidgetGOs) { Standard3DTransformWidget widget = Widgets[go]; bool visible = widget.CheckVisibility(ref frameW, ref camPosW); go.SetVisible(visible); } } }
public override Capture BeginCapture(InputState input, CaptureSide eSide) { HandInfo hi = new HandInfo(); hi.eMode = ActionMode.TransformCamera; Frame3f camFrame = cockpit.ActiveCamera.GetWorldFrame(); // if both rays hit scene and are within a few visual degrees, then we // we pull out a sphere and use it to re-scale the world. // Otherwise do normal hand-transform actions AnyRayHit hit1, hit2; bool bHit1 = cockpit.Scene.FindSceneRayIntersection(input.vLeftSpatialWorldRay, out hit1); bool bHit2 = cockpit.Scene.FindSceneRayIntersection(input.vRightSpatialWorldRay, out hit2); if (bHit1 && bHit2) { Vector3 avg = (hit1.hitPos + hit2.hitPos) * 0.5f; float d = VRUtil.GetVRRadiusForVisualAngle(avg, camFrame.Origin, 2.0f); if ((hit1.hitPos - hit2.hitPos).Length < d) { hi.eMode = ActionMode.SetWorldScale; Frame3f centerF = cockpit.Scene.SceneFrame; centerF.Origin = avg; Frame3f planeF = new Frame3f(centerF.Origin, camFrame.Z); hi.BeginSetWorldScale(centerF, planeF, 2 * d); hi.UpdateSetWorldScale(input.vLeftSpatialWorldRay, input.vRightSpatialWorldRay); } } hi.leftStartF = input.LeftHandFrame; hi.rightStartF = input.RightHandFrame; hi.camState = cockpit.ActiveCamera.Manipulator().GetCurrentState(cockpit.Scene); hi.camRight = camFrame.X; hi.camForward = camFrame.Z; cockpit.ActiveCamera.SetTargetVisible(true); return(Capture.Begin(this, CaptureSide.Both, hi)); }
public void Update() { targetGO.transform.position = TargetPoint; float fScaling = VRUtil.GetVRRadiusForVisualAngle(TargetPoint, gameObject.transform.position, 1.0f); targetGO.transform.localScale = new Vector3(fScaling, fScaling, fScaling); if (ShowTarget) { Material setMaterial = hiddenMaterial; // raycast into scene and if we hit ball before we hit anything else, render // it darker red, to give some sense of inside/outside if (this.context != null) { Vector3 camPos = this.context.ActiveCamera.GetPosition(); float fDistSqr = (TargetPoint - camPos).sqrMagnitude; Ray ray_t = new Ray(camPos, (TargetPoint - camPos).normalized); AnyRayHit hit; if (this.context.Scene.FindSceneRayIntersection(ray_t, out hit) == false) { setMaterial = visibleMaterial; } else if (hit.fHitDist * hit.fHitDist * 1.005f > fDistSqr) { setMaterial = visibleMaterial; } } MaterialUtil.SetMaterial(targetGO, setMaterial); targetGO.Show(); } else { targetGO.Hide(); } }
public void Update() { targetGO.transform.position = TargetPoint; float fScaling = VRUtil.GetVRRadiusForVisualAngle(TargetPoint, gameObject.transform.position, SceneGraphConfig.CameraPivotVisualDegrees); targetGO.transform.localScale = fScaling * Vector3f.One; if (ShowTarget && SceneGraphConfig.EnableVisibleCameraPivot) { Material setMaterial = hiddenMaterial; // raycast into scene and if we hit ball before we hit anything else, render // it darker red, to give some sense of inside/outside if (this.context != null) { Vector3f camPos = this.context.ActiveCamera.GetPosition(); float fDistSqr = TargetPoint.DistanceSquared(camPos); Ray3f ray_t = new Ray3f(camPos, (TargetPoint - camPos).Normalized); AnyRayHit hit; if (this.context.Scene.FindSceneRayIntersection(ray_t, out hit) == false) { setMaterial = visibleMaterial; } else if (hit.fHitDist * hit.fHitDist * 1.005f > fDistSqr) { setMaterial = visibleMaterial; } } MaterialUtil.SetMaterial(targetGO, setMaterial); targetGO.Show(); } else { targetGO.Hide(); } }
// 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); } }
public void Initialize(Cockpit cockpit, float fCockpitRadius) { activeCockpit = cockpit; activeCockpit.PositionMode = Cockpit.MovementMode.Static; activeCockpit.GrabFocus = true; activeHUDRadius = fCockpitRadius; string[] files, folders; try { files = Source.GetFiles(FolderPath); folders = Source.GetFolders(FolderPath); } catch (Exception e) { Debug.Log("[CurrentFolderList.Initialize] exception! " + e.Message); return; } float fMinHorz = -45.0f, fMaxHorz = 45.0f; float fStartVert = 15.0f; float fTop = HUDUtil.GetSphereFrame(fCockpitRadius, 0.0f, fStartVert).Origin.y; int folderi = 0, filei = 0; Mesh folderMesh = Resources.Load <Mesh>("icon_meshes/folder_v1"); Color folderColor = ColorUtil.make(241, 213, 146); Color inaccessibleFolderColor = ColorUtil.make(100, 100, 100); Mesh fileMesh = Resources.Load <Mesh>("icon_meshes/file_v1"); Color fileColor = ColorUtil.make(250, 250, 250); // [TODO] something wrong here, icons are loading backwards...?? Quaternion meshRotation = Quaternion.AngleAxis(270.0f, Vector3.right) * Quaternion.AngleAxis(180.0f + 25.0f, Vector3.forward); float meshScale = IconSize * 0.9f; HUDCylinder hudSurf = new HUDCylinder() { Radius = fCockpitRadius, VerticalCoordIsAngle = false }; float fStepH = VRUtil.HorizontalStepAngle(fCockpitRadius, 0, IconSize + IconPadding); int nStepsHorz = (int)((fMaxHorz - fMinHorz) / fStepH); fMinHorz = -(nStepsHorz * fStepH * 0.5f); fMaxHorz = (nStepsHorz * fStepH * 0.5f); float fStepV = IconSize + IconPadding; IconCollection = new HUDCollection(); IconCollection.Create(); bool bDone = false; int yi = 0; while (!bDone) { float fCurV = fTop - yi * fStepV; yi++; for (int xi = 0; xi < nStepsHorz && bDone == false; ++xi) { float fCurH = fMinHorz + ((float)xi + 0.5f) * fStepH; string name = "x"; fMesh useMesh = null; Color useColor = Color.white; bool bAccessible = true; bool bIsFile = false; if (folderi < folders.Length) { name = folders[folderi++]; useMesh = new fMesh(UnityEngine.Object.Instantiate <Mesh>(folderMesh)); useColor = folderColor; if (Source.FilterInaccessibleFolders == false && FileSystemUtils.CanAccessFolder(Path.Combine(FolderPath, name)) == false) { bAccessible = false; useColor = inaccessibleFolderColor; } } else if (filei < files.Length) { name = files[filei++]; useMesh = new fMesh(UnityEngine.Object.Instantiate <Mesh>(fileMesh)); useColor = fileColor; bIsFile = true; } else { bDone = true; break; } //useColor.a = 0.999f; // [RMS] can use this to force into alpha pass string displayName = name; if (displayName.Length > 12) { displayName = name.Substring(0, 12) + "..."; } //float TextScale = 0.01f, ShiftX = -IconSize * 0.5f; float TextScale = 0.005f, ShiftX = -IconSize * 0.5f; HUDButton iconButton = HUDBuilder.CreateMeshClickButton( useMesh, useColor, meshScale, meshRotation, hudSurf, fCurH, fCurV, new TextLabelGenerator() { Text = displayName, Scale = TextScale, Translate = new Vector3(ShiftX, 0.0f, 0.0f) }); iconButton.Name = name; //cockpit.AddUIElement(iconButton, true); IconCollection.AddChild(iconButton); if (bIsFile) { iconButton.OnClicked += (o, e) => { if (this.OnFileClicked != null) { this.OnFileClicked(name); } }; } if (bAccessible) { iconButton.OnDoubleClicked += IconButton_DoubleClick; } } } cockpit.AddUIElement(IconCollection, true); fCurMaxScroll = Mathf.Max(0, (yi - 4) * fStepV); }
public void UpdateDraw_Spatial(Ray3f ray, Frame3f handFrame, int nStep) { // scene xform may have changed during steps (eg view rotation), so we // need to reconstruct our local frame Frame3f primCurW = scene.ToWorldFrame(primStartS); // step 1: find radius in plane if (nStep == 0) { Vector3f forwardDir = ray.Direction; Vector3f plane_hit = VRUtil.SafeRayPlaneIntersection(ray, forwardDir, primCurW.Origin, primCurW.Y); plane_hit_local = primCurW.ToFrameP(plane_hit); } float fX = MathUtil.SignedClamp(plane_hit_local[0], MinDimension, MaxDimension); float fY = MinDimension; float fZ = MathUtil.SignedClamp(plane_hit_local[2], MinDimension, MaxDimension); float fR_plane = MathUtil.Clamp(plane_hit_local.Length, MinDimension / 2, MaxDimension / 2); // step 2: find height from plane if (nStep == 1) { Vector3f plane_hit = primCurW.FromFrameP(plane_hit_local); Line3d l = new Line3d(plane_hit, primCurW.Y); Vector3f handTip = handFrame.Origin + SceneGraphConfig.HandTipOffset * handFrame.Z; float fHandDist = (float)l.DistanceSquared(handTip); if (fHandDist < fR_plane * 1.5f) { fY = (float)l.Project(handTip); } else { fY = (float)DistLine3Ray3.MinDistanceLineParam(ray, l); } } // figure out possible dimensions, clamp to ranges fY = MathUtil.SignedClamp(fY, MinDimension, MaxDimension); // update frame primitive.Frame = primCurW; // update dimensions bool bIsCorner = (primitive.Center == CenterModes.Corner); float fScale = 1.0f; // object is not in scene coordinates! if (primitive.Type == MeshPrimitivePreview.PrimType.Cylinder) { primitive.Width = (bIsCorner) ? fR_plane * fScale : 2 * fR_plane * fScale; primitive.Depth = primitive.Width; //primitive.Depth = Mathf.Sign(fZ) * primitive.Width; //primitive.Width = Mathf.Sign(fX) * primitive.Width; primitive.Height = fY * fScale; } else if (primitive.Type == MeshPrimitivePreview.PrimType.Box) { primitive.Width = (bIsCorner) ? fX : 2 * fX * fScale; primitive.Depth = (bIsCorner) ? fZ : 2 * fZ * fScale; primitive.Height = fY * fScale; } else if (primitive.Type == MeshPrimitivePreview.PrimType.Sphere) { primitive.Width = (bIsCorner) ? fR_plane * fScale : 2 * fR_plane * fScale; primitive.Depth = primitive.Height = primitive.Width; //primitive.Depth = Mathf.Sign(fZ) * primitive.Width; //primitive.Width = Mathf.Sign(fX) * primitive.Width; //primitive.Height = Mathf.Sign(fY) * primitive.Width; } else { throw new NotImplementedException("DrawPrimitivesTool.UpdateDraw_Ray - type not supported"); } }
public Capture Update_TransformCamera(InputState input, CaptureData data) { // need both controllers to have valid positions if (input.bLeftControllerActive == false || input.bRightControllerActive == false) { return(Capture.Continue); } float fSceneScale = cockpit.Scene.GetSceneScale(); HandInfo hi = (HandInfo)data.custom_data; // deadzones and scaling factors const float fZoomDeadzoneInM = 0.1f; const float fZoomScale = 25.0f; const float fPanDeadzoneInM = 0.05f; const float fPanScale = 25.0f; const float fAzimuthDeadzoneInDeg = 10.0f; const float fAltitudeDeadzoneInDeg = 15.0f; const float fTrackAlpha = 0.1f; // larger == faster tracking // zoom is indicated by moving hands together/apart. But we want to get rid of // influence from other gestures (like rotate below) so we project to camera-right axis first. Frame3f camFrame = cockpit.ActiveCamera.GetWorldFrame(); Vector3 right = camFrame.X; float fOrig = Vector3.Dot((hi.rightStartF.Origin - hi.leftStartF.Origin), hi.camRight); float fCur = Vector3.Dot((input.RightHandFrame.Origin - input.LeftHandFrame.Origin), right); float deltaZAbs = fCur - fOrig; float deltaZ = (hi.bInZoomDeadzone) ? ApplyDeadzone(deltaZAbs, fZoomDeadzoneInM) : deltaZAbs - hi.fZoomShift; if (Math.Abs(deltaZ) > 0 && hi.bInZoomDeadzone) { hi.bInZoomDeadzone = false; hi.fZoomShift = deltaZAbs - deltaZ; } hi.runningZ = Mathf.Lerp(hi.runningZ, deltaZ, fTrackAlpha); float tz = hi.runningZ * fZoomScale; if (tz != 0.0f && hi.activeXForm == TransformType.NoTransform) { hi.activeXForm = TransformType.Zoom; } // translation is done by moving both hands in unison. We find the midpoint // of the start and current pairs, delta is translate Vector3f cOrig = 0.5f * (hi.leftStartF.Origin + hi.rightStartF.Origin); Vector3f cCur = 0.5f * (input.LeftHandFrame.Origin + input.RightHandFrame.Origin); Vector3f translateO = cCur - cOrig; Vector3f translate = (hi.bInPanDeadzone) ? ApplyDeadzone(translateO, fPanDeadzoneInM) : translateO - hi.vPanShift; if (translate.LengthSquared > 0 && hi.bInPanDeadzone) { hi.bInPanDeadzone = false; hi.vPanShift = translateO - translate; } hi.runningT = Vector3.Lerp(hi.runningT, translate, fTrackAlpha); Vector3f tx = hi.runningT * fPanScale * fSceneScale; if (tx.Length != 0.0f && hi.activeXForm == TransformType.NoTransform) { hi.activeXForm = TransformType.Pan; } // azimuth (left/right rotate) is specified by making a spin-the-wheel gesture, // where one hand slides forward and the other back. We guess a center-of-rotation // as the midpoint of the start and current frames, and then average the two rotation // angles in the XZ plane (by the vectors on the left and right sides). // *But* if there is also a translation, this goes wonky, so we have to subtract // of the translation first! // (above only applies if we permit simultaneous rotate & translate, which is currently disabled...) //Vector3 rotTranslate = translate; Vector3f rotTranslate = Vector3.zero; Vector3f origCenterXY = new Vector3(cOrig[0], 0, cOrig[2]); Vector3f shiftLeftO = input.LeftHandFrame.Origin - rotTranslate; Vector3f shiftRightO = input.RightHandFrame.Origin - rotTranslate; Vector3f shiftCenter = 0.5f * (shiftLeftO + shiftRightO); Vector3f curCenterXY = new Vector3(shiftCenter[0], 0, shiftCenter[2]); Vector3f sharedC = 0.5f * (origCenterXY + curCenterXY); float aLeft = VRUtil.PlaneAngleSigned(hi.leftStartF.Origin - sharedC, shiftLeftO - sharedC, 1); float aRight = VRUtil.PlaneAngleSigned(hi.rightStartF.Origin - sharedC, shiftRightO - sharedC, 1); float azO = -(aLeft + aRight) * 0.5f; float az = (hi.bInAzimuthDeadzone) ? ApplyDeadzone(azO, fAzimuthDeadzoneInDeg) : azO - hi.fAzimuthShift; if (Math.Abs(az) > 0 && hi.bInAzimuthDeadzone) { hi.bInAzimuthDeadzone = false; hi.fAzimuthShift = azO - az; } hi.runningAz = Mathf.Lerp(hi.runningAz, az, fTrackAlpha); float fAzimuth = hi.runningAz; if (fAzimuth != 0.0f && hi.activeXForm == TransformType.NoTransform) { hi.activeXForm = TransformType.Rotate; } // altitude (up/down rotate) is specified by tilting controllers up or down. // This is the trickiest one as hands tend to tilt up/down during the other actions. // We compute an average tilt up/down angle at the start, and then the per-hand delta // each frame, as well as the average delta, and use the smallest of these. // mean hand-tilt at start frame float o1 = VRUtil.PlaneAngleSigned(hi.leftStartF.Rotation * Vector3.forward, hi.camForward, hi.camRight); float o2 = VRUtil.PlaneAngleSigned(hi.rightStartF.Rotation * Vector3.forward, hi.camForward, hi.camRight); float oa = (o1 + o2) * 0.5f; // per-frame hand tilt Vector3 camfw = camFrame.Z, camright = camFrame.X; float c1 = VRUtil.PlaneAngleSigned(input.LeftHandFrame.Rotation * Vector3.forward, camfw, camright); float c2 = VRUtil.PlaneAngleSigned(input.RightHandFrame.Rotation * Vector3.forward, camfw, camright); // use the smallest per-hand tilt delta, to prevent one-hand tilting from having an effect float d1 = oa - c1, d2 = oa - c2; float altO = (Mathf.Abs(d1) < Mathf.Abs(d2)) ? d1 : d2; // also consider the average, to reduce crazy popping from each hand tilting in opposite direction float dm = 0.5f * (d1 + d2); altO = (Mathf.Abs(altO) < Mathf.Abs(dm)) ? altO : dm; // deadzone and smoothing float alt = (hi.bInAltitudeDeadzone) ? ApplyDeadzone(altO, fAltitudeDeadzoneInDeg) : altO - hi.fAltitudeShift; if (Math.Abs(alt) > 0 && hi.bInAltitudeDeadzone) { hi.bInAltitudeDeadzone = false; hi.fAltitudeShift = altO - alt; } hi.runningAlt = Mathf.Lerp(hi.runningAlt, alt, fTrackAlpha); float fAltitude = hi.runningAlt; if (fAltitude != 0.0f && hi.activeXForm == TransformType.NoTransform) { hi.activeXForm = TransformType.Rotate; } // reset view to state when we started, then apply the accumulated rotate/zoom/translate cockpit.ActiveCamera.Manipulator().SetCurrentSceneState(cockpit.Scene, hi.camState); if (hi.activeXForm == TransformType.Rotate) { cockpit.ActiveCamera.Manipulator().SceneOrbit(cockpit.Scene, cockpit.ActiveCamera, fAzimuth, fAltitude); } else if (hi.activeXForm == TransformType.Pan) { cockpit.ActiveCamera.Manipulator().SceneTranslate(cockpit.Scene, tx, false); } else if (hi.activeXForm == TransformType.Zoom) { cockpit.ActiveCamera.Manipulator().SceneZoom(cockpit.Scene, cockpit.ActiveCamera, tz); } return(Capture.Continue); }
// 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(); } } }