Merge branch 'openxr-dx11-master' of github.com:arturoc/stride into openxr-dx11-master

This commit is contained in:
Arturo Castro 2022-11-30 16:42:58 +00:00
Родитель ad43724d99 6517900fc0
Коммит 41d598f6a9
9 изменённых файлов: 1574 добавлений и 1 удалений

3
deps/OpenXR/openxr_loader.dll поставляемый Normal file
Просмотреть файл

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7d0a7cbb3fd2fe6bebd8fb6717e2434223483a9d79e0b93b47395f6dd65272ad
size 2133504

Просмотреть файл

@ -26,13 +26,31 @@ namespace Stride.Rendering.Compositing
[DefaultValue(false)]
public bool IgnoreDevicePosition { get; set; } = false;
private bool _copyMirror = true;
/// <summary>
/// Specifies if VR rendering should be copied to the current render target.
/// </summary>
/// <userdoc>Copy VR rendering to the current render target. Leave disabled to have different rendering on desktop than VR headset.</userdoc>
[DataMember(25)]
[DefaultValue(true)]
public bool CopyMirror { get; set; } = true;
public bool CopyMirror
{
get
{
if(VRDevice != null)
{
return VRDevice.CanMirrorTexture && _copyMirror;
}
else
{
return _copyMirror;
}
}
set
{
_copyMirror = value;
}
}
[DataMember(30)]
public List<VRDeviceDescription> RequiredApis { get; } = new List<VRDeviceDescription>();

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -0,0 +1,293 @@
using System;
using System.Collections.Generic;
using System.Text;
using Silk.NET.Core.Native;
using Silk.NET.OpenXR;
namespace Stride.VirtualReality
{
class OpenXRInput
{
// different types of input we are interested in
public enum HAND_PATHS
{
BaseIndex = 0,
Position = 1,
TriggerValue = 2,
TriggerClick = 3,
ThumbstickX = 4,
ThumbstickY = 5,
ThumbstickClick = 6,
TrackpadX = 7,
TrackpadY = 8,
TrackpadClick = 9,
GripValue = 10,
GripClick = 11,
ButtonXA = 12, // x on left, a on right (or either index)
ButtonYB = 13, // y on left, b on right (or either index)
Menu = 14,
System = 15, // may be inaccessible
HapticOut = 16
}
public const int HAND_PATH_COUNT = 17;
// most likely matches for input types above
private static List<string>[] PathPriorities =
{
new List<string>() { "" }, // BaseIndex 0
new List<string>() { "/input/grip/pose" }, // Position 1
new List<string>() { "/input/trigger/value", "/input/select/value" }, // TriggerValue 2
new List<string>() { "/input/trigger/click", "/input/select/click" }, // TriggerClick 3
new List<string>() { "/input/thumbstick/x", "/input/trackpad/x" }, // ThumbstickX 4
new List<string>() { "/input/thumbstick/y", "/input/trackpad/y" }, // ThumbstickY 5
new List<string>() { "/input/thumbstick/click", "/input/trackpad/click" }, // ThumbstickClick 6
new List<string>() { "/input/trackpad/x", "/input/thumbstick/x" }, // TrackpadX 7
new List<string>() { "/input/trackpad/y", "/input/thumbstick/y" }, // TrackpadY 8
new List<string>() { "/input/trackpad/click", "/input/thumbstick/click" }, // TrackpadClick 9
new List<string>() { "/input/squeeze/value" }, // GripValue 10
new List<string>() { "/input/squeeze/click" }, // GripClick 11
new List<string>() { "/input/x/click", "/input/a/click" }, // ButtonXA 12
new List<string>() { "/input/y/click", "/input/b/click" }, // ButtonYB 13
new List<string>() { "/input/menu/click" }, // Menu 14
new List<string>() { "/input/system/click" }, // System 15
new List<string>() { "/output/haptic" }, // HapticOut 16
};
private static ActionType GetActionType(HAND_PATHS hp)
{
switch (hp)
{
case HAND_PATHS.BaseIndex:
case HAND_PATHS.Position:
return ActionType.PoseInput;
case HAND_PATHS.GripValue:
case HAND_PATHS.ThumbstickX:
case HAND_PATHS.ThumbstickY:
case HAND_PATHS.TrackpadX:
case HAND_PATHS.TrackpadY:
case HAND_PATHS.TriggerValue:
return ActionType.FloatInput;
case HAND_PATHS.HapticOut:
return ActionType.VibrationOutput;
case HAND_PATHS.GripClick:
case HAND_PATHS.ThumbstickClick:
case HAND_PATHS.TriggerClick:
case HAND_PATHS.TrackpadClick:
case HAND_PATHS.Menu:
case HAND_PATHS.System:
default:
return ActionType.BooleanInput;
}
}
private static OpenXRHmd baseHMD;
internal static Silk.NET.OpenXR.Action[,] MappedActions = new Silk.NET.OpenXR.Action[2, HAND_PATH_COUNT];
public static string[] InteractionProfiles =
{
"/interaction_profiles/khr/simple_controller",
"/interaction_profiles/google/daydream_controller",
"/interaction_profiles/htc/vive_controller",
"/interaction_profiles/htc/vive_pro",
"/interaction_profiles/microsoft/motion_controller",
"/interaction_profiles/hp/mixed_reality_controller",
"/interaction_profiles/samsung/odyssey_controller",
"/interaction_profiles/oculus/go_controller",
"/interaction_profiles/oculus/touch_controller",
"/interaction_profiles/valve/index_controller",
"/interaction_profiles/htc/vive_cosmos_controller",
"/interaction_profiles/huawei/controller",
"/interaction_profiles/microsoft/hand_interaction",
};
internal static unsafe bool IsPathSupported(OpenXRHmd hmd, ulong profile, ActionSuggestedBinding* suggested)
{
InteractionProfileSuggestedBinding suggested_bindings = new InteractionProfileSuggestedBinding()
{
Type = StructureType.TypeInteractionProfileSuggestedBinding,
InteractionProfile = profile,
CountSuggestedBindings = 1,
SuggestedBindings = suggested
};
return hmd.Xr.SuggestInteractionProfileBinding(hmd.Instance, &suggested_bindings) == Result.Success;
}
public static Silk.NET.OpenXR.Action GetAction(TouchControllerHand hand, TouchControllerButton button, bool YAxis = false, bool wantBoolean = false)
{
switch (button)
{
case TouchControllerButton.X:
case TouchControllerButton.A:
return MappedActions[(int)hand, (int)HAND_PATHS.ButtonXA];
case TouchControllerButton.Y:
case TouchControllerButton.B:
return MappedActions[(int)hand, (int)HAND_PATHS.ButtonYB];
case TouchControllerButton.Grip:
return wantBoolean ? MappedActions[(int)hand, (int)HAND_PATHS.GripClick] : MappedActions[(int)hand, (int)HAND_PATHS.GripValue];
case TouchControllerButton.Menu:
return MappedActions[(int)hand, (int)HAND_PATHS.Menu];
//case TouchControllerButton.System:
// return MappedActions[(int)hand, (int)HAND_PATHS.System];
case TouchControllerButton.Trigger:
return wantBoolean ? MappedActions[(int)hand, (int)HAND_PATHS.TriggerClick] : MappedActions[(int)hand, (int)HAND_PATHS.TriggerValue];
case TouchControllerButton.Thumbstick:
return wantBoolean ? MappedActions[(int)hand, (int)HAND_PATHS.ThumbstickClick] : MappedActions[(int)hand, YAxis ? (int)HAND_PATHS.ThumbstickY : (int)HAND_PATHS.ThumbstickX];
case TouchControllerButton.Touchpad:
return wantBoolean ? MappedActions[(int)hand, (int)HAND_PATHS.TrackpadClick] : MappedActions[(int)hand, YAxis ? (int)HAND_PATHS.TrackpadY : (int)HAND_PATHS.TrackpadX];
default:
throw new ArgumentException("Don't know button: " + button);
}
}
public static bool GetActionBool(TouchControllerHand hand, TouchControllerButton button, out bool wasChangedSinceLast, bool fallback = false)
{
ActionStateGetInfo getbool = new ActionStateGetInfo()
{
Action = GetAction(hand, button, false, true),
Type = StructureType.TypeActionStateGetInfo
};
ActionStateBoolean boolresult = new ActionStateBoolean()
{
Type = StructureType.TypeActionStateBoolean
};
baseHMD.Xr.GetActionStateBoolean(baseHMD.globalSession, in getbool, ref boolresult);
if (boolresult.IsActive == 0)
{
if (fallback)
{
// couldn't find an input...
wasChangedSinceLast = false;
return false;
}
// fallback if couldn't find bool
return GetActionFloat(hand, button, out wasChangedSinceLast, false, true) == 1f;
}
wasChangedSinceLast = boolresult.ChangedSinceLastSync == 1;
return boolresult.CurrentState == 1;
}
public static float GetActionFloat(TouchControllerHand hand, TouchControllerButton button, out bool wasChangedSinceLast, bool YAxis = false, bool fallback = false)
{
ActionStateGetInfo getfloat = new ActionStateGetInfo()
{
Action = GetAction(hand, button, YAxis),
Type = StructureType.TypeActionStateGetInfo
};
ActionStateFloat floatresult = new ActionStateFloat()
{
Type = StructureType.TypeActionStateFloat
};
baseHMD.Xr.GetActionStateFloat(baseHMD.globalSession, in getfloat, ref floatresult);
if (floatresult.IsActive == 0)
{
if (fallback)
{
// couldn't find an input...
wasChangedSinceLast = false;
return 0f;
}
// fallback if couldn't find float
return GetActionBool(hand, button, out wasChangedSinceLast, true) ? 1f : 0f;
}
wasChangedSinceLast = floatresult.ChangedSinceLastSync == 1;
return floatresult.CurrentState;
}
public static unsafe void Initialize(OpenXRHmd hmd)
{
baseHMD = hmd;
// make actions
for (int i=0; i<HAND_PATH_COUNT; i++)
{
for (int j=0; j<2; j++)
{
ActionCreateInfo action_info = new ActionCreateInfo()
{
Type = StructureType.TypeActionCreateInfo,
ActionType = GetActionType((HAND_PATHS)i),
};
Span<byte> aname = new Span<byte>(action_info.ActionName, 32);
Span<byte> lname = new Span<byte>(action_info.LocalizedActionName, 32);
string fullname = ((HAND_PATHS)i).ToString() + "H" + j.ToString() + '\0';
SilkMarshal.StringIntoSpan(fullname.ToLower(), aname);
SilkMarshal.StringIntoSpan(fullname, lname);
fixed (Silk.NET.OpenXR.Action* aptr = &MappedActions[j, i])
hmd.Xr.CreateAction(hmd.globalActionSet, &action_info, aptr);
}
}
// probe bindings for all profiles
for (int i=0; i<InteractionProfiles.Length; i++)
{
ulong profile = 0;
hmd.Xr.StringToPath(hmd.Instance, InteractionProfiles[i], ref profile);
List<ActionSuggestedBinding> bindings = new List<ActionSuggestedBinding>();
// for each hand...
for (int hand=0; hand<2; hand++)
{
// for each path we want to bind...
for (int path=0; path<HAND_PATH_COUNT; path++)
{
// list all possible paths that might be valid and pick the first one
List<string> possiblePaths = PathPriorities[path];
for (int pathattempt=0; pathattempt<possiblePaths.Count; pathattempt++)
{
// get the hand at the start, then put in the attempt
string final_path = hand == (int)TouchControllerHand.Left ? "/user/hand/left" : "/user/hand/right";
final_path += possiblePaths[pathattempt];
ulong hp_ulong = 0;
hmd.Xr.StringToPath(hmd.Instance, final_path, ref hp_ulong);
var suggest = new ActionSuggestedBinding()
{
Action = MappedActions[hand, path],
Binding = hp_ulong
};
if (IsPathSupported(hmd, profile, &suggest))
{
// got one!
bindings.Add(suggest);
break;
}
}
}
}
// ok, we got all supported paths for this profile, lets do the final suggestion with all of them
if (bindings.Count > 0)
{
ActionSuggestedBinding[] final_bindings = bindings.ToArray();
fixed (ActionSuggestedBinding* asbptr = &final_bindings[0])
{
InteractionProfileSuggestedBinding suggested_bindings = new InteractionProfileSuggestedBinding()
{
Type = StructureType.TypeInteractionProfileSuggestedBinding,
InteractionProfile = profile,
CountSuggestedBindings = (uint)final_bindings.Length,
SuggestedBindings = asbptr
};
OpenXRHmd.CheckResult(hmd.Xr.SuggestInteractionProfileBinding(hmd.Instance, &suggested_bindings), "SuggestInteractionProfileBinding");
}
}
}
}
}
}

Просмотреть файл

@ -0,0 +1,170 @@
using System;
using System.Collections.Generic;
using System.Text;
using Silk.NET.OpenXR;
using Stride.Core.Mathematics;
using Stride.Games;
namespace Stride.VirtualReality
{
class OpenXrTouchController : TouchController
{
private string baseHandPath;
private OpenXRHmd baseHMD;
private SpaceLocation handLocation;
private TouchControllerHand myHand;
public ulong[] hand_paths = new ulong[12];
public Space myHandSpace;
public Silk.NET.OpenXR.Action myHandAction;
public OpenXrTouchController(OpenXRHmd hmd, TouchControllerHand whichHand)
{
baseHMD = hmd;
handLocation.Type = StructureType.TypeSpaceLocation;
myHand = whichHand;
myHandAction = OpenXRInput.MappedActions[(int)myHand, (int)OpenXRInput.HAND_PATHS.Position];
ActionSpaceCreateInfo action_space_info = new ActionSpaceCreateInfo()
{
Type = StructureType.TypeActionSpaceCreateInfo,
Action = myHandAction,
PoseInActionSpace = new Posef(new Quaternionf(0f, 0f, 0f, 1f), new Vector3f(0f, 0f, 0f)),
};
OpenXRHmd.CheckResult(baseHMD.Xr.CreateActionSpace(baseHMD.globalSession, in action_space_info, ref myHandSpace), "CreateActionSpace");
}
private Vector3 currentPos;
public override Vector3 Position => currentPos;
private Quaternion currentRot;
public override Quaternion Rotation => currentRot;
private Vector3 currentVel;
public override Vector3 LinearVelocity => currentVel;
private Vector3 currentAngVel;
public override Vector3 AngularVelocity => currentAngVel;
public override DeviceState State => (handLocation.LocationFlags & SpaceLocationFlags.SpaceLocationPositionValidBit) != 0 ? DeviceState.Valid : DeviceState.OutOfRange;
private Quaternion? holdOffset;
private float _holdoffset;
public override float Trigger => OpenXRInput.GetActionFloat(myHand, TouchControllerButton.Trigger, out _);
public override float Grip => OpenXRInput.GetActionFloat(myHand, TouchControllerButton.Grip, out _);
public override bool IndexPointing => false;
public override bool IndexResting => false;
public override bool ThumbUp => false;
public override bool ThumbResting => false;
public override Vector2 ThumbstickAxis => GetAxis((int)TouchControllerButton.Thumbstick);
public override Vector2 ThumbAxis => GetAxis((int)TouchControllerButton.Touchpad);
public Vector2 GetAxis(int index)
{
TouchControllerButton button = index == 0 ? TouchControllerButton.Thumbstick : TouchControllerButton.Touchpad;
return new Vector2(OpenXRInput.GetActionFloat(myHand, button, out _, false),
OpenXRInput.GetActionFloat(myHand, button, out _, true));
}
public override bool IsPressed(TouchControllerButton button)
{
return OpenXRInput.GetActionBool(myHand, button, out _);
}
public override bool IsPressedDown(TouchControllerButton button)
{
bool isDownNow = OpenXRInput.GetActionBool(myHand, button, out bool changed);
return isDownNow && changed;
}
public override bool IsPressReleased(TouchControllerButton button)
{
bool isDownNow = OpenXRInput.GetActionBool(myHand, button, out bool changed);
return !isDownNow && changed;
}
public override bool IsTouched(TouchControllerButton button)
{
// unsupported right now
return false;
}
public override bool IsTouchedDown(TouchControllerButton button)
{
// unsupported right now
return false;
}
public override bool IsTouchReleased(TouchControllerButton button)
{
// unsupported right now
return false;
}
public override unsafe void Update(GameTime time)
{
ActionStatePose hand_pose_state = new ActionStatePose()
{
Type = StructureType.TypeActionStatePose,
Next = null,
};
ActionStateGetInfo get_info = new ActionStateGetInfo()
{
Type = StructureType.TypeActionStateGetInfo,
Action = myHandAction,
Next = null,
};
SpaceVelocity sv = new SpaceVelocity()
{
Type = StructureType.TypeSpaceVelocity,
Next = null,
};
handLocation.Next = &sv;
baseHMD.Xr.GetActionStatePose(baseHMD.globalSession, in get_info, ref hand_pose_state);
baseHMD.Xr.LocateSpace(myHandSpace, baseHMD.globalPlaySpace, baseHMD.globalFrameState.PredictedDisplayTime,
ref handLocation);
currentPos.X = handLocation.Pose.Position.X;
currentPos.Y = handLocation.Pose.Position.Y;
currentPos.Z = handLocation.Pose.Position.Z;
currentVel.X = sv.LinearVelocity.X;
currentVel.Y = sv.LinearVelocity.Y;
currentVel.Z = sv.LinearVelocity.Z;
currentAngVel.X = sv.AngularVelocity.X;
currentAngVel.Y = sv.AngularVelocity.Y;
currentAngVel.Z = sv.AngularVelocity.Z;
if (holdOffset.HasValue)
{
Quaternion orig = new Quaternion(handLocation.Pose.Orientation.X, handLocation.Pose.Orientation.Y,
handLocation.Pose.Orientation.Z, handLocation.Pose.Orientation.W);
currentRot = holdOffset.Value * orig;
}
else
{
currentRot.X = handLocation.Pose.Orientation.X;
currentRot.Y = handLocation.Pose.Orientation.Y;
currentRot.Z = handLocation.Pose.Orientation.Z;
currentRot.W = handLocation.Pose.Orientation.W;
}
}
}
}

Просмотреть файл

@ -31,6 +31,11 @@
<RelativePath>%(RecursiveDir)%(Filename)%(Extension)</RelativePath>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</StrideNativeLib>
<StrideNativeLib Include="..\..\..\deps\OpenXR\openxr_loader.dll">
<Link>%(RecursiveDir)%(Filename)%(Extension)</Link>
<RelativePath>%(RecursiveDir)%(Filename)%(Extension)</RelativePath>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</StrideNativeLib>
</ItemGroup>
</When>
</Choose>
@ -57,6 +62,9 @@
<None Include="OculusOVR\OculusOVR.cpp" />
<None Include="Fove\Fove.cpp" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Silk.NET.OpenXR" Version="2.6.0" />
</ItemGroup>
<Import Project="$(StrideSdkTargets)" />
<PropertyGroup>

Просмотреть файл

@ -7,6 +7,7 @@ namespace Stride.VirtualReality
Oculus = 0,
OpenVR = 1,
WindowsMixedReality = 2,
OpenXR = 3,
Dummy = 100,
//Fove,
//Google,

Просмотреть файл

@ -50,6 +50,8 @@ namespace Stride.VirtualReality
public abstract bool CanInitialize { get; }
public virtual bool CanMirrorTexture { get; } = true;
public bool SupportsOverlays { get; protected set; } = false;
public virtual VROverlay CreateOverlay(int width, int height, int mipLevels, int sampleCount)

Просмотреть файл

@ -80,6 +80,13 @@ namespace Stride.VirtualReality
#endif
break;
}
case VRApi.OpenXR:
{
#if STRIDE_GRAPHICS_API_DIRECT3D11
Device = new OpenXRHmd(Game.GraphicsDevice);
#endif
break;
}
case VRApi.WindowsMixedReality:
{
#if STRIDE_GRAPHICS_API_DIRECT3D11 && STRIDE_PLATFORM_UWP