/// <summary>
        /// Returns the Quaternion to apply to p0 to get it to face p1.
        /// Determined by correcting the (p0 -> p1) segment's pitch first, then yaw.
        /// The roll of the segment will drift naturally over time.
        ///
        /// Also see CalculateCanvasAlignmentRotation, which adds a Roll rotation for
        /// rolling the segment to align itself with a canvas when such an alignment is possible on the Roll axis.
        /// </summary>
        public static Quaternion CalculateRotation(StrokePoint p0, StrokePoint p1)
        {
            StrokePoint point     = p0;
            StrokePoint nextPoint = p1;

            Vector3 T = point.rotation * Vector3.forward;
            Vector3 N = point.rotation * Vector3.up;

            Vector3 segmentDirection = (nextPoint.position - point.position).normalized;

            // Pitch correction
            Vector3    sD_TN               = (Vector3.Dot(T, segmentDirection) * T + Vector3.Dot(N, segmentDirection) * N).normalized;
            Vector3    T_x_sD_TN           = Vector3.Cross(T, sD_TN);
            float      T_x_sD_TN_magnitude = Mathf.Clamp(T_x_sD_TN.magnitude, 0F, 1F); // Fun fact! Sometimes the magnitude of this vector is 0.000002 larger than 1F, which causes NaNs from Mathf.Asin().
            Quaternion pitchCorrection;

            if (Vector3.Dot(T, sD_TN) >= 0F)
            {
                pitchCorrection = Quaternion.AngleAxis(Mathf.Asin(T_x_sD_TN_magnitude) * Mathf.Rad2Deg, T_x_sD_TN.normalized);
            }
            else
            {
                pitchCorrection = Quaternion.AngleAxis(180F - (Mathf.Asin(T_x_sD_TN_magnitude) * Mathf.Rad2Deg), T_x_sD_TN.normalized);
            }

            // Yaw correction
            Vector3    T_pC          = pitchCorrection * T;
            Vector3    T_pC_x_sD     = Vector3.Cross(T_pC, segmentDirection);
            Quaternion yawCorrection = Quaternion.AngleAxis(Mathf.Asin(T_pC_x_sD.magnitude) * Mathf.Rad2Deg, T_pC_x_sD.normalized);

            return(pitchCorrection * yawCorrection);
        }
        /// <summary>
        /// Returns the Quaternion to apply to p0 to get it to face p1,
        /// then an additional rotation to align it to a plane when the stroke segment's normal (rotation * Vector3.up)
        /// is close to the planeNormal.
        ///
        /// Also provides output parameters for getting the pitch+yaw and roll rotations independently, but beware:
        /// The rollOnly rotation only works when applied on top of the pitchYaw rotation.
        /// </summary>
        public static Quaternion CalculateCanvasAlignmentRotation(StrokePoint p0, StrokePoint p1, Vector3 planeNormal, out Quaternion pitchYawOnly, out Quaternion rollOnly)
        {
            StrokePoint point     = p0;
            StrokePoint nextPoint = p1;

            Vector3 T = point.rotation * Vector3.forward;
            Vector3 N = point.rotation * Vector3.up;

            Vector3 segmentDirection = (nextPoint.position - point.position).normalized;

            // Pitch correction
            Vector3    sD_TN               = (Vector3.Dot(T, segmentDirection) * T + Vector3.Dot(N, segmentDirection) * N).normalized;
            Vector3    T_x_sD_TN           = Vector3.Cross(T, sD_TN);
            float      T_x_sD_TN_magnitude = Mathf.Clamp(T_x_sD_TN.magnitude, 0F, 1F); // Fun fact! Sometimes the magnitude of this vector is 0.000002 larger than 1F, which causes NaNs from Mathf.Asin().
            Quaternion pitchCorrection;

            if (Vector3.Dot(T, sD_TN) >= 0F)
            {
                pitchCorrection = Quaternion.AngleAxis(Mathf.Asin(T_x_sD_TN_magnitude) * Mathf.Rad2Deg, T_x_sD_TN.normalized);
            }
            else
            {
                pitchCorrection = Quaternion.AngleAxis(180F - (Mathf.Asin(T_x_sD_TN_magnitude) * Mathf.Rad2Deg), T_x_sD_TN.normalized);
            }

            // Yaw correction
            Vector3    T_pC          = pitchCorrection * T;
            Vector3    T_pC_x_sD     = Vector3.Cross(T_pC, segmentDirection);
            Quaternion yawCorrection = Quaternion.AngleAxis(Mathf.Asin(T_pC_x_sD.magnitude) * Mathf.Rad2Deg, T_pC_x_sD.normalized);

            // Roll correction (align to canvas)
            T = pitchCorrection * yawCorrection * T;
            N = pitchCorrection * yawCorrection * N;
            Vector3 planeUp   = planeNormal;
            Vector3 planeDown = -planeNormal;
            Vector3 canvasDirection;

            if (Vector3.Dot(N, planeUp) >= 0F)
            {
                canvasDirection = planeUp;
            }
            else
            {
                canvasDirection = planeDown;
            }
            Vector3    B                      = Vector3.Cross(T, N).normalized; // binormal
            Vector3    canvasCastNB           = (Vector3.Dot(N, canvasDirection) * N + Vector3.Dot(B, canvasDirection) * B);
            Vector3    N_x_canvasNB           = Vector3.Cross(N, canvasCastNB.normalized);
            float      N_x_canvasNB_magnitude = Mathf.Clamp(N_x_canvasNB.magnitude, 0F, 1F); // Fun fact! Sometimes the magnitude of this vector is 0.000002 larger than 1F, which causes NaNs from Mathf.Asin().
            Quaternion rollCorrection         = Quaternion.AngleAxis(
                DeadzoneDampenFilter(canvasCastNB.magnitude) * Mathf.Asin(N_x_canvasNB_magnitude) * Mathf.Rad2Deg,
                N_x_canvasNB.normalized);

            pitchYawOnly = pitchCorrection * yawCorrection;
            rollOnly     = rollCorrection;
            return(pitchYawOnly * rollOnly);
        }
Beispiel #3
0
        public void AddPoint(StrokePoint strokePoint)
        {
            SegmentNode node = new SegmentNode(strokePoint, this);

            _segmentNodes.Add(node);
            if (_segmentNodes.Count > 1)
            {
                node.prevNode          = _segmentNodes[_segmentNodes.Count - 2];
                node.prevNode.nextNode = node;
                AddMeshSegment(node.prevNode, node);
            }
        }
Beispiel #4
0
        public virtual StrokePoint StrokePointFromCurrentState()
        {
            StrokePoint strokePoint = new StrokePoint();

            strokePoint.position  = brushTip.position;
            strokePoint.rotation  = brushTip.rotation;
            strokePoint.scale     = Vector3.Scale((brushTip.localScale * 0.01F), scalingVector);
            strokePoint.deltaTime = Time.time - _curStroke.timeCreated;
            strokePoint.color     = Color.white;
            strokePoint.pressure  = 1F;

            return(strokePoint);
        }
Beispiel #5
0
        public void Begin()
        {
            _isBrushing = true;

            _curStroke         = new Stroke();
            _lastStrokePoint   = StrokePointFromCurrentState();
            _curStrokeObj      = Instantiate <GameObject>(strokeRendererPrefab.gameObject, outputParent);
            _curStrokeObj.name = "Brush " + this.name + " Stroke " + (++_curStrokeObjIdx);
            //var curStrokeRenderer = _curStrokeObj.GetComponent<StrokeRendererBase>();
            _curStrokeFilter = _curStrokeObj.GetComponent <StrokeFilter>();
            if (_curStrokeFilter == null)
            {
                _curStrokeFilter = _curStrokeObj.AddComponent <StrokeFilter>();
            }
            _curStrokeFilter.stroke = _curStroke;
            AddStrokePoint(_lastStrokePoint);
        }
        public void OnDrawRuntimeGizmos(RuntimeGizmoDrawer drawer)
        {
            if (stroke == null)
            {
                return;
            }
            bool colorSet = false;
            IEnumerator <StrokePoint> pointsEnum = stroke.GetPointsEnumerator();

            for (; pointsEnum.MoveNext();)
            {
                StrokePoint point = pointsEnum.Current;
                if (!colorSet)
                {
                    drawer.color = point.color;
                }
                drawer.DrawSphere(point.position, 0.01F * point.pressure);
            }
        }
Beispiel #7
0
        public void Add(StrokePoint strokePoint)
        {
            strokePoints.Add(strokePoint);
            OnStrokeModified(this, StrokeModificationHint.AddedPoint());

            if (autoOrient && Count > 1)
            {
                StrokePoint p0            = strokePoints[Count - 2];
                StrokePoint p1            = strokePoints[Count - 1];
                Quaternion  rotCorrection = StrokeUtil.CalculateRotation(p0, p1);
                p0.rotation = rotCorrection * p0.rotation;
                p1.rotation = p0.rotation;

                _cachedModifiedPointIndices.Clear();
                _cachedModifiedPointIndices.Add(Count - 2);
                _cachedModifiedPointIndices.Add(Count - 1);
                OnStrokeModified(this, StrokeModificationHint.ModifiedPoints(_cachedModifiedPointIndices));
            }
        }
Beispiel #8
0
 private void AddStrokePoint(StrokePoint s)
 {
     _curStroke.Add(s);
     _lastStrokePoint = s;
 }
Beispiel #9
0
 public void End()
 {
     _isBrushing      = false;
     _lastStrokePoint = null;
 }
Beispiel #10
0
 public SegmentNode(StrokePoint point, SegmentRenderer renderer)
 {
     this.renderer    = renderer;
     this.strokePoint = point;
 }