/// Pass a Canvas parent, and a transform in that canvas's space. /// If overrideDesc passed, use that for the visuals -- m_CurrentBrush does not change. public void CreateNewLine(CanvasScript canvas, TrTransform xf_CS, ParametricStrokeCreator creator, BrushDescriptor overrideDesc = null) { // If straightedge is enabled, we may have a minimum size requirement. // Initialize parametric stroke creator for our type of straightedge. // Maybe change the brush to a proxy brush. BrushDescriptor desc = overrideDesc != null ? overrideDesc : m_CurrentBrush; m_CurrentCreator = creator; // Parametric creators want control over the brush size. if (m_CurrentCreator != null) { m_ParametricCreatorBackupStrokeSize = m_CurrentBrushSize; m_CurrentBrushSize = m_CurrentCreator.ProcessBrushSize(m_CurrentBrushSize); } m_LastUsedBrushSize_CS = (1 / Coords.CanvasPose.scale) * BrushSizeAbsolute; m_LineLength_CS = 0.0f; float jitteredBrushSize = m_CurrentBrushSize; if (PointerManager.m_Instance.JitterEnabled) { jitteredBrushSize = PointerManager.m_Instance.GenerateJitteredSize(desc, m_CurrentBrushSize); } m_CurrentLine = BaseBrushScript.Create( canvas.transform, xf_CS, desc, m_CurrentColor, jitteredBrushSize); }
// Only called during interactive creation. // isContinue is true if the line is the logical (if not physical) continuation // of a previous line -- ie, previous line ran out of verts and we transparently // stopped and started a new one. void InitiateLine(bool isContinue = false) { // Turn off the preview when we start drawing for (int i = 0; i < m_NumActivePointers; ++i) { m_Pointers[i].m_Script.DisablePreviewLine(); m_Pointers[i].m_Script.AllowPreviewLine(false); } if (m_StraightEdgeEnabled) { // This causes the line to be drawn with a proxy brush; and also to be // discarded and redrawn upon completion. m_StraightEdgeProxyActive = MainPointer.CurrentBrush.NeedsStraightEdgeProxy; // Turn on the straight edge and hold on to our start position m_StraightEdgeGuide.ShowGuide(MainPointer.transform.position); for (int i = 0; i < m_NumActivePointers; ++i) { m_Pointers[i].m_StraightEdgeXf_CS = Coords.AsCanvas[m_Pointers[i].m_Script.transform]; } } CanvasScript canvas = App.Scene.ActiveCanvas; for (int i = 0; i < m_NumActivePointers; ++i) { PointerScript script = m_Pointers[i].m_Script; var xfPointer_CS = canvas.AsCanvas[script.transform]; // Pass in parametric stroke creator. ParametricStrokeCreator currentCreator = null; if (m_StraightEdgeEnabled) { switch (StraightEdgeGuide.CurrentShape) { case StraightEdgeGuideScript.Shape.Line: currentCreator = new LineCreator(xfPointer_CS, flat: true); break; case StraightEdgeGuideScript.Shape.Circle: currentCreator = new CircleCreator(xfPointer_CS); break; case StraightEdgeGuideScript.Shape.Sphere: currentCreator = new SphereCreator(xfPointer_CS, script.BrushSizeAbsolute, canvas.transform.GetUniformScale()); break; } } script.CreateNewLine( canvas, xfPointer_CS, currentCreator, m_StraightEdgeProxyActive ? m_StraightEdgeProxyBrush : null); script.SetControlPoint(xfPointer_CS, isKeeper: true); } }
// During playback, rMemoryObjectForPlayback is non-null, and strokeFlags should not be passed. // otherwise, rMemoryObjectForPlayback is null, and strokeFlags should be valid. // When non-null, rMemoryObjectForPlayback corresponds to the current line. public void DetachLine( bool bDiscard, Stroke rMemoryObjectForPlayback, SketchMemoryScript.StrokeFlags strokeFlags = SketchMemoryScript.StrokeFlags.None) { if (rMemoryObjectForPlayback != null) { Debug.Assert(strokeFlags == SketchMemoryScript.StrokeFlags.None); } if (bDiscard) { m_CurrentLine.DestroyMesh(); Destroy(m_CurrentLine.gameObject); } else if (App.Config.m_UseBatchedBrushes && m_CurrentLine.m_bCanBatch) { // Save line: batched case // ClearRedo() is called by MemorizeXxx(), but also do it earlier as a // fragmentation optimization; this allows GenerateBatchSubset() to reuse the // reclaimed space. // NB: batching currently handles fragmentation poorly, so consider this // optimization necessary. if (rMemoryObjectForPlayback == null) { SketchMemoryScript.m_Instance.ClearRedo(); } var subset = m_CurrentLine.FinalizeBatchedBrush(); if (rMemoryObjectForPlayback == null) { SketchMemoryScript.m_Instance.MemorizeBatchedBrushStroke( subset, m_CurrentColor, m_CurrentBrush.m_Guid, m_CurrentBrushSize, m_CurrentLine.StrokeScale, m_ControlPoints, strokeFlags, WidgetManager.m_Instance.ActiveStencil, m_LineLength_CS, m_CurrentLine.RandomSeed); } else { //if we're in playback, patch up the in-memory stroke with its position in the batch rMemoryObjectForPlayback.m_Type = Stroke.Type.BatchedBrushStroke; rMemoryObjectForPlayback.m_Object = null; rMemoryObjectForPlayback.m_BatchSubset = subset; subset.m_Stroke = rMemoryObjectForPlayback; } //destroy original stroke, as it's now part of the batch stroke m_CurrentLine.DestroyMesh(); Destroy(m_CurrentLine.gameObject); // recreate the stroke if it's just been drawn by the user, so we can run the simplifier on it. if (!App.Instance.IsLoading() && QualityControls.m_Instance.UserStrokeSimplifier.Level > 0.0f && m_CurrentLine.Descriptor.m_SupportsSimplification) { var stroke = subset.m_Stroke; stroke.InvalidateCopy(); stroke.Uncreate(); stroke.Recreate(); } } else { // Save line: non-batched case if (rMemoryObjectForPlayback == null) { SketchMemoryScript.m_Instance.MemorizeBrushStroke( m_CurrentLine, m_CurrentColor, m_CurrentBrush.m_Guid, m_CurrentBrushSize, m_CurrentLine.StrokeScale, m_ControlPoints, strokeFlags, WidgetManager.m_Instance.ActiveStencil, m_LineLength_CS); } else { rMemoryObjectForPlayback.m_Type = Stroke.Type.BrushStroke; rMemoryObjectForPlayback.m_BatchSubset = null; m_CurrentLine.Stroke = rMemoryObjectForPlayback; } //copy master brush over to current line m_CurrentLine.FinalizeSolitaryBrush(); } if (rMemoryObjectForPlayback == null) { SilenceAudio(); } m_CurrentLine = null; // Restore brush size if our parametric creator had to modify it. if (m_CurrentCreator != null) { m_CurrentBrushSize = m_ParametricCreatorBackupStrokeSize; } m_CurrentCreator = null; m_ControlPoints.Clear(); }
// During playback, rMemoryObjectForPlayback is non-null, and strokeFlags should not be passed. // otherwise, rMemoryObjectForPlayback is null, and strokeFlags should be valid. // When non-null, rMemoryObjectForPlayback corresponds to the current line. public void DetachLine( bool bDiscard, Stroke rMemoryObjectForPlayback, SketchMemoryScript.StrokeFlags strokeFlags = SketchMemoryScript.StrokeFlags.None) { if (ApiManager.Instance.HasOutgoingListeners) { var color = App.BrushColor.CurrentColor; var pointsAsStrings = new List <string>(); foreach (var cp in m_ControlPoints) { var pos = cp.m_Pos; var rot = cp.m_Orient.eulerAngles; pointsAsStrings.Add($"[{pos.x},{pos.y},{pos.z},{rot.x},{rot.y},{rot.z},{cp.m_Pressure}]"); } ApiManager.Instance.EnqueueOutgoingCommands( new List <KeyValuePair <string, string> > { new KeyValuePair <string, string>("brush.type", CurrentBrush.m_Guid.ToString()), new KeyValuePair <string, string>("color.set.rgb", $"{color.r},{color.g},{color.b}"), new KeyValuePair <string, string>("draw.stroke", string.Join(",", pointsAsStrings)) } ); } if (rMemoryObjectForPlayback != null) { Debug.Assert(strokeFlags == SketchMemoryScript.StrokeFlags.None); } if (bDiscard) { m_CurrentLine.DestroyMesh(); Destroy(m_CurrentLine.gameObject); } else if (App.Config.m_UseBatchedBrushes && m_CurrentLine.m_bCanBatch) { // Save line: batched case // ClearRedo() is called by MemorizeXxx(), but also do it earlier as a // fragmentation optimization; this allows GenerateBatchSubset() to reuse the // reclaimed space. // NB: batching currently handles fragmentation poorly, so consider this // optimization necessary. if (rMemoryObjectForPlayback == null) { SketchMemoryScript.m_Instance.ClearRedo(); } var subset = m_CurrentLine.FinalizeBatchedBrush(); if (rMemoryObjectForPlayback == null) { SketchMemoryScript.m_Instance.MemorizeBatchedBrushStroke( subset, m_CurrentColor, m_CurrentBrush.m_Guid, m_CurrentBrushSize, m_CurrentLine.StrokeScale, m_ControlPoints, strokeFlags, WidgetManager.m_Instance.ActiveStencil, m_LineLength_CS, m_CurrentLine.RandomSeed); } else { //if we're in playback, patch up the in-memory stroke with its position in the batch rMemoryObjectForPlayback.m_Type = Stroke.Type.BatchedBrushStroke; rMemoryObjectForPlayback.m_Object = null; rMemoryObjectForPlayback.m_BatchSubset = subset; subset.m_Stroke = rMemoryObjectForPlayback; } //destroy original stroke, as it's now part of the batch stroke m_CurrentLine.DestroyMesh(); Destroy(m_CurrentLine.gameObject); // recreate the stroke if it's just been drawn by the user, so we can run the simplifier on it. if (!App.Instance.IsLoading() && QualityControls.m_Instance.UserStrokeSimplifier.Level > 0.0f && m_CurrentLine.Descriptor.m_SupportsSimplification) { var stroke = subset.m_Stroke; stroke.InvalidateCopy(); stroke.Uncreate(); stroke.Recreate(); } } else { // Save line: non-batched case if (rMemoryObjectForPlayback == null) { SketchMemoryScript.m_Instance.MemorizeBrushStroke( m_CurrentLine, m_CurrentColor, m_CurrentBrush.m_Guid, m_CurrentBrushSize, m_CurrentLine.StrokeScale, m_ControlPoints, strokeFlags, WidgetManager.m_Instance.ActiveStencil, m_LineLength_CS); } else { rMemoryObjectForPlayback.m_Type = Stroke.Type.BrushStroke; rMemoryObjectForPlayback.m_BatchSubset = null; m_CurrentLine.Stroke = rMemoryObjectForPlayback; } //copy master brush over to current line m_CurrentLine.FinalizeSolitaryBrush(); } if (rMemoryObjectForPlayback == null) { SilenceAudio(); } m_CurrentLine = null; // Restore brush size if our parametric creator had to modify it. if (m_CurrentCreator != null) { m_CurrentBrushSize = m_ParametricCreatorBackupStrokeSize; } m_CurrentCreator = null; m_ControlPoints.Clear(); }