Fix 3D UI Canvas point conversion from parent control space to local space

#1129
This commit is contained in:
Wojtek Figat
2023-05-28 23:21:36 +02:00
parent f60007da83
commit 6a482c987c
2 changed files with 56 additions and 34 deletions

View File

@@ -29,34 +29,6 @@ namespace FlaxEngine.GUI
return ((CanvasRootControl)a).Canvas.Order - ((CanvasRootControl)b).Canvas.Order;
}
private bool IntersectsChildContent(CanvasRootControl child, ref Ray ray, out Float2 childSpaceLocation)
{
// Inline bounds calculations (it will reuse world matrix)
var bounds = new OrientedBoundingBox
{
Extents = new Vector3(child.Size * 0.5f, Mathf.Epsilon)
};
child.Canvas.GetWorldMatrix(out var world);
Matrix.Translation((float)bounds.Extents.X, (float)bounds.Extents.Y, 0, out var offset);
Matrix.Multiply(ref offset, ref world, out var boxWorld);
boxWorld.Decompose(out bounds.Transformation);
// Hit test
if (bounds.Intersects(ref ray, out Vector3 hitPoint))
{
// Transform world-space hit point to canvas local-space
world.Invert();
Vector3.Transform(ref hitPoint, ref world, out Vector3 localHitPoint);
childSpaceLocation = new Float2(localHitPoint);
return child.ContainsPoint(ref childSpaceLocation);
}
childSpaceLocation = Float2.Zero;
return false;
}
/// <inheritdoc />
public override void OnChildrenChanged()
{
@@ -102,7 +74,7 @@ namespace FlaxEngine.GUI
var child = (CanvasRootControl)_children[i];
if (child.Visible && child.Enabled && child.Is3D)
{
if (IntersectsChildContent(child, ref ray, out var childLocation))
if (child.Intersects3D(ref ray, out var childLocation))
{
child.OnMouseEnter(childLocation);
return;
@@ -148,7 +120,7 @@ namespace FlaxEngine.GUI
}
else
{
if (!isFirst3DHandled && IntersectsChildContent(child, ref ray, out var childLocation))
if (!isFirst3DHandled && child.Intersects3D(ref ray, out var childLocation))
{
isFirst3DHandled = true;
@@ -189,7 +161,7 @@ namespace FlaxEngine.GUI
var child = (CanvasRootControl)_children[i];
if (child.Visible && child.Enabled && child.Is3D)
{
if (IntersectsChildContent(child, ref ray, out var childLocation))
if (child.Intersects3D(ref ray, out var childLocation))
{
child.OnMouseWheel(childLocation, delta);
return true;
@@ -216,7 +188,7 @@ namespace FlaxEngine.GUI
var child = (CanvasRootControl)_children[i];
if (child.Visible && child.Enabled && child.Is3D)
{
if (IntersectsChildContent(child, ref ray, out var childLocation))
if (child.Intersects3D(ref ray, out var childLocation))
{
child.OnMouseDown(childLocation, button);
return true;
@@ -243,7 +215,7 @@ namespace FlaxEngine.GUI
var child = (CanvasRootControl)_children[i];
if (child.Visible && child.Enabled && child.Is3D)
{
if (IntersectsChildContent(child, ref ray, out var childLocation))
if (child.Intersects3D(ref ray, out var childLocation))
{
child.OnMouseUp(childLocation, button);
return true;
@@ -270,7 +242,7 @@ namespace FlaxEngine.GUI
var child = (CanvasRootControl)_children[i];
if (child.Visible && child.Enabled && child.Is3D)
{
if (IntersectsChildContent(child, ref ray, out var childLocation))
if (child.Intersects3D(ref ray, out var childLocation))
{
child.OnMouseDoubleClick(childLocation, button);
return true;

View File

@@ -40,6 +40,40 @@ namespace FlaxEngine.GUI
_canvas = canvas;
}
/// <summary>
/// Checks if the 3D canvas intersects with a given 3D mouse ray.
/// </summary>
/// <param name="ray">The input ray to test (in world-space).</param>
/// <param name="canvasLocation">Output canvas-space local position.</param>
/// <returns>True if canvas intersects with that point, otherwise false.</returns>
public bool Intersects3D(ref Ray ray, out Float2 canvasLocation)
{
// Inline bounds calculations (it will reuse world matrix)
var bounds = new OrientedBoundingBox
{
Extents = new Vector3(Size * 0.5f, Mathf.Epsilon)
};
_canvas.GetWorldMatrix(out var world);
Matrix.Translation((float)bounds.Extents.X, (float)bounds.Extents.Y, 0, out var offset);
Matrix.Multiply(ref offset, ref world, out var boxWorld);
boxWorld.Decompose(out bounds.Transformation);
// Hit test
if (bounds.Intersects(ref ray, out Vector3 hitPoint))
{
// Transform world-space hit point to canvas local-space
world.Invert();
Vector3.Transform(ref hitPoint, ref world, out Vector3 localHitPoint);
canvasLocation = new Float2(localHitPoint);
return ContainsPoint(ref canvasLocation);
}
canvasLocation = Float2.Zero;
return false;
}
/// <inheritdoc />
public override CursorType Cursor
{
@@ -136,6 +170,22 @@ namespace FlaxEngine.GUI
return location;
}
/// <inheritdoc />
public override Float2 PointFromParent(ref Float2 locationParent)
{
if (Is2D)
return base.PointFromParent(ref locationParent);
var camera = Camera.MainCamera;
if (!camera)
return locationParent;
// Use world-space ray to convert it to the local-space of the canvas
UICanvas.CalculateRay(ref locationParent, out Ray ray);
Intersects3D(ref ray, out var location);
return location;
}
/// <inheritdoc />
public override bool ContainsPoint(ref Float2 location)
{