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})";
}
}
}