override protected void OnUserBeginInteracting() { GrabWidgetData data = WidgetManager.m_Instance.GetCurrentCameraPath(); m_EatInteractingInput = (data == null) ? false : data.m_WidgetScript != this; WidgetManager.m_Instance.SetCurrentCameraPath(this); Debug.Assert(m_InteractingController == InputManager.ControllerName.Brush || m_InteractingController == InputManager.ControllerName.Wand); m_ActiveKnot = m_LastValidCollisionResults[(int)m_InteractingController]; // If we just grabbed a knot control, record the Y position of the controller grab point. if (m_ActiveKnot.control != CameraPathKnot.kDefaultControl) { BaseControllerBehavior b = InputManager.Controllers[(int)m_InteractingController].Behavior; if (m_ActiveKnot.knot.KnotType == CameraPathKnot.Type.Fov) { CameraPathFovKnot fovKnot = m_ActiveKnot.knot as CameraPathFovKnot; m_GrabControlInitialYDiff = b.PointerAttachPoint.transform.position.y - fovKnot.GetGrabTransform( (int)CameraPathFovKnot.ControlType.FovControl).position.y; } if (m_ActiveKnot.knot.KnotType == CameraPathKnot.Type.Speed) { CameraPathSpeedKnot speedKnot = m_ActiveKnot.knot as CameraPathSpeedKnot; m_GrabControlInitialYDiff = b.PointerAttachPoint.transform.position.y - speedKnot.GetGrabTransform( (int)CameraPathSpeedKnot.ControlType.SpeedControl).position.y; } } }
public void HighlightEntirePath() { CameraPathTinter t = WidgetManager.m_Instance.PathTinter; for (int i = 0; i < Path.PositionKnots.Count; ++i) { CameraPathPositionKnot pk = Path.PositionKnots[i]; pk.RegisterHighlight(0, true); t.TintKnot(pk); } for (int i = 0; i < Path.RotationKnots.Count; ++i) { CameraPathRotationKnot rk = Path.RotationKnots[i]; rk.RegisterHighlight(0, true); t.TintKnot(rk); } for (int i = 0; i < Path.SpeedKnots.Count; ++i) { CameraPathSpeedKnot sk = Path.SpeedKnots[i]; sk.RegisterHighlight(0, true); t.TintKnot(sk); } for (int i = 0; i < Path.FovKnots.Count; ++i) { CameraPathFovKnot fk = Path.FovKnots[i]; fk.RegisterHighlight(0, true); t.TintKnot(fk); } for (int i = 0; i < Path.Segments.Count; ++i) { t.TintSegment(m_Path.Segments[i]); } }
override protected void OnUserEndInteracting() { base.OnUserEndInteracting(); if (!m_EatInteractingInput) { // Finalize our move commands with the current state of whatever's being actively modified. switch (m_ActiveKnot.knot.KnotType) { case CameraPathKnot.Type.Position: if (m_ActiveKnot.control == 0) { SketchMemoryScript.m_Instance.PerformAndRecordCommand( new MovePositionKnotCommand(m_Path, m_ActiveKnot, TrTransform.FromTransform(m_ActiveKnot.knot.transform), true)); } else { CameraPathPositionKnot pk = m_ActiveKnot.knot as CameraPathPositionKnot; Vector3 knotFwd = m_ActiveKnot.knot.transform.forward; SketchMemoryScript.m_Instance.PerformAndRecordCommand( new ModifyPositionKnotCommand( m_Path, m_ActiveKnot, pk.TangentMagnitude, knotFwd, final:true)); } break; case CameraPathKnot.Type.Rotation: case CameraPathKnot.Type.Speed: case CameraPathKnot.Type.Fov: // Reset any PreviewWidget overrides. m_ActiveKnot.knot.gameObject.SetActive(true); InputManager.Wand.Geometry.PreviewKnotHint.Activate(false); InputManager.Brush.Geometry.PreviewKnotHint.Activate(false); SketchControlsScript.m_Instance.CameraPathCaptureRig.OverridePreviewWidgetPathT(null); if (m_ActiveKnot.control == 0) { SketchMemoryScript.m_Instance.PerformAndRecordCommand( new MoveConstrainedKnotCommand(m_Path, m_ActiveKnot, m_ActiveKnot.knot.transform.rotation, final:true)); } else { if (m_ActiveKnot.knot.KnotType == CameraPathKnot.Type.Speed) { CameraPathSpeedKnot sk = m_ActiveKnot.knot as CameraPathSpeedKnot; SketchMemoryScript.m_Instance.PerformAndRecordCommand( new ModifySpeedKnotCommand(sk, sk.SpeedValue)); } else if (m_ActiveKnot.knot.KnotType == CameraPathKnot.Type.Fov) { CameraPathFovKnot fk = m_ActiveKnot.knot as CameraPathFovKnot; SketchMemoryScript.m_Instance.PerformAndRecordCommand( new ModifyFovKnotCommand(fk, fk.FovValue)); } } break; } } m_KnotEditingLastInputXf = null; m_ActiveKnot = null; }
override public void UpdateTool() { base.UpdateTool(); // If we're in the recording state, just look for cancel and get out. if (CurrentMode == Mode.Recording) { if (InputManager.m_Instance.GetCommandDown(InputManager.SketchCommands.MenuContextClick)) { SketchControlsScript.m_Instance.CameraPathCaptureRig.StopRecordingPath(false); } return; } var widgets = WidgetManager.m_Instance.CameraPathWidgets; Transform toolAttachXf = InputManager.Brush.Geometry.ToolAttachPoint; bool input = InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate); bool inputDown = InputManager.m_Instance.GetCommandDown(InputManager.SketchCommands.Activate); // Tint any path we're intersecting with. if (!input && m_LastValidPath != null && m_Mode != Mode.RemoveKnot) { m_LastValidPath.TintSegments(m_LastValidPosition); } // Initiating input. if (inputDown) { // We clicked, but the path we clicked on isn't the active path. In that case, // switch it to the active path and eat up this input. // Don't do this for removing knots. That input should be explicit. if (m_Mode != Mode.RemoveKnot && m_LastValidPath != null) { GrabWidgetData data = WidgetManager.m_Instance.GetCurrentCameraPath(); bool lastValidIsCurrent = (data == null) ? false : data.m_WidgetScript == m_LastValidPath; if (!lastValidIsCurrent) { WidgetManager.m_Instance.SetCurrentCameraPath(m_LastValidPath); return; } } switch (m_Mode) { case Mode.AddPositionKnot: // Create a new path if none exists or if we're trying to add a position point // in a place where we're not extending an existing path. if (!WidgetManager.m_Instance.AnyCameraPathWidgetsActive || (m_LastValidPath == null && m_ExtendPath == null)) { m_ExtendPath = WidgetManager.m_Instance.CreatePathWidget(); m_ExtendPathType = ExtendPathType.ExtendAtHead; WidgetManager.m_Instance.SetCurrentCameraPath(m_ExtendPath); } if (m_LastValidPath != null) { m_LastValidPath.AddPathConstrainedKnot( CameraPathKnot.Type.Position, m_LastValidPosition, toolAttachXf.rotation); m_LastPlacedKnot = m_LastValidPath.Path.LastPlacedKnotInfo; m_LastPlacedKnotPath = m_LastValidPath; } else if (m_ExtendPath != null) { // Manipulation of a path we wish to extend. m_ExtendPath.ExtendPath(toolAttachXf.position, m_ExtendPathType); // Remember the index of the path we just added to, so we can manipulate it // while input is held. // Don't record this if we just made our path loop. if (!m_ExtendPath.Path.PathLoops) { m_LastPlacedKnot = m_ExtendPath.Path.LastPlacedKnotInfo; m_LastPlacedKnotPath = m_ExtendPath; } } break; case Mode.AddRotationKnot: if (m_LastValidPath != null) { m_LastValidPath.AddPathConstrainedKnot( CameraPathKnot.Type.Rotation, m_LastValidPosition, toolAttachXf.rotation); m_LastPlacedKnot = m_LastValidPath.Path.LastPlacedKnotInfo; m_LastPlacedKnotPath = m_LastValidPath; } break; case Mode.AddSpeedKnot: if (m_LastValidPath != null) { m_LastValidPath.AddPathConstrainedKnot( CameraPathKnot.Type.Speed, m_LastValidPosition, toolAttachXf.rotation); m_LastPlacedKnot = m_LastValidPath.Path.LastPlacedKnotInfo; m_LastPlacedKnotPath = m_LastValidPath; } break; case Mode.AddFovKnot: if (m_LastValidPath != null) { m_LastValidPath.AddPathConstrainedKnot( CameraPathKnot.Type.Fov, m_LastValidPosition, toolAttachXf.rotation); m_LastPlacedKnot = m_LastValidPath.Path.LastPlacedKnotInfo; m_LastPlacedKnotPath = m_LastValidPath; } break; case Mode.RemoveKnot: CheckToRemoveKnot(toolAttachXf.position); break; } // Remember what our controller looked like so we can manipulate this knot. if (m_LastPlacedKnot != null) { Transform controller = InputManager.Brush.Transform; Transform knotXf = m_LastPlacedKnot.knot.transform; TrTransform newWidgetXf = Coords.AsGlobal[knotXf]; m_LastPlacedKnotXf_LS = Coords.AsGlobal[controller].inverse * newWidgetXf; HideAllMeshes(); } } else if (input) { if (m_Mode == Mode.RemoveKnot) { CheckToRemoveKnot(toolAttachXf.position); } else if (m_LastPlacedKnot != null) { // Holding input from last frame can allow us to manipulate a just placed position knot. WidgetManager.m_Instance.PathTinter.TintKnot(m_LastPlacedKnot.knot); TrTransform controllerXf = Coords.AsGlobal[InputManager.Brush.Transform]; TrTransform inputXf = controllerXf * m_LastPlacedKnotXf_LS; switch (m_LastPlacedKnot.knot.KnotType) { case CameraPathKnot.Type.Position: if (m_LastPlacedKnot.control != 0) { CameraPathPositionKnot pk = m_LastPlacedKnot.knot as CameraPathPositionKnot; float tangentMag = pk.GetTangentMagnitudeFromControlXf(inputXf); Vector3 knotFwd = (inputXf.translation - m_LastPlacedKnot.knot.transform.position).normalized; if ((CameraPathPositionKnot.ControlType)m_LastPlacedKnot.control == CameraPathPositionKnot.ControlType.TangentControlBack) { knotFwd *= -1.0f; } SketchMemoryScript.m_Instance.PerformAndRecordCommand( new ModifyPositionKnotCommand( m_LastPlacedKnotPath.Path, m_LastPlacedKnot, tangentMag, knotFwd, mergesWithCreateCommand: true)); } break; case CameraPathKnot.Type.Rotation: // Rotation knots hide when we grab them, and in their place, we set the preview widget. m_LastPlacedKnot.knot.gameObject.SetActive(false); SketchControlsScript.m_Instance.CameraPathCaptureRig.OverridePreviewWidgetPathT( m_LastPlacedKnot.knot.PathT); SketchMemoryScript.m_Instance.PerformAndRecordCommand( new MoveConstrainedKnotCommand(m_LastPlacedKnotPath.Path, m_LastPlacedKnot, inputXf.rotation, mergesWithCreateCommand: true)); break; case CameraPathKnot.Type.Speed: CameraPathSpeedKnot sk = m_LastPlacedKnot.knot as CameraPathSpeedKnot; float speed = sk.GetSpeedValueFromY( InputManager.Brush.Behavior.PointerAttachPoint.transform.position.y); SketchMemoryScript.m_Instance.PerformAndRecordCommand( new ModifySpeedKnotCommand(sk, speed, mergesWithCreateCommand: true)); break; case CameraPathKnot.Type.Fov: CameraPathFovKnot fk = m_LastPlacedKnot.knot as CameraPathFovKnot; float fov = fk.GetFovValueFromY( InputManager.Brush.Behavior.PointerAttachPoint.transform.position.y); SketchMemoryScript.m_Instance.PerformAndRecordCommand( new ModifyFovKnotCommand(fk, fov, mergesWithCreateCommand: true)); break; } } } else { // No input to work with. Forget we had anything and make sure our meshes are showing. if (m_LastPlacedKnot != null) { RefreshMeshVisibility(); // Rotation knots hide when we grab them, make sure it's enabled. if (m_LastPlacedKnot.knot.KnotType == CameraPathKnot.Type.Rotation) { m_LastPlacedKnot.knot.gameObject.SetActive(true); SketchControlsScript.m_Instance.CameraPathCaptureRig.OverridePreviewWidgetPathT(null); } } m_LastPlacedKnot = null; m_LastPlacedKnotPath = null; } }
static public void CreateFromSaveData(CameraPathMetadata cameraPath) { // Create a new widget. CameraPathWidget widget = Instantiate<CameraPathWidget>( WidgetManager.m_Instance.CameraPathWidgetPrefab); widget.transform.parent = App.Scene.MainCanvas.transform; // The scale of path widgets is arbitrary. However, the scale should be one at creation // time so the knots added below have appropriate mesh scales. widget.transform.localScale = Vector3.one; widget.transform.localPosition = Vector3.zero; widget.transform.localRotation = Quaternion.identity; // Add the path knots and set their tangent speed. for (int i = 0; i < cameraPath.PathKnots.Length; ++i) { GameObject go = Instantiate<GameObject>( WidgetManager.m_Instance.CameraPathPositionKnotPrefab); go.transform.position = cameraPath.PathKnots[i].Xf.translation; go.transform.rotation = cameraPath.PathKnots[i].Xf.rotation; go.transform.parent = widget.transform; CameraPathPositionKnot knot = go.GetComponent<CameraPathPositionKnot>(); knot.TangentMagnitude = cameraPath.PathKnots[i].TangentMagnitude; widget.m_Path.PositionKnots.Add(knot); widget.m_Path.AllKnots.Add(knot); if (i > 0) { widget.m_Path.Segments.Add(CameraPath.CreateSegment(widget.transform)); } } // Refresh the path so the segment curves are correct. for (int i = 0; i < cameraPath.PathKnots.Length - 1; ++i) { widget.m_Path.RefreshSegment(i); } // Add the rotation knots. Note this list is ordered, and they're serialized in order, // so we need to make sure they're created in order. for (int i = 0; i < cameraPath.RotationKnots.Length; ++i) { GameObject go = Instantiate<GameObject>( WidgetManager.m_Instance.CameraPathRotationKnotPrefab); go.transform.position = cameraPath.RotationKnots[i].Xf.translation; go.transform.rotation = cameraPath.RotationKnots[i].Xf.rotation; go.transform.parent = widget.transform; CameraPathRotationKnot knot = go.GetComponent<CameraPathRotationKnot>(); knot.PathT = new PathT(cameraPath.RotationKnots[i].PathTValue); knot.DistanceAlongSegment = widget.m_Path.GetSegmentDistanceToT(knot.PathT); widget.m_Path.RotationKnots.Add(knot); widget.m_Path.AllKnots.Add(knot); } // Align quaternions on all rotation knots so we don't have unexpected camera flips // when calculating rotation as we walk the path. widget.m_Path.RefreshRotationKnotPolarities(); // Add the speed knots. Note this list is ordered, and they're serialized in order, // so we need to make sure they're created in order. for (int i = 0; i < cameraPath.SpeedKnots.Length; ++i) { GameObject go = Instantiate<GameObject>( WidgetManager.m_Instance.CameraPathSpeedKnotPrefab); go.transform.position = cameraPath.SpeedKnots[i].Xf.translation; go.transform.rotation = cameraPath.SpeedKnots[i].Xf.rotation; go.transform.parent = widget.transform; CameraPathSpeedKnot knot = go.GetComponent<CameraPathSpeedKnot>(); knot.PathT = new PathT(cameraPath.SpeedKnots[i].PathTValue); knot.DistanceAlongSegment = widget.m_Path.GetSegmentDistanceToT(knot.PathT); knot.SpeedValue = cameraPath.SpeedKnots[i].Speed; widget.m_Path.SpeedKnots.Add(knot); widget.m_Path.AllKnots.Add(knot); } // Add the fov knots. Note this list is ordered, and they're serialized in order, // so we need to make sure they're created in order. for (int i = 0; i < cameraPath.FovKnots.Length; ++i) { GameObject go = Instantiate<GameObject>( WidgetManager.m_Instance.CameraPathFovKnotPrefab); go.transform.position = cameraPath.FovKnots[i].Xf.translation; go.transform.rotation = cameraPath.FovKnots[i].Xf.rotation; go.transform.parent = widget.transform; CameraPathFovKnot knot = go.GetComponent<CameraPathFovKnot>(); knot.PathT = new PathT(cameraPath.FovKnots[i].PathTValue); knot.DistanceAlongSegment = widget.m_Path.GetSegmentDistanceToT(knot.PathT); knot.FovValue = cameraPath.FovKnots[i].Fov; widget.m_Path.FovKnots.Add(knot); widget.m_Path.AllKnots.Add(knot); } // Refresh visuals on the whole path. for (int i = 0; i < widget.m_Path.AllKnots.Count; ++i) { widget.m_Path.AllKnots[i].RefreshVisuals(); widget.m_Path.AllKnots[i].ActivateTint(false); widget.m_Path.AllKnots[i].SetActivePathVisuals(false); } // And turn them off. widget.m_Path.ValidatePathLooping(); widget.m_Path.SetKnotsActive(false); App.Switchboard.TriggerCameraPathCreated(); }
override public float GetActivationScore(Vector3 point, InputManager.ControllerName name) { float nearestKnotScore = -1.0f; KnotDescriptor nearestResult = new KnotDescriptor(); if (VideoRecorderUtils.ActiveVideoRecording == null && !WidgetManager.m_Instance.WidgetsDormant && !InputManager.m_Instance.GetCommand(InputManager.SketchCommands.Activate)) { // Check against all knots and put our results in storage bins. // Note that we're walking along the sorted lists instead of using m_Path.m_AllKnots. // This ensures the knotIndex stored in KnotCollisionResult is correct. for (int i = 0; i < m_Path.PositionKnots.Count; ++i) { int control = -1; CameraPathPositionKnot pk = m_Path.PositionKnots[i]; float knotScore = pk.gameObject.activeSelf ? pk.CollisionWithPoint(point, out control) : -1.0f; if (knotScore > nearestKnotScore) { nearestResult.Set(pk, control, i, null); nearestKnotScore = knotScore; } } for (int i = 0; i < m_Path.RotationKnots.Count; ++i) { int control = -1; CameraPathRotationKnot rk = m_Path.RotationKnots[i]; float knotScore = rk.CollisionWithPoint(point, out control); if (knotScore > nearestKnotScore) { nearestResult.Set(rk, control, null, rk.PathT); nearestKnotScore = knotScore; } } for (int i = 0; i < m_Path.SpeedKnots.Count; ++i) { int control = -1; CameraPathSpeedKnot sk = m_Path.SpeedKnots[i]; float knotScore = sk.CollisionWithPoint(point, out control); if (knotScore > nearestKnotScore) { nearestResult.Set(sk, control, null, sk.PathT); nearestKnotScore = knotScore; } } for (int i = 0; i < m_Path.FovKnots.Count; ++i) { int control = -1; CameraPathFovKnot fk = m_Path.FovKnots[i]; float knotScore = fk.CollisionWithPoint(point, out control); if (knotScore > nearestKnotScore) { nearestResult.Set(fk, control, null, fk.PathT); nearestKnotScore = knotScore; } } } bool brushOrWand = (name == InputManager.ControllerName.Brush) || (name == InputManager.ControllerName.Wand); if (!m_UserInteracting) { if (brushOrWand) { m_LastCollisionResults[(int)name].Set(nearestResult); if ((nearestResult.knot != null) && (nearestResult.control != -1)) { m_LastValidCollisionResults[(int)name].Set(nearestResult); } } } else if (m_InteractingController != name && brushOrWand) { m_LastCollisionResults[(int)name].Set(null, CameraPathKnot.kDefaultControl, null, null); } return nearestKnotScore; }
override public void RecordAndSetPosRot(TrTransform inputXf) { // Don't manipulate anything if we're eating input. if (m_EatInteractingInput) { return; } SnapEnabled = (m_ActiveKnot.knot.KnotType == CameraPathKnot.Type.Rotation || m_ActiveKnot.knot.KnotType == CameraPathKnot.Type.Position) && InputManager.Controllers[(int)m_InteractingController].GetCommand( InputManager.SketchCommands.MenuContextClick); inputXf = GetDesiredTransform(inputXf); if (m_ActiveKnot.knot.KnotType == CameraPathKnot.Type.Position) { // Move the base knot. if (m_ActiveKnot.control == 0) { // If this knot is the tail or the head and we're within snapping distance of the other // end, snap to the transform. TrTransform snappedXf = inputXf; int positionKnot = m_ActiveKnot.positionKnotIndex.Value; if (positionKnot == 0 || positionKnot == Path.NumPositionKnots - 1) { int otherIndex = positionKnot == 0 ? Path.NumPositionKnots - 1 : 0; float distToOther = Vector3.Distance(inputXf.translation, Path.PositionKnots[otherIndex].KnotXf.position); if (distToOther < m_KnotSnapDistanceToEnd) { snappedXf.translation = Path.PositionKnots[otherIndex].KnotXf.position; snappedXf.rotation = Path.PositionKnots[otherIndex].KnotXf.rotation; } } SketchMemoryScript.m_Instance.PerformAndRecordCommand( new MovePositionKnotCommand(m_Path, m_ActiveKnot, snappedXf)); } else { // Modify the knot tangents. CameraPathPositionKnot pk = m_ActiveKnot.knot as CameraPathPositionKnot; if (SnapEnabled) { Vector3 snappedTranslation = inputXf.translation; snappedTranslation.y = pk.transform.position.y; inputXf.translation = snappedTranslation; } float tangentMag = pk.GetTangentMagnitudeFromControlXf(inputXf); Vector3 knotFwd = (inputXf.translation - m_ActiveKnot.knot.transform.position).normalized; if ((CameraPathPositionKnot.ControlType)m_ActiveKnot.control == CameraPathPositionKnot.ControlType.TangentControlBack) { knotFwd *= -1.0f; } SketchMemoryScript.m_Instance.PerformAndRecordCommand( new ModifyPositionKnotCommand(m_Path, m_ActiveKnot, tangentMag, knotFwd)); } return; } // Constrain rotation and speed knots to the path. // Instead of testing the raw value that comes in from the controller position, test our // last valid path position plus any translation that's happened the past frame. This // method keeps the test positions near the path, allowing continuous movement when the // user has moved beyond the intersection distance to the path. Vector3 positionToProject = inputXf.translation; if (m_KnotEditingLastInputXf.HasValue) { Vector3 translationDiff = inputXf.translation - m_KnotEditingLastInputXf.Value; positionToProject = m_ActiveKnot.knot.KnotXf.position + translationDiff; } m_KnotEditingLastInputXf = inputXf.translation; // Project transform on to the path to get t. Vector3 error = Vector3.zero; if (m_Path.ProjectPositionOnToPath(positionToProject, out PathT pathT, out error)) { // Move the base knot. if (m_ActiveKnot.control == 0) { // Path constrained knots are a little sticky on the ends of the path. Knots very // near the ends *probably* want to be on the ends, and when there are small deltas // near the ends, it causes unwanted erratic curves. m_ActiveKnot.pathT = m_Path.MaybeSnapPathTToEnd(pathT, m_KnotSnapDistanceToEnd); // Rotation knots allow the user to place the preview widget at their position // for live preview. if (m_ActiveKnot.knot.KnotType == CameraPathKnot.Type.Rotation) { CheckForPreviewWidgetOverride(pathT); } SketchMemoryScript.m_Instance.PerformAndRecordCommand( new MoveConstrainedKnotCommand(m_Path, m_ActiveKnot, inputXf.rotation)); } else { // Alternate controls. BaseControllerBehavior b = InputManager.Controllers[(int)m_InteractingController].Behavior; float controllerY = b.PointerAttachPoint.transform.position.y; if (m_ActiveKnot.knot.KnotType == CameraPathKnot.Type.Speed) { CameraPathSpeedKnot sk = m_ActiveKnot.knot as CameraPathSpeedKnot; float speed = sk.GetSpeedValueFromY(controllerY - m_GrabControlInitialYDiff); SketchMemoryScript.m_Instance.PerformAndRecordCommand( new ModifySpeedKnotCommand(sk, speed)); } else if (m_ActiveKnot.knot.KnotType == CameraPathKnot.Type.Fov) { CameraPathFovKnot fk = m_ActiveKnot.knot as CameraPathFovKnot; float fov = fk.GetFovValueFromY(controllerY - m_GrabControlInitialYDiff); CheckForPreviewWidgetOverride(fk.PathT); SketchMemoryScript.m_Instance.PerformAndRecordCommand( new ModifyFovKnotCommand(fk, fov)); } } } m_KnotEditingLastInputXf -= error; }