Files
2026-07-13 13:13:17 +08:00

132 lines
4.9 KiB
C#

using System.Windows.Forms;
using T3.Editor.App;
using T3.Editor.Gui.UiHelpers;
namespace T3.Editor.Gui.Interaction.Camera;
/// <summary>
/// Gathers update event information from SpaceMouse and apply it to a <see cref="CameraSetup"/>.
/// </summary>
public sealed partial class SpaceMouse : ICameraManipulator, IWindowsFormsMessageHandler
{
private readonly SpaceMouseDevice _spaceMouseDevice;
public SpaceMouse(IntPtr windowHandle)
{
_spaceMouseDevice = new(windowHandle);
}
public void ManipulateCamera(CameraSetup intendedSetup, double frameTime)
{
if (!_initialized)
{
_spaceMouseDevice.MotionEvent += SpaceMouseMotionHandler;
_spaceMouseDevice.ButtonEvent += SpaceMouseButtonHandler;
_initialized = true;
}
var wasEvaluatedThisFrame = Math.Abs(frameTime - _lastUpdateTime) < 0.0001f;
var stillMoving = _dampedRotation.LengthSquared() + _dampedTranslation.LengthSquared() > 0.001f;
if (_eventCount == 0 && !stillMoving)
return;
if (!wasEvaluatedThisFrame)
{
var tooLong = Math.Abs(frameTime - _lastUpdateTime) > 0.5f;
if (!tooLong)
{
_dampedRotation= Vector3.Lerp( _rotationSum,_dampedRotation, UserSettings.Config.SpaceMouseDamping);
_dampedTranslation= Vector3.Lerp( _translationSum, _dampedTranslation,UserSettings.Config.SpaceMouseDamping);
}
else
{
_dampedRotation = Vector3.Zero;
_dampedTranslation= Vector3.Zero;
}
_lastUpdateTime = frameTime;
}
_eventCount = 0;
_translationSum = new Vector3(0, 0, 0);
_rotationSum = new Vector3(0, 0, 0);
var viewDir = intendedSetup.Target - intendedSetup.Position;
var upDir = new Vector3(0, 1, 0);
var sideDir = Vector3.Cross(upDir, viewDir);
var viewDirLength = viewDir.Length();
viewDir /= viewDirLength;
// Prevent gimbal lock
if (viewDir.Y > 0.95f)
{
_dampedRotation.X = MathF.Min(_dampedRotation.X, 0);
}
else if (viewDir.Y < -0.95f)
{
_dampedRotation.X = MathF.Max(_dampedRotation.X, 0);
}
float translationVelocity = _dampedTranslation.Length() / 2000.0f;
var direction = _dampedTranslation;
if(direction.LengthSquared() > 0.0001f)
direction = Vector3.Normalize(direction);
if (translationVelocity < CameraInteractionParameters.MaxMoveVelocity)
direction *= translationVelocity;
else
direction *= CameraInteractionParameters.MaxMoveVelocity;
var moveDir = direction.X * sideDir - direction.Y * viewDir - direction.Z * upDir;
var rotAroundX = Matrix4x4.CreateFromAxisAngle(sideDir, -_dampedRotation.X / 8000.0f);
var rotAroundY = Matrix4x4.CreateFromAxisAngle(upDir, -_dampedRotation.Y / 8000.0f);
var rot = Matrix4x4.Multiply(rotAroundX, rotAroundY);
var newViewDir = Vector3.Transform(viewDir, rot);
newViewDir = Vector3.Normalize(newViewDir);
var oldPosition = intendedSetup.Position;
intendedSetup.Position += moveDir;
intendedSetup.Target = oldPosition + (moveDir + newViewDir * viewDirLength);
//Log.Debug($"space mouse move: {moveDir} eventCount:{_eventCount}");
}
private void SpaceMouseButtonHandler(object sender, SpaceMouseDevice.ButtonEventArgs e)
{
// Todo: Think about what the buttons can be used for
}
private void SpaceMouseMotionHandler(object sender, SpaceMouseDevice.MotionEventArgs e)
{
if (e.TranslationVector != null)
{
_translationSum.X -= e.TranslationVector.X * UserSettings.Config.SpaceMouseMoveSpeedFactor;
_translationSum.Y += e.TranslationVector.Y * UserSettings.Config.SpaceMouseMoveSpeedFactor;
_translationSum.Z += e.TranslationVector.Z * UserSettings.Config.SpaceMouseMoveSpeedFactor;
}
if (e.RotationVector != null)
{
// Swap axes from HID orientation to matching coordinates
_rotationSum.X += e.RotationVector.X * UserSettings.Config.SpaceMouseRotationSpeedFactor * 0.5f;
_rotationSum.Y += e.RotationVector.Z * UserSettings.Config.SpaceMouseRotationSpeedFactor;
_rotationSum.Z += e.RotationVector.Y * UserSettings.Config.SpaceMouseRotationSpeedFactor;
}
_eventCount++;
}
private int _eventCount;
private Vector3 _translationSum;
private Vector3 _rotationSum;
private Vector3 _dampedTranslation;
private Vector3 _dampedRotation;
private double _lastUpdateTime;
private bool _initialized;
public void ProcessMessage(Message message) => _spaceMouseDevice.ProcessMessage(message);
}