using Sandbox.UI; namespace Sandbox; /// /// Renders any attached PanelComponents to the world in 3D space. /// [Title( "World Panel" )] [Category( "UI" )] [Icon( "panorama_horizontal" )] [EditorHandle( "materials/gizmo/ui.png" )] public sealed class WorldPanel : Renderer, IRootPanelComponent { Sandbox.UI.WorldPanel worldPanel; [Property] public float RenderScale { get; set; } = 1.0f; [Property] public bool LookAtCamera { get; set; } [Property] public Vector2 PanelSize { get; set; } = new Vector2( 512 ); // todo: show these as group buttons [Property] public HAlignment HorizontalAlign { get; set; } = HAlignment.Center; [Property] public VAlignment VerticalAlign { get; set; } = VAlignment.Center; /// /// How far can we interact with this world panel? /// [Property, MakeDirty] public float InteractionRange { get; set; } = 1000.0f; public enum HAlignment { [Icon( "align_horizontal_left" )] Left = 1, [Icon( "align_horizontal_center" )] Center = 2, [Icon( "align_horizontal_right" )] Right = 3, } public enum VAlignment { [Icon( "align_vertical_top" )] Top = 1, [Icon( "align_vertical_center" )] Center = 2, [Icon( "align_vertical_bottom" )] Bottom = 3, } Rect CalculateRect() { var r = new Rect( 0, PanelSize ); if ( HorizontalAlign == HAlignment.Center ) r.Position -= new Vector2( PanelSize.x * 0.5f, 0 ); if ( HorizontalAlign == HAlignment.Right ) r.Position -= new Vector2( PanelSize.x, 0 ); if ( VerticalAlign == VAlignment.Center ) r.Position -= new Vector2( 0, PanelSize.y * 0.5f ); if ( VerticalAlign == VAlignment.Bottom ) r.Position -= new Vector2( 0, PanelSize.y ); return r; } protected override void OnDirty() { if ( !worldPanel.IsValid() ) return; worldPanel.MaxInteractionDistance = InteractionRange; if ( worldPanel.SceneObject.IsValid() ) { RenderOptions.Apply( worldPanel.SceneObject ); } } protected override void DrawGizmos() { using ( Gizmo.Scope( null, new Transform( 0, Rotation.From( 0, 90, -90 ), Sandbox.UI.WorldPanel.ScreenToWorldScale ) ) ) { var r = CalculateRect(); Gizmo.Draw.Line( r.TopLeft, r.TopRight ); Gizmo.Draw.Line( r.TopLeft, r.BottomLeft ); Gizmo.Draw.Line( r.TopRight, r.BottomRight ); Gizmo.Draw.Line( r.BottomLeft, r.BottomRight ); Gizmo.Draw.Color = Color.Cyan.WithAlpha( 0.2f ); Gizmo.Draw.SolidTriangle( new Triangle( r.TopLeft, r.TopRight, r.BottomRight ) ); Gizmo.Draw.SolidTriangle( new Triangle( r.BottomRight, r.BottomLeft, r.TopLeft ) ); } } protected override void OnEnabled() { worldPanel = new Sandbox.UI.WorldPanel( Scene.SceneWorld ); worldPanel.Scene = Scene; worldPanel.Transform = WorldTransform; worldPanel.Tags.SetFrom( GameObject.Tags ); worldPanel.MaxInteractionDistance = InteractionRange; OnSceneObjectCreated( worldPanel.SceneObject ); OnRenderOptionsChanged(); EnsurePanelParents(); } void EnsurePanelParents() { Components.ExecuteEnabledInSelfAndDescendants( c => c.EnsureParentPanel() ); } protected override void OnDisabled() { if ( worldPanel.IsValid() ) { BackupRenderAttributes( worldPanel?.SceneObject?.Attributes ); worldPanel.Delete( true ); worldPanel = null; } } protected override void OnPreRender() { if ( !worldPanel.IsValid() ) return; var currentRot = WorldRotation; var currentScale = WorldScale; if ( LookAtCamera && Scene.Camera is not null ) { var camPos = Scene.Camera.WorldPosition; var camDelta = camPos - WorldPosition; currentRot = Rotation.LookAt( camDelta, Scene.Camera.WorldRotation.Up ); } worldPanel.Transform = WorldTransform.WithRotation( currentRot ).WithScale( currentScale * RenderScale ); var rect = CalculateRect(); rect.Left /= RenderScale; rect.Right /= RenderScale; rect.Top /= RenderScale; rect.Bottom /= RenderScale; worldPanel.PanelBounds = rect; } public Panel GetPanel() { return worldPanel; } /// /// Tags have been updated /// protected override void OnTagsChanged() { if ( !worldPanel.IsValid() ) return; worldPanel?.Tags.SetFrom( Tags ); } protected override void OnRenderOptionsChanged() { if ( worldPanel?.SceneObject.IsValid() ?? false ) { RenderOptions.Apply( worldPanel.SceneObject ); } } }