You're breathtaking!
This commit is contained in:
206
Source/Editor/Tools/Terrain/SculptTerrainGizmo.cs
Normal file
206
Source/Editor/Tools/Terrain/SculptTerrainGizmo.cs
Normal file
@@ -0,0 +1,206 @@
|
||||
// Copyright (c) 2012-2020 Wojciech Figat. All rights reserved.
|
||||
|
||||
using System;
|
||||
using FlaxEditor.Gizmo;
|
||||
using FlaxEditor.SceneGraph;
|
||||
using FlaxEditor.SceneGraph.Actors;
|
||||
using FlaxEditor.Tools.Terrain.Sculpt;
|
||||
using FlaxEngine;
|
||||
|
||||
namespace FlaxEditor.Tools.Terrain
|
||||
{
|
||||
/// <summary>
|
||||
/// Gizmo for carving terrain. Managed by the <see cref="SculptTerrainGizmoMode"/>.
|
||||
/// </summary>
|
||||
/// <seealso cref="FlaxEditor.Gizmo.GizmoBase" />
|
||||
[HideInEditor]
|
||||
public sealed class SculptTerrainGizmo : GizmoBase
|
||||
{
|
||||
private FlaxEngine.Terrain _paintTerrain;
|
||||
private Ray _prevRay;
|
||||
|
||||
/// <summary>
|
||||
/// The parent mode.
|
||||
/// </summary>
|
||||
public readonly SculptTerrainGizmoMode Mode;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether gizmo tool is painting the terrain heightmap.
|
||||
/// </summary>
|
||||
public bool IsPainting => _paintTerrain != null;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when terrain paint has been started.
|
||||
/// </summary>
|
||||
public event Action PaintStarted;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when terrain paint has been ended.
|
||||
/// </summary>
|
||||
public event Action PaintEnded;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SculptTerrainGizmo"/> class.
|
||||
/// </summary>
|
||||
/// <param name="owner">The owner.</param>
|
||||
/// <param name="mode">The mode.</param>
|
||||
public SculptTerrainGizmo(IGizmoOwner owner, SculptTerrainGizmoMode mode)
|
||||
: base(owner)
|
||||
{
|
||||
Mode = mode;
|
||||
}
|
||||
|
||||
private FlaxEngine.Terrain SelectedTerrain
|
||||
{
|
||||
get
|
||||
{
|
||||
var sceneEditing = Editor.Instance.SceneEditing;
|
||||
var terrainNode = sceneEditing.SelectionCount == 1 ? sceneEditing.Selection[0] as TerrainNode : null;
|
||||
return (FlaxEngine.Terrain)terrainNode?.Actor;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Draw(ref RenderContext renderContext)
|
||||
{
|
||||
if (!IsActive)
|
||||
return;
|
||||
|
||||
var terrain = SelectedTerrain;
|
||||
if (!terrain)
|
||||
return;
|
||||
|
||||
if (Mode.HasValidHit)
|
||||
{
|
||||
var brushPosition = Mode.CursorPosition;
|
||||
var brushColor = new Color(1.0f, 0.85f, 0.0f); // TODO: expose to editor options
|
||||
var brushMaterial = Mode.CurrentBrush.GetBrushMaterial(ref brushPosition, ref brushColor);
|
||||
if (!brushMaterial)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < Mode.ChunksUnderCursor.Count; i++)
|
||||
{
|
||||
var chunk = Mode.ChunksUnderCursor[i];
|
||||
terrain.DrawChunk(ref renderContext, ref chunk.PatchCoord, ref chunk.ChunkCoord, brushMaterial);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called to start terrain painting
|
||||
/// </summary>
|
||||
/// <param name="terrain">The terrain.</param>
|
||||
private void PaintStart(FlaxEngine.Terrain terrain)
|
||||
{
|
||||
// Skip if already is painting
|
||||
if (IsPainting)
|
||||
return;
|
||||
|
||||
_paintTerrain = terrain;
|
||||
PaintStarted?.Invoke();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called to update terrain painting logic.
|
||||
/// </summary>
|
||||
/// <param name="dt">The delta time (in seconds).</param>
|
||||
private void PaintUpdate(float dt)
|
||||
{
|
||||
// Skip if is not painting
|
||||
if (!IsPainting)
|
||||
return;
|
||||
|
||||
// Edit the terrain
|
||||
Profiler.BeginEvent("Edit Terrain");
|
||||
var options = new Mode.Options
|
||||
{
|
||||
Strength = 1.0f,
|
||||
DeltaTime = dt,
|
||||
Invert = Owner.IsControlDown
|
||||
};
|
||||
Mode.CurrentMode.Apply(Mode.CurrentBrush, ref options, Mode, _paintTerrain);
|
||||
Profiler.EndEvent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called to end terrain painting.
|
||||
/// </summary>
|
||||
private void PaintEnd()
|
||||
{
|
||||
// Skip if nothing was painted
|
||||
if (!IsPainting)
|
||||
return;
|
||||
|
||||
_paintTerrain = null;
|
||||
PaintEnded?.Invoke();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Update(float dt)
|
||||
{
|
||||
base.Update(dt);
|
||||
|
||||
// Check if gizmo is not active
|
||||
if (!IsActive)
|
||||
{
|
||||
PaintEnd();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if no terrain is selected
|
||||
var terrain = SelectedTerrain;
|
||||
if (!terrain)
|
||||
{
|
||||
PaintEnd();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if selected terrain was changed during painting
|
||||
if (terrain != _paintTerrain && IsPainting)
|
||||
{
|
||||
PaintEnd();
|
||||
}
|
||||
|
||||
// Special case if user is sculpting terrain and mouse is not moving then freeze the brush location to help painting vertical tip objects
|
||||
var mouseRay = Owner.MouseRay;
|
||||
if (IsPainting && _prevRay == mouseRay)
|
||||
{
|
||||
// Freeze cursor
|
||||
}
|
||||
// Perform detailed tracing to find cursor location on the terrain
|
||||
else if (terrain.RayCast(mouseRay, out var closest, out var patchCoord, out var chunkCoord))
|
||||
{
|
||||
var hitLocation = mouseRay.GetPoint(closest);
|
||||
Mode.SetCursor(ref hitLocation);
|
||||
}
|
||||
// No hit
|
||||
else
|
||||
{
|
||||
Mode.ClearCursor();
|
||||
}
|
||||
_prevRay = mouseRay;
|
||||
|
||||
// Handle painting
|
||||
if (Owner.IsLeftMouseButtonDown)
|
||||
PaintStart(terrain);
|
||||
else
|
||||
PaintEnd();
|
||||
PaintUpdate(dt);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Pick()
|
||||
{
|
||||
// Get mouse ray and try to hit terrain
|
||||
var ray = Owner.MouseRay;
|
||||
var view = new Ray(Owner.ViewPosition, Owner.ViewDirection);
|
||||
var rayCastFlags = SceneGraphNode.RayCastData.FlagTypes.SkipColliders;
|
||||
var hit = Editor.Instance.Scene.Root.RayCast(ref ray, ref view, out _, rayCastFlags);
|
||||
|
||||
// Update selection
|
||||
var sceneEditing = Editor.Instance.SceneEditing;
|
||||
if (hit is TerrainNode)
|
||||
sceneEditing.Select(hit);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user