123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168 |
- using Godot;
- // This is identical to the GDScript version, yet it doesn't work.
- [Tool]
- public partial class Gizmo25D : Node2D
- {
- // Not pixel perfect for all axes in all modes, but works well enough.
- // Rounding is not done until after the movement is finished.
- private const bool RoughlyRoundToPixels = true;
- // Set when the node is created.
- public Node25D node25d;
- public Node3D spatialNode;
- // Input from Viewport25D, represents if the mouse is clicked.
- public bool wantsToMove = false;
- // Used to control the state of movement.
- private bool _moving = false;
- private Vector2 _startPosition = Vector2.Zero;
- // Stores state of closest or currently used axis.
- private int dominantAxis;
- private Node2D linesRoot;
- private Line2D[] lines = new Line2D[3];
- public override void _Ready()
- {
- linesRoot = GetChild<Node2D>(0);
- lines[0] = linesRoot.GetChild<Line2D>(0);
- lines[1] = linesRoot.GetChild<Line2D>(1);
- lines[2] = linesRoot.GetChild<Line2D>(2);
- }
- public override void _Process(float delta)
- {
- if (lines == null)
- {
- return; // Somehow this node hasn't been set up yet.
- }
- if (node25d == null)
- {
- return; // We're most likely viewing the Gizmo25D scene.
- }
- // While getting the mouse position works in any viewport, it doesn't do
- // anything significant unless the mouse is in the 2.5D viewport.
- Vector2 mousePosition = GetLocalMousePosition();
- if (!_moving)
- {
- // If the mouse is farther than this many pixels, it won't grab anything.
- float closestDistance = 20.0f;
- dominantAxis = -1;
- for (int i = 0; i < 3; i++)
- {
- // Unrelated, but needs a loop too.
- Color modulateLine = lines[i].Modulate;
- modulateLine.a = 0.8f;
- lines[i].Modulate = modulateLine;
- var distance = DistanceToSegmentAtIndex(i, mousePosition);
- if (distance < closestDistance)
- {
- closestDistance = distance;
- dominantAxis = i;
- }
- }
- if (dominantAxis == -1)
- {
- // If we're not hovering over a line, ensure they are placed correctly.
- linesRoot.GlobalPosition = node25d.GlobalPosition;
- return;
- }
- }
- Color modulate = lines[dominantAxis].Modulate;
- modulate.a = 1;
- lines[dominantAxis].Modulate = modulate;
- if (!wantsToMove)
- {
- _moving = false;
- }
- else if (wantsToMove && !_moving)
- {
- _moving = true;
- _startPosition = mousePosition;
- }
- if (_moving)
- {
- // Change modulate of unselected axes.
- modulate = lines[(dominantAxis + 1) % 3].Modulate;
- modulate.a = 0.5f;
- lines[(dominantAxis + 1) % 3].Modulate = modulate;
- lines[(dominantAxis + 2) % 3].Modulate = modulate;
- // Calculate mouse movement and reset for next frame.
- var mouseDiff = mousePosition - _startPosition;
- _startPosition = mousePosition;
- // Calculate movement.
- var projectedDiff = mouseDiff.Project(lines[dominantAxis].Points[1]);
- var movement = projectedDiff.Length() / Node25D.SCALE;
- if (Mathf.IsEqualApprox(Mathf.Pi, projectedDiff.AngleTo(lines[dominantAxis].Points[1])))
- {
- movement *= -1;
- }
- // Apply movement.
- Transform3D t = spatialNode.Transform;
- t.origin += t.basis[dominantAxis] * movement;
- spatialNode.Transform = t;
- }
- else
- {
- // Make sure the gizmo is located at the object.
- GlobalPosition = node25d.GlobalPosition;
- if (RoughlyRoundToPixels)
- {
- Transform3D t = spatialNode.Transform;
- t.origin = (t.origin * Node25D.SCALE).Round() / Node25D.SCALE;
- spatialNode.Transform = t;
- }
- }
- // Move the gizmo lines appropriately.
- linesRoot.GlobalPosition = node25d.GlobalPosition;
- node25d.PropertyListChangedNotify();
- }
- // Initializes after _ready due to the onready vars, called manually in Viewport25D.gd.
- // Sets up the points based on the basis values of the Node25D.
- public void Initialize()
- {
- var basis = node25d.Basis25D;
- for (int i = 0; i < 3; i++)
- {
- lines[i].Points[1] = basis[i] * 3;
- }
- GlobalPosition = node25d.GlobalPosition;
- spatialNode = node25d.GetChild<Node3D>(0);
- }
- // Figures out if the mouse is very close to a segment. This method is
- // specialized for this script, it assumes that each segment starts at
- // (0, 0) and it provides a deadzone around the origin.
- private float DistanceToSegmentAtIndex(int index, Vector2 point)
- {
- if (lines == null)
- {
- return Mathf.Inf;
- }
- if (point.LengthSquared() < 400)
- {
- return Mathf.Inf;
- }
- Vector2 segmentEnd = lines[index].Points[1];
- float lengthSquared = segmentEnd.LengthSquared();
- if (lengthSquared < 400)
- {
- return Mathf.Inf;
- }
- var t = Mathf.Clamp(point.Dot(segmentEnd) / lengthSquared, 0, 1);
- var projection = t * segmentEnd;
- return point.DistanceTo(projection);
- }
- }
|