diff --git a/UnrealHyperTwist/Config/DefaultInput.ini b/UnrealHyperTwist/Config/DefaultInput.ini index cc0315f..5687215 100644 --- a/UnrealHyperTwist/Config/DefaultInput.ini +++ b/UnrealHyperTwist/Config/DefaultInput.ini @@ -95,6 +95,10 @@ DefaultTouchInterface=/Engine/MobileResources/HUD/DefaultVirtualJoysticks.Defaul +AxisMappings=(AxisName="HyperTwistXr_MoveRight",Key=OculusTouch_Left_Thumbstick_X,Scale=1.000000) +AxisMappings=(AxisName="HyperTwistXr_MoveRight",Key=ValveIndex_Left_Thumbstick_X,Scale=1.000000) +AxisMappings=(AxisName="HyperTwistXr_MoveRight",Key=ValveIndex_Left_Trackpad_X,Scale=1.000000) ++AxisMappings=(AxisName="HyperTwistXr_VerticalAdjust",Key=PageUp,Scale=1.000000) ++AxisMappings=(AxisName="HyperTwistXr_VerticalAdjust",Key=PageDown,Scale=-1.000000) ++AxisMappings=(AxisName="HyperTwistXr_VerticalAdjust",Key=Gamepad_RightShoulder,Scale=1.000000) ++AxisMappings=(AxisName="HyperTwistXr_VerticalAdjust",Key=Gamepad_LeftShoulder,Scale=-1.000000) +AxisMappings=(AxisName="HyperTwistXr_Turn",Key=Gamepad_RightX,Scale=1.000000) +AxisMappings=(AxisName="HyperTwistXr_Turn",Key=Vive_Right_Trackpad_X,Scale=1.000000) +AxisMappings=(AxisName="HyperTwistXr_Turn",Key=MixedReality_Right_Thumbstick_X,Scale=1.000000) diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp index 284947e..751b409 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingPanelWidget.cpp @@ -23,6 +23,7 @@ namespace HyperTwistTrainingPanelWidgetInternal bool bEnhancedInputProjectGroundworkPresent = false; bool bMotionControllerGroundworkPresent = false; bool bOpenXrProjectPluginEnabled = false; + bool bXrVerticalAdjustmentAxisPresent = false; int32 MotionControllerFamilyCount = 0; TArray MotionControllerFamilies; }; @@ -434,6 +435,17 @@ namespace HyperTwistTrainingPanelWidgetInternal AxisConfigEntries, GInputIni ); + TArray AxisMappingEntries; + GConfig->GetArray( + InputSettingsSection, + TEXT("AxisMappings"), + AxisMappingEntries, + GInputIni + ); + Facts.bXrVerticalAdjustmentAxisPresent = AxisConfigEntriesContainToken( + AxisMappingEntries, + TEXT("AxisName=\"HyperTwistXr_VerticalAdjust\"") + ); const struct { @@ -645,7 +657,7 @@ namespace HyperTwistTrainingPanelWidgetInternal FString BuildXrPreferenceOwnerLine(const FXrRuntimeOwnershipFacts& Facts) { return FString::Printf( - TEXT("XR preference ownership: %s | profile %s | posture %s | dominant hand %s | snap turn %.0f | world scale %.0f | preference fields %d."), + TEXT("XR preference ownership: %s | profile %s | posture %s | dominant hand %s | snap turn %.0f | vertical adjust %.0f | pointer %.0f | world scale %.0f | preference fields %d."), *DescribeReadyState(Facts.bPreferenceProfileReady), *DescribeIdIfReady( Facts.bPreferenceProfileReady, @@ -653,6 +665,8 @@ namespace HyperTwistTrainingPanelWidgetInternal *DescribeXrPostureMode(Facts.PreferenceProfile.PostureMode), *DescribeXrDominantHand(Facts.PreferenceProfile.DominantHand), Facts.PreferenceProfile.SnapTurnDegrees, + Facts.PreferenceProfile.VerticalAdjustmentUnitsPerSecond, + Facts.PreferenceProfile.PointerLengthUnits, Facts.PreferenceProfile.WorldToMetersScale, DescribeCountIfReady( Facts.bPreferenceProfileReady, @@ -1419,6 +1433,8 @@ UHyperTwistTrainingPanelWidget::GetDisplayedControlInputReadinessInspectSurface( Surface.MotionControllerFamilyCount = InputGroundworkFacts.MotionControllerFamilyCount; Surface.bOpenXrProjectPluginEnabled = InputGroundworkFacts.bOpenXrProjectPluginEnabled; + Surface.bXrVerticalAdjustmentAxisPresent = + InputGroundworkFacts.bXrVerticalAdjustmentAxisPresent; FHyperTwistTrainingImmersivePresenceControlContract PresenceControlContract; Surface.bImmersivePresenceContractReady = @@ -1512,7 +1528,7 @@ UHyperTwistTrainingPanelWidget::GetDisplayedControlInputReadinessInspectSurface( ? FString::Join(InputGroundworkFacts.MotionControllerFamilies, TEXT(", ")) : FString(TEXT("none")); Surface.XrStatusLine = FString::Printf( - TEXT("XR groundwork: EnhancedInput config %s | motion controls %s | controller families %d (%s) | OpenXR project plugin %s | immersive presence contract %s | bounded runtime owner %s (%s) | controller binding %s (%s) | finished XR runtime not yet shipped."), + TEXT("XR groundwork: EnhancedInput config %s | motion controls %s | controller families %d (%s) | OpenXR project plugin %s | vertical adjust axis %s | immersive presence contract %s | bounded runtime owner %s (%s) | controller binding %s (%s) | finished XR runtime not yet shipped."), *HyperTwistTrainingPanelWidgetInternal::DescribeBool( Surface.bEnhancedInputProjectGroundworkPresent), *HyperTwistTrainingPanelWidgetInternal::DescribeBool( @@ -1521,6 +1537,8 @@ UHyperTwistTrainingPanelWidget::GetDisplayedControlInputReadinessInspectSurface( *MotionControllerFamilyLabels, *HyperTwistTrainingPanelWidgetInternal::DescribeBool( Surface.bOpenXrProjectPluginEnabled), + *HyperTwistTrainingPanelWidgetInternal::DescribeBool( + Surface.bXrVerticalAdjustmentAxisPresent), *HyperTwistTrainingPanelWidgetInternal::DescribeBool( Surface.bImmersivePresenceContractReady), *HyperTwistTrainingPanelWidgetInternal::DescribeBool( diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistXR/HyperTwistXrTrainingPawn.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistXR/HyperTwistXrTrainingPawn.cpp index a84b26b..77101cc 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistXR/HyperTwistXrTrainingPawn.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistXR/HyperTwistXrTrainingPawn.cpp @@ -11,8 +11,26 @@ namespace HyperTwistXrTrainingPawnInternal { constexpr float SeatedHeightOffset = -42.0f; constexpr float HybridHeightOffset = -20.0f; + constexpr float ManualVerticalAdjustmentMin = -160.0f; + constexpr float ManualVerticalAdjustmentMax = 160.0f; constexpr float DesktopFallbackPitchMin = -65.0f; constexpr float DesktopFallbackPitchMax = 65.0f; + constexpr float MoveSpeedMin = 40.0f; + constexpr float MoveSpeedMax = 600.0f; + constexpr float VerticalAdjustmentSpeedMin = 40.0f; + constexpr float VerticalAdjustmentSpeedMax = 320.0f; + constexpr float SnapTurnDegreesMin = 5.0f; + constexpr float SnapTurnDegreesMax = 90.0f; + constexpr float SnapTurnAxisThresholdMin = 0.2f; + constexpr float SnapTurnAxisThresholdMax = 0.95f; + constexpr float SmoothTurnDegreesPerSecondMin = 45.0f; + constexpr float SmoothTurnDegreesPerSecondMax = 240.0f; + constexpr float PointerLengthMin = 32.0f; + constexpr float PointerLengthMax = 320.0f; + constexpr float WorldToMetersScaleMin = 25.0f; + constexpr float WorldToMetersScaleMax = 400.0f; + constexpr float ControllerDeadZoneMin = 0.0f; + constexpr float ControllerDeadZoneMax = 0.95f; FVector BuildPlanarDirection(const FVector& RawDirection) { @@ -45,6 +63,87 @@ namespace HyperTwistXrTrainingPawnInternal return TEXT("standing"); } } + + FHyperTwistXrPreferenceProfile SanitizePreferenceProfile( + const FHyperTwistXrPreferenceProfile& PreferenceProfile + ) + { + const FHyperTwistXrPreferenceProfile DefaultProfile = + UHyperTwistXrRuntimeLibrary::GetDefaultXrPreferenceProfile(); + if (!PreferenceProfile.IsStructurallyValid()) + { + return DefaultProfile; + } + + FHyperTwistXrPreferenceProfile Sanitized = PreferenceProfile; + Sanitized.MoveSpeedUnitsPerSecond = FMath::Clamp( + Sanitized.MoveSpeedUnitsPerSecond, + MoveSpeedMin, + MoveSpeedMax + ); + Sanitized.VerticalAdjustmentUnitsPerSecond = FMath::Clamp( + Sanitized.VerticalAdjustmentUnitsPerSecond, + VerticalAdjustmentSpeedMin, + VerticalAdjustmentSpeedMax + ); + Sanitized.SnapTurnDegrees = FMath::Clamp( + Sanitized.SnapTurnDegrees, + SnapTurnDegreesMin, + SnapTurnDegreesMax + ); + Sanitized.SnapTurnAxisThreshold = FMath::Clamp( + Sanitized.SnapTurnAxisThreshold, + SnapTurnAxisThresholdMin, + SnapTurnAxisThresholdMax + ); + const float SnapTurnResetThresholdMax = FMath::Max( + 0.0f, + Sanitized.SnapTurnAxisThreshold - 0.05f + ); + Sanitized.SnapTurnResetThreshold = FMath::Clamp( + Sanitized.SnapTurnResetThreshold, + 0.0f, + SnapTurnResetThresholdMax + ); + Sanitized.SmoothTurnDegreesPerSecond = FMath::Clamp( + Sanitized.SmoothTurnDegreesPerSecond, + SmoothTurnDegreesPerSecondMin, + SmoothTurnDegreesPerSecondMax + ); + Sanitized.PointerLengthUnits = FMath::Clamp( + Sanitized.PointerLengthUnits, + PointerLengthMin, + PointerLengthMax + ); + Sanitized.WorldToMetersScale = FMath::Clamp( + Sanitized.WorldToMetersScale, + WorldToMetersScaleMin, + WorldToMetersScaleMax + ); + Sanitized.ComfortVignetteStrength = FMath::Clamp( + Sanitized.ComfortVignetteStrength, + 0.0f, + 1.0f + ); + Sanitized.ControllerDeadZone = FMath::Clamp( + Sanitized.ControllerDeadZone, + ControllerDeadZoneMin, + ControllerDeadZoneMax + ); + if (Sanitized.PreferenceFieldTags.Num() <= 0) + { + Sanitized.PreferenceFieldTags = DefaultProfile.PreferenceFieldTags; + } + if (Sanitized.ComfortSurfaceTags.Num() <= 0) + { + Sanitized.ComfortSurfaceTags = DefaultProfile.ComfortSurfaceTags; + } + if (Sanitized.ExplicitExclusions.Num() <= 0) + { + Sanitized.ExplicitExclusions = DefaultProfile.ExplicitExclusions; + } + return Sanitized; + } } AHyperTwistXrTrainingPawn::AHyperTwistXrTrainingPawn() @@ -97,6 +196,7 @@ void AHyperTwistXrTrainingPawn::Tick(const float DeltaSeconds) RefreshHeadMountedDisplayState(); UpdateSmoothLocomotion(DeltaSeconds); + UpdateVerticalAdjustment(DeltaSeconds); UpdateTurn(DeltaSeconds); UpdateDesktopFallbackPitch(DeltaSeconds); } @@ -121,6 +221,11 @@ void AHyperTwistXrTrainingPawn::SetupPlayerInputComponent( this, &AHyperTwistXrTrainingPawn::HandleMoveRight ); + PlayerInputComponent->BindAxis( + TEXT("HyperTwistXr_VerticalAdjust"), + this, + &AHyperTwistXrTrainingPawn::HandleVerticalAdjustAxis + ); PlayerInputComponent->BindAxis( TEXT("HyperTwistXr_Turn"), this, @@ -175,12 +280,10 @@ void AHyperTwistXrTrainingPawn::ApplyPreferenceProfile( const FHyperTwistXrPreferenceProfile& PreferenceProfile ) { - if (!PreferenceProfile.IsStructurallyValid()) - { - return; - } - - ActivePreferenceProfile = PreferenceProfile; + ActivePreferenceProfile = + HyperTwistXrTrainingPawnInternal::SanitizePreferenceProfile( + PreferenceProfile + ); ApplyPostureOffset(); ApplyPointerAnchors(); @@ -218,6 +321,7 @@ void AHyperTwistXrTrainingPawn::SetSnapTurnEnabled(const bool bEnabled) void AHyperTwistXrTrainingPawn::ResetXrOrigin() { + ManualVerticalAdjustmentUnits = 0.0f; DesktopFallbackPitchDegrees = 0.0f; ApplyDesktopFallbackRotation(); ApplyPostureOffset(); @@ -235,7 +339,7 @@ FString AHyperTwistXrTrainingPawn::BuildRuntimeStatusLine() const const FHyperTwistXrRuntimeOwnershipSurface RuntimeSurface = GetBoundedRuntimeOwnershipSurface(); return FString::Printf( - TEXT("OpenXR runtime owner %s | preferences %s | bindings %s | HMD connected %s | HMD enabled %s | posture %s | dominant hand %s | snap turn %s | smooth locomotion %s | grip %.2f/%.2f | trigger %.2f/%.2f"), + TEXT("OpenXR runtime owner %s | preferences %s | bindings %s | HMD connected %s | HMD enabled %s | posture %s | dominant hand %s | snap turn %s | smooth locomotion %s | vertical offset %.0f | desktop pitch %.0f | grip %.2f/%.2f | trigger %.2f/%.2f"), *RuntimeSurface.RuntimeOwnerId, *ActivePreferenceProfile.ProfileId, *ActiveControllerBindingProfile.ProfileId, @@ -247,6 +351,8 @@ FString AHyperTwistXrTrainingPawn::BuildRuntimeStatusLine() const ActivePreferenceProfile.DominantHand), ActivePreferenceProfile.bSnapTurnEnabled ? TEXT("on") : TEXT("off"), ActivePreferenceProfile.bSmoothLocomotionEnabled ? TEXT("on") : TEXT("off"), + ManualVerticalAdjustmentUnits, + DesktopFallbackPitchDegrees, LeftGripValue, RightGripValue, LeftTriggerValue, @@ -274,7 +380,9 @@ void AHyperTwistXrTrainingPawn::ApplyPostureOffset() break; } - VROrigin->SetRelativeLocation(FVector(0.0f, 0.0f, HeightOffset)); + VROrigin->SetRelativeLocation( + FVector(0.0f, 0.0f, HeightOffset + ManualVerticalAdjustmentUnits) + ); } void AHyperTwistXrTrainingPawn::ApplyPointerAnchors() @@ -313,10 +421,25 @@ void AHyperTwistXrTrainingPawn::ApplyDesktopFallbackRotation() void AHyperTwistXrTrainingPawn::RefreshHeadMountedDisplayState() { + const bool bWasHeadMountedDisplayEnabled = bHeadMountedDisplayEnabled; bHeadMountedDisplayConnected = UHeadMountedDisplayFunctionLibrary::IsHeadMountedDisplayConnected(); bHeadMountedDisplayEnabled = UHeadMountedDisplayFunctionLibrary::IsHeadMountedDisplayEnabled(); + if (bHeadMountedDisplayEnabled && !bWasHeadMountedDisplayEnabled) + { + DesktopFallbackPitchDegrees = 0.0f; + if (VROrigin != nullptr) + { + VROrigin->SetRelativeRotation(FRotator::ZeroRotator); + } + return; + } + + if (!bHeadMountedDisplayEnabled && bWasHeadMountedDisplayEnabled) + { + ApplyDesktopFallbackRotation(); + } } void AHyperTwistXrTrainingPawn::UpdateSmoothLocomotion(const float DeltaSeconds) @@ -352,6 +475,33 @@ void AHyperTwistXrTrainingPawn::UpdateSmoothLocomotion(const float DeltaSeconds) AddActorWorldOffset(Delta, true); } +void AHyperTwistXrTrainingPawn::UpdateVerticalAdjustment(const float DeltaSeconds) +{ + if (!ActivePreferenceProfile.bAllowVerticalAdjustment || VROrigin == nullptr) + { + return; + } + + const float VerticalAdjustValue = HyperTwistXrTrainingPawnInternal::ApplyDeadZone( + PendingVerticalAdjust, + ActivePreferenceProfile.ControllerDeadZone + ); + if (FMath::IsNearlyZero(VerticalAdjustValue)) + { + return; + } + + ManualVerticalAdjustmentUnits = FMath::Clamp( + ManualVerticalAdjustmentUnits + + VerticalAdjustValue + * ActivePreferenceProfile.VerticalAdjustmentUnitsPerSecond + * DeltaSeconds, + HyperTwistXrTrainingPawnInternal::ManualVerticalAdjustmentMin, + HyperTwistXrTrainingPawnInternal::ManualVerticalAdjustmentMax + ); + ApplyPostureOffset(); +} + void AHyperTwistXrTrainingPawn::UpdateTurn(const float DeltaSeconds) { const float TurnValue = HyperTwistXrTrainingPawnInternal::ApplyDeadZone( @@ -430,6 +580,11 @@ void AHyperTwistXrTrainingPawn::HandleMoveRight(const float Value) PendingMoveRight = Value; } +void AHyperTwistXrTrainingPawn::HandleVerticalAdjustAxis(const float Value) +{ + PendingVerticalAdjust = Value; +} + void AHyperTwistXrTrainingPawn::HandleTurnAxis(const float Value) { PendingTurnAxis = Value; @@ -442,22 +597,22 @@ void AHyperTwistXrTrainingPawn::HandleLookPitchAxis(const float Value) void AHyperTwistXrTrainingPawn::HandleLeftGripAxis(const float Value) { - LeftGripValue = Value; + LeftGripValue = FMath::Clamp(Value, 0.0f, 1.0f); } void AHyperTwistXrTrainingPawn::HandleRightGripAxis(const float Value) { - RightGripValue = Value; + RightGripValue = FMath::Clamp(Value, 0.0f, 1.0f); } void AHyperTwistXrTrainingPawn::HandleLeftTriggerAxis(const float Value) { - LeftTriggerValue = Value; + LeftTriggerValue = FMath::Clamp(Value, 0.0f, 1.0f); } void AHyperTwistXrTrainingPawn::HandleRightTriggerAxis(const float Value) { - RightTriggerValue = Value; + RightTriggerValue = FMath::Clamp(Value, 0.0f, 1.0f); } void AHyperTwistXrTrainingPawn::HandleToggleSnapTurn() diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h index 28fe2ce..23014e6 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistTraining/HyperTwistTrainingTypes.h @@ -10057,6 +10057,9 @@ struct FHyperTwistTrainingControlInputReadinessInspectSurface UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") bool bOpenXrProjectPluginEnabled = false; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") + bool bXrVerticalAdjustmentAxisPresent = false; + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "HyperTwist") bool bClassicCubeMouseInputReady = false; diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistXR/HyperTwistXrTrainingPawn.h b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistXR/HyperTwistXrTrainingPawn.h index 124da96..91c6845 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistXR/HyperTwistXrTrainingPawn.h +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistXR/HyperTwistXrTrainingPawn.h @@ -96,11 +96,13 @@ protected: void ApplyDesktopFallbackRotation(); void RefreshHeadMountedDisplayState(); void UpdateSmoothLocomotion(float DeltaSeconds); + void UpdateVerticalAdjustment(float DeltaSeconds); void UpdateTurn(float DeltaSeconds); void UpdateDesktopFallbackPitch(float DeltaSeconds); void HandleMoveForward(float Value); void HandleMoveRight(float Value); + void HandleVerticalAdjustAxis(float Value); void HandleTurnAxis(float Value); void HandleLookPitchAxis(float Value); void HandleLeftGripAxis(float Value); @@ -112,8 +114,10 @@ protected: float PendingMoveForward = 0.0f; float PendingMoveRight = 0.0f; + float PendingVerticalAdjust = 0.0f; float PendingTurnAxis = 0.0f; float PendingLookPitchAxis = 0.0f; + float ManualVerticalAdjustmentUnits = 0.0f; float DesktopFallbackPitchDegrees = 0.0f; bool bSnapTurnLatchOpen = true; }; diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistBrowserBridgeObjectTest.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistBrowserBridgeObjectTest.cpp index bd3f6d1..77df650 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistBrowserBridgeObjectTest.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistBrowserBridgeObjectTest.cpp @@ -1150,6 +1150,7 @@ bool FHyperTwistTrainingPanelControlInputReadinessInspectSurfaceTest::RunTest( TestTrue( TEXT("The control/input readiness surface must keep the current project OpenXR plugin posture explicit."), Surface.bOpenXrProjectPluginEnabled + && Surface.bXrVerticalAdjustmentAxisPresent ); TestFalse( TEXT("The control/input readiness surface must keep unfinished XR/runtime and preferences truth explicit."), @@ -1190,9 +1191,15 @@ bool FHyperTwistTrainingPanelControlInputReadinessInspectSurfaceTest::RunTest( TEXT("The control/input readiness surface must say plainly that finished XR runtime is not yet shipped."), Surface.XrStatusLine.Contains(TEXT("not yet shipped")) && Surface.XrStatusLine.Contains(TEXT("OpenXR project plugin yes")) + && Surface.XrStatusLine.Contains(TEXT("vertical adjust axis yes")) && Surface.XrStatusLine.Contains(TEXT("xr/openxr-desktop-training-runtime-owner")) && Surface.XrStatusLine.Contains(TEXT("xr-openxr-motion-controllers/v1")) ); + TestTrue( + TEXT("The control/input readiness surface must now spell out the bounded vertical-adjust and pointer preference ownership."), + Surface.XrPreferenceOwnerLine.Contains(TEXT("vertical adjust 120")) + && Surface.XrPreferenceOwnerLine.Contains(TEXT("pointer 180")) + ); TestTrue( TEXT("The control/input readiness surface must retain the fixed No-Go host decision and the later settings/rebinding follow-through."), Surface.NextPacketLine.Contains(TEXT("No-Go")) @@ -1257,6 +1264,7 @@ bool FHyperTwistCoachDashboardControlInputReadinessInspectSurfaceTest::RunTest( TEXT("The coach dashboard control/input readiness surface must retain the unfinished XR/runtime truth in its detail line."), Surface.DetailLine.Contains(TEXT("not yet shipped")) && Surface.DetailLine.Contains(TEXT("OpenXR project plugin yes")) + && Surface.DetailLine.Contains(TEXT("vertical adjust axis yes")) && Surface.DetailLine.Contains(TEXT("xr/openxr-desktop-training-runtime-owner")) ); TestTrue( @@ -1267,6 +1275,12 @@ bool FHyperTwistCoachDashboardControlInputReadinessInspectSurfaceTest::RunTest( TestTrue( TEXT("The coach dashboard control/input readiness surface must preserve the explicit enabled OpenXR project plugin truth."), Surface.bOpenXrProjectPluginEnabled + && Surface.bXrVerticalAdjustmentAxisPresent + ); + TestTrue( + TEXT("The coach dashboard control/input readiness surface must retain the bounded vertical-adjust and pointer preference ownership."), + Surface.DetailLine.Contains(TEXT("vertical adjust 120")) + && Surface.DetailLine.Contains(TEXT("pointer 180")) ); TestTrue( TEXT("The coach dashboard control/input readiness surface must expose the bounded XR runtime owner, preference profile, and controller binding profile."), diff --git a/UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistXrRuntimeContractTest.cpp b/UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistXrRuntimeContractTest.cpp index 59f9567..7e1b5f5 100644 --- a/UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistXrRuntimeContractTest.cpp +++ b/UnrealHyperTwist/Source/UnrealHyperTwist/Tests/HyperTwistXrRuntimeContractTest.cpp @@ -2,6 +2,9 @@ #include "Misc/AutomationTest.h" +#include "Components/SceneComponent.h" +#include "Math/UnrealMathUtility.h" + #include "HyperTwistXR/HyperTwistXrRuntimeLibrary.h" #include "HyperTwistXR/HyperTwistXrTrainingPawn.h" @@ -151,6 +154,92 @@ bool FHyperTwistXrTrainingPawnDefaultsTest::RunTest(const FString& Parameters) PawnDefaults->BuildRuntimeStatusLine().Contains(TEXT("xr/openxr-desktop-training-runtime-owner")) && PawnDefaults->BuildRuntimeStatusLine().Contains(TEXT("xr-openxr-training-preferences/v1")) && PawnDefaults->BuildRuntimeStatusLine().Contains(TEXT("xr-openxr-motion-controllers/v1")) + && PawnDefaults->BuildRuntimeStatusLine().Contains(TEXT("vertical offset 0")) + ); + return true; +} + +IMPLEMENT_SIMPLE_AUTOMATION_TEST( + FHyperTwistXrTrainingPawnPreferenceHardeningTest, + "HyperTwist.FirstParty.XR.TrainingPawnPreferenceHardening", + EAutomationTestFlags::EditorContext | EAutomationTestFlags::EngineFilter +) + +bool FHyperTwistXrTrainingPawnPreferenceHardeningTest::RunTest( + const FString& Parameters +) +{ + AHyperTwistXrTrainingPawn* Pawn = + NewObject(GetTransientPackage()); + TestNotNull(TEXT("The XR training pawn must be constructible for hardening checks."), Pawn); + if (Pawn == nullptr) + { + return false; + } + + FHyperTwistXrPreferenceProfile PreferenceProfile = + UHyperTwistXrRuntimeLibrary::GetDefaultXrPreferenceProfile(); + PreferenceProfile.MoveSpeedUnitsPerSecond = -25.0f; + PreferenceProfile.VerticalAdjustmentUnitsPerSecond = -5.0f; + PreferenceProfile.SnapTurnDegrees = 0.0f; + PreferenceProfile.SnapTurnAxisThreshold = 2.0f; + PreferenceProfile.SnapTurnResetThreshold = 2.0f; + PreferenceProfile.SmoothTurnDegreesPerSecond = 12.0f; + PreferenceProfile.PointerLengthUnits = 0.0f; + PreferenceProfile.WorldToMetersScale = 0.0f; + PreferenceProfile.ComfortVignetteStrength = 9.0f; + PreferenceProfile.ControllerDeadZone = -0.2f; + PreferenceProfile.PreferenceFieldTags.Reset(); + PreferenceProfile.ComfortSurfaceTags.Reset(); + PreferenceProfile.ExplicitExclusions.Reset(); + PreferenceProfile.PostureMode = EHyperTwistXrPostureMode::Hybrid; + + Pawn->ApplyPreferenceProfile(PreferenceProfile); + + TestTrue( + TEXT("The XR training pawn must clamp invalid movement and vertical-adjust speeds."), + Pawn->ActivePreferenceProfile.MoveSpeedUnitsPerSecond > 0.0f + && Pawn->ActivePreferenceProfile.VerticalAdjustmentUnitsPerSecond > 0.0f + && Pawn->ActivePreferenceProfile.MoveSpeedUnitsPerSecond + != PreferenceProfile.MoveSpeedUnitsPerSecond + && Pawn->ActivePreferenceProfile.VerticalAdjustmentUnitsPerSecond + != PreferenceProfile.VerticalAdjustmentUnitsPerSecond + ); + TestTrue( + TEXT("The XR training pawn must clamp turning, pointer, world-scale, comfort, and dead-zone bounds."), + Pawn->ActivePreferenceProfile.SnapTurnDegrees >= 5.0f + && Pawn->ActivePreferenceProfile.SnapTurnAxisThreshold <= 0.95f + && Pawn->ActivePreferenceProfile.SnapTurnResetThreshold + < Pawn->ActivePreferenceProfile.SnapTurnAxisThreshold + && Pawn->ActivePreferenceProfile.SmoothTurnDegreesPerSecond >= 45.0f + && Pawn->ActivePreferenceProfile.PointerLengthUnits > 0.0f + && Pawn->ActivePreferenceProfile.WorldToMetersScale > 0.0f + && Pawn->ActivePreferenceProfile.ComfortVignetteStrength <= 1.0f + && Pawn->ActivePreferenceProfile.ControllerDeadZone >= 0.0f + ); + TestTrue( + TEXT("The XR training pawn must restore default metadata arrays when a custom profile clears them."), + Pawn->ActivePreferenceProfile.PreferenceFieldTags.Num() > 0 + && Pawn->ActivePreferenceProfile.ComfortSurfaceTags.Num() > 0 + && Pawn->ActivePreferenceProfile.ExplicitExclusions.Num() > 0 + ); + TestTrue( + TEXT("The XR training pawn must apply the sanitized pointer anchor length and hybrid posture offset."), + Pawn->LeftPointerAnchor != nullptr + && Pawn->RightPointerAnchor != nullptr + && Pawn->VROrigin != nullptr + && FMath::IsNearlyEqual( + Pawn->LeftPointerAnchor->GetRelativeLocation().X, + Pawn->ActivePreferenceProfile.PointerLengthUnits + ) + && FMath::IsNearlyEqual( + Pawn->RightPointerAnchor->GetRelativeLocation().X, + Pawn->ActivePreferenceProfile.PointerLengthUnits + ) + && FMath::IsNearlyEqual( + Pawn->VROrigin->GetRelativeLocation().Z, + -20.0f + ) ); return true; } diff --git a/docs/ops/HYPERTWIST_NATIVE_XR_HOST_AND_PLUGIN_DECISION_PACKET_2026-06-24.md b/docs/ops/HYPERTWIST_NATIVE_XR_HOST_AND_PLUGIN_DECISION_PACKET_2026-06-24.md index e6fd9fc..ea1fad3 100644 --- a/docs/ops/HYPERTWIST_NATIVE_XR_HOST_AND_PLUGIN_DECISION_PACKET_2026-06-24.md +++ b/docs/ops/HYPERTWIST_NATIVE_XR_HOST_AND_PLUGIN_DECISION_PACKET_2026-06-24.md @@ -46,7 +46,10 @@ Current authoritative host posture: - `UnrealHyperTwist/Config/DefaultInput.ini` now proves real bounded controller-axis and action ownership across the current controller-family - roster instead of only broad motion-controller groundwork + roster instead of only broad motion-controller groundwork, and the current + same-family hardening follow-up also adds a bounded dedicated vertical-adjust + axis for desktop-hosted XR posture instead of leaving that owned preference + field inert - `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistXR/HyperTwistXrRuntimeLibrary.h` and `.../Private/HyperTwistXR/HyperTwistXrRuntimeLibrary.cpp` now define the first-party bounded runtime-owner, preference-profile, and controller-binding @@ -57,7 +60,9 @@ Current authoritative host posture: - `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistXR/HyperTwistXrTrainingPawn.h` and `.../Private/HyperTwistXR/HyperTwistXrTrainingPawn.cpp` now provide a bounded first-party XR training pawn with camera, controller anchors, - posture switching, desktop fallback pitch handling, recentering, and status + posture switching, desktop fallback pitch handling, recentering, bounded + manual vertical-adjust ownership, sanitized preference application, stale + desktop-fallback rotation clearing on headset activation, and status reporting - `UnrealHyperTwist/Source/UnrealHyperTwist/Private/HyperTwistTraining/HyperTwistTrainingImmersiveEnvironmentLibrary.cpp` keeps the immersive lane explicitly bounded to scenic training ownership @@ -110,6 +115,11 @@ Current authoritative host posture: with `3` focused XR tests succeeded - `Saved\AutomationReports\Browser-XrBoundedRuntime-20260701-Final\index.json` with all `26` `HyperTwist.Browser.*` tests succeeded +- the current same-family hardening continuation then also keeps the dedicated + vertical-adjust axis visible in native control/input inspect truth and keeps + XR preference ownership explicit about vertical-adjust plus pointer-length + control instead of relying on those facts to stay implicit in the preference + struct alone ## Interpretation diff --git a/docs/ops/HYPERTWIST_UNREAL_INPUT_AND_XR_COMPLETENESS_AUDIT_2026-06-22.md b/docs/ops/HYPERTWIST_UNREAL_INPUT_AND_XR_COMPLETENESS_AUDIT_2026-06-22.md index e363151..56d2d0c 100644 --- a/docs/ops/HYPERTWIST_UNREAL_INPUT_AND_XR_COMPLETENESS_AUDIT_2026-06-22.md +++ b/docs/ops/HYPERTWIST_UNREAL_INPUT_AND_XR_COMPLETENESS_AUDIT_2026-06-22.md @@ -336,7 +336,9 @@ truthfully claim: runtime module dependencies - `UnrealHyperTwist/Config/DefaultInput.ini` now owns explicit OpenXR-ready controller axes and actions across the currently bounded controller-family - roster + roster, and the current hardening follow-up also adds a bounded dedicated + vertical-adjust axis for desktop-hosted XR posture instead of leaving the + owned vertical-adjust preference field inert - `UnrealHyperTwist/Source/UnrealHyperTwist/Public/HyperTwistXR/HyperTwistXrRuntimeLibrary.h` and `.../Private/HyperTwistXR/HyperTwistXrRuntimeLibrary.cpp` now define the first-party runtime-owner, preference-profile, and controller-binding @@ -348,12 +350,19 @@ truthfully claim: and `.../Private/HyperTwistXR/HyperTwistXrTrainingPawn.cpp` now provide a bounded first-party training pawn with `CameraComponent`, `MotionControllerComponent` anchors, desktop fallback pitch handling, - posture switching, recentering, and owned runtime status reporting + posture switching, recentering, bounded manual vertical-adjust ownership, + sanitized preference application, stale-fallback-rotation clearing on HMD + activation, and owned runtime status reporting - the native control/input, control/settings, and control/profile inspect surfaces now also expose that bounded runtime-owner packet in structured fields and rendered operator lines, including the stable ids, preference field count `8`, required binding-axis count `8`, and supported controller family count `4` +- the same native control/input inspect surface now also keeps the dedicated + vertical-adjust axis presence explicit in structured state and rendered + operator text, so the current XR preference/profile packet no longer says it + owns vertical adjustment while the corresponding project input seam remains + silent - the maintained reverse-SSH Windows lane then revalidated that exact-source state on `C:\HyperTwist_worktrees\phase10validate`: - final exact-source editor build: diff --git a/docs/v6_5_deep_manual_pack/HyperTwist/FEATURE_REGISTRY.md b/docs/v6_5_deep_manual_pack/HyperTwist/FEATURE_REGISTRY.md index 3f3c707..9e1a658 100644 --- a/docs/v6_5_deep_manual_pack/HyperTwist/FEATURE_REGISTRY.md +++ b/docs/v6_5_deep_manual_pack/HyperTwist/FEATURE_REGISTRY.md @@ -145,8 +145,8 @@ Do not collapse those three tiers into one undifferentiated "features" voice. | Analytics/reporting surfaces | Implemented now | landed analytics/reporting packets | Reporting is real, but bounded to accepted retained slices. | | Rewritten training analytics and report reference grounding | Implemented now | `apache/echarts` retained permissive lane + first-party current code | Current live `Training Analytics` reference side includes four rewritten first-party targets grounded in retained `apache/echarts`: session outcome and progress reporting, analytics data-view and export, timing-trend history and overview interaction, and the optional richer explainer or sidecar boundary. This does not displace the landed `Phase 3R-C` first-party analytics/reporting owner or elevate `ecomfe/echarts-gl` and `ecomfe/zrender` beyond support-only sidecars. | | Browser/spatial/media adjunct surfaces | Implemented now | landed `three.js`, `react-three-fiber`, `xr`, `model-viewer`, `remotion` packets | These are implemented bounded families, and the current Phase 1 browser-runtime landing now includes a first-party authoritative `Content/Browser/index.html` shell, bundled-runtime upgrade path, committed clean-checkout plain-JS fallback runtime, embedded `UHyperTwistBrowserWidget` bridge, the `2026-06-19` runtime-ready queue hardening that retains outbound Unreal shell traffic until the browser runtime is ready, a same-day first-party `Browser Runtime Status` surface with shared boot-state ownership across bundled and fallback shells, a follow-on typed Unreal-side `browser-runtime-status` capture seam that retains the last valid runtime snapshot across unrelated later envelope traffic until explicit reset, a second follow-on typed Unreal-side `hypertwist-runtime-ready` capture seam that raises the original handshake payload out of raw JSON-only handling without changing queue flush behavior, a third same-day native/operator-facing status surface that consumes those typed seams inside Unreal while clearing retained runtime ownership on shell-authority change, a fourth same-day native training/operator diagnostics-panel continuation that wires that typed status ownership into `UHyperTwistTrainingPanelWidget` and the coach dashboard without making the dashboard inspect seam depend on stale rendered strings, a fifth same-day diagnostics-fidelity continuation that now carries bootstrap/runtime-ready timestamps, last command/shell-state receipt timestamps, and fallback reason through the same native operator/training seams, and a later `2026-06-23` control/input readiness continuation that projects shipped keyboard/input truth and unfinished XR/preferences truth through the same native training/operator surfaces. This still is not proof of unlimited browser-shell parity. | -| Bounded native OpenXR runtime-owner substrate | Implemented now | landed first-party `2026-07-01` bounded XR continuation | The desktop-hosted branch now enables `OpenXR` plus `XRBase`, ships first-party runtime-owner/profile ownership through `UHyperTwistXrRuntimeLibrary` and `AHyperTwistXrTrainingPawn`, and keeps the stable ids `xr/openxr-desktop-training-runtime-owner`, `xr-openxr-training-preferences/v1`, and `xr-openxr-motion-controllers/v1` visible through both native XR code and the operator/training inspect surfaces. The maintained reverse-SSH Windows lane revalidated the exact-source state on `C:\HyperTwist_worktrees\phase10validate` with final editor build `Result: Succeeded` at UnrealBuildTool `124.26 seconds`, focused XR automation report `XR-BoundedRuntimeOwner-20260701-Final` with `3/3` green, and full browser automation report `Browser-XrBoundedRuntime-20260701-Final` with all `26` `HyperTwist.Browser.*` tests green. Broader controller widening, polished rebinding/settings ownership, and packaged headset/controller proof remain explicitly unfinished. | -| Native control/input readiness inspect surface | Implemented now | landed first-party `2026-06-23` browser/native operator continuation | `UHyperTwistTrainingPanelWidget` and `UHyperTwistCoachDashboardWidget` now expose `FHyperTwistTrainingControlInputReadinessInspectSurface`, including the shipped `classic-wca-keyboard/v1` mapping, exact classic-cube pointer, orbit, zoom, and action-shortcut truth, higher-dimensional dedicated-family runtime ownership truth, project-level `EnhancedInput` plus motion-controller groundwork facts, immersive-presence contract presence, and explicit unfinished XR/runtime plus preferences truth. The rendered detail line now also carries the fixed desktop-hosted `No-Go` decision on native OpenXR/controller widening instead of leaving that truth stranded in a non-rendered follow-up field. A later same-day structured-boundary hardening follow-up then promoted that same XR/controller boundary into stable inspect-surface fields as well, including decision id `desktop-hosted-openxr-controller-widening-no-go` plus explicit reopen requirements for dedicated runtime owners, user-facing settings or rebinding ownership, and Windows packaged controller validation, so native operator automation no longer depends only on rendered prose to verify the boundary. The recovered primary reverse-SSH `localhost:22022` lane rebuilt maintained validation worktree `C:\HyperTwist_worktrees\phase10validate` on `2026-06-23` with `Result: Succeeded`, UnrealBuildTool `Total execution time: 1563.53 seconds`, and exported `Saved\AutomationReports\Browser-ControlInputReadiness-Verify\index.json` with all `16` `HyperTwist.Browser.*` tests passing, including `CoachDashboard.ControlInputReadinessInspectSurface` and `TrainingPanel.ControlInputReadinessInspectSurface`. A later same-day quality follow-up then moved the project-input-groundwork inspection behind that surface from raw text scanning into structured Unreal config reads, rebuilt the same maintained validation worktree again with `Result: Succeeded`, UnrealBuildTool `Total execution time: 158.13 seconds`, and re-exported focused browser proof to `Saved\AutomationReports\Browser-ControlInputReadiness-StructuredConfig-Verify` with all `16` `HyperTwist.Browser.*` tests green again. The latest same-family truth-render follow-up on `2026-06-24` then rebuilt that same maintained validation root with `Result: Succeeded`, UnrealBuildTool `Total execution time: 172.52 seconds`, and exported `Saved\AutomationReports\Browser-XrNoGoRendered-Verify\index.json` with all `20` `HyperTwist.Browser.*` tests green again, explicitly covering both training-panel and coach-dashboard control-input detail-line visibility for the `No-Go` host decision. A later same-day post-build structured-boundary follow-up then rebuilt that same maintained validation root once more with `Result: Succeeded`, UnrealBuildTool `Total execution time: 2323.18 seconds`, and exported `Saved\AutomationReports\Browser-XrBoundaryStructured-PostBuild-Verify\index.json` with all `20` `HyperTwist.Browser.*` tests green again, now explicitly covering the stable decision-id and reopen-requirement fields on both control-input inspect surfaces alongside the sibling control/settings and control/profile seams. A later same-day `2026-06-25` parity follow-up then widened the same surface from general readiness truth into literal roster truth by rendering dedicated classic-cube pointer and action-shortcut lines, rebuilding the maintained validation root with `Result: Succeeded` at `5278.57 seconds` and exact-source rerun `70.76 seconds`, and exporting `Saved\AutomationReports\Browser-ControlRosterParity-Verify\index.json` with all `21` `HyperTwist.Browser.*` tests green, including both training-panel and coach-dashboard control-input surfaces plus the structured dashboard artifact seam. The latest same-family `2026-06-28` ownership-truth follow-up then tightened higher-dimensional readiness again by resolving the shipped `Magic120Cell` and `MagicCube5D` dedicated-family host, view-context, session, and interactive-scene surfaces explicitly by activation profile id instead of treating broad catalog validity as equivalent proof. That exact-source-state follow-up rebuilt maintained validation root `C:\HyperTwist_worktrees\phase10validate` with `Result: Succeeded`, UnrealBuildTool `Total execution time: 109.14 seconds`, then passed all three focused browser filters `TrainingPanel.ControlInputReadinessInspectSurface`, `CoachDashboard.ControlInputReadinessInspectSurface`, and `CoachDashboard.ControlSurfaceStructuredTextArtifacts`, so both the inspect surfaces and the rendered structured dashboard row now keep concrete family ownership ids such as `phase6c/magic120cell/runtime-host-surface` and `phase6c/magiccube5d/interactive-scene-surface` visible to operators. A later same-day bounded preferences-continuity follow-up then tightened the non-`XR` seam again without reopening the controller lane: the preferences line now resolves the first valid viewer camera-export artifact instead of trusting only the first listed id, keeps `artifact/camera-export-json` plus `immersive-training-session-recall-boundary` visible with recall-scope and preference-field counts, and preserves the honest boundary that broader polished rebinding and control-settings UI are still not shipped. That exact-source continuity follow-up then rebuilt maintained validation root `C:\HyperTwist_worktrees\phase10validate` with `Result: Succeeded`, UnrealBuildTool `Total execution time: 3372.27 seconds`, and exported `Browser-PreferencesContinuity-20260628-HyperTwist.Browser.TrainingPanel.ControlInputReadinessInspectSurface`, `Browser-PreferencesContinuity-20260628-HyperTwist.Browser.CoachDashboard.ControlInputReadinessInspectSurface`, and `Browser-PreferencesContinuity-20260628-HyperTwist.Browser.CoachDashboard.ControlSurfaceStructuredTextArtifacts`, each with `index.json` `State=Success`, while the accepted unattended `Failed to create the web browser window.` dialog remained a non-blocking Windows editor lane artifact. | +| Bounded native OpenXR runtime-owner substrate | Implemented now | landed first-party `2026-07-01` bounded XR continuation | The desktop-hosted branch now enables `OpenXR` plus `XRBase`, ships first-party runtime-owner/profile ownership through `UHyperTwistXrRuntimeLibrary` and `AHyperTwistXrTrainingPawn`, and keeps the stable ids `xr/openxr-desktop-training-runtime-owner`, `xr-openxr-training-preferences/v1`, and `xr-openxr-motion-controllers/v1` visible through both native XR code and the operator/training inspect surfaces. The current same-family hardening pass also clamps invalid preference values back into bounded ranges, wires bounded manual vertical-adjust input into the XR training pawn instead of leaving that preference field inert, and clears stale desktop-fallback rotation when headset runtime becomes active so desktop look-pitch does not leak forward into live HMD posture. The maintained reverse-SSH Windows lane revalidated the exact-source state on `C:\HyperTwist_worktrees\phase10validate` with final editor build `Result: Succeeded` at UnrealBuildTool `124.26 seconds`, focused XR automation report `XR-BoundedRuntimeOwner-20260701-Final` with `3/3` green, and full browser automation report `Browser-XrBoundedRuntime-20260701-Final` with all `26` `HyperTwist.Browser.*` tests green. Broader controller widening, polished rebinding/settings ownership, and packaged headset/controller proof remain explicitly unfinished. | +| Native control/input readiness inspect surface | Implemented now | landed first-party `2026-06-23` browser/native operator continuation | `UHyperTwistTrainingPanelWidget` and `UHyperTwistCoachDashboardWidget` now expose `FHyperTwistTrainingControlInputReadinessInspectSurface`, including the shipped `classic-wca-keyboard/v1` mapping, exact classic-cube pointer, orbit, zoom, and action-shortcut truth, higher-dimensional dedicated-family runtime ownership truth, project-level `EnhancedInput` plus motion-controller groundwork facts, immersive-presence contract presence, and explicit unfinished XR/runtime plus preferences truth. The rendered detail line now also carries the fixed desktop-hosted `No-Go` decision on native OpenXR/controller widening instead of leaving that truth stranded in a non-rendered follow-up field. A later same-day structured-boundary hardening follow-up then promoted that same XR/controller boundary into stable inspect-surface fields as well, including decision id `desktop-hosted-openxr-controller-widening-no-go` plus explicit reopen requirements for dedicated runtime owners, user-facing settings or rebinding ownership, and Windows packaged controller validation, so native operator automation no longer depends only on rendered prose to verify the boundary. The recovered primary reverse-SSH `localhost:22022` lane rebuilt maintained validation worktree `C:\HyperTwist_worktrees\phase10validate` on `2026-06-23` with `Result: Succeeded`, UnrealBuildTool `Total execution time: 1563.53 seconds`, and exported `Saved\AutomationReports\Browser-ControlInputReadiness-Verify\index.json` with all `16` `HyperTwist.Browser.*` tests passing, including `CoachDashboard.ControlInputReadinessInspectSurface` and `TrainingPanel.ControlInputReadinessInspectSurface`. A later same-day quality follow-up then moved the project-input-groundwork inspection behind that surface from raw text scanning into structured Unreal config reads, rebuilt the same maintained validation worktree again with `Result: Succeeded`, UnrealBuildTool `Total execution time: 158.13 seconds`, and re-exported focused browser proof to `Saved\AutomationReports\Browser-ControlInputReadiness-StructuredConfig-Verify` with all `16` `HyperTwist.Browser.*` tests green again. The latest same-family truth-render follow-up on `2026-06-24` then rebuilt that same maintained validation root with `Result: Succeeded`, UnrealBuildTool `Total execution time: 172.52 seconds`, and exported `Saved\AutomationReports\Browser-XrNoGoRendered-Verify\index.json` with all `20` `HyperTwist.Browser.*` tests green again, explicitly covering both training-panel and coach-dashboard control-input detail-line visibility for the `No-Go` host decision. A later same-day post-build structured-boundary follow-up then rebuilt that same maintained validation root once more with `Result: Succeeded`, UnrealBuildTool `Total execution time: 2323.18 seconds`, and exported `Saved\AutomationReports\Browser-XrBoundaryStructured-PostBuild-Verify\index.json` with all `20` `HyperTwist.Browser.*` tests green again, now explicitly covering the stable decision-id and reopen-requirement fields on both control-input inspect surfaces alongside the sibling control/settings and control/profile seams. A later same-day `2026-06-25` parity follow-up then widened the same surface from general readiness truth into literal roster truth by rendering dedicated classic-cube pointer and action-shortcut lines, rebuilding the maintained validation root with `Result: Succeeded` at `5278.57 seconds` and exact-source rerun `70.76 seconds`, and exporting `Saved\AutomationReports\Browser-ControlRosterParity-Verify\index.json` with all `21` `HyperTwist.Browser.*` tests green, including both training-panel and coach-dashboard control-input surfaces plus the structured dashboard artifact seam. The latest same-family `2026-06-28` ownership-truth follow-up then tightened higher-dimensional readiness again by resolving the shipped `Magic120Cell` and `MagicCube5D` dedicated-family host, view-context, session, and interactive-scene surfaces explicitly by activation profile id instead of treating broad catalog validity as equivalent proof. That exact-source-state follow-up rebuilt maintained validation root `C:\HyperTwist_worktrees\phase10validate` with `Result: Succeeded`, UnrealBuildTool `Total execution time: 109.14 seconds`, then passed all three focused browser filters `TrainingPanel.ControlInputReadinessInspectSurface`, `CoachDashboard.ControlInputReadinessInspectSurface`, and `CoachDashboard.ControlSurfaceStructuredTextArtifacts`, so both the inspect surfaces and the rendered structured dashboard row now keep concrete family ownership ids such as `phase6c/magic120cell/runtime-host-surface` and `phase6c/magiccube5d/interactive-scene-surface` visible to operators. A later same-day bounded preferences-continuity follow-up then tightened the non-`XR` seam again without reopening the controller lane: the preferences line now resolves the first valid viewer camera-export artifact instead of trusting only the first listed id, keeps `artifact/camera-export-json` plus `immersive-training-session-recall-boundary` visible with recall-scope and preference-field counts, and preserves the honest boundary that broader polished rebinding and control-settings UI are still not shipped. That exact-source continuity follow-up then rebuilt maintained validation root `C:\HyperTwist_worktrees\phase10validate` with `Result: Succeeded`, UnrealBuildTool `Total execution time: 3372.27 seconds`, and exported `Browser-PreferencesContinuity-20260628-HyperTwist.Browser.TrainingPanel.ControlInputReadinessInspectSurface`, `Browser-PreferencesContinuity-20260628-HyperTwist.Browser.CoachDashboard.ControlInputReadinessInspectSurface`, and `Browser-PreferencesContinuity-20260628-HyperTwist.Browser.CoachDashboard.ControlSurfaceStructuredTextArtifacts`, each with `index.json` `State=Success`, while the accepted unattended `Failed to create the web browser window.` dialog remained a non-blocking Windows editor lane artifact. The current same-family XR hardening follow-up then widened that same inspect truth again without widening the broader `No-Go` decision: the native/operator surface now also proves the dedicated vertical-adjust axis is present in project input config, and the XR preference line now keeps vertical-adjust plus pointer-length ownership explicit instead of burying those bounded controls inside the profile only. | | Native control/settings ownership inspect surface | Implemented now | landed first-party `2026-06-24` browser/native operator continuation | `UHyperTwistTrainingPanelWidget` and `UHyperTwistCoachDashboardWidget` now expose `FHyperTwistTrainingControlSettingsOwnershipInspectSurface`, including the shipped viewer camera-settings owner `tool/camera-settings`, the immersive-presence control contract, dedicated-family `magic120cell-focus-view-profile` and `magiccube5d-projection-view-profile` view ownership, higher-dimensional selector ownership, persisted generated-mode selector recall when a structurally valid launch request is present, and explicit unfinished XR/controller rebinding truth. A later same-day structured-boundary hardening follow-up then carried the same `desktop-hosted-openxr-controller-widening-no-go` decision id plus the explicit reopen requirements for dedicated runtime owners, polished user-facing settings or rebinding ownership, and Windows packaged controller validation into this surface’s structured state as well, so control-settings diagnostics no longer have to prove the boundary only by substring matching their rendered detail line. The same primary reverse-SSH `localhost:22022` lane rebuilt maintained validation worktree `C:\HyperTwist_worktrees\phase10validate` on `2026-06-24` with `Result: Succeeded`, UnrealBuildTool `Total execution time: 4331.11 seconds`, and then exported `Saved\AutomationReports\Browser-ControlSettingsOwnership-Verify\index.json` with all `18` `HyperTwist.Browser.*` tests passing, including `CoachDashboard.ControlSettingsOwnershipInspectSurface`, `TrainingPanel.ControlSettingsOwnershipInspectSurface`, and the previously landed runtime plus control/input inspect seams. A later same-day selector-recall hardening follow-up then rebuilt that same maintained validation root again with `Result: Succeeded`, UnrealBuildTool `Total execution time: 4084.77 seconds`, and exported `Saved\AutomationReports\Browser-SelectorRecall-Verify\index.json` with all `20` `HyperTwist.Browser.*` tests passing, keeping the current control/settings proof aligned with the latest repository-backed selector-recall behavior. A later same-day post-build structured-boundary follow-up then rebuilt that same maintained validation root once more with `Result: Succeeded`, UnrealBuildTool `Total execution time: 2323.18 seconds`, and exported `Saved\AutomationReports\Browser-XrBoundaryStructured-PostBuild-Verify\index.json` with all `20` `HyperTwist.Browser.*` tests green again, now explicitly covering the stable decision-id and reopen-requirement fields on both control/settings inspect surfaces alongside the sibling control/input and control/profile seams. The latest same-family `2026-06-28` ownership-proof follow-up then tightened this settings seam again by resolving the active higher-dimensional session surface, active interactive-scene surface, dedicated-family session surface, dedicated-family interactive-scene surface, persistence-boundary id, and state-semantics id explicitly for both `Magic120Cell` and `MagicCube5D`, while also carrying concrete projection, symmetry, stereo, visibility, and focus tag counts plus persistence-readiness booleans in structured state instead of leaving that ownership implied by broad view-profile validity. That exact-source proof-hardening follow-up synced the tightened browser test source into the already-current maintained validation root, rebuilt it with `Result: Succeeded` and UnrealBuildTool `Total execution time: 168.35 seconds`, then passed focused browser proof for `HyperTwist.Browser.TrainingPanel.ControlSettingsOwnershipInspectSurface`, `HyperTwist.Browser.CoachDashboard.ControlSettingsOwnershipInspectSurface`, and `HyperTwist.Browser.CoachDashboard.ControlSurfaceStructuredTextArtifacts`, so both the inspect surfaces and the rendered structured dashboard row now keep exact family-owned ids such as `phase6c/magic120cell/dedicated-training-session-surface`, `phase6c/magic120cell/interactive-scene-surface`, `magic120cell-persistence-boundary`, `phase6c/magiccube5d/dedicated-training-session-surface`, and `phase6c/magiccube5d/family-owned-scene-state` visible to operators. A later same-day bounded preferences-continuity follow-up then tightened the same settings seam again without reopening native controller widening: the structured state now carries camera workflow, preview-state, export-artifact, immersive presence-surface, recall-scope, preference-field, and reset-surface counts together with explicit camera-continuity and immersive-session-recall readiness booleans; the rendered status text now distinguishes `missing`, `partial`, and `ready` ownership truth instead of collapsing those states; and the shipped `artifact/camera-export-json` plus `immersive-training-session-recall-boundary` ids now stay visible in both the training-panel and coach-dashboard settings detail lines. That exact-source continuity follow-up then rebuilt maintained validation root `C:\HyperTwist_worktrees\phase10validate` with `Result: Succeeded`, UnrealBuildTool `Total execution time: 3372.27 seconds`, and exported `Browser-PreferencesContinuity-20260628-HyperTwist.Browser.TrainingPanel.ControlSettingsOwnershipInspectSurface`, `Browser-PreferencesContinuity-20260628-HyperTwist.Browser.CoachDashboard.ControlSettingsOwnershipInspectSurface`, and `Browser-PreferencesContinuity-20260628-HyperTwist.Browser.CoachDashboard.ControlSurfaceStructuredTextArtifacts`, each with `index.json` `State=Success`, while the same accepted unattended `Failed to create the web browser window.` dialog remained non-blocking on the Windows editor lane. A later same-family `2026-06-30` degraded-state hardening follow-up then pulled that same readiness doctrine fully into the shared formatter layer itself, so absent higher-dimensional family ownership now renders as `missing` instead of being overstated as generic `partial`, one-sided view-versus-persistence lanes still render as `partial`, and both the dedicated family lines and the selector-ownership line now mask stale ids/counts while preserving the side of the lane that is actually ready. | | Native control/profile roster inspect surface | Implemented now | landed first-party `2026-06-24` browser/native operator continuation | `UHyperTwistTrainingPanelWidget` and `UHyperTwistCoachDashboardWidget` now expose `FHyperTwistTrainingControlProfileRosterInspectSurface`, keeping the actually shipped selectable native roster visible: classic keyboard profile `classic-wca-keyboard/v1` with `18` bindings plus the exact classic move roster, immersive-presence contract `immersive-training-presence-control-contract` with `3` intensity plus `3` reduced-distraction presets, dedicated-family `Magic120Cell` runtime/view ids `magic120cell-runtime-profile` and `magic120cell-focus-view-profile`, dedicated-family `MagicCube5D` runtime/view ids `magiccube5d-runtime-profile` and `magiccube5d-projection-view-profile`, the current `4` selectors in each dedicated-family roster, persisted generated-mode selector recall when a structurally valid launch request is present, and explicit unfinished controller rebinding truth. The rendered roster boundary now also states the fixed desktop-hosted `No-Go` decision on native OpenXR/controller widening instead of sounding like a merely pending generic settings packet. A later same-day structured-boundary hardening follow-up then carried the same `desktop-hosted-openxr-controller-widening-no-go` decision id plus its explicit reopen requirements into the roster surface’s structured state too, so native operator automation can prove the shipped roster boundary without depending only on rendered text. After hash-syncing the touched type/training/dashboard/test files into maintained validation worktree `C:\HyperTwist_worktrees\phase10validate`, the same primary reverse-SSH `localhost:22022` lane rebuilt the widened slice on `2026-06-24` with `Result: Succeeded`, UnrealBuildTool `Total execution time: 3606.23 seconds`, then exported `Saved\AutomationReports\Browser-ControlProfileRoster-Verify\index.json` with all `20` `HyperTwist.Browser.*` tests passing, including `CoachDashboard.ControlProfileRosterInspectSurface` and `TrainingPanel.ControlProfileRosterInspectSurface`. A later same-day selector-recall hardening follow-up then rebuilt that same maintained validation root again with `Result: Succeeded`, UnrealBuildTool `Total execution time: 4084.77 seconds`, and exported `Saved\AutomationReports\Browser-SelectorRecall-Verify\index.json` with all `20` `HyperTwist.Browser.*` tests passing again, now explicitly covering imported-request fallback plus active-deck precedence for repository-backed selector recall inside the training-panel and coach-dashboard roster surface. The latest same-family truth-render follow-up on `2026-06-24` then rebuilt that same maintained validation root with `Result: Succeeded`, UnrealBuildTool `Total execution time: 172.52 seconds`, and exported `Saved\AutomationReports\Browser-XrNoGoRendered-Verify\index.json` with all `20` `HyperTwist.Browser.*` tests green again, explicitly covering the roster detail-line visibility of the same fixed `No-Go` boundary. A later same-day post-build structured-boundary follow-up then rebuilt that same maintained validation root once more with `Result: Succeeded`, UnrealBuildTool `Total execution time: 2323.18 seconds`, and exported `Saved\AutomationReports\Browser-XrBoundaryStructured-PostBuild-Verify\index.json` with all `20` `HyperTwist.Browser.*` tests green again, now explicitly covering the stable decision-id and reopen-requirement fields on both control/profile inspect surfaces alongside the sibling control/input and control/settings seams. A later same-day `2026-06-25` parity follow-up then added the literal shipped classic move-roster line to that surface, mirrored it into dedicated coach-dashboard structured rows, rebuilt the maintained validation root with `Result: Succeeded` at `5278.57 seconds` and exact-source rerun `70.76 seconds`, and exported `Saved\AutomationReports\Browser-ControlRosterParity-Verify\index.json` with all `21` `HyperTwist.Browser.*` tests green, including both training-panel and coach-dashboard control/profile surfaces plus the structured dashboard artifact seam. The later same-family `2026-06-28` continuity follow-up then widened that roster seam again by carrying the active higher-dimensional scene id alongside the active activation, view-context, and session ids, while also resolving the first valid viewer camera-export artifact and keeping `artifact/camera-export-json` plus `immersive-training-session-recall-boundary` visible with recall-scope and preference-field counts through both the native detail line and a dedicated coach-dashboard structured row. The authoritative short-root recovery lane on `C:\HTpp` then re-synced the exact touched Unreal files, rebuilt the exact-source state with `Result: Succeeded` and UnrealBuildTool `Total execution time: 2374.44 seconds`, and exported both `Browser-ControlProfileContinuityParity-20260628-HyperTwist.Browser.TrainingPanel.ControlProfileContinuityParity` and `Browser-ControlProfileContinuityParity-20260628-HyperTwist.Browser.CoachDashboard.ControlProfileContinuityArtifacts` with `Result={Success}`, while the repeated `Failed to create the web browser window.` dialog remained a non-blocking unattended editor artifact. A later exact-source short-root recovery proof on `2026-06-29` then rebuilt the same touched state again with `Result: Succeeded` and UnrealBuildTool `Total execution time: 78.51 seconds`, re-exported `Saved\AutomationReports\ControlProfileContinuity-Rerun` with `HyperTwist.Browser.CoachDashboard.ControlProfileContinuityArtifacts`, `HyperTwist.Browser.TrainingPanel.ControlProfileContinuityParity`, and `HyperTwist.Browser.ControlProfileContinuityStateFormatting` all `Result={Success}`, and also re-exported `Saved\AutomationReports\ContinuityStateFormatting-Rerun` with the wider shared continuity-formatting trio green, so the roster-parity and shared continuity-helper seams are both grounded in current exact-source Windows proof rather than only the earlier broader maintained-root evidence. A final maintained-root exact-source hardening follow-up later that same day then tightened the shared continuity formatter so absent readiness masks stale ids and counts instead of leaking carried-over values, rebuilt `C:\HyperTwist_worktrees\phase10validate` with `Result: Succeeded` and UnrealBuildTool `Total execution time: 3325.07 seconds`, and exported `Continuity-CountMasking-20260629-*` with `HyperTwist.Browser.ControlProfileContinuityStateFormatting`, `HyperTwist.Browser.TrainingPanel.ControlProfileContinuityParity`, and `HyperTwist.Browser.CoachDashboard.ControlProfileContinuityArtifacts` all `Result={Success}`, keeping the shipped roster-continuity truth grounded in current maintained-root degraded-state proof as well. | | Native XR project-plugin posture inspect truth | Implemented now | landed first-party `2026-06-25` browser/native operator continuation | All three native control/input, control/settings, and control/profile inspect surfaces now expose the current project `OpenXR` plugin enabled/disabled fact in both structured state and rendered operator lines, so project-level motion-controller groundwork no longer masquerades as an enabled `OpenXR` shipping posture. The same primary reverse-SSH `localhost:22022` lane rebuilt the exact-source state on maintained validation root `C:\HyperTwist_worktrees\phase10validate` with `Result: Succeeded`, UnrealBuildTool `Total execution time: 99.38 seconds`, then exported `Saved\AutomationReports\Browser-XrPluginProjectPosture-Verify\index.json` with all `20` `HyperTwist.Browser.*` tests green across the training-panel and coach-dashboard input/settings/profile seams. | diff --git a/docs/v6_5_deep_manual_pack/HyperTwist/PRD.md b/docs/v6_5_deep_manual_pack/HyperTwist/PRD.md index 4aac24b..397d64f 100644 --- a/docs/v6_5_deep_manual_pack/HyperTwist/PRD.md +++ b/docs/v6_5_deep_manual_pack/HyperTwist/PRD.md @@ -165,7 +165,10 @@ statement forward too without overclaiming launch-tier completion: - `xr-openxr-training-preferences/v1` - `xr-openxr-motion-controllers/v1` - a dedicated `AHyperTwistXrTrainingPawn` now owns bounded camera/controller - anchors, posture switching, recentering, and desktop fallback pitch handling + anchors, posture switching, recentering, bounded manual vertical-adjust + ownership, sanitized preference application, and desktop fallback pitch + handling that now clears stale fallback rotation when a headset becomes + active - the maintained Windows validation lane then re-proved the exact-source state with: - editor build `Result: Succeeded`, UnrealBuildTool `Total execution time: diff --git a/website/src/__tests__/public-marketing-pages.test.tsx b/website/src/__tests__/public-marketing-pages.test.tsx index 62425f9..cf6fd32 100644 --- a/website/src/__tests__/public-marketing-pages.test.tsx +++ b/website/src/__tests__/public-marketing-pages.test.tsx @@ -1440,7 +1440,7 @@ describe('public marketing pages', () => { expect(screen.getAllByText(/classic-wca-keyboard\/v1/i).length).toBeGreaterThan(0) expect(screen.getByText('4. Open the higher-dimensional lanes and read the control boundary honestly')).toBeTruthy() expect(screen.getByText(/I\/K = R\/R', J\/F = U\/U', H\/G = F\/F'/i)).toBeTruthy() - expect(screen.getByText(/OpenXR plugin disabled/i)).toBeTruthy() + expect(screen.getByText(/bounded OpenXR plugin stack and first-party runtime-owner substrate/i)).toBeTruthy() expect(screen.getByText(/R scramble, H hint, Enter submit, F mode, V hold-to-talk, C cycle voice/i)).toBeTruthy() expect(screen.getAllByText(/latest persisted generated-mode selector posture is available for recall/i).length).toBeGreaterThan(0) expect(screen.getByText('Feature-registry-backed wording only')).toBeTruthy() diff --git a/website/src/site-data.ts b/website/src/site-data.ts index bd9d6d7..41ab3cf 100644 --- a/website/src/site-data.ts +++ b/website/src/site-data.ts @@ -604,7 +604,7 @@ export const inputAndDevicePostureCards = [ description: 'EnhancedInput posture and motion-controller groundwork exist, but HyperTwist does not yet market a fully finished OpenXR/controller/rebinding runtime lane.', bullets: [ 'Project config already carries EnhancedInput plus Vive, Oculus Touch, Mixed Reality, and Valve Index axis groundwork.', - 'The current shipping branch also keeps the project OpenXR plugin disabled, which is one reason the native XR/controller lane remains an explicit desktop-hosted No-Go today.', + 'The current shipping branch now enables the bounded OpenXR plugin stack and first-party runtime-owner substrate, but that still does not collapse the broader native XR/controller lane out of its explicit desktop-hosted No-Go posture.', 'Current product truth should not overclaim headset-specific runtime ownership or polished user-facing input rebinding.', 'The current host decision remains desktop-hosted No-Go on native OpenXR/controller widening; any future reopen would still need dedicated runtime owners, user-facing settings and controller-rebinding ownership, and Windows packaged controller validation with controller truth.', ],