Add RayCast utility to UI controls

#1952
This commit is contained in:
Wojtek Figat
2024-02-26 16:22:56 +01:00
parent 996a2cb09e
commit f1fe83ca81
16 changed files with 168 additions and 12 deletions

View File

@@ -360,15 +360,15 @@ namespace FlaxEditor.GUI.ContextMenu
}
/// <inheritdoc />
public override bool ContainsPoint(ref Float2 location)
public override bool ContainsPoint(ref Float2 location, bool precise)
{
if (base.ContainsPoint(ref location))
if (base.ContainsPoint(ref location, precise))
return true;
var cLocation = location - Location;
for (int i = 0; i < _panel.Children.Count; i++)
{
if (_panel.Children[i].ContainsPoint(ref cLocation))
if (_panel.Children[i].ContainsPoint(ref cLocation, precise))
return true;
}

View File

@@ -231,7 +231,7 @@ namespace FlaxEditor.Surface
}
/// <inheritdoc />
public override bool ContainsPoint(ref Float2 location)
public override bool ContainsPoint(ref Float2 location, bool precise)
{
return _headerRect.Contains(ref location) || _resizeButtonRect.Contains(ref location);
}

View File

@@ -148,6 +148,18 @@ namespace FlaxEditor.Windows
{
public bool EnableEvents => !Time.GamePaused;
public override bool RayCast(ref Float2 location, out Control hit)
{
return RayCastChildren(ref location, out hit);
}
public override bool ContainsPoint(ref Float2 location, bool precise = false)
{
if (precise)
return false;
return base.ContainsPoint(ref location, precise);
}
public override bool OnCharInput(char c)
{
if (!EnableEvents)

View File

@@ -60,6 +60,21 @@ namespace FlaxEngine.GUI
return ((CanvasRootControl)child).Is2D && base.IntersectsChildContent(child, location, out childSpaceLocation);
}
/// <inheritdoc />
public override bool RayCast(ref Float2 location, out Control hit)
{
// Ignore self
return RayCastChildren(ref location, out hit);
}
/// <inheritdoc />
public override bool ContainsPoint(ref Float2 location, bool precise = false)
{
if (precise) // Ignore as utility-only element
return false;
return base.ContainsPoint(ref location, precise);
}
/// <inheritdoc />
public override void OnMouseEnter(Float2 location)
{

View File

@@ -45,8 +45,9 @@ namespace FlaxEngine.GUI
/// </summary>
/// <param name="ray">The input ray to test (in world-space).</param>
/// <param name="canvasLocation">Output canvas-space local position.</param>
/// <param name="precise">True if perform precise intersection test against the control content (eg. with hit mask or transparency threshold). Otherwise, only simple bounds-check will be performed.</param>
/// <returns>True if canvas intersects with that point, otherwise false.</returns>
public bool Intersects3D(ref Ray ray, out Float2 canvasLocation)
public bool Intersects3D(ref Ray ray, out Float2 canvasLocation, bool precise = false)
{
// Inline bounds calculations (it will reuse world matrix)
var bounds = new OrientedBoundingBox
@@ -67,7 +68,7 @@ namespace FlaxEngine.GUI
Vector3.Transform(ref hitPoint, ref world, out Vector3 localHitPoint);
canvasLocation = new Float2(localHitPoint);
return ContainsPoint(ref canvasLocation);
return ContainsPoint(ref canvasLocation, precise);
}
canvasLocation = Float2.Zero;
@@ -189,9 +190,9 @@ namespace FlaxEngine.GUI
}
/// <inheritdoc />
public override bool ContainsPoint(ref Float2 location)
public override bool ContainsPoint(ref Float2 location, bool precise)
{
return base.ContainsPoint(ref location)
return base.ContainsPoint(ref location, precise)
&& (_canvas.TestCanvasIntersection == null || _canvas.TestCanvasIntersection(ref location));
}

View File

@@ -429,6 +429,23 @@ namespace FlaxEngine.GUI
return ContainsPoint(ref location);
}
/// <inheritdoc />
public override bool ContainsPoint(ref Float2 location, bool precise = false)
{
if (precise) // Ignore as utility-only element
return false;
return base.ContainsPoint(ref location, precise);
}
/// <inheritdoc />
public override bool RayCast(ref Float2 location, out Control hit)
{
var p = location / _scale;
if (RayCastChildren(ref p, out hit))
return true;
return base.RayCast(ref location, out hit);
}
/// <inheritdoc />
public override Float2 PointToParent(ref Float2 location)
{

View File

@@ -36,5 +36,13 @@ namespace FlaxEngine.GUI
Render2D.DrawRectangle(new Rectangle(Float2.Zero, Size), BorderColor, BorderWidth);
}
/// <inheritdoc />
public override bool ContainsPoint(ref Float2 location, bool precise = false)
{
if (precise) // Ignore border
return false;
return base.ContainsPoint(ref location, precise);
}
}
}

View File

@@ -257,6 +257,14 @@ namespace FlaxEngine.GUI
}
}
/// <inheritdoc />
public override bool ContainsPoint(ref Float2 location, bool precise = false)
{
if (precise) // Precise check for checkbox element
return _box.Contains(ref location);
return base.ContainsPoint(ref location, precise);
}
/// <inheritdoc />
public override void OnMouseMove(Float2 location)
{

View File

@@ -26,5 +26,13 @@ namespace FlaxEngine.GUI
{
AutoFocus = false;
}
/// <inheritdoc />
public override bool ContainsPoint(ref Float2 location, bool precise = false)
{
if (precise) // Ignore as visual-only element
return false;
return base.ContainsPoint(ref location, precise);
}
}
}

View File

@@ -879,6 +879,30 @@ namespace FlaxEngine.GUI
UnlockChildrenRecursive();
}
/// <inheritdoc />
public override bool RayCast(ref Float2 location, out Control hit)
{
if (RayCastChildren(ref location, out hit))
return true;
return base.RayCast(ref location, out hit);
}
internal bool RayCastChildren(ref Float2 location, out Control hit)
{
for (int i = _children.Count - 1; i >= 0 && _children.Count > 0; i--)
{
var child = _children[i];
if (child.Visible)
{
IntersectsChildContent(child, location, out var childLocation);
if (child.RayCast(ref childLocation, out hit))
return true;
}
}
hit = null;
return false;
}
/// <inheritdoc />
public override void OnMouseEnter(Float2 location)
{

View File

@@ -1088,6 +1088,23 @@ namespace FlaxEngine.GUI
#region Helper Functions
/// <summary>
/// Performs a raycast against UI controls hierarchy to find any intersecting control content. Uses <see cref="ContainsPoint"/> with precise check (skips transparent surfaces and empty panels).
/// </summary>
/// <param name="location">The position to intersect UI with.</param>
/// <param name="hit">The result control that intersects with the raycast.</param>
/// <returns>True if ray hits any matching control, otherwise false.</returns>
public virtual bool RayCast(ref Float2 location, out Control hit)
{
if (ContainsPoint(ref location, true))
{
hit = this;
return true;
}
hit = null;
return false;
}
/// <summary>
/// Checks if given location point in Parent Space intersects with the control content and calculates local position.
/// </summary>
@@ -1101,11 +1118,12 @@ namespace FlaxEngine.GUI
}
/// <summary>
/// Checks if control contains given point in local Control Space.
/// Checks if this control contains given point in local Control Space.
/// </summary>
/// <param name="location">Point location in Control Space to check</param>
/// <param name="precise">True if perform precise intersection test against the control content (eg. with hit mask or transparency threshold). Otherwise, only simple bounds-check will be performed.</param>
/// <returns>True if point is inside control's area, otherwise false.</returns>
public virtual bool ContainsPoint(ref Float2 location)
public virtual bool ContainsPoint(ref Float2 location, bool precise = false)
{
return location.X >= 0 &&
location.Y >= 0 &&
@@ -1123,12 +1141,10 @@ namespace FlaxEngine.GUI
{
if (parent == null)
throw new ArgumentNullException();
Control c = this;
while (c != null)
{
location = c.PointToParent(ref location);
c = c.Parent;
if (c == parent)
break;

View File

@@ -46,5 +46,13 @@ namespace FlaxEngine.GUI
Render2D.PopTint();
}
/// <inheritdoc />
public override bool ContainsPoint(ref Float2 location, bool precise = false)
{
if (precise) // Ignore as visual-only element
return false;
return base.ContainsPoint(ref location, precise);
}
}
}

View File

@@ -41,5 +41,13 @@ namespace FlaxEngine.GUI
Render2D.DrawBlur(new Rectangle(Float2.Zero, size), strength);
}
}
/// <inheritdoc />
public override bool ContainsPoint(ref Float2 location, bool precise = false)
{
if (precise) // Ignore as visual-only element
return false;
return base.ContainsPoint(ref location, precise);
}
}
}

View File

@@ -371,6 +371,14 @@ namespace FlaxEngine.GUI
}
}
/// <inheritdoc />
public override bool ContainsPoint(ref Float2 location, bool precise = false)
{
if (precise && BackgroundColor.A <= 0.0f) // Go through transparency
return false;
return base.ContainsPoint(ref location, precise);
}
/// <inheritdoc />
public override bool IntersectsChildContent(Control child, Float2 location, out Float2 childSpaceLocation)
{

View File

@@ -180,5 +180,13 @@ namespace FlaxEngine.GUI
PerformLayout();
}
/// <inheritdoc />
public override bool ContainsPoint(ref Float2 location, bool precise = false)
{
if (precise && BackgroundColor.A <= 0.0f) // Go through transparency
return false;
return base.ContainsPoint(ref location, precise);
}
}
}

View File

@@ -159,6 +159,21 @@ namespace FlaxEngine.GUI
}
}
/// <inheritdoc />
public override bool RayCast(ref Float2 location, out Control hit)
{
// Ignore self
return RayCastChildren(ref location, out hit);
}
/// <inheritdoc />
public override bool ContainsPoint(ref Float2 location, bool precise = false)
{
if (precise) // Ignore as utility-only element
return false;
return base.ContainsPoint(ref location, precise);
}
/// <summary>
/// Starts the mouse tracking. Used by the scrollbars, splitters, etc.
/// </summary>