public void MemorizeBrushStroke( BaseBrushScript brushScript, Color rColor, Guid brushGuid, float fBrushSize, float brushScale, List <PointerManager.ControlPoint> rControlPoints, StrokeFlags strokeFlags, StencilWidget stencil, float lineLength) { ClearRedo(); Stroke rNewStroke = new Stroke(); rNewStroke.m_Type = Stroke.Type.BrushStroke; rNewStroke.m_Object = brushScript.gameObject; rNewStroke.m_ControlPoints = rControlPoints.ToArray(); rNewStroke.m_ControlPointsToDrop = new bool[rNewStroke.m_ControlPoints.Length]; rNewStroke.m_Color = rColor; rNewStroke.m_BrushGuid = brushGuid; rNewStroke.m_BrushSize = fBrushSize; rNewStroke.m_BrushScale = brushScale; rNewStroke.m_Flags = strokeFlags; brushScript.Stroke = rNewStroke; SketchMemoryScript.m_Instance.RecordCommand( new BrushStrokeCommand(rNewStroke, stencil, lineLength)); MemoryListAdd(rNewStroke); TiltMeterScript.m_Instance.AdjustMeter(rNewStroke, up: true); }
private static string Compare(Mesh oldMesh, BaseBrushScript newBrush) { Vector3[] verts = oldMesh.vertices; int[] tris = oldMesh.triangles; // There is no way of querying Unity what the format of uv0 is, so don't check it return(Compare(verts, 0, verts.Length, tris, 0, tris.Length, null, newBrush)); }
protected override void OnUndo() { AudioManager.m_Instance.PlayUndoSound(CommandAudioPosition); switch (m_Stroke.m_Type) { case Stroke.Type.BrushStroke: { GameObject gameObj = m_Stroke.m_Object; if (gameObj) { BaseBrushScript rBrushScript = gameObj.GetComponent<BaseBrushScript>(); if (rBrushScript) { rBrushScript.HideBrush(true); } } break; } case Stroke.Type.BatchedBrushStroke: { var batch = m_Stroke.m_BatchSubset.m_ParentBatch; batch.DisableSubset(m_Stroke.m_BatchSubset); break; } case Stroke.Type.NotCreated: Debug.LogError("Unexpected: undo NotCreated stroke"); break; } if (m_Widget != null) { m_Widget.AdjustLift(-m_LineLength_CS); } TiltMeterScript.m_Instance.AdjustMeter(m_Stroke, up: false); }
protected override void OnUndo() { SelectionManager.m_Instance.SelectionTransform = m_InitialSelectionTransform; if (m_Strokes != null) { for (int i = 0; i < m_Strokes.Length; ++i) { Stroke stroke = m_Strokes[i]; switch (m_Strokes[i].m_Type) { case Stroke.Type.BrushStroke: BaseBrushScript rBrushScript = stroke.m_Object.GetComponent<BaseBrushScript>(); if (rBrushScript) { rBrushScript.HideBrush(false); } break; case Stroke.Type.BatchedBrushStroke: stroke.m_BatchSubset.m_ParentBatch.EnableSubset(stroke.m_BatchSubset); break; case Stroke.Type.NotCreated: Debug.LogError("Unexpected: redo delete NotCreated stroke"); break; } TiltMeterScript.m_Instance.AdjustMeter(stroke, up: true); } } if (m_Widgets != null) { for (int i = 0; i < m_Widgets.Count; ++i) { m_Widgets[i].RestoreFromToss(); } } SelectionManager.m_Instance.RegisterStrokesInSelectionCanvas(m_Strokes); SelectionManager.m_Instance.RegisterWidgetsInSelectionCanvas(m_Widgets); SketchSurfacePanel.m_Instance.EnableSpecificTool(BaseTool.ToolType.SelectionTool); }
/// 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); }
// Finishes initializing the passed PbChild, and adds to children array protected void InitializeAndAddChild( PbChild child, BrushDescriptor desc, Color color, float relativeSize = 1) { Debug.Assert(child.m_brush == null); TrTransform childXf = child.CalculateChildXfFixedScale(m_knots); BaseBrushScript brush = Create( transform.parent, childXf, desc, color, m_BaseSize_PS * relativeSize); ParentBrush pb = brush as ParentBrush; string originalName = brush.gameObject.name; string newName; if (pb != null) { newName = string.Format("{0}.{1}", gameObject.name, m_children.Count); } else { newName = string.Format( "{0}.{1} (Leaf {2})", gameObject.name, m_children.Count, originalName); } brush.gameObject.name = newName; if (pb != null) { pb.m_recursionLevel = m_recursionLevel + 1; } child.m_brush = brush; m_children.Add(child); }
protected override void OnRedo() { SelectionManager.m_Instance.SelectionTransform = TrTransform.identity; if (m_Strokes != null) { for (int i = 0; i < m_Strokes.Length; ++i) { Stroke stroke = m_Strokes[i]; switch (m_Strokes[i].m_Type) { case Stroke.Type.BrushStroke: BaseBrushScript rBrushScript = stroke.m_Object.GetComponent<BaseBrushScript>(); if (rBrushScript) { rBrushScript.HideBrush(true); } break; case Stroke.Type.BatchedBrushStroke: stroke.m_BatchSubset.m_ParentBatch.DisableSubset(stroke.m_BatchSubset); break; case Stroke.Type.NotCreated: Debug.LogError("Unexpected: redo delete NotCreated stroke"); break; } TiltMeterScript.m_Instance.AdjustMeter(stroke, up: false); } } if (m_Widgets != null) { for (int i = 0; i < m_Widgets.Count; ++i) { m_Widgets[i].Hide(); } } SelectionManager.m_Instance.DeregisterStrokesInSelectionCanvas(m_Strokes); SelectionManager.m_Instance.DeregisterWidgetsInSelectionCanvas(m_Widgets); }
void CreatePreviewLine() { UnityEngine.Profiling.Profiler.BeginSample("PointerScript.CreatePreviewLine"); if (m_PreviewLine == null && m_CurrentBrush.m_BrushPrefab != null) { // We don't have the transform for the line because it hasn't been created // yet, but we can assume that the line transform == the canvas transform, // since the line is parented to the canvas with an identity local transform. // See also the TODO in GetTransformForLine; fixing that will resolve this wart. Transform notReallyTheLineTransformButCloseEnough = App.Instance.m_CanvasTransform; TrTransform xf_LS = GetTransformForLine(notReallyTheLineTransformButCloseEnough); BaseBrushScript line = BaseBrushScript.Create( App.Instance.m_CanvasTransform, xf_LS, m_CurrentBrush, m_CurrentColor, m_CurrentBrushSize); line.gameObject.name = string.Format("Preview {0}", m_CurrentBrush.m_Description); line.SetPreviewMode(); m_PreviewLine = line; ResetPreviewProperties(); m_PreviewControlPoints.Clear(); } UnityEngine.Profiling.Profiler.EndSample(); }
/// Creates and returns a BatchSubset in the passed Canvas. /// If stroke contains too many CPs to fit, it will be cut short. /// This differs from what TB does, which is to create multiple subsets. public static BatchSubset CreateSubsetFromStroke(CanvasScript canvas, Stroke stroke) { // See PointerScript.RecreateLineFromMemory BrushDescriptor desc = BrushCatalog.m_Instance.GetBrush(stroke.m_BrushGuid); var cp0 = stroke.m_ControlPoints[0]; var xf0 = TrTransform.TRS(cp0.m_Pos, cp0.m_Orient, stroke.m_BrushScale); BaseBrushScript bbs = BaseBrushScript.Create( canvas.transform, xf0, desc, stroke.m_Color, stroke.m_BrushSize); try { bbs.SetIsLoading(); Assert.True(bbs.Canvas != null); foreach (var cp in stroke.m_ControlPoints) { bbs.UpdatePosition_LS( TrTransform.TRS(cp.m_Pos, cp.m_Orient, stroke.m_BrushScale), cp.m_Pressure); } return(bbs.FinalizeBatchedBrush()); } finally { UnityEngine.Object.DestroyImmediate(bbs.gameObject); } }
protected override void OnUndo() { if (!m_SilenceFirstAudio) { AudioManager.m_Instance.PlayRedoSound(CommandAudioPosition); } m_SilenceFirstAudio = false; switch (m_TargetStroke.m_Type) { case Stroke.Type.BrushStroke: BaseBrushScript rBrushScript = m_TargetStroke.m_Object.GetComponent <BaseBrushScript>(); if (rBrushScript) { rBrushScript.HideBrush(false); } break; case Stroke.Type.BatchedBrushStroke: m_TargetStroke.m_BatchSubset.m_ParentBatch.EnableSubset(m_TargetStroke.m_BatchSubset); break; case Stroke.Type.NotCreated: Debug.LogError("Unexpected: undo delete NotCreated stroke"); break; } TiltMeterScript.m_Instance.AdjustMeter(m_TargetStroke, up: true); }
/// Will flag a control point to keep, and a control point in the specified direction that is /// greater than the 'spawn interval' away. private void SavePointSequence(Stroke stroke, bool[] toDrop, int point, int dir, BaseBrushScript brushScript) { int count = (dir == 1) ? brushScript.Descriptor.m_HeadMinPoints : brushScript.Descriptor.m_TailMinPoints; int step = (dir == 1) ? brushScript.Descriptor.m_HeadPointStep : brushScript.Descriptor.m_TailPointStep; int lastPoint = point; toDrop[point] = false; float spawnInterval = brushScript.GetSpawnInterval(stroke.m_ControlPoints[point].m_Pressure); float sqrMinDist = spawnInterval * spawnInterval; for (int i = point + dir; i < stroke.m_ControlPoints.Length && i >= 0; i += dir) { Vector3 diff = stroke.m_ControlPoints[i].m_Pos - stroke.m_ControlPoints[lastPoint].m_Pos; if (count % step == 0) { toDrop[i] = false; } if (diff.sqrMagnitude >= sqrMinDist) { if (--count <= 0) { return; } lastPoint = i; } } toDrop[point + dir] = false; }
private void FlagPointsToKeep(Stroke stroke, bool[] toDrop, BaseBrushScript brushScript) { if (stroke.m_ControlPoints.Length < 2) { return; } Vector3 lastDiff = stroke.m_ControlPoints[1].m_Pos - stroke.m_ControlPoints[0].m_Pos; for (int i = 1; i < stroke.m_ControlPoints.Length - 2; ++i) { Vector3 nextDiff = stroke.m_ControlPoints[i + 1].m_Pos - stroke.m_ControlPoints[i].m_Pos; if (Vector3.Dot(lastDiff, nextDiff) < 0) { SavePointSequence(stroke, toDrop, i, -1, brushScript); SavePointSequence(stroke, toDrop, i, 1, brushScript); } lastDiff = nextDiff; } if (brushScript.Descriptor.m_MiddlePointStep != 0) { for (int i = 0; i < stroke.m_ControlPointsToDrop.Length; i += brushScript.Descriptor.m_MiddlePointStep) { stroke.m_ControlPointsToDrop[i] = false; } } SavePointSequence(stroke, toDrop, 0, 1, brushScript); SavePointSequence(stroke, toDrop, stroke.m_ControlPoints.Length - 1, -1, brushScript); }
void OnDestroy() { foreach (var child in m_children) { BaseBrushScript bbs = child.m_brush; bbs.DestroyMesh(); Destroy(bbs.gameObject); } }
public void DisablePreviewLine() { if (m_PreviewLine) { // TODO: Remove this (and other) calls to DestroyMesh() and do it inside OnDestroy() instead? m_PreviewLine.DestroyMesh(); Destroy(m_PreviewLine.gameObject); m_PreviewLine = null; } }
public void CalculatePointsToDrop(Stroke stroke, BaseBrushScript brushScript) { if (!brushScript.Descriptor.m_SupportsSimplification) { return; } float sqrMaxError = Mathf.Pow(stroke.m_BrushScale * Level * 0.001f, 2f); if (stroke.m_ControlPoints.Length >= 4) { FlagPointsToDrop(stroke.m_ControlPoints, stroke.m_ControlPointsToDrop, 0, stroke.m_ControlPoints.Length - 1, sqrMaxError); FlagPointsToKeep(stroke, stroke.m_ControlPointsToDrop, brushScript); } }
protected override void OnRedo() { // Deselect selected strokes. if (m_SelectedStrokes != null) { SelectionManager.m_Instance.DeselectStrokes(m_SelectedStrokes); } // Deselect selected widgets. if (m_SelectedWidgets != null) { SelectionManager.m_Instance.DeselectWidgets(m_SelectedWidgets); } // Place duplicated strokes. foreach (var stroke in m_DuplicatedStrokes) { switch (stroke.m_Type) { case Stroke.Type.BrushStroke: { BaseBrushScript brushScript = stroke.m_Object.GetComponent <BaseBrushScript>(); if (brushScript) { brushScript.HideBrush(false); } } break; case Stroke.Type.BatchedBrushStroke: { stroke.m_BatchSubset.m_ParentBatch.EnableSubset(stroke.m_BatchSubset); } break; default: Debug.LogError("Unexpected: redo NotCreated duplicate stroke"); break; } TiltMeterScript.m_Instance.AdjustMeter(stroke, up: true); } SelectionManager.m_Instance.RegisterStrokesInSelectionCanvas(m_DuplicatedStrokes); // Place duplicated widgets. for (int i = 0; i < m_DuplicatedWidgets.Count; ++i) { m_DuplicatedWidgets[i].RestoreFromToss(); } SelectionManager.m_Instance.RegisterWidgetsInSelectionCanvas(m_DuplicatedWidgets); // Set selection widget transforms. SelectionManager.m_Instance.SelectionTransform = m_DuplicateTransform; SelectionManager.m_Instance.UpdateSelectionWidget(); }
/// Like BeginLineFromMemory + EndLineFromMemory /// To help catch bugs in higher-level stroke code, it is considered /// an error unless the stroke is in state NotCreated. public void RecreateLineFromMemory(Stroke stroke) { if (stroke.m_Type != Stroke.Type.NotCreated) { throw new InvalidOperationException(); } if (BeginLineFromMemory(stroke, stroke.Canvas) == null) { // Unclear why it would have failed, but okay. // I guess we keep the old version? Debug.LogError("Unexpected error recreating line"); return; } UpdateLineFromStroke(stroke); // It's kind of warty that this needs to happen; brushes should probably track // the mesh-dirty state and flush it in Finalize(). // TODO: Check if this is still necessary now that QuadStripBrushStretchUV // flushes pending geometry changes in Finalize*Brush() m_CurrentLine.ApplyChangesToVisuals(); // Copy in new contents if (App.Config.m_UseBatchedBrushes && m_CurrentLine.m_bCanBatch) { var subset = m_CurrentLine.FinalizeBatchedBrush(); stroke.m_Type = Stroke.Type.BatchedBrushStroke; stroke.m_IntendedCanvas = null; Debug.Assert(stroke.m_Object == null); stroke.m_BatchSubset = subset; stroke.m_BatchSubset.m_Stroke = stroke; m_CurrentLine.DestroyMesh(); Destroy(m_CurrentLine.gameObject); } else { m_CurrentLine.FinalizeSolitaryBrush(); stroke.m_Type = Stroke.Type.BrushStroke; stroke.m_IntendedCanvas = null; Debug.Assert(stroke.m_BatchSubset == null); stroke.m_Object = m_CurrentLine.gameObject; stroke.m_Object.GetComponent <BaseBrushScript>().Stroke = stroke; } m_CurrentLine = null; }
private void AdjustMeter(BatchSubset subset, float fBrushSize, bool bAddToMeter) { //make brush size not matter if we're ignorning it if (!m_BrushSizeAffectsCost) { fBrushSize = 1.0f; } //figure out how much this batch costs BrushDescriptor brush = BrushCatalog.m_Instance.GetBrush( subset.m_ParentBatch.ParentPool.m_BrushGuid); float fCost = BaseBrushScript.GetStrokeCost(brush, subset.m_VertLength, fBrushSize); AdjustMeter(bAddToMeter ? fCost : -fCost); }
public const float kPreviewDuration = 0.2f; // Must be > 0 for the particles shader to work. /// Creates and properly initializes a new line. /// Pass the initial transform in parent-local (Canvas) space. /// Pass the size in pointer (Room) space. public static BaseBrushScript Create( Transform parent, TrTransform xfInParentSpace, BrushDescriptor desc, Color color, float size_PS) { GameObject line = Instantiate(desc.m_BrushPrefab); line.transform.SetParent(parent); Coords.AsLocal[line.transform] = TrTransform.identity; line.name = desc.m_Description; BaseBrushScript currentLine = line.GetComponent <BaseBrushScript>(); // TODO: pass this into InitBrush and do it there currentLine.m_Color = color; currentLine.m_BaseSize_PS = size_PS; currentLine.InitBrush(desc, xfInParentSpace); return(currentLine); }
public static IEnumerable <Stroke> StrokesForTransforms(IEnumerable <Transform> transforms) { foreach (var t in transforms) { Batch batch = t.GetComponent <Batch>(); if (batch != null) { foreach (var group in batch.m_Groups) { yield return(group.m_Stroke); } } BaseBrushScript brush = t.GetComponent <BaseBrushScript>(); if (brush != null) { yield return(brush.Stroke); } } }
private void AdjustMeter(GameObject rBrushStroke, float fBrushSize, bool bAddToMeter) { // Make brush size not matter if we're ignoring it if (!m_BrushSizeAffectsCost) { fBrushSize = 1.0f; } // If this brush stroke had a valid cost, adjust the meter BaseBrushScript rBrush = rBrushStroke.GetComponent <BaseBrushScript>(); if (rBrush != null) { float fCost = BaseBrushScript.GetStrokeCost( rBrush.Descriptor, rBrush.GetNumUsedVerts(), fBrushSize); if (fCost > 0.0f) { AdjustMeter(bAddToMeter ? fCost : -fCost); } } }
public static void InitUndoObject(BaseBrushScript rBrushScript) { rBrushScript.CloneAsUndoObject(); }
// // Sanity-checking geometry generation // // Return a non-null error message if the mesh data is not identical private static string Compare( Vector3[] oldVerts, int iOldVert0, int iOldVert1, int[] oldTris, int iOldTri0, int iOldTri1, Vector2[] oldUv0s, BaseBrushScript newBrush) { Vector3[] newVerts; int nNewVerts; Vector2[] newUv0s; int[] newTris; int nNewTris; newBrush.DebugGetGeometry(out newVerts, out nNewVerts, out newUv0s, out newTris, out nNewTris); if (nNewVerts != iOldVert1 - iOldVert0) { return("vert count mismatch"); } for (int i = 0; i < nNewVerts; ++i) { Vector3 vo = oldVerts[iOldVert0 + i]; Vector3 vn = newVerts[i]; if (vo != vn) { return(string.Format("vert mismatch @ {0}/{1}", i, nNewVerts)); } } // Before enabling, we need a way of skipping this for brushes that // legitimately want randomized UVs. There is also a known nondeterminism // for QuadStripBrushStretchUV, though. #if false if (oldUv0s != null) { if (newUv0s == null) { return("uv0 mismatch (loaded mesh has no UVs)"); } for (int i = 0; i < nNewVerts; ++i) { Vector2 uvo = oldUv0s[iOldVert0 + i]; Vector2 uvn = newUv0s[i]; if (uvo != uvn) { return(string.Format("uv mismatch @ {0}/{1}", i, nNewVerts)); } } } #endif if (nNewTris != iOldTri1 - iOldTri0) { return("tri count mismatch"); } // Try to account for vert numbering { int triOffset = newTris[0] - oldTris[iOldTri0]; for (int i = 0; i < nNewTris; ++i) { int to = oldTris[iOldTri0 + i]; int tn = newTris[i]; if (to + triOffset != tn) { return(string.Format("tri mismatch @ {0}/{1}", i, nNewTris)); } } } return(null); }
void GetInfoFromSelection() { //don't start looking for a color until we're done with our selection Transform rCanvas = App.ActiveCanvas.transform; if (!m_SelectionInfoQueryComplete && (m_SelectionObjectIndex >= rCanvas.childCount)) { int iBestObject = -1; float fBestDistance = 999999.0f; Vector3 vSelectionCenter = transform.position; float fSelectionRadius = GetSize(); for (int i = 0; i < m_CurrentSelection.Count; ++i) { //don't try to get info from complex objects SelectionObject rSelectedObject = m_CurrentSelection[i]; if (rSelectedObject.m_ComplexObject) { continue; } MeshFilter rMeshFilter = rSelectedObject.m_Object.GetComponent <MeshFilter>(); Vector3 vTransformedCenter = rSelectedObject.m_Object.transform.InverseTransformPoint(vSelectionCenter); //check each triangle for intersection int iMeshVertCount = rMeshFilter.mesh.vertexCount; Vector3[] aVerts = rMeshFilter.mesh.vertices; Vector3[] aNorms = rMeshFilter.mesh.normals; for (int j = 0; j < iMeshVertCount; j += 3) { //check to see if we're within the sphere radius to the plane of this triangle Vector3 vVert = aVerts[j]; Vector3 vNorm = aNorms[j]; float fDistToPlane = SignedDistancePlanePoint(ref vNorm, ref vVert, ref vTransformedCenter); float fAbsDistToPlane = Mathf.Abs(fDistToPlane); if (fAbsDistToPlane < fSelectionRadius && fAbsDistToPlane < fBestDistance) { //we're within the radius to this triangle's plane, find the projected point fDistToPlane *= -1.0f; Vector3 vPlaneOffsetVector = vNorm * fDistToPlane; Vector3 vPlaneIntersection = vTransformedCenter + vPlaneOffsetVector; Vector3 vVert2 = aVerts[i + 1]; Vector3 vVert3 = aVerts[i + 2]; //walk the projected point toward the triangle center Vector3 vTriCenter = (vVert + vVert2 + vVert3) * 0.33333f; Vector3 vToTriCenter = vTriCenter - vPlaneIntersection; float fWalkDistance = Mathf.Min(vToTriCenter.magnitude, fSelectionRadius); vToTriCenter.Normalize(); vToTriCenter *= fWalkDistance; vPlaneIntersection += vToTriCenter; //see if this projected point is in the triangle if (PointInTriangle(ref vPlaneIntersection, ref vVert, ref vVert2, ref vVert3)) { //our projected point is in this triangle, store this distance as the best iBestObject = i; fBestDistance = fDistToPlane; break; } } } } //this can happen if we don't have a selection if (iBestObject != -1) { BaseBrushScript rBrushScript = m_CurrentSelection[iBestObject].m_Object.GetComponent <BaseBrushScript>(); if (rBrushScript) { m_SelectionColor = rBrushScript.CurrentColor; m_SelectionBrush = rBrushScript.Descriptor; m_SelectionInfoValid = true; } } else { m_SelectionColor = Color.white; m_SelectionBrush = null; } m_SelectionInfoQueryComplete = 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 (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(); }
// 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(); }