/// <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); }
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); } }
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); }
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 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)); } }
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); } }
private void AddStrokePoint(StrokePoint s) { _curStroke.Add(s); _lastStrokePoint = s; }
public void End() { _isBrushing = false; _lastStrokePoint = null; }
public SegmentNode(StrokePoint point, SegmentRenderer renderer) { this.renderer = renderer; this.strokePoint = point; }