// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved. using System; using System.Collections.Generic; using FlaxEditor.Gizmo; using FlaxEditor.SceneGraph; using FlaxEditor.SceneGraph.Actors; using FlaxEditor.Tools.Foliage.Undo; using FlaxEngine; namespace FlaxEditor.Tools.Foliage { /// /// Gizmo for painting with foliage. Managed by the . /// /// public sealed class PaintFoliageGizmo : GizmoBase { private FlaxEngine.Foliage _paintFoliage; private Model _brushModel; private List _foliageTypesIndices; private EditFoliageAction _undoAction; private int _paintUpdateCount; /// /// The parent mode. /// public readonly PaintFoliageGizmoMode Mode; /// /// Gets a value indicating whether gizmo tool is painting the foliage. /// public bool IsPainting => _paintFoliage != null; /// /// Occurs when foliage paint has been started. /// public event Action PaintStarted; /// /// Occurs when foliage paint has been ended. /// public event Action PaintEnded; /// /// Initializes a new instance of the class. /// /// The owner. /// The mode. public PaintFoliageGizmo(IGizmoOwner owner, PaintFoliageGizmoMode mode) : base(owner) { Mode = mode; } private FlaxEngine.Foliage SelectedFoliage { get { var sceneEditing = Editor.Instance.SceneEditing; var foliageNode = sceneEditing.SelectionCount == 1 ? sceneEditing.Selection[0] as FoliageNode : null; return (FlaxEngine.Foliage)foliageNode?.Actor; } } /// public override void Draw(ref RenderContext renderContext) { if (!IsActive) return; var foliage = SelectedFoliage; if (!foliage) return; if (Mode.HasValidHit) { var brushPosition = Mode.CursorPosition; var brushNormal = Mode.CursorNormal; var brushColor = new Color(1.0f, 0.85f, 0.0f); // TODO: expose to editor options var sceneDepth = Owner.RenderTask.Buffers.DepthBuffer; var brushMaterial = Mode.CurrentBrush.GetBrushMaterial(ref brushPosition, ref brushColor, sceneDepth); if (!_brushModel) { _brushModel = FlaxEngine.Content.LoadAsyncInternal("Editor/Primitives/Sphere"); } // Draw paint brush if (_brushModel && brushMaterial) { Quaternion rotation; if (brushNormal == Vector3.Down) rotation = Quaternion.RotationZ(Mathf.Pi); else rotation = Quaternion.LookRotation(Vector3.Cross(Vector3.Cross(brushNormal, Vector3.Forward), brushNormal), brushNormal); Matrix transform = Matrix.Scaling(Mode.CurrentBrush.Size * 0.01f) * Matrix.RotationQuaternion(rotation) * Matrix.Translation(brushPosition); _brushModel.Draw(ref renderContext, brushMaterial, ref transform); } } } /// /// Called to start foliage painting /// /// The foliage. private void PaintStart(FlaxEngine.Foliage foliage) { // Skip if already is painting if (IsPainting) return; if (Editor.Instance.Undo.Enabled) _undoAction = new EditFoliageAction(foliage); _paintFoliage = foliage; _paintUpdateCount = 0; PaintStarted?.Invoke(); } /// /// Called to update foliage painting logic. /// /// The delta time (in seconds). private void PaintUpdate(float dt) { // Skip if is not painting if (!IsPainting) return; if (Mode.CurrentBrush.SingleClick && _paintUpdateCount > 0) return; // Edit the foliage var foliage = SelectedFoliage; int foliageTypesCount = foliage.FoliageTypesCount; var foliageTypeModelIdsToPaint = Editor.Instance.Windows.ToolboxWin.Foliage.FoliageTypeModelIdsToPaint; if (_foliageTypesIndices == null) _foliageTypesIndices = new List(foliageTypesCount); else _foliageTypesIndices.Clear(); for (int index = 0; index < foliageTypesCount; index++) { var model = foliage.GetFoliageType(index).Model; if (model && (!foliageTypeModelIdsToPaint.TryGetValue(model.ID, out var selected) || selected)) { _foliageTypesIndices.Add(index); } } // TODO: don't call _foliageTypesIndices.ToArray() but reuse allocation FoliageTools.Paint(foliage, _foliageTypesIndices.ToArray(), Mode.CursorPosition, Mode.CurrentBrush.Size * 0.5f, !Owner.IsControlDown, Mode.CurrentBrush.DensityScale); _paintUpdateCount++; } /// /// Called to end foliage painting. /// private void PaintEnd() { // Skip if nothing was painted if (!IsPainting) return; if (_undoAction != null) { _undoAction.RecordEnd(); Editor.Instance.Undo.AddAction(_undoAction); _undoAction = null; } _paintFoliage = null; _paintUpdateCount = 0; PaintEnded?.Invoke(); } /// public override void Update(float dt) { base.Update(dt); // Check if gizmo is not active if (!IsActive) { PaintEnd(); return; } // Check if no foliage is selected var foliage = SelectedFoliage; if (!foliage) { PaintEnd(); return; } // Check if selected foliage was changed during painting if (foliage != _paintFoliage && IsPainting) { PaintEnd(); } // Perform detailed tracing to find cursor location for the foliage placement var mouseRay = Owner.MouseRay; var ray = Owner.MouseRay; var view = new Ray(Owner.ViewPosition, Owner.ViewDirection); var rayCastFlags = SceneGraphNode.RayCastData.FlagTypes.SkipEditorPrimitives | SceneGraphNode.RayCastData.FlagTypes.SkipColliders; var hit = Editor.Instance.Scene.Root.RayCast(ref ray, ref view, out var closest, out var hitNormal, rayCastFlags); if (hit != null) { var hitLocation = mouseRay.GetPoint(closest); Mode.SetCursor(ref hitLocation, ref hitNormal); } // No hit else { Mode.ClearCursor(); } // Handle painting if (Owner.IsLeftMouseButtonDown) PaintStart(foliage); else PaintEnd(); PaintUpdate(dt); } } }