From 039bf8253acb9185a9bcae9c5b117c8b723369d2 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Sun, 3 Jul 2022 17:12:35 +0200 Subject: [PATCH] Add UI Canvas support for Large Worlds --- Source/Engine/UI/UICanvas.cs | 37 ++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/Source/Engine/UI/UICanvas.cs b/Source/Engine/UI/UICanvas.cs index 1dc150399..d305d2bb0 100644 --- a/Source/Engine/UI/UICanvas.cs +++ b/Source/Engine/UI/UICanvas.cs @@ -17,25 +17,21 @@ namespace FlaxEngine /// /// The screen space rendering mode that places UI elements on the screen rendered on top of the scene. If the screen is resized or changes resolution, the Canvas will automatically change size to match this. /// - [Tooltip("The screen space rendering mode that places UI elements on the screen rendered on top of the scene. If the screen is resized or changes resolution, the Canvas will automatically change size to match this.")] ScreenSpace = 0, /// /// The camera space rendering mode that places Canvas in a given distance in front of a specified Camera. The UI elements are rendered by this camera, which means that the Camera settings affect the appearance of the UI. If the Camera is set to Perspective, the UI elements will be rendered with perspective, and the amount of perspective distortion can be controlled by the Camera Field of View. If the screen is resized, changes resolution, or the camera frustum changes, the Canvas will automatically change size to match as well. /// - [Tooltip("The camera space rendering mode that places Canvas in a given distance in front of a specified Camera. The UI elements are rendered by this camera, which means that the Camera settings affect the appearance of the UI. If the Camera is set to Perspective, the UI elements will be rendered with perspective, and the amount of perspective distortion can be controlled by the Camera Field of View. If the screen is resized, changes resolution, or the camera frustum changes, the Canvas will automatically change size to match as well.")] CameraSpace = 1, /// /// The world space rendering mode that places Canvas as any other object in the scene. The size of the Canvas can be set manually using its Transform, and UI elements will render in front of or behind other objects in the scene based on 3D placement. This is useful for UIs that are meant to be a part of the world. This is also known as a 'diegetic interface'. /// - [Tooltip("The world space rendering mode that places Canvas as any other object in the scene. The size of the Canvas can be set manually using its Transform, and UI elements will render in front of or behind other objects in the scene based on 3D placement. This is useful for UIs that are meant to be a part of the world. This is also known as a 'diegetic interface'.")] WorldSpace = 2, /// /// The world space rendering mode that places Canvas as any other object in the scene and orients it to face the camera. The size of the Canvas can be set manually using its Transform, and UI elements will render in front of or behind other objects in the scene based on 3D placement. This is useful for UIs that are meant to be a part of the world. This is also known as a 'diegetic interface'. /// - [Tooltip("The world space rendering mode that places Canvas as any other object in the scene and orients canvas to face the camera. The size of the Canvas can be set manually using its Transform, and UI elements will render in front of or behind other objects in the scene based on 3D placement. This is useful for UIs that are meant to be a part of the world. This is also known as a 'diegetic interface'.")] WorldSpaceFaceCamera = 3, } @@ -63,13 +59,15 @@ namespace FlaxEngine /// public override void Render(GPUContext context, ref RenderContext renderContext, GPUTexture input, GPUTexture output) { - if (renderContext.View.Frustum.Contains(Canvas.Bounds.GetBoundingBox()) == ContainmentType.Disjoint) + var bounds = Canvas.Bounds; + bounds.Transformation.Translation -= renderContext.View.Origin; + if (renderContext.View.Frustum.Contains(bounds.GetBoundingBox()) == ContainmentType.Disjoint) return; Profiler.BeginEventGPU("UI Canvas"); // Calculate rendering matrix (world*view*projection) - Canvas.GetWorldMatrix(out Matrix worldMatrix); + Canvas.GetWorldMatrix(renderContext.View.Origin, out Matrix worldMatrix); Matrix.Multiply(ref worldMatrix, ref renderContext.View.View, out Matrix viewMatrix); Matrix.Multiply(ref viewMatrix, ref renderContext.View.Projection, out Matrix viewProjectionMatrix); @@ -352,25 +350,37 @@ namespace FlaxEngine /// The world. public void GetWorldMatrix(out Matrix world) { + GetWorldMatrix(Vector3.Zero, out world); + } + + /// + /// Gets the world matrix used to transform the GUI from the local space to the world space. Handles canvas rendering mode + /// + /// The view origin (when using relative-to-camera rendering). + /// The world. + public void GetWorldMatrix(Vector3 viewOrigin, out Matrix world) + { + var transform = Transform; + Float3 translation = transform.Translation - viewOrigin; + #if FLAX_EDITOR // Override projection for editor preview if (_editorTask) { if (_renderMode == CanvasRenderMode.WorldSpace) { - GetLocalToWorldMatrix(out world); + Matrix.Transformation(ref transform.Scale, ref transform.Orientation, ref translation, out world); } else if (_renderMode == CanvasRenderMode.WorldSpaceFaceCamera) { var view = _editorTask.View; - var transform = Transform; Matrix.Translation(_guiRoot.Width * -0.5f, _guiRoot.Height * -0.5f, 0, out var m1); Matrix.Scaling(ref transform.Scale, out var m2); Matrix.Multiply(ref m1, ref m2, out var m3); Quaternion.Euler(180, 180, 0, out var quat); Matrix.RotationQuaternion(ref quat, out m2); Matrix.Multiply(ref m3, ref m2, out m1); - m2 = Matrix.Transformation(Float3.One, Quaternion.FromDirection(-view.Direction), transform.Translation); + m2 = Matrix.Transformation(Float3.One, Quaternion.FromDirection(-view.Direction), translation); Matrix.Multiply(ref m1, ref m2, out world); } else if (_renderMode == CanvasRenderMode.CameraSpace) @@ -384,7 +394,7 @@ namespace FlaxEngine Matrix.Translation(_guiRoot.Width / -2.0f, _guiRoot.Height / -2.0f, 0, out world); Matrix.RotationYawPitchRoll(Mathf.Pi, Mathf.Pi, 0, out var tmp2); Matrix.Multiply(ref world, ref tmp2, out var tmp1); - var viewPos = (Float3)view.Position; // TODO: large-worlds + Float3 viewPos = view.Position - viewOrigin; var viewRot = view.Direction != Float3.Up ? Quaternion.LookRotation(view.Direction, Float3.Up) : Quaternion.LookRotation(view.Direction, Float3.Right); var viewUp = Float3.Up * viewRot; var viewForward = view.Direction; @@ -407,19 +417,18 @@ namespace FlaxEngine if (_renderMode == CanvasRenderMode.WorldSpace || (_renderMode == CanvasRenderMode.WorldSpaceFaceCamera && !camera)) { // In 3D world - GetLocalToWorldMatrix(out world); + Matrix.Transformation(ref transform.Scale, ref transform.Orientation, ref translation, out world); } else if (_renderMode == CanvasRenderMode.WorldSpaceFaceCamera) { // In 3D world face camera - var transform = Transform; Matrix.Translation(_guiRoot.Width * -0.5f, _guiRoot.Height * -0.5f, 0, out var m1); Matrix.Scaling(ref transform.Scale, out var m2); Matrix.Multiply(ref m1, ref m2, out var m3); Quaternion.Euler(180, 180, 0, out var quat); Matrix.RotationQuaternion(ref quat, out m2); Matrix.Multiply(ref m3, ref m2, out m1); - m2 = Matrix.Transformation(Vector3.One, Quaternion.FromDirection(-camera.Direction), transform.Translation); + m2 = Matrix.Transformation(Vector3.One, Quaternion.FromDirection(-camera.Direction), translation); Matrix.Multiply(ref m1, ref m2, out world); } else if (_renderMode == CanvasRenderMode.CameraSpace && camera) @@ -446,7 +455,7 @@ namespace FlaxEngine Matrix.Multiply(ref world, ref tmp2, out tmp1); // In front of the camera - var viewPos = (Float3)camera.Position; // TODO: large-worlds + Float3 viewPos = camera.Position - viewOrigin; var viewRot = camera.Orientation; var viewUp = Float3.Up * viewRot; var viewForward = Float3.Forward * viewRot;