Skip to content
Permalink
Browse files

[Render] Decouple RenderUIElement from ECS

  • Loading branch information
xen2 committed Feb 7, 2019
1 parent abfb006 commit fc805ea6b8528d200906102bba59ae5c35ee2820
@@ -8,15 +8,21 @@ namespace Xenko.Rendering.UI
{
public class RenderUIElement : RenderObject
{
public RenderUIElement(UIComponent uiComponent, TransformComponent transformComponent)
public RenderUIElement()
{
UIComponent = uiComponent;
TransformComponent = transformComponent;
}

public readonly UIComponent UIComponent;

public readonly TransformComponent TransformComponent;
public Matrix WorldMatrix;

// UIComponent values
public UIPage Page;
public bool IsFullScreen;
public Vector3 Resolution;
public Vector3 Size;
public ResolutionStretch ResolutionStretch;
public bool IsBillboard;
public bool SnapText;
public bool IsFixedSize;

/// <summary>
/// Last registered position of teh mouse
@@ -21,7 +21,7 @@ public partial class UIRenderFeature

partial void PickingUpdate(RenderUIElement renderUIElement, Viewport viewport, ref Matrix worldViewProj, GameTime drawTime)
{
if (renderUIElement.UIComponent.Page?.RootElement == null)
if (renderUIElement.Page?.RootElement == null)
return;

var inverseZViewProj = worldViewProj;
@@ -112,7 +112,7 @@ private Ray GetWorldRay(ref Viewport viewport, Vector2 screenPos, ref Matrix wor
/// <param name="screenPosition">The position of the lick on the screen in normalized (0..1, 0..1) range</param>
/// <param name="uiRay"><see cref="Ray"/> from the click in object space of the ui component in (-Resolution.X/2 .. Resolution.X/2, -Resolution.Y/2 .. Resolution.Y/2) range</param>
/// <returns></returns>
private bool GetTouchPosition(UIComponent uiComponent, ref Viewport viewport, ref Matrix worldViewProj, Vector2 screenPosition, out Ray uiRay)
private bool GetTouchPosition(Vector3 resolution, ref Viewport viewport, ref Matrix worldViewProj, Vector2 screenPosition, out Ray uiRay)
{
uiRay = new Ray(new Vector3(float.NegativeInfinity), new Vector3(0, 1, 0));

@@ -123,8 +123,8 @@ private bool GetTouchPosition(UIComponent uiComponent, ref Viewport viewport, re

// If the click point is outside the canvas ignore any further testing
var dist = -touchRay.Position.Z / touchRay.Direction.Z;
if (Math.Abs(touchRay.Position.X + touchRay.Direction.X * dist) > uiComponent.Resolution.X * 0.5f ||
Math.Abs(touchRay.Position.Y + touchRay.Direction.Y * dist) > uiComponent.Resolution.Y * 0.5f)
if (Math.Abs(touchRay.Position.X + touchRay.Direction.X * dist) > resolution.X * 0.5f ||
Math.Abs(touchRay.Position.Y + touchRay.Direction.Y * dist) > resolution.Y * 0.5f)
return false;

uiRay = touchRay;
@@ -133,7 +133,7 @@ private bool GetTouchPosition(UIComponent uiComponent, ref Viewport viewport, re

private void UpdateTouchEvents(ref Viewport viewport, ref Matrix worldViewProj, RenderUIElement state, GameTime gameTime)
{
var rootElement = state.UIComponent.Page.RootElement;
var rootElement = state.Page.RootElement;
var intersectionPoint = Vector3.Zero;
var lastTouchPosition = new Vector2(float.NegativeInfinity);

@@ -154,7 +154,7 @@ private void UpdateTouchEvents(ref Viewport viewport, ref Matrix worldViewProj,
if (lastTouchPosition != currentTouchPosition)
{
Ray uiRay;
if (!GetTouchPosition(state.UIComponent, ref viewport, ref worldViewProj, currentTouchPosition, out uiRay))
if (!GetTouchPosition(state.Resolution, ref viewport, ref worldViewProj, currentTouchPosition, out uiRay))
continue;

currentTouchedElement = GetElementAtScreenPosition(rootElement, ref uiRay, ref worldViewProj, ref intersectionPoint);
@@ -236,15 +236,15 @@ private void UpdateMouseOver(ref Viewport viewport, ref Matrix worldViewProj, Re

var intersectionPoint = Vector3.Zero;
var mousePosition = input.MousePosition;
var rootElement = state.UIComponent.Page.RootElement;
var rootElement = state.Page.RootElement;
var lastMouseOverElement = state.LastMouseOverElement;
var mouseOverElement = lastMouseOverElement;

// determine currently overred element.
if (mousePosition != state.LastMousePosition)
{
Ray uiRay;
if (!GetTouchPosition(state.UIComponent, ref viewport, ref worldViewProj, mousePosition, out uiRay))
if (!GetTouchPosition(state.Resolution, ref viewport, ref worldViewProj, mousePosition, out uiRay))
return;

mouseOverElement = GetElementAtScreenPosition(rootElement, ref uiRay, ref worldViewProj, ref intersectionPoint);
@@ -111,7 +111,7 @@ private void DrawInternal(RenderDrawContext context, RenderView renderView, Rend
Texture scopedDepthBuffer = null;
foreach (var uiElement in uiElementStates)
{
if (uiElement.RenderObject.UIComponent.IsFullScreen)
if (uiElement.RenderObject.IsFullScreen)
{
var renderTarget = renderingContext.RenderTarget;
var description = TextureDescription.New2D(renderTarget.Width, renderTarget.Height, PixelFormat.D24_UNorm_S8_UInt, TextureFlags.DepthStencil);
@@ -123,32 +123,32 @@ private void DrawInternal(RenderDrawContext context, RenderView renderView, Rend
// update view parameters and perform UI picking
foreach (var uiElementState in uiElementStates)
{
var uiComponent = uiElementState.RenderObject.UIComponent;
var rootElement = uiComponent.Page?.RootElement;
var renderObject = uiElementState.RenderObject;
var rootElement = renderObject.Page?.RootElement;
if (rootElement == null)
continue;

// calculate the size of the virtual resolution depending on target size (UI canvas)
var virtualResolution = uiComponent.Resolution;
var virtualResolution = renderObject.Resolution;

if (uiComponent.IsFullScreen)
if (renderObject.IsFullScreen)
{
//var targetSize = viewportSize;
var targetSize = new Vector2(renderingContext.RenderTarget.Width, renderingContext.RenderTarget.Height);

// update the virtual resolution of the renderer
if (uiComponent.ResolutionStretch == ResolutionStretch.FixedWidthAdaptableHeight)
if (renderObject.ResolutionStretch == ResolutionStretch.FixedWidthAdaptableHeight)
virtualResolution.Y = virtualResolution.X * targetSize.Y / targetSize.X;
if (uiComponent.ResolutionStretch == ResolutionStretch.FixedHeightAdaptableWidth)
if (renderObject.ResolutionStretch == ResolutionStretch.FixedHeightAdaptableWidth)
virtualResolution.X = virtualResolution.Y * targetSize.X / targetSize.Y;

uiElementState.Update(uiComponent.Entity, virtualResolution);
uiElementState.Update(renderObject, virtualResolution);
}
else
{
var cameraComponent = context.RenderContext.Tags.Get(CameraComponentRendererExtensions.Current);
if (cameraComponent != null)
uiElementState.Update(uiComponent.Entity, cameraComponent);
uiElementState.Update(renderObject, cameraComponent);
}

// Check if the current UI component is being picked based on the current ViewParameters (used to draw this element)
@@ -161,19 +161,19 @@ private void DrawInternal(RenderDrawContext context, RenderView renderView, Rend
// render the UI elements of all the entities
foreach (var uiElementState in uiElementStates)
{
var uiComponent = uiElementState.RenderObject.UIComponent;
var rootElement = uiComponent.Page?.RootElement;
var renderObject = uiElementState.RenderObject;
var rootElement = renderObject.Page?.RootElement;
if (rootElement == null)
continue;

var updatableRootElement = (IUIElementUpdate)rootElement;
var virtualResolution = uiComponent.Resolution;
var virtualResolution = renderObject.Resolution;

// update the rendering context values specific to this element
renderingContext.Resolution = virtualResolution;
renderingContext.ViewProjectionMatrix = uiElementState.WorldViewProjectionMatrix;
renderingContext.DepthStencilBuffer = uiComponent.IsFullScreen ? scopedDepthBuffer : context.CommandList.DepthStencilBuffer;
renderingContext.ShouldSnapText = uiComponent.SnapText;
renderingContext.DepthStencilBuffer = renderObject.IsFullScreen ? scopedDepthBuffer : context.CommandList.DepthStencilBuffer;
renderingContext.ShouldSnapText = renderObject.SnapText;

// calculate an estimate of the UI real size by projecting the element virtual resolution on the screen
var virtualOrigin = uiElementState.WorldViewProjectionMatrix.Row4;
@@ -217,7 +217,7 @@ private void DrawInternal(RenderDrawContext context, RenderView renderView, Rend
uiElementState.RenderObject.LastRootMatrix = rootMatrix;

// clear and set the Depth buffer as required
if (uiComponent.IsFullScreen)
if (renderObject.IsFullScreen)
{
context.CommandList.Clear(renderingContext.DepthStencilBuffer, DepthStencilClearOptions.DepthBuffer | DepthStencilClearOptions.Stencil);
}
@@ -323,17 +323,14 @@ public UIElementState(RenderUIElement renderObject)
WorldViewProjectionMatrix = Matrix.Identity;
}

public void Update(Entity entity, CameraComponent camera)
public void Update(RenderUIElement renderObject, CameraComponent camera)
{
var frustumHeight = 2 * (float)Math.Tan(MathUtil.DegreesToRadians(camera.VerticalFieldOfView) / 2);

// extract the world matrix of the UI entity
var worldMatrix = entity.Get<TransformComponent>().WorldMatrix;
var worldMatrix = renderObject.WorldMatrix;

// rotate the UI element perpendicular to the camera view vector, if billboard is activated
var uiComponent = entity.Get<UIComponent>();

if (uiComponent.IsFullScreen)
if (renderObject.IsFullScreen)
{
worldMatrix = Matrix.Identity;
}
@@ -343,7 +340,7 @@ public void Update(Entity entity, CameraComponent camera)
Matrix.Invert(ref camera.ViewMatrix, out viewInverse);
var forwardVector = viewInverse.Forward;

if (uiComponent.IsBillboard)
if (renderObject.IsBillboard)
{
// remove scale of the camera
viewInverse.Row1 /= viewInverse.Row1.XYZ().Length();
@@ -359,7 +356,7 @@ public void Update(Entity entity, CameraComponent camera)
worldMatrix.Row3 = viewInverse.Row3;
}

if (uiComponent.IsFixedSize)
if (renderObject.IsFixedSize)
{
forwardVector.Normalize();
var distVec = (worldMatrix.TranslationVector - camera.Entity.Transform.Position);
@@ -375,7 +372,7 @@ public void Update(Entity entity, CameraComponent camera)
}

// If the UI component is not drawn fullscreen it should be drawn as a quad with world sizes corresponding to its actual size
worldMatrix = Matrix.Scaling(uiComponent.Size / uiComponent.Resolution) * worldMatrix;
worldMatrix = Matrix.Scaling(renderObject.Size / renderObject.Resolution) * worldMatrix;
}

// Rotation of Pi along 0x to go from UI space to world space
@@ -387,7 +384,7 @@ public void Update(Entity entity, CameraComponent camera)
Matrix.Multiply(ref worldViewMatrix, ref camera.ProjectionMatrix, out WorldViewProjectionMatrix);
}

public void Update(Entity entity, Vector3 virtualResolution)
public void Update(RenderUIElement renderObject, Vector3 virtualResolution)
{
var nearPlane = virtualResolution.Z / 2;
var farPlane = nearPlane + virtualResolution.Z;
@@ -404,7 +401,7 @@ public void Update(Entity entity, Vector3 virtualResolution)
ProjectionMatrix = Matrix.PerspectiveFovRH(verticalFov, aspectRatio, nearPlane, farPlane),
};

Update(entity, cameraComponent);
Update(renderObject, cameraComponent);
}
}
}
@@ -30,14 +30,28 @@ public override void Draw(RenderContext gameTime)
UIRoots.Clear();
foreach (var spriteStateKeyPair in ComponentDatas)
{
var uiComponent = spriteStateKeyPair.Key;
var renderUIElement = spriteStateKeyPair.Value;
renderUIElement.Enabled = renderUIElement.UIComponent.Enabled;
renderUIElement.Enabled = uiComponent.Enabled;

if (renderUIElement.Enabled)
{
// TODO GRAPHICS REFACTOR: Proper bounding box.
//renderSprite.BoundingBox = new BoundingBoxExt(new Vector3(float.NegativeInfinity), new Vector3(float.PositiveInfinity));
renderUIElement.RenderGroup = renderUIElement.UIComponent.RenderGroup;

// Copy values from ECS to render object
renderUIElement.WorldMatrix = uiComponent.Entity.Transform.WorldMatrix;

renderUIElement.RenderGroup = uiComponent.RenderGroup;

renderUIElement.Page = uiComponent.Page;
renderUIElement.IsFullScreen = uiComponent.IsFullScreen;
renderUIElement.Resolution = uiComponent.Resolution;
renderUIElement.Size = uiComponent.Size;
renderUIElement.ResolutionStretch = uiComponent.ResolutionStretch;
renderUIElement.IsBillboard = uiComponent.IsBillboard;
renderUIElement.SnapText = uiComponent.SnapText;
renderUIElement.IsFixedSize = uiComponent.IsFixedSize;

UIRoots.Add(renderUIElement);
}
@@ -56,14 +70,12 @@ protected override void OnEntityComponentRemoved(Entity entity, UIComponent uiCo

protected override RenderUIElement GenerateComponentData(Entity entity, UIComponent component)
{
return new RenderUIElement(component, entity.Transform) { RenderGroup = component.RenderGroup };
return new RenderUIElement { RenderGroup = component.RenderGroup };
}

protected override bool IsAssociatedDataValid(Entity entity, UIComponent component, RenderUIElement associatedData)
{
return
component == associatedData.UIComponent &&
entity.Transform == associatedData.TransformComponent;
return true;
}
}
}

0 comments on commit fc805ea

Please sign in to comment.
You can’t perform that action at this time.