Esempio n. 1
0
  public bool RemoveKnotAtPosition(Vector3 pos) {
    CameraPathKnot removeKnot = m_Path.GetKnotAtPosition(pos);
    if (removeKnot != null) {
      // If we're removing the last position knot, just destroy the whole path.
      if (removeKnot is CameraPathPositionKnot && m_Path.NumPositionKnots == 1) {
        WidgetManager.m_Instance.DeleteCameraPath(this);
      } else {
        TrTransform knotXf = TrTransform.TR(pos, removeKnot.transform.rotation);

        // Gather all the knots affected by this removal.
        List<CameraPathKnot> pks = m_Path.GetKnotsOrphanedByKnotRemoval(removeKnot);
        // If we have any knots affected by this change, we need to remove those first, before
        // we remove the original knot.  This is because, on undo, we need to add the parent
        // first, before adding all the orphaned knots.
        if (pks.Count > 0) {
          BaseCommand parent = new BaseCommand();
          for (int i = 0; i < pks.Count; ++i) {
            TrTransform childXf = TrTransform.TR(pos, pks[i].transform.rotation);
            new RemovePathKnotCommand(this, pks[i], childXf, parent);
          }
          new RemovePathKnotCommand(this, removeKnot, knotXf, parent);
          SketchMemoryScript.m_Instance.PerformAndRecordCommand(parent);
        } else {
          SketchMemoryScript.m_Instance.PerformAndRecordCommand(
              new RemovePathKnotCommand(this, removeKnot, knotXf));
        }
      }

      // Reset collision results after a delete.
      for (int i = 0; i < m_LastCollisionResults.Length; ++i) {
        m_LastCollisionResults[i].Set(null, CameraPathKnot.kDefaultControl, null, null);
      }
    }
    return removeKnot != null;
  }
Esempio n. 2
0
        public override void FindClosestPointOnSurface(Vector3 pos,
                                                       out Vector3 surfacePos, out Vector3 surfaceNorm)
        {
            // The closest-point functions operate on an unrotated ellipsoid at the origin.
            // I'll call that coordinate system "ellipse space (ES)".
            // "object space (OS)" is the true scale-compensated object-space coordinate system.

            // Take (uniform) scale from parent, but no scale from this object.
            // Our local scale is instead treated as Extents
            TrTransform xfWorldFromEllipse =
                TrTransform.FromTransform(transform.parent) *
                TrTransform.TR(transform.localPosition, transform.localRotation);

            TrTransform xfEllipseFromWorld = xfWorldFromEllipse.inverse;

            Vector3 halfExtent = Extents * .5f;
            Vector3 pos_OS     = transform.InverseTransformPoint(pos);
            Vector3 pos_ES     = xfEllipseFromWorld * pos;
            Vector3 closest_ES = MathEllipsoidAnton.ClosestPointEllipsoid(halfExtent, pos_ES);

            // Transform from ES -> OS, get the OS normal, then transform OS -> WS.
            // Normals are axial, so OS -> WS uses the inv-transpose. That all ends
            // up simplifying to this:
            surfaceNorm = transform.rotation *
                          CDiv(closest_ES,
                               CMul(halfExtent, halfExtent)).normalized;
            surfacePos = xfWorldFromEllipse * closest_ES;
        }
Esempio n. 3
0
        private void State_ProcessingStraightEdge(bool terminate)
        {
            int cpPerFrame = Mathf.Max(
                m_StraightEdgeControlPoints_CS.Count / STRAIGHTEDGE_DRAWIN_FRAMES, 2);

            TrTransform xfCanvas = Coords.CanvasPose;

            for (int p = 0; p < cpPerFrame &&
                 m_StraightEdgeControlPointIndex < m_StraightEdgeControlPoints_CS.Count;
                 p++, m_StraightEdgeControlPointIndex++)
            {
                ControlPoint cp        = m_StraightEdgeControlPoints_CS[m_StraightEdgeControlPointIndex];
                TrTransform  xfPointer = xfCanvas * TrTransform.TR(cp.m_Pos, cp.m_Orient);
                SetMainPointerPosition(xfPointer.translation);
                SetMainPointerRotation(xfPointer.rotation);
                for (int i = 0; i < m_NumActivePointers; ++i)
                {
                    m_Pointers[i].m_Script.UpdateLineFromObject();
                }

                var stencil = WidgetManager.m_Instance.ActiveStencil;
                if (stencil != null)
                {
                    stencil.AdjustLift(1);
                }
            }

            // we reached the end!
            if (terminate || m_StraightEdgeControlPointIndex >= m_StraightEdgeControlPoints_CS.Count)
            {
                FinalizeLine();
                m_CurrentLineCreationState = LineCreationState.WaitingForInput;
            }
        }
Esempio n. 4
0
        void UpdateSymmetryPointerTransforms()
        {
            switch (m_CurrentSymmetryMode)
            {
            case SymmetryMode.SinglePlane:
            {
                Plane       plane = m_SymmetryWidgetScript.ReflectionPlane;
                TrTransform xf0   = TrTransform.FromTransform(m_MainPointerData.m_Script.transform);
                TrTransform xf1   = plane.ReflectPoseKeepHandedness(xf0);
                xf1.ToTransform(m_Pointers[1].m_Script.transform);

                // This is a hack.
                // In the event that the user is painting on a plane stencil and that stencil is
                // orthogonal to the symmetry plane, the main pointer and mirrored pointer will
                // have the same depth and their strokes will overlap, causing z-fighting.
                if (WidgetManager.m_Instance.ActiveStencil != null)
                {
                    m_Pointers[1].m_Script.transform.position +=
                        m_Pointers[1].m_Script.transform.forward * m_SymmetryPointerStencilBoost;
                }
                break;
            }

            case SymmetryMode.FourAroundY:
            {
                TrTransform pointer0 = TrTransform.FromTransform(m_MainPointerData.m_Script.transform);
                // aboutY is an operator that rotates worldspace objects N degrees around the widget's Y
                TrTransform aboutY;
                {
                    var   xfWidget = TrTransform.FromTransform(m_SymmetryWidget);
                    float angle    = 360f / m_NumActivePointers;
                    aboutY = TrTransform.TR(Vector3.zero, Quaternion.AngleAxis(angle, Vector3.up));
                    // convert from widget-local coords to world coords
                    aboutY = xfWidget * aboutY * xfWidget.inverse;
                }

                TrTransform cur = TrTransform.identity;
                for (int i = 1; i < m_NumActivePointers; ++i)
                {
                    cur = aboutY * cur;                 // stack another rotation on top
                    var tmp = (cur * pointer0);         // Work around 2018.3.x Mono parse bug
                    tmp.ToTransform(m_Pointers[i].m_Script.transform);
                }
                break;
            }

            case SymmetryMode.DebugMultiple:
            {
                var xf0 = m_Pointers[0].m_Script.transform;
                for (int i = 1; i < m_NumActivePointers; ++i)
                {
                    var xf = m_Pointers[i].m_Script.transform;
                    xf.position = xf0.position + m_SymmetryDebugMultipleOffset * i;
                    xf.rotation = xf0.rotation;
                }
                break;
            }
            }
        }
Esempio n. 5
0
        protected override IEnumerable <ControlPoint> DoGetPoints(TrTransform finalTransform)
        {
            float  radius = (finalTransform.translation - m_initialTransform.translation).magnitude;
            double time0  = m_initialTime;
            double time1  = App.Instance.CurrentSketchTime;

            // The number of times that theta wraps around.
            float loops; {
                // Distance is the length of a longitude line, which is half a circumference.
                float distance = radius * Mathf.PI;
                loops = distance / m_BrushSize_CS;
            }

            // "steal" some of the pretty rotation on the butt end and put it on the front end.
            float thetaOffset = -(loops * .5f * 2 * Mathf.PI);

            int pointTotal; {
                pointTotal = (int)Mathf.Max(loops * 20, 2f);
                // Keeping this removes a very slight objectionable artifact, likely the result of
                // the thetaOffset stealing.
                if (pointTotal % 2 == 1)
                {
                    pointTotal += 1;
                }
            }

            TrTransform spherePose; {
                var sphereForward = (finalTransform.translation - m_initialTransform.translation).normalized;
                var sphereRot     = Quaternion.FromToRotation(Vector3.forward, sphereForward);
                spherePose = TrTransform.TR(m_initialTransform.translation, sphereRot);
            }

            for (int k = 0; k < pointTotal; k++)
            {
                float t     = (float)k / (pointTotal - 1); // parametrization variable
                float theta = t * loops * 2 * Mathf.PI + thetaOffset;
                float phi   = t * Mathf.PI;

                TrTransform cpPose; {
                    Vector3 localPos = radius * new Vector3(
                        Mathf.Cos(theta) * Mathf.Sin(phi),
                        Mathf.Sin(theta) * Mathf.Sin(phi),
                        Mathf.Cos(phi));
                    Quaternion localRot =
                        QuaternionE.AngleAxisRad(theta, Vector3.forward) * // latitudinal motion
                        QuaternionE.AngleAxisRad(phi, Vector3.up);         // longitudinal motion
                    cpPose = spherePose * TrTransform.TR(localPos, localRot);
                }

                yield return(new ControlPoint {
                    m_Pos = cpPose.translation,
                    m_Orient = cpPose.rotation,
                    m_Pressure = 1,
                    m_TimestampMs = (uint)(Mathf.Lerp((float)time0, (float)time1, t) * kSecondsToMs)
                });
            }
        }
Esempio n. 6
0
  public void AddPathConstrainedKnot(CameraPathKnot.Type type, Vector3 pos, Quaternion rot) {
    // Determine the position knot and path t for this new knot.
    Vector3 error = Vector3.zero;

    if (m_Path.ProjectPositionOnToPath(pos, out PathT pathT, out error)) {
      // For PositionKnots, validT is the position in the position list.
      if (type == CameraPathKnot.Type.Position) {
        pathT = new PathT(pathT.T + 1.0f);
      }

      SketchMemoryScript.m_Instance.PerformAndRecordCommand(new CreatePathKnotCommand(
          this, type, pathT, TrTransform.TR(pos, rot)));
    }
  }
Esempio n. 7
0
        //
        // BaseBrushScript api
        //

        protected override bool UpdatePositionImpl(
            Vector3 translation, Quaternion rotation, float pressure)
        {
            TrTransform parentXf = TrTransform.TR(translation, rotation);

            // Update m_knots
            {
                Debug.Assert(m_knots.Count > 1, "There should always be at least 2 knots");
                PbKnot  cur  = m_knots[m_knots.Count - 1];
                PbKnot  prev = m_knots[m_knots.Count - 2];
                Vector3 move = parentXf.translation - prev.m_pointer.translation;

                cur.m_pointer = parentXf;
                float moveLen = move.magnitude;
                cur.m_tangentFrame = (moveLen > 1e-5f)
                    ? MathUtils.ComputeMinimalRotationFrame(
                    move / moveLen, prev.m_tangentFrame, cur.m_pointer.rotation)
                    : prev.m_tangentFrame;
                cur.m_distance      = prev.m_distance + moveLen;
                cur.m_pressuredSize = PressuredSize(pressure);
            }

            MaybeCreateChildren();

            bool createdControlPoint = false;

            for (int i = 0; i < m_children.Count; ++i)
            {
                PbChild child   = m_children[i];
                var     childXf = child.CalculateChildXfFixedScale(m_knots);
                if (child.m_brush.UpdatePosition_LS(childXf, pressure))
                {
                    // Need to save off any control point which is applicable to any of our children.
                    // This does mean that if we have a giant tree of children, we might be saving
                    // off every control point.
                    // TODO: maybe there's a way for the parent to impose some order on this;
                    // like it doesn't always send positions to its children? But that would make
                    // interactive drawing less pretty.
                    createdControlPoint = true;
                }
            }

            if (createdControlPoint)
            {
                m_knots.Add(m_knots[m_knots.Count - 1].Clone());
            }

            return(createdControlPoint);
        }
Esempio n. 8
0
        public void BringToUser()
        {
            // Get brush controller and place a little in front and a little higher.
            Vector3 controllerPos =
                InputManager.m_Instance.GetController(InputManager.ControllerName.Brush).position;
            Vector3 headPos          = ViewpointScript.Head.position;
            Vector3 headToController = controllerPos - headPos;
            Vector3 offset           = headToController.normalized * m_JumpToUserControllerOffsetDistance +
                                       Vector3.up * m_JumpToUserControllerYOffset;
            TrTransform xf_GS = TrTransform.TR(controllerPos + offset, transform.rotation);

            // The transform we built was global space, but we need it in widget local for the command.
            TrTransform newXf = TrTransform.FromTransform(m_NonScaleChild.parent).inverse *xf_GS;

            SketchMemoryScript.m_Instance.PerformAndRecordCommand(
                new MoveWidgetCommand(this, newXf, CustomDimension, final: true),
                discardIfNotMerged: false);
        }
Esempio n. 9
0
        /// Given a transform, returns that transform in another basis.
        /// The new basis is specified by outputFromInput.
        ///
        /// Not guaranteed to work if the change-of-basis matrix has non-axis-aligned
        /// scale, or if the rotation portion can't be expressed as the product of 90 degree
        /// rotations about an axis. Otherwise, the resulting scale will not "fit" in a Vec3.
        public static void ChangeBasis(
            Vector3 inputTranslation, Quaternion inputRotation, Vector3 inputScale,
            out Vector3 translation, out Quaternion rotation, out Vector3 scale,
            Matrix4x4 outputFromInput, Matrix4x4 inputFromOutput)
        {
            TrTransform output = ChangeBasis(
                TrTransform.TR(inputTranslation, inputRotation),
                outputFromInput, inputFromOutput);

            translation = output.translation;
            rotation    = output.rotation;
            // Scale is a bit trickier.
            Matrix4x4 m = outputFromInput * Matrix4x4.Scale(inputScale) * inputFromOutput;

            scale   = new Vector3(m[0, 0], m[1, 1], m[2, 2]);
            m[0, 0] = m[1, 1] = m[2, 2] = 1;
            Debug.Assert(m.isIdentity);
        }
Esempio n. 10
0
        private void FlipSelection()
        {
            foreach (var stroke in m_StrokesFlipped)
            {
                for (int i = 0; i < stroke.m_ControlPoints.Length; i++)
                {
                    var xf_CS = m_FlipPlane_CS.ReflectPoseKeepHandedness(
                        TrTransform.TR(stroke.m_ControlPoints[i].m_Pos,
                                       stroke.m_ControlPoints[i].m_Orient));
                    stroke.m_ControlPoints[i].m_Pos    = xf_CS.translation;
                    stroke.m_ControlPoints[i].m_Orient = xf_CS.rotation;
                }

                // Recreate the stroke
                stroke.InvalidateCopy();
                stroke.Uncreate();
                stroke.Recreate();
            }

            foreach (var widget in m_WidgetsFlipped)
            {
                if (Canvas != widget.Canvas)
                {
                    // The widget API we use only operates in the space of the widget's own canvas
                    // We could get around this by transforming m_FlipPlane from the selection canvas
                    // to the widget's canvas, but better to put effort into removing the localScale
                    // restriction from GrabWidget.
#if false
                    // aka SelectionCanvasPoseInWidgetCanvasSpace
                    TrTransform widgetCanvasFromSelectionCanvas = widget.Canvas.AsCanvas[Canvas.transform];
                    Plane       flipPlaneInWidgetCanvasSpace    = widgetCanvasFromSelectionCanvas * m_FlipPlane_CS;
#else
                    Debug.LogError("Cannot currently flip widgets in other canvases");
                    continue;
#endif
                }
                widget.LocalTransform = widget.SupportsNegativeSize
          ? m_FlipPlane_CS.ToTrTransform() * widget.LocalTransform
          : m_FlipPlane_CS.ReflectPoseKeepHandedness(widget.LocalTransform);
            }

            // Update the bounds for the selection widget
            SelectionManager.m_Instance.UpdateSelectionWidget();
        }
Esempio n. 11
0
        //
        // Mutators
        //

        private void BakeGameObjTransform(Stroke stroke)
        {
            TrTransform xf_CS = Coords.AsCanvas[TransformForStroke(stroke)];

            if (xf_CS == TrTransform.identity)
            {
                return;
            }

            var cps = stroke.m_ControlPoints;

            for (int i = 0; i < cps.Length; ++i)
            {
                var cp = xf_CS * TrTransform.TR(cps[i].m_Pos, cps[i].m_Orient);
                cps[i].m_Pos    = cp.translation;
                cps[i].m_Orient = cp.rotation;
            }
            stroke.m_BrushScale *= xf_CS.scale;
        }
Esempio n. 12
0
        /// Given the position of a main pointer, find a corresponding symmetry position.
        /// Results are undefined unless you pass MainPointer or one of its
        /// dedicated symmetry pointers.
        public TrTransform GetSymmetryTransformFor(PointerScript pointer, TrTransform xfMain)
        {
            int child = pointer.ChildIndex;

            // "active pointers" is the number of pointers the symmetry widget is using,
            // including the main pointer.
            if (child == 0 || child >= m_NumActivePointers)
            {
                return(xfMain);
            }

            // This needs to be kept in sync with UpdateSymmetryPointerTransforms
            switch (m_CurrentSymmetryMode)
            {
            case SymmetryMode.SinglePlane:
            {
                return(m_SymmetryWidgetScript.ReflectionPlane.ReflectPoseKeepHandedness(xfMain));
            }

            case SymmetryMode.FourAroundY:
            {
                // aboutY is an operator that rotates worldspace objects N degrees around the widget's Y
                TrTransform aboutY;
                {
                    var   xfWidget = TrTransform.FromTransform(m_SymmetryWidget);
                    float angle    = (360f * child) / m_NumActivePointers;
                    aboutY = TrTransform.TR(Vector3.zero, Quaternion.AngleAxis(angle, Vector3.up));
                    // convert from widget-local coords to world coords
                    aboutY = aboutY.TransformBy(xfWidget);
                }
                return(aboutY * xfMain);
            }

            case SymmetryMode.DebugMultiple:
            {
                var xfLift = TrTransform.T(m_SymmetryDebugMultipleOffset * child);
                return(xfLift * xfMain);
            }

            default:
                return(xfMain);
            }
        }
Esempio n. 13
0
        public void TestMengerCurvatureNonDegenerate()
        {
            float kRadius    = 2.5f;
            float kInvRadius = 1 / kRadius;

            Vector3 radius = new Vector3(kRadius, 0, 0);
            Vector3 axis   = new Vector3(0, 1, 0); // should be perpendicular to radius

            TrTransform arbitrary = TrTransform.TR(
                new Vector3(5, 8, -10),
                Quaternion.AngleAxis(35, new Vector3(-1, 3, -5)));

            // Pick a bunch of triplets on a circle of radius r.
            // Points are chosen by selecting two arc angles a, b, and computing the third arc angle c.
            // Repetitions are avoided by choosing a, b, c such that a <= b <= c.
            // Degenerate cases are avoided by choosing a > 0 (implying a, b, c > 0).

            // If a > 120, one of b or c will always be smaller than a
            for (int a = 12; a <= 120; a += 24)
            {
                for (int b = a; /*b <= c*/; b += 24)
                {
                    int c = 360 - a - b;
                    if (!(b <= c))
                    {
                        break;
                    }
                    Vector3 va = TrTransform.R(0, axis) * radius;
                    Vector3 vb = TrTransform.R(a, axis) * radius;
                    Vector3 vc = TrTransform.R(a + b, axis) * radius;
                    AssertAlmostEqual(kInvRadius, MathUtils.MengerCurvature(va, vb, vc));

                    Vector3 va2 = arbitrary * va;
                    Vector3 vb2 = arbitrary * vb;
                    Vector3 vc2 = arbitrary * vc;
                    AssertAlmostEqual(kInvRadius, MathUtils.MengerCurvature(va2, vb2, vc2));
                }
            }
        }
Esempio n. 14
0
        // Continue drawing stroke for this frame.
        public void Update()
        {
            if (m_isDone)
            {
                return;
            }

            var rPointerScript = m_pointer;
            var rPointerObject = m_pointer.gameObject;

            bool needMeshUpdate    = false;
            bool needPointerUpdate = false;
            bool strokeFinished    = false;
            var  lastCp            = new PointerManager.ControlPoint();

            OverlayManager.m_Instance.UpdateProgress(SketchMemoryScript.m_Instance.GetDrawnPercent());

            RdpStrokeSimplifier simplifier = QualityControls.m_Instance.StrokeSimplifier;

            if (simplifier.Level > 0.0f)
            {
                simplifier.CalculatePointsToDrop(m_stroke, m_pointer.CurrentBrushScript);
            }

            while (true)
            {
                if (m_nextControlPoint >= m_stroke.m_ControlPoints.Length)
                {
                    needMeshUpdate = true; // Is this really necessary?
                    strokeFinished = true;
                    break;
                }
                var cp = m_stroke.m_ControlPoints[m_nextControlPoint];
                if (!IsControlPointReady(cp))
                {
                    break;
                }

                if (!m_stroke.m_ControlPointsToDrop[m_nextControlPoint])
                {
                    rPointerScript.UpdateLineFromControlPoint(cp);
                    needMeshUpdate    = true;
                    lastCp            = cp;
                    needPointerUpdate = true;
                }
                ++m_nextControlPoint;
            }

            if (needMeshUpdate)
            {
                rPointerScript.UpdateLineVisuals();
            }
            if (needPointerUpdate)
            {
                // This is only really done for visual reasons
                var xf_GS = Coords.CanvasPose * TrTransform.TR(lastCp.m_Pos, lastCp.m_Orient);
                xf_GS.scale = rPointerObject.transform.GetUniformScale();
                Coords.AsGlobal[rPointerObject.transform] = xf_GS;
                rPointerScript.SetPressure(lastCp.m_Pressure);
            }

            if (strokeFinished)
            {
                rPointerScript.EndLineFromMemory(m_stroke);
                m_isDone = true;
            }
        }