using Annulus; using Azimuth; using Blacklight.Core; using Blacklight.Core.Cameras; using Blacklight.Core.Drawables; using System; namespace Glass.Testbed { /// /// A nameplate pinned to the Arrow in the game scene. /// public class WorldAnchorTestCase : TestCase { /// /// A hoisted panel pinned to a point in the game world rather than to a control. /// private class WorldAnchoredOverlay : Panel { private readonly Camera gameCamera; private readonly ScreenSpaceCamera glassCamera; /// /// The point to sit on, in the game camera's world space. /// public Vector WorldPosition { get; set; } /// /// Where to sit relative to that point once it has reached Glass space. /// public Vector OffsetFromAnchor { get; set; } public WorldAnchoredOverlay(Camera gameCamera, ScreenSpaceCamera glassCamera) { this.gameCamera = gameCamera; this.glassCamera = glassCamera; Hoisted = true; AlwaysOnTop = true; ClipToAncestors = false; } public override void Update(TimeSpan elapsed, UpdateState state) { base.Update(elapsed, state); Vector anchor = gameCamera.WorldToWorldOf(glassCamera).TransformPoint(WorldPosition); Bounds = Rect.FromPositionAndSize(anchor + OffsetFromAnchor, Bounds.Size); } } private static readonly Vector ArrowHome = new Vector(120, -70); private const double ArrowOrbitRadius = 60; private const double ArrowOrbitSecondsPerTurn = 9; private readonly World world; private Arrow arrow; private EntityHandle arrowHandle; private WorldAnchoredOverlay arrowNameplate; private TextMaterial anchorReadoutMaterial; private double arrowOrbitPhase; public WorldAnchorTestCase(string name, World world) : base(name) { this.world = world; } protected override void Populate() { arrow = new Arrow(5, 2) { InsideColor = new Color(0.5, 0.5, 0), ShellColor = new Color(0.75, 0.75, 0), ShellModelSpaceThickness = 0, ShellPixelThickness = 3, }; arrowHandle = world.cel.AddEntity(arrow, new AffineMatrix(0, ArrowHome)); AddCaption("The nameplate tracks the world.arrow as the world.arrow orbits"); AddCaption("Drag to pan and wheel to zoom"); anchorReadoutMaterial = Widgets.Caption("anchor: measuring..."); Panel anchorReadout = anchorReadoutMaterial.BuildControl(Constraints.Unbounded); anchorReadout.Bounds = ContentAggregator.NewRow(anchorReadout.Bounds.Height); Add(anchorReadout); Material nameplateMaterial = UiPacksheetMaterials.YellowPanel; Panel nameplateLabel = Widgets.ButtonLabel("world.arrow").BuildControl(Constraints.Unbounded); Vector nameplateSize = nameplateMaterial.BoundsFor(nameplateLabel.Bounds).Size; arrowNameplate = new WorldAnchoredOverlay(world.control.Camera, world.uiWorld.camera) { DebugName = "world.arrow nameplate", Material = nameplateMaterial, Bounds = Rect.FromPositionAndSize(Vector.Zero, nameplateSize), WorldPosition = ArrowHome, OffsetFromAnchor = new Vector(-nameplateSize.X / 2, -nameplateSize.Y - 14), }; nameplateLabel.FillParent(arrowNameplate); Add(arrowNameplate); } /// /// Walks the arrow around its home position so the nameplate has something to track without the user /// having to drag the camera. /// public override void Update(TimeSpan elapsed) { arrowOrbitPhase += elapsed.TotalSeconds / ArrowOrbitSecondsPerTurn * 2 * Math.PI; Vector position = ArrowHome + new Vector( Math.Cos(arrowOrbitPhase) * ArrowOrbitRadius, Math.Sin(arrowOrbitPhase) * ArrowOrbitRadius); var entity = new Entity(arrow, new AffineMatrix(arrowOrbitPhase, position)); world.cel.ReplaceEntity(arrowHandle, ref entity); arrowNameplate.WorldPosition = position; Vector glassPosition = world.control.Camera .WorldToWorldOf(world.uiWorld.camera).TransformPoint(position); anchorReadoutMaterial.Text = $"anchor: world ({position.X:F0}, {position.Y:F0})" + $" -> glass ({glassPosition.X:F0}, {glassPosition.Y:F0})"; } } }