Files
FoxyzandClaude Opus 5.5 2aff2b61da Body step 2.1: climbing, mantling, crawling, legs, playground, 144 Hz
- Step rule + lower-body collider: no walking onto tables, steps <= 0.35 m
- Structural hand strength: two-hand mantle/pull-up works, one hand can't
- Arms aim from the physical shoulder horizontally, keep True Body pose vertically
- Crawl pose, crawl carry, 35 deg slope limit, ground feed-forward ignores
  play-space corrections (fixes a runaway feedback loop)
- Grab static world (climbing), procedural visual legs
- Playground sandbox; menu strength slider, spectator (old drag) and turn toggles
- Physics 144 Hz with interpolation; legacy chase-body spikes pin 90 Hz
- RagdollSpike: 27 phases incl. mantle, pull-up, crawl, ledge drop, ramps

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 07:56:08 +03:00

110 lines
3.4 KiB
C#

using Godot;
public partial class Main : Node3D
{
[Signal]
public delegate void FocusLostEventHandler();
[Signal]
public delegate void FocusGainedEventHandler();
[Signal]
public delegate void PoseRecenteredEventHandler();
[Export]
public int MaximumRefreshRate { get; set; } = 90;
public const int PhysicsRate = 144;
private OpenXRInterface _xrInterface;
private bool _xrIsFocused;
public override void _Ready()
{
_xrInterface = (OpenXRInterface)XRServer.FindInterface("OpenXR");
if (_xrInterface != null && _xrInterface.IsInitialized())
{
GD.Print("OpenXR instantiated successfully.");
Viewport vp = GetViewport();
vp.UseXR = true;
DisplayServer.WindowSetVsyncMode(DisplayServer.VSyncMode.Disabled);
if (RenderingServer.GetRenderingDevice() != null)
vp.VrsMode = Viewport.VrsModeEnum.XR;
_xrInterface.SessionBegun += OnSessionBegun;
_xrInterface.SessionVisible += OnSessionVisible;
_xrInterface.SessionFocussed += OnSessionFocussed;
_xrInterface.SessionStopping += OnSessionStopping;
_xrInterface.PoseRecentered += OnPoseRecentered;
}
else
{
GD.PushWarning("OpenXR not instantiated, running in flat preview mode. Check headset and XR runtime.");
}
}
private void OnSessionBegun()
{
float currentRefreshRate = _xrInterface.DisplayRefreshRate;
GD.Print(currentRefreshRate > 0.0f
? $"OpenXR: refresh rate reported as {currentRefreshRate}"
: "OpenXR: no refresh rate given by XR runtime");
float newRate = currentRefreshRate;
var availableRates = _xrInterface.GetAvailableDisplayRefreshRates();
if (availableRates.Count == 0)
GD.Print("OpenXR: target does not support refresh rate extension");
else if (availableRates.Count == 1)
newRate = (float)availableRates[0];
else
{
foreach (float rate in availableRates)
{
if (rate > newRate && rate <= MaximumRefreshRate)
newRate = rate;
}
}
if (currentRefreshRate != newRate)
{
GD.Print($"OpenXR: setting refresh rate to {newRate}");
_xrInterface.DisplayRefreshRate = newRate;
currentRefreshRate = newRate;
}
// Physics runs at a fixed PhysicsRate (the ragdoll's joints like small steps), decoupled from
// the display; physics interpolation smooths rendering (XR origin subtree excluded — the
// runtime and locomotion place it every frame).
Engine.PhysicsTicksPerSecond = PhysicsRate;
}
private void OnSessionVisible()
{
if (_xrIsFocused)
{
GD.Print("OpenXR lost focus");
_xrIsFocused = false;
GetTree().Paused = true;
EmitSignal(SignalName.FocusLost);
}
}
private void OnSessionFocussed()
{
GD.Print("OpenXR gained focus");
_xrIsFocused = true;
GetTree().Paused = false;
EmitSignal(SignalName.FocusGained);
}
private void OnSessionStopping()
{
GD.Print("OpenXR is stopping");
}
private void OnPoseRecentered()
{
EmitSignal(SignalName.PoseRecentered);
}
}