Code/Marker3d.cs
namespace Marker3d;
public class Marker3d : Component, Component.ExecuteInEditor {
[Property] public Color Tint { get; set; } = Color.White;
[Group("Center"), Title("Texture")]
[Property] public Texture Center { get; set; } = null;
[Group("Center"), Title("Tint")]
[Property] public Color CenterTint { get; set; } = Color.White;
[Group("Center"), Title("Scale")]
[Property] public float CenterScale { get; set; } = 1f;
[Group("Arrow"), Title("Texture"), Order(100)]
[Property] public Texture Arrow { get; set; } = null;
[Group("Arrow"), Title("Tint"), Order(100)]
[Property] public Color ArrowTint { get; set; } = Color.White;
[Group("Arrow"), Title("Rotation"), Order(100)]
[Property] public float ArrowRotation { get; set; } = 0f;
[Group("Arrow"), Title("Scale"), Order(100)]
[Property] public Vector2 ArrowScale { get; set; } = 1f;
[Group("Arrow"), Title("Distance"), Order(100)]
[Property] public float ArrowDistance { get; set; } = 32f;
[Property] public bool UseMarginPercent { get; set; } = false;
[ShowIf(nameof(UseMarginPercent), false)]
[Property] public Vector2 MarginPixels { get; set; } = 128f;
[ShowIf(nameof(UseMarginPercent), true)]
[Property, Range(0f, 0.5f)] public float MarginPercent { get; set; } = 0.05f;
protected Vector2 Margin => UseMarginPercent ? MarginPercent : (MarginPixels / Screen.Size);
[Property, Hide] public bool CenterOverridenOnce { get; set; } = false;
[Property, Hide] public bool ArrowOverridenOnce { get; set; } = false;
protected override void OnAwake() {
FixDefaults();
}
protected override void OnUpdate() {
FixDefaults();
}
protected void FixDefaults() {
if (!Scene.IsEditor) { return; }
if (!CenterOverridenOnce && Center == null) {
CenterOverridenOnce = true;
Center ??= Texture.Load("materials/gizmo/charactercontroller.png");
}
if (!ArrowOverridenOnce && Arrow == null) {
ArrowOverridenOnce = true;
Arrow ??= Texture.Load("textures/shapes/arrow2.vtex_c");
}
}
public Data? GetData() {
if (!Scene.Camera.IsValid()) { return null; }
var percentPosition = Scene.Camera.PointToScreenNormal(WorldPosition, out bool behind);
var center = Vector2.One * 0.5f;
var factor = Vector2.One - Margin;
percentPosition = (percentPosition - center) / factor + center;
if (behind) {
percentPosition = (percentPosition - center) * 1000f + center;
}
var showArrow = false;
var arrowDirection = (center - percentPosition).Normal;
if (percentPosition.x < 0f || percentPosition.x > 1f || percentPosition.y < 0f || percentPosition.y > 1f) {
var planes = new List<Plane>() {
new(Vector2.Zero, Vector2.Right),
new(Vector2.Zero, Vector2.Down),
new(Vector2.One, Vector2.Left),
new(Vector2.One, Vector2.Up),
};
var intersections = planes
.Select(x => (x, x.Trace(new Ray(center, (percentPosition - center).Normal))))
.SelectWhere<(Plane, Vector3?), (Plane, Vector3)>(x => x.Item2.HasValue ? (x.Item1, x.Item2.Value) : null)
.Select(x => (x.Item2, x.Item1.Normal, x.Item2.DistanceSquared(center)))
.OrderBy(x => x.Item3)
.Select(x => (x.Item1, x.Normal));
if (!intersections.Any()) {
return null;
}
var intersection = intersections.First();
percentPosition = intersection.Item1;
showArrow = true;
// arrowDirection = intersection.Normal;
}
percentPosition = (percentPosition - center) * factor + center;
var position = percentPosition;
var arrowRotation = float.Atan2(-arrowDirection.Normal.y, -arrowDirection.Normal.x) / float.Pi * 180f + 90f;
return new() {
Center = Center,
CenterTint = Tint * CenterTint,
CenterPosition = percentPosition,
CenterScale = CenterScale,
ShowArrow = showArrow,
Arrow = Arrow,
ArrowTint = Tint * ArrowTint,
ArrowRotation = arrowRotation,
ArrowLocalRotation = ArrowRotation,
ArrowScale = ArrowScale,
ArrowDistance = ArrowDistance,
};
}
public struct Data {
public Data() {
}
public Texture Center { get; set; }
public Color CenterTint { get; set; } = Color.White;
public Vector2 CenterPosition { get; set; }
public float CenterScale { get; set; }
public bool ShowArrow { get; set; }
public Texture Arrow { get; set; }
public Color ArrowTint { get; set; } = Color.White;
public float ArrowRotation { get; set; }
public float ArrowLocalRotation { get; set; }
public Vector2 ArrowScale { get; set; }
public float ArrowDistance { get; set; }
public readonly Vector2 ArrowOffset => Vector2.FromDegrees(ArrowRotation) * ArrowDistance;
public override readonly int GetHashCode() => HashCode.Combine(
Center,
CenterTint,
CenterPosition,
ShowArrow,
Arrow,
ArrowTint,
ArrowRotation,
ArrowDistance
);
}
}