Bring over initial copy of the game and plugin
This commit is contained in:
Родитель
a1189dddec
Коммит
800c19a0b7
|
@ -0,0 +1,12 @@
|
|||
# Batch scripts need CR/LF line endings
|
||||
*.bat eol=crlf
|
||||
|
||||
# Shell scripts need unix line endings
|
||||
*.sh text eol=lf
|
||||
|
||||
# Binary files
|
||||
*.png binary
|
||||
*.jpg binary
|
||||
*.umap binary
|
||||
*.uasset binary
|
||||
*.dll binary
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 167 KiB |
|
@ -0,0 +1,128 @@
|
|||
---
|
||||
# File originally from https://gist.github.com/intinig/9bba3a3faee80250b781bf916a4ab8b7
|
||||
Language: Cpp
|
||||
AccessModifierOffset: -4
|
||||
AlignAfterOpenBracket: DontAlign
|
||||
AlignConsecutiveAssignments: false
|
||||
AlignConsecutiveDeclarations: false
|
||||
AlignEscapedNewlines: Left
|
||||
AlignOperands: true
|
||||
AlignTrailingComments: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortBlocksOnASingleLine: false
|
||||
AllowShortCaseLabelsOnASingleLine: false
|
||||
AllowShortFunctionsOnASingleLine: None
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakAfterReturnType: None
|
||||
AlwaysBreakBeforeMultilineStrings: true
|
||||
AlwaysBreakTemplateDeclarations: Yes
|
||||
BinPackArguments: true
|
||||
BinPackParameters: true
|
||||
BreakBeforeBinaryOperators: None
|
||||
BreakBeforeBraces: Allman
|
||||
BreakBeforeInheritanceComma: false
|
||||
BreakInheritanceList: BeforeColon
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
BreakConstructorInitializers: BeforeComma
|
||||
BreakAfterJavaFieldAnnotations: false
|
||||
BreakStringLiterals: true
|
||||
ColumnLimit: 132
|
||||
CommentPragmas: '^ IWYU pragma:'
|
||||
CompactNamespaces: true
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: true
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
ContinuationIndentWidth: 4
|
||||
Cpp11BracedListStyle: true
|
||||
DerivePointerAlignment: false
|
||||
DisableFormat: false
|
||||
FixNamespaceComments: true
|
||||
ForEachMacros:
|
||||
- for
|
||||
IncludeBlocks: Regroup
|
||||
IncludeCategories:
|
||||
- Regex: '.*\.generated\.h'
|
||||
Priority: 100
|
||||
- Regex: '.*(PCH).*'
|
||||
Priority: -1
|
||||
- Regex: '".*"'
|
||||
Priority: 1
|
||||
- Regex: '^<.*\.(h)>'
|
||||
Priority: 3
|
||||
- Regex: '^<.*>'
|
||||
Priority: 4
|
||||
IncludeIsMainRegex: '([-_](test|unittest))?$'
|
||||
IndentCaseLabels: true
|
||||
IndentPPDirectives: None
|
||||
IndentWidth: 4
|
||||
IndentWrappedFunctionNames: false
|
||||
JavaScriptQuotes: Leave
|
||||
JavaScriptWrapImports: true
|
||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||
MacroBlockBegin: ''
|
||||
MacroBlockEnd: ''
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: All
|
||||
ObjCBinPackProtocolList: Never
|
||||
ObjCBlockIndentWidth: 2
|
||||
ObjCSpaceAfterProperty: false
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PenaltyBreakAssignment: 2
|
||||
PenaltyBreakBeforeFirstCallParameter: 1
|
||||
PenaltyBreakComment: 300
|
||||
PenaltyBreakFirstLessLess: 120
|
||||
PenaltyBreakString: 1000
|
||||
PenaltyBreakTemplateDeclaration: 10
|
||||
PenaltyExcessCharacter: 1000000
|
||||
PenaltyReturnTypeOnItsOwnLine: 200
|
||||
PointerAlignment: Left
|
||||
RawStringFormats:
|
||||
- Language: Cpp
|
||||
Delimiters:
|
||||
- cc
|
||||
- CC
|
||||
- cpp
|
||||
- Cpp
|
||||
- CPP
|
||||
- 'c++'
|
||||
- 'C++'
|
||||
CanonicalDelimiter: ''
|
||||
BasedOnStyle: google
|
||||
- Language: TextProto
|
||||
Delimiters:
|
||||
- pb
|
||||
- PB
|
||||
- proto
|
||||
- PROTO
|
||||
EnclosingFunctions:
|
||||
- EqualsProto
|
||||
- EquivToProto
|
||||
- PARSE_PARTIAL_TEXT_PROTO
|
||||
- PARSE_TEST_PROTO
|
||||
- PARSE_TEXT_PROTO
|
||||
- ParseTextOrDie
|
||||
- ParseTextProtoOrDie
|
||||
CanonicalDelimiter: ''
|
||||
ReflowComments: true
|
||||
SortIncludes: true
|
||||
SortUsingDeclarations: true
|
||||
SpaceAfterCStyleCast: true
|
||||
SpaceAfterTemplateKeyword: true
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
SpaceBeforeCpp11BracedList: false
|
||||
SpaceBeforeCtorInitializerColon: true
|
||||
SpaceBeforeInheritanceColon: true
|
||||
SpaceBeforeParens: ControlStatements
|
||||
SpaceBeforeRangeBasedForLoopColon: true
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesBeforeTrailingComments: 4
|
||||
SpacesInAngles: false
|
||||
SpacesInContainerLiterals: true
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInSquareBrackets: false
|
||||
Standard: Auto
|
||||
TabWidth: 4
|
||||
UseTab: Always
|
||||
...
|
|
@ -0,0 +1,41 @@
|
|||
# Visual Studio 2015 user specific files
|
||||
.vs/
|
||||
|
||||
# These project files can be generated by the engine
|
||||
*.sln
|
||||
|
||||
# Precompiled Assets
|
||||
SourceArt/**/*.png
|
||||
SourceArt/**/*.tga
|
||||
|
||||
# Binary Files
|
||||
Binaries/*
|
||||
Plugins/*/Binaries/*
|
||||
|
||||
# Builds
|
||||
Build/*
|
||||
|
||||
# Whitelist PakBlacklist-<BuildConfiguration>.txt files
|
||||
!Build/*/
|
||||
Build/*/**
|
||||
!Build/*/PakBlacklist*.txt
|
||||
|
||||
# Don't ignore icon files in Build
|
||||
!Build/**/*.ico
|
||||
|
||||
# Built data for maps
|
||||
# Not ignoring this beacuse it forces each person to regenerate it locally (by building lighting) and that leads to the correspoding .umap file being modified.
|
||||
# *_BuiltData.uasset
|
||||
|
||||
# Configuration files generated by the Editor
|
||||
Saved/*
|
||||
|
||||
# Compiled source files for the engine to use
|
||||
Intermediate/*
|
||||
Plugins/*/Intermediate/*
|
||||
|
||||
# Cache files for the editor to use
|
||||
DerivedDataCache/*
|
||||
|
||||
Hololens/*
|
||||
WindowsNoEditor/*
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:63e70c898d49938c6920670806bc47263b652aec48b1728e511de12ddc0ca6a4
|
||||
size 35735
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:e007f415760022ae05201c8d72cf3eadca634c872fd1820e30e504b67324992e
|
||||
size 4345
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:6215495eccf992ddd5d27ad27a742379189a9e14712bdfe565a2812bbb2eda0c
|
||||
size 240279
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:284f073d7edd69de5ab4410bdb101967bd658c3cc321ddf449ec22f0c7ebd874
|
||||
size 5351
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:fc5550f7e77df19eb040f7727313f896167965e635b6b8af6a3c1426a4badd06
|
||||
size 68457
|
Двоичный файл не отображается.
|
@ -0,0 +1,140 @@
|
|||
|
||||
|
||||
[/Script/HardwareTargeting.HardwareTargetingSettings]
|
||||
TargetedHardwareClass=Mobile
|
||||
AppliedTargetedHardwareClass=Mobile
|
||||
DefaultGraphicsPerformance=Scalable
|
||||
AppliedDefaultGraphicsPerformance=Scalable
|
||||
|
||||
[/Script/Engine.Engine]
|
||||
|
||||
[/Script/Engine.RendererSettings]
|
||||
r.Mobile.DisableVertexFog=True
|
||||
r.Shadow.CSM.MaxMobileCascades=1
|
||||
r.MobileMSAA=1
|
||||
r.Mobile.UseLegacyShadingModel=False
|
||||
r.Mobile.AllowDitheredLODTransition=False
|
||||
r.Mobile.AllowSoftwareOcclusion=False
|
||||
r.Mobile.VirtualTextures=False
|
||||
r.DiscardUnusedQuality=False
|
||||
r.AllowOcclusionQueries=False
|
||||
r.MinScreenRadiusForLights=0.030000
|
||||
r.MinScreenRadiusForDepthPrepass=0.030000
|
||||
r.MinScreenRadiusForCSMDepth=0.010000
|
||||
r.PrecomputedVisibilityWarning=False
|
||||
r.TextureStreaming=True
|
||||
Compat.UseDXT5NormalMaps=False
|
||||
r.VirtualTextures=False
|
||||
r.VirtualTexturedLightmaps=False
|
||||
r.VT.TileSize=128
|
||||
r.VT.TileBorderSize=4
|
||||
r.vt.FeedbackFactor=16
|
||||
r.VT.EnableCompressZlib=True
|
||||
r.VT.EnableCompressCrunch=False
|
||||
r.ClearCoatNormal=False
|
||||
r.AnisotropicBRDF=False
|
||||
r.ReflectionCaptureResolution=128
|
||||
r.ReflectionEnvironmentLightmapMixBasedOnRoughness=True
|
||||
r.ForwardShading=True
|
||||
r.VertexFoggingForOpaque=False
|
||||
r.AllowStaticLighting=True
|
||||
r.NormalMapsForStaticLighting=False
|
||||
r.GenerateMeshDistanceFields=False
|
||||
r.DistanceFieldBuild.EightBit=False
|
||||
r.GenerateLandscapeGIData=False
|
||||
r.DistanceFieldBuild.Compress=False
|
||||
r.TessellationAdaptivePixelsPerTriangle=48.000000
|
||||
r.SeparateTranslucency=False
|
||||
r.TranslucentSortPolicy=0
|
||||
TranslucentSortAxis=(X=0.000000,Y=-1.000000,Z=0.000000)
|
||||
r.CustomDepth=0
|
||||
r.CustomDepthTemporalAAJitter=True
|
||||
r.PostProcessing.PropagateAlpha=0
|
||||
r.DefaultFeature.Bloom=False
|
||||
r.DefaultFeature.AmbientOcclusion=False
|
||||
r.DefaultFeature.AmbientOcclusionStaticFraction=True
|
||||
r.DefaultFeature.AutoExposure=False
|
||||
r.DefaultFeature.AutoExposure.Method=0
|
||||
r.DefaultFeature.AutoExposure.Bias=1.000000
|
||||
r.DefaultFeature.AutoExposure.ExtendDefaultLuminanceRange=False
|
||||
r.UsePreExposure=True
|
||||
r.EyeAdaptation.EditorOnly=False
|
||||
r.DefaultFeature.MotionBlur=False
|
||||
r.DefaultFeature.LensFlare=False
|
||||
r.TemporalAA.Upsampling=False
|
||||
r.SSGI.Enable=False
|
||||
r.DefaultFeature.AntiAliasing=0
|
||||
r.DefaultFeature.LightUnits=1
|
||||
r.DefaultBackBufferPixelFormat=4
|
||||
r.Shadow.UnbuiltPreviewInGame=True
|
||||
r.StencilForLODDither=False
|
||||
r.EarlyZPass=3
|
||||
r.EarlyZPassOnlyMaterialMasking=False
|
||||
r.DBuffer=True
|
||||
r.ClearSceneMethod=1
|
||||
r.BasePassOutputsVelocity=False
|
||||
r.VertexDeformationOutputsVelocity=False
|
||||
r.SelectiveBasePassOutputs=False
|
||||
bDefaultParticleCutouts=False
|
||||
fx.GPUSimulationTextureSizeX=1024
|
||||
fx.GPUSimulationTextureSizeY=1024
|
||||
r.AllowGlobalClipPlane=False
|
||||
r.GBufferFormat=1
|
||||
r.MorphTarget.Mode=True
|
||||
r.GPUCrashDebugging=False
|
||||
vr.InstancedStereo=True
|
||||
r.MobileHDR=False
|
||||
vr.MobileMultiView=True
|
||||
r.Mobile.UseHWsRGBEncoding=False
|
||||
vr.RoundRobinOcclusion=False
|
||||
vr.ODSCapture=False
|
||||
r.MeshStreaming=False
|
||||
r.WireframeCullThreshold=5.000000
|
||||
r.RayTracing=False
|
||||
r.RayTracing.UseTextureLod=False
|
||||
r.SupportStationarySkylight=True
|
||||
r.SupportLowQualityLightmaps=True
|
||||
r.SupportPointLightWholeSceneShadows=True
|
||||
r.SupportAtmosphericFog=True
|
||||
r.SupportSkyAtmosphere=True
|
||||
r.SupportSkyAtmosphereAffectsHeightFog=False
|
||||
r.SkinCache.CompileShaders=False
|
||||
r.SkinCache.DefaultBehavior=1
|
||||
r.SkinCache.SceneMemoryLimitInMB=128.000000
|
||||
r.Mobile.EnableStaticAndCSMShadowReceivers=True
|
||||
r.Mobile.EnableMovableLightCSMShaderCulling=True
|
||||
r.Mobile.AllowDistanceFieldShadows=True
|
||||
r.Mobile.AllowMovableDirectionalLights=True
|
||||
r.MobileNumDynamicPointLights=0
|
||||
r.MobileDynamicPointLightsUseStaticBranch=True
|
||||
r.Mobile.EnableMovableSpotlights=False
|
||||
r.GPUSkin.Support16BitBoneIndex=False
|
||||
r.GPUSkin.Limit2BoneInfluences=False
|
||||
r.SupportDepthOnlyIndexBuffers=True
|
||||
r.SupportReversedIndexBuffers=True
|
||||
r.SupportMaterialLayers=False
|
||||
r.LightPropagationVolume=False
|
||||
bStreamSkeletalMeshLODs=(Default=False,PerPlatform=())
|
||||
bDiscardSkeletalMeshOptionalLODs=(Default=False,PerPlatform=())
|
||||
VisualizeCalibrationColorMaterialPath=None
|
||||
VisualizeCalibrationCustomMaterialPath=None
|
||||
VisualizeCalibrationGrayscaleMaterialPath=None
|
||||
|
||||
[/Script/EngineSettings.GameMapsSettings]
|
||||
EditorStartupMap=/Game/Maps/DefaultMap.DefaultMap
|
||||
LocalMapOptions=
|
||||
TransitionMap=None
|
||||
bUseSplitscreen=False
|
||||
TwoPlayerSplitscreenLayout=Horizontal
|
||||
ThreePlayerSplitscreenLayout=FavorTop
|
||||
FourPlayerSplitscreenLayout=Grid
|
||||
bOffsetPlayerGamepadIds=False
|
||||
GameInstanceClass=/Script/Engine.GameInstance
|
||||
GameDefaultMap=/Game/Maps/DefaultMap.DefaultMap
|
||||
ServerDefaultMap=/Engine/Maps/Entry.Entry
|
||||
GlobalDefaultGameMode=/Script/Engine.GameModeBase
|
||||
GlobalDefaultServerGameMode=None
|
||||
|
||||
[/Script/Slate.SlateSettings]
|
||||
bExplicitCanvasChildZOrder=True
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
|
||||
[/Script/EngineSettings.GeneralProjectSettings]
|
||||
ProjectID=99F0EDDB4B092C7CA6713EAA6EFCB918
|
||||
CompanyName=Microsoft
|
||||
CompanyDistinguishedName=CN=Microsoft
|
||||
bStartInVR=True
|
||||
ProjectName=MsftOpenXRGame
|
||||
ProjectVersion=1.0.0.0
|
||||
|
||||
[/Script/EngineSettings.XRVisualizationSettings]
|
||||
HandMeshMaterial=Material'/Game/Materials/M_BasicUnlit.M_BasicUnlit'
|
|
@ -0,0 +1,114 @@
|
|||
[/Script/Engine.InputSettings]
|
||||
-AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f))
|
||||
-AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f))
|
||||
-AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f))
|
||||
-AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.25,Exponent=1.f,Sensitivity=1.f))
|
||||
-AxisConfig=(AxisKeyName="MouseX",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f))
|
||||
-AxisConfig=(AxisKeyName="MouseY",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f))
|
||||
-AxisConfig=(AxisKeyName="Mouse2D",AxisProperties=(DeadZone=0.f,Exponent=1.f,Sensitivity=0.07f))
|
||||
+AxisConfig=(AxisKeyName="Vive_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Vive_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Vive_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="MixedReality_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="MixedReality_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="MixedReality_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="MixedReality_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="MixedReality_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="MixedReality_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="MixedReality_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="MixedReality_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="MixedReality_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="MixedReality_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="OculusGo_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="OculusGo_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="OculusGo_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="OculusGo_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="OculusTouch_Left_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="OculusTouch_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="OculusTouch_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="OculusTouch_Right_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="OculusTouch_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="OculusTouch_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Left_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Left_Grip_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Left_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Left_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Left_Trackpad_Touch",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Right_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Right_Grip_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Right_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Right_Thumbstick_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Right_Thumbstick_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="ValveIndex_Right_Trackpad_Force",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Gamepad_LeftX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Gamepad_LeftY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Gamepad_RightX",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Gamepad_RightY",AxisProperties=(DeadZone=0.250000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="MouseX",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="MouseY",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Mouse2D",AxisProperties=(DeadZone=0.000000,Sensitivity=0.070000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="MouseWheelAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Gamepad_LeftTriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Gamepad_RightTriggerAxis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Gamepad_Special_Left_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Gamepad_Special_Left_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Daydream_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Daydream_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Daydream_Right_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Daydream_Right_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Vive_Left_Trigger_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Vive_Left_Trackpad_X",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="Vive_Left_Trackpad_Y",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="OpenXRMsftHandInteraction_Left_Select_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="OpenXRMsftHandInteraction_Right_Select_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="OpenXRMsftHandInteraction_Left_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
+AxisConfig=(AxisKeyName="OpenXRMsftHandInteraction_Right_Grip_Axis",AxisProperties=(DeadZone=0.000000,Sensitivity=1.000000,Exponent=1.000000,bInvert=False))
|
||||
bAltEnterTogglesFullscreen=True
|
||||
bF11TogglesFullscreen=True
|
||||
bUseMouseForTouch=False
|
||||
bEnableMouseSmoothing=True
|
||||
bEnableFOVScaling=True
|
||||
bCaptureMouseOnLaunch=True
|
||||
bAlwaysShowTouchInterface=False
|
||||
bShowConsoleOnFourFingerTap=True
|
||||
bEnableGestureRecognizer=False
|
||||
bUseAutocorrect=False
|
||||
DefaultViewportMouseCaptureMode=CapturePermanently_IncludingInitialMouseDown
|
||||
DefaultViewportMouseLockMode=LockOnCapture
|
||||
FOVScale=0.011110
|
||||
DoubleClickTime=0.200000
|
||||
+ActionMappings=(ActionName="Squeeze_Left",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=OpenXRMsftHandInteraction_Left_Grip_Axis)
|
||||
+ActionMappings=(ActionName="Squeeze_Right",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=OpenXRMsftHandInteraction_Right_Grip_Axis)
|
||||
+ActionMappings=(ActionName="Select_Left",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=OpenXRMsftHandInteraction_Left_Select_Axis)
|
||||
+ActionMappings=(ActionName="Select_Right",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=OpenXRMsftHandInteraction_Right_Select_Axis)
|
||||
+ActionMappings=(ActionName="Squeeze_Left",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=MixedReality_Left_Grip_Click)
|
||||
+ActionMappings=(ActionName="Squeeze_Right",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=MixedReality_Right_Grip_Click)
|
||||
+ActionMappings=(ActionName="Select_Left",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=MixedReality_Left_Trigger_Axis)
|
||||
+ActionMappings=(ActionName="Select_Right",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=MixedReality_Right_Trigger_Axis)
|
||||
+ActionMappings=(ActionName="Squeeze_Left",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=OculusTouch_Left_Grip_Axis)
|
||||
+ActionMappings=(ActionName="Squeeze_Right",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=OculusTouch_Right_Grip_Axis)
|
||||
+ActionMappings=(ActionName="Select_Left",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=OculusTouch_Left_Trigger_Axis)
|
||||
+ActionMappings=(ActionName="Select_Right",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=OculusTouch_Right_Trigger_Axis)
|
||||
+ActionMappings=(ActionName="Throw_Left",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=OculusTouch_Left_X_Click)
|
||||
+ActionMappings=(ActionName="Throw_Left",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=OculusTouch_Left_Thumbstick_Click)
|
||||
+ActionMappings=(ActionName="Throw_Right",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=OculusTouch_Right_A_Click)
|
||||
+ActionMappings=(ActionName="Throw_Right",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=OculusTouch_Right_Thumbstick_Click)
|
||||
+SpeechMappings=(ActionName="Next",SpeechKeyword="Next")
|
||||
+SpeechMappings=(ActionName="ShowSpatialMapping",SpeechKeyword="ShowSpatialMapping")
|
||||
+SpeechMappings=(ActionName="HideSpatialMapping",SpeechKeyword="HideSpatialMapping")
|
||||
+SpeechMappings=(ActionName="StartSpatialMapping",SpeechKeyword="StartSpatialMapping")
|
||||
+SpeechMappings=(ActionName="StopSpatialMapping",SpeechKeyword="StopSpatialMapping")
|
||||
DefaultPlayerInputClass=/Script/Engine.PlayerInput
|
||||
DefaultInputComponentClass=/Script/Engine.InputComponent
|
||||
DefaultTouchInterface=/Engine/MobileResources/HUD/DefaultVirtualJoysticks.DefaultVirtualJoysticks
|
||||
-ConsoleKeys=Tilde
|
||||
+ConsoleKeys=Tilde
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
|
||||
|
||||
[/Script/HoloLensPlatformEditor.HoloLensTargetSettings]
|
||||
bBuildForEmulation=False
|
||||
bBuildForDevice=True
|
||||
bUseNameForLogo=True
|
||||
bBuildForRetailWindowsStore=False
|
||||
bAutoIncrementVersion=False
|
||||
bShouldCreateAppInstaller=False
|
||||
AppInstallerInstallationURL=
|
||||
HoursBetweenUpdateChecks=0
|
||||
bEnablePIXProfiling=False
|
||||
TileBackgroundColor=(B=64,G=0,R=0,A=255)
|
||||
SplashScreenBackgroundColor=(B=64,G=0,R=0,A=255)
|
||||
+PerCultureResources=(CultureId="",Strings=(PackageDisplayName="Unreal OpenXR Game",PublisherDisplayName="Microsoft Corporation",PackageDescription="Demonstrates the Microsoft OpenXR plugin for Unreal",ApplicationDisplayName="Unreal OpenXR Game",ApplicationDescription=""),Images=())
|
||||
TargetDeviceFamily=Windows.Holographic
|
||||
MinimumPlatformVersion=
|
||||
MaximumPlatformVersionTested=10.0.18362.0
|
||||
MaxTrianglesPerCubicMeter=500.000000
|
||||
SpatialMeshingVolumeSize=20.000000
|
||||
CompilerVersion=Default
|
||||
Windows10SDKVersion=10.0.18362.0
|
||||
+CapabilityList=internetClientServer
|
||||
+CapabilityList=privateNetworkClientServer
|
||||
+DeviceCapabilityList=gazeInput
|
||||
+DeviceCapabilityList=webcam
|
||||
+DeviceCapabilityList=microphone
|
||||
+Uap2CapabilityList=spatialPerception
|
||||
bSetDefaultCapabilities=False
|
||||
SpatializationPlugin=
|
||||
ReverbPlugin=
|
||||
OcclusionPlugin=
|
||||
SoundCueCookQualityIndex=-1
|
||||
|
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичные данные
MsftOpenXRGame/Content/Materials/MaterialLayers/T_ML_Aluminum01_N.uasset
Normal file
Двоичные данные
MsftOpenXRGame/Content/Materials/MaterialLayers/T_ML_Aluminum01_N.uasset
Normal file
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 54 KiB |
|
@ -0,0 +1,66 @@
|
|||
{
|
||||
"FileVersion": 3,
|
||||
"EngineAssociation": "{B902521A-44AC-D40D-A44D-D684CFEBF4D2}",
|
||||
"Category": "",
|
||||
"Description": "",
|
||||
"Modules": [
|
||||
{
|
||||
"Name": "MsftOpenXRGame",
|
||||
"Type": "Runtime",
|
||||
"LoadingPhase": "Default"
|
||||
}
|
||||
],
|
||||
"Plugins": [
|
||||
{
|
||||
"Name": "SteamVR",
|
||||
"Enabled": false,
|
||||
"SupportedTargetPlatforms": [
|
||||
"Win32",
|
||||
"Win64",
|
||||
"Linux",
|
||||
"Mac",
|
||||
"Android"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "MagicLeap",
|
||||
"Enabled": false,
|
||||
"SupportedTargetPlatforms": [
|
||||
"Lumin",
|
||||
"Mac",
|
||||
"Win64"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "MagicLeapMedia",
|
||||
"Enabled": false,
|
||||
"SupportedTargetPlatforms": [
|
||||
"Lumin"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "LuminPlatformFeatures",
|
||||
"Enabled": false,
|
||||
"SupportedTargetPlatforms": [
|
||||
"Lumin"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "MLSDK",
|
||||
"Enabled": false
|
||||
},
|
||||
{
|
||||
"Name": "MagicLeapPassableWorld",
|
||||
"Enabled": false,
|
||||
"SupportedTargetPlatforms": [
|
||||
"Lumin",
|
||||
"Mac",
|
||||
"Win64"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "XRVisualization",
|
||||
"Enabled": true
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
[FilterPlugin]
|
||||
; This section lists additional files which will be packaged along with your plugin. Paths should be listed relative to the root plugin directory, and
|
||||
; may include "...", "*", and "?" wildcards to match directories, files, and individual characters respectively.
|
||||
;
|
||||
; Examples:
|
||||
; /README.txt
|
||||
; /Extras/...
|
||||
; /Binaries/ThirdParty/*.dll
|
|
@ -0,0 +1,74 @@
|
|||
{
|
||||
"FileVersion": 3,
|
||||
"Version": 1,
|
||||
"VersionName": "1.0",
|
||||
"FriendlyName": "Microsoft OpenXR",
|
||||
"Description": "The Microsoft OpenXR plugin is a game plugin which provides additional features available on Microsoft's Mixed Reality devices like the HoloLens 2 when using OpenXR.",
|
||||
"Category": "Augmented Reality",
|
||||
"CreatedBy": "Microsoft",
|
||||
"CreatedByURL": "",
|
||||
"DocsURL": "",
|
||||
"MarketplaceURL": "com.epicgames.launcher://ue/marketplace/product/ef8930ca860148c498b46887da196239",
|
||||
"SupportURL": "https://github.com/microsoft/Microsoft-OpenXR-Unreal",
|
||||
"EngineVersion": "4.26.0",
|
||||
"CanContainContent": true,
|
||||
"IsBetaVersion": false,
|
||||
"IsExperimentalVersion": false,
|
||||
"Installed": false,
|
||||
"SupportedTargetPlatforms": [
|
||||
"Win64",
|
||||
"HoloLens"
|
||||
],
|
||||
"Modules": [
|
||||
{
|
||||
"Name": "MicrosoftOpenXR",
|
||||
"Type": "Runtime",
|
||||
"LoadingPhase": "PostConfigInit"
|
||||
},
|
||||
{
|
||||
"Name": "MicrosoftOpenXRRuntimeSettings",
|
||||
"Type": "Runtime",
|
||||
"LoadingPhase": "PostConfigInit",
|
||||
"WhitelistPlatforms": [
|
||||
"Win64"
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "MicrosoftOpenXREditor",
|
||||
"Type": "Editor",
|
||||
"LoadingPhase": "PostEngineInit",
|
||||
"WhitelistPlatforms": [
|
||||
"Win64"
|
||||
]
|
||||
}
|
||||
],
|
||||
"Plugins": [
|
||||
{
|
||||
"Name": "OpenXR",
|
||||
"Enabled": true
|
||||
},
|
||||
{
|
||||
"Name": "OpenXREyeTracker",
|
||||
"Enabled": true
|
||||
},
|
||||
{
|
||||
"Name": "OpenXRHandTracking",
|
||||
"Enabled": true
|
||||
},
|
||||
{
|
||||
"Name": "OpenXRMsftHandInteraction",
|
||||
"Enabled": true
|
||||
},
|
||||
{
|
||||
"Name": "XRVisualization",
|
||||
"Enabled": true
|
||||
},
|
||||
{
|
||||
"Name": "HoloLensAR",
|
||||
"Enabled": true,
|
||||
"WhitelistPlatforms": [
|
||||
"Win64"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 27 KiB |
|
@ -0,0 +1,225 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Runtime.Remoting.Messaging;
|
||||
using UnrealBuildTool;
|
||||
using Tools.DotNETCommon;
|
||||
using System;
|
||||
|
||||
public class MicrosoftOpenXR : ModuleRules
|
||||
{
|
||||
public MicrosoftOpenXR(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
|
||||
PrivatePCHHeaderFile = @"Private\OpenXRCommon.h";
|
||||
|
||||
PrivateIncludePaths.AddRange(
|
||||
new string[] {
|
||||
// This private include path ensures our newer copy of the openxr headers take precedence over the engine's copy.
|
||||
"MicrosoftOpenXR/Private/External"
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"Core",
|
||||
"CoreUObject",
|
||||
"ApplicationCore",
|
||||
"Engine",
|
||||
"Slate",
|
||||
"SlateCore",
|
||||
"InputCore",
|
||||
"OpenXRHMD",
|
||||
"MicrosoftOpenXRRuntimeSettings",
|
||||
"HeadMountedDisplay",
|
||||
"AugmentedReality",
|
||||
"OpenXRAR",
|
||||
"RHI",
|
||||
"RenderCore",
|
||||
"Projects",
|
||||
}
|
||||
);
|
||||
|
||||
if (Target.bBuildEditor)
|
||||
{
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"UnrealEd"
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
PrivateIncludePathModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"HeadMountedDisplay"
|
||||
}
|
||||
);
|
||||
|
||||
PublicIncludePathModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"HeadMountedDisplay"
|
||||
}
|
||||
);
|
||||
|
||||
// WinRT with Nuget support
|
||||
if (Target.Platform == UnrealTargetPlatform.Win64 || Target.Platform == UnrealTargetPlatform.HoloLens)
|
||||
{
|
||||
// these parameters mandatory for winrt support
|
||||
bEnableExceptions = true;
|
||||
bUseUnity = false;
|
||||
CppStandard = CppStandardVersion.Cpp17;
|
||||
PublicSystemLibraries.AddRange(new string [] { "shlwapi.lib", "runtimeobject.lib" });
|
||||
|
||||
// prepare everything for nuget
|
||||
string MyModuleName = GetType().Name;
|
||||
string NugetFolder = Path.Combine(PluginDirectory, "Intermediate", "Nuget", MyModuleName);
|
||||
Directory.CreateDirectory(NugetFolder);
|
||||
|
||||
string BinariesSubFolder = Path.Combine("Binaries", "ThirdParty", Target.Type.ToString(), Target.Platform.ToString(), Target.Architecture);
|
||||
|
||||
PrivateDefinitions.Add(string.Format("THIRDPARTY_BINARY_SUBFOLDER=\"{0}\"", BinariesSubFolder.Replace(@"\", @"\\")));
|
||||
|
||||
string BinariesFolder = Path.Combine(PluginDirectory, BinariesSubFolder);
|
||||
Directory.CreateDirectory(BinariesFolder);
|
||||
|
||||
// download nuget
|
||||
string NugetExe = Path.Combine(NugetFolder, "nuget.exe");
|
||||
if (!File.Exists(NugetExe))
|
||||
{
|
||||
using (System.Net.WebClient myWebClient = new System.Net.WebClient())
|
||||
{
|
||||
// we aren't focusing on a specific nuget version, we can use any of them but the latest one is preferable
|
||||
myWebClient.DownloadFile(@"https://dist.nuget.org/win-x86-commandline/latest/nuget.exe", NugetExe);
|
||||
}
|
||||
}
|
||||
|
||||
// run nuget to update the packages
|
||||
{
|
||||
var StartInfo = new System.Diagnostics.ProcessStartInfo(NugetExe, string.Format("install \"{0}\" -OutputDirectory \"{1}\"", Path.Combine(ModuleDirectory, "packages.config"), NugetFolder));
|
||||
StartInfo.UseShellExecute = false;
|
||||
StartInfo.CreateNoWindow = true;
|
||||
var ExitCode = Utils.RunLocalProcessAndPrintfOutput(StartInfo);
|
||||
if (ExitCode < 0)
|
||||
{
|
||||
throw new BuildException("Failed to get nuget packages. See log for details.");
|
||||
}
|
||||
}
|
||||
|
||||
// get list of the installed packages, that's needed because the code should get particular versions of the installed packages
|
||||
string[] InstalledPackages = Utils.RunLocalProcessAndReturnStdOut(NugetExe, string.Format("list -Source \"{0}\"", NugetFolder)).Split(new char[] { '\r', '\n' });
|
||||
|
||||
// get WinRT package
|
||||
string CppWinRTPackage = InstalledPackages.First(x => x.StartsWith("Microsoft.Windows.CppWinRT"));
|
||||
if (!string.IsNullOrEmpty(CppWinRTPackage))
|
||||
{
|
||||
string CppWinRTName = CppWinRTPackage.Replace(" ", ".");
|
||||
string CppWinRTExe = Path.Combine(NugetFolder, CppWinRTName, "bin", "cppwinrt.exe");
|
||||
string CppWinRTFolder = Path.Combine(PluginDirectory, "Intermediate", CppWinRTName, MyModuleName);
|
||||
Directory.CreateDirectory(CppWinRTFolder);
|
||||
|
||||
// search all downloaded packages for winmd files
|
||||
string[] WinMDFiles = Directory.GetFiles(NugetFolder, "*.winmd", SearchOption.AllDirectories);
|
||||
|
||||
// all downloaded winmd file with WinSDK to be processed by cppwinrt.exe
|
||||
var WinMDFilesStringbuilder = new System.Text.StringBuilder();
|
||||
foreach (var winmd in WinMDFiles)
|
||||
{
|
||||
WinMDFilesStringbuilder.Append(" -input \"");
|
||||
WinMDFilesStringbuilder.Append(winmd);
|
||||
WinMDFilesStringbuilder.Append("\"");
|
||||
}
|
||||
|
||||
// generate winrt headers and add them into include paths
|
||||
var StartInfo = new System.Diagnostics.ProcessStartInfo(CppWinRTExe, string.Format("{0} -input \"{1}\" -output \"{2}\"", WinMDFilesStringbuilder, Target.WindowsPlatform.WindowsSdkVersion, CppWinRTFolder));
|
||||
StartInfo.UseShellExecute = false;
|
||||
StartInfo.CreateNoWindow = true;
|
||||
var ExitCode = Utils.RunLocalProcessAndPrintfOutput(StartInfo);
|
||||
if (ExitCode < 0)
|
||||
{
|
||||
throw new BuildException("Failed to get generate WinRT headers. See log for details.");
|
||||
}
|
||||
|
||||
PrivateIncludePaths.Add(CppWinRTFolder);
|
||||
}
|
||||
else
|
||||
{
|
||||
// fall back to default WinSDK headers if no winrt package in our list
|
||||
PrivateIncludePaths.Add(Path.Combine(Target.WindowsPlatform.WindowsSdkDir, "Include", Target.WindowsPlatform.WindowsSdkVersion, "cppwinrt"));
|
||||
}
|
||||
|
||||
// WinRT lib for some job
|
||||
string QRPackage = InstalledPackages.First(x => x.StartsWith("Microsoft.MixedReality.QR"));
|
||||
if (!string.IsNullOrEmpty(QRPackage))
|
||||
{
|
||||
string QRFolderName = QRPackage.Replace(" ", ".");
|
||||
|
||||
// copying dll and winmd binaries to our local binaries folder
|
||||
// !!!!! please make sure that you use the path of file! Unreal can't do it for you !!!!!
|
||||
SafeCopy(Path.Combine(NugetFolder, QRFolderName, @"lib\uap10.0.18362\Microsoft.MixedReality.QR.winmd"),
|
||||
Path.Combine(BinariesFolder, "Microsoft.MixedReality.QR.winmd"));
|
||||
|
||||
SafeCopy(Path.Combine(NugetFolder, QRFolderName, string.Format(@"runtimes\win10-{0}\native\Microsoft.MixedReality.QR.dll", Target.WindowsPlatform.Architecture.ToString())),
|
||||
Path.Combine(BinariesFolder, "Microsoft.MixedReality.QR.dll"));
|
||||
|
||||
// also both both binaries must be in RuntimeDependencies, unless you get failures in Hololens platform
|
||||
RuntimeDependencies.Add(Path.Combine(BinariesFolder, "Microsoft.MixedReality.QR.dll"));
|
||||
RuntimeDependencies.Add(Path.Combine(BinariesFolder, "Microsoft.MixedReality.QR.winmd"));
|
||||
}
|
||||
|
||||
if (Target.Platform == UnrealTargetPlatform.Win64)
|
||||
{
|
||||
// Microsoft.VCRTForwarders.140 is needed to run WinRT dlls in Win64 platforms
|
||||
string VCRTForwardersPackage = InstalledPackages.First(x => x.StartsWith("Microsoft.VCRTForwarders.140"));
|
||||
if (!string.IsNullOrEmpty(VCRTForwardersPackage))
|
||||
{
|
||||
string VCRTForwardersName = VCRTForwardersPackage.Replace(" ", ".");
|
||||
foreach (var Dll in Directory.EnumerateFiles(Path.Combine(NugetFolder, VCRTForwardersName, "runtimes/win10-x64/native/release"), "*_app.dll"))
|
||||
{
|
||||
string newDll = Path.Combine(BinariesFolder, Path.GetFileName(Dll));
|
||||
SafeCopy(Dll, newDll);
|
||||
RuntimeDependencies.Add(newDll);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Target.Platform == UnrealTargetPlatform.Win64)
|
||||
{
|
||||
PrivateIncludePaths.Add(Path.Combine(ModuleDirectory, "ThirdParty", "HolographicAppRemoting"));
|
||||
|
||||
RuntimeDependencies.Add("$(PluginDir)/ThirdParty/HolographicAppRemoting/Windows/Win64/Microsoft.Holographic.AppRemoting.OpenXr.dll");
|
||||
RuntimeDependencies.Add("$(PluginDir)/ThirdParty/HolographicAppRemoting/Windows/Win64/RemotingXR.json");
|
||||
}
|
||||
}
|
||||
|
||||
private void SafeCopy(string source, string destination)
|
||||
{
|
||||
if(!File.Exists(source))
|
||||
{
|
||||
Log.TraceError("Class {0} can't find {1} file for copying", this.GetType().Name, source);
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
File.Copy(source, destination, true);
|
||||
}
|
||||
catch(IOException ex)
|
||||
{
|
||||
Log.TraceWarning("Failed to copy {0} to {1} with exception: {2}", source, destination, ex.Message);
|
||||
if (!File.Exists(destination))
|
||||
{
|
||||
Log.TraceError("Destination file {0} does not exist", destination);
|
||||
return;
|
||||
}
|
||||
|
||||
Log.TraceWarning("Destination file {0} already existed and is probably in use. The old file will be used for the runtime dependency. This may happen when packaging a Win64 exe from the editor.", destination);
|
||||
}
|
||||
}
|
||||
}
|
2196
MsftOpenXRGame/Plugins/MicrosoftOpenXR/Source/MicrosoftOpenXR/Private/External/openxr/openxr.h
поставляемый
Normal file
2196
MsftOpenXRGame/Plugins/MicrosoftOpenXR/Source/MicrosoftOpenXR/Private/External/openxr/openxr.h
поставляемый
Normal file
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
426
MsftOpenXRGame/Plugins/MicrosoftOpenXR/Source/MicrosoftOpenXR/Private/External/openxr/openxr_platform.h
поставляемый
Normal file
426
MsftOpenXRGame/Plugins/MicrosoftOpenXR/Source/MicrosoftOpenXR/Private/External/openxr/openxr_platform.h
поставляемый
Normal file
|
@ -0,0 +1,426 @@
|
|||
#ifndef OPENXR_PLATFORM_H_
|
||||
#define OPENXR_PLATFORM_H_ 1
|
||||
|
||||
/*
|
||||
** Copyright (c) 2017-2020 The Khronos Group Inc.
|
||||
**
|
||||
** SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*/
|
||||
|
||||
/*
|
||||
** This header is generated from the Khronos OpenXR XML API Registry.
|
||||
**
|
||||
*/
|
||||
|
||||
#include "openxr.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef XR_USE_PLATFORM_ANDROID
|
||||
|
||||
#define XR_KHR_android_thread_settings 1
|
||||
#define XR_KHR_android_thread_settings_SPEC_VERSION 5
|
||||
#define XR_KHR_ANDROID_THREAD_SETTINGS_EXTENSION_NAME "XR_KHR_android_thread_settings"
|
||||
|
||||
typedef enum XrAndroidThreadTypeKHR {
|
||||
XR_ANDROID_THREAD_TYPE_APPLICATION_MAIN_KHR = 1,
|
||||
XR_ANDROID_THREAD_TYPE_APPLICATION_WORKER_KHR = 2,
|
||||
XR_ANDROID_THREAD_TYPE_RENDERER_MAIN_KHR = 3,
|
||||
XR_ANDROID_THREAD_TYPE_RENDERER_WORKER_KHR = 4,
|
||||
XR_ANDROID_THREAD_TYPE_MAX_ENUM_KHR = 0x7FFFFFFF
|
||||
} XrAndroidThreadTypeKHR;
|
||||
typedef XrResult (XRAPI_PTR *PFN_xrSetAndroidApplicationThreadKHR)(XrSession session, XrAndroidThreadTypeKHR threadType, uint32_t threadId);
|
||||
|
||||
#ifndef XR_NO_PROTOTYPES
|
||||
XRAPI_ATTR XrResult XRAPI_CALL xrSetAndroidApplicationThreadKHR(
|
||||
XrSession session,
|
||||
XrAndroidThreadTypeKHR threadType,
|
||||
uint32_t threadId);
|
||||
#endif
|
||||
#endif /* XR_USE_PLATFORM_ANDROID */
|
||||
|
||||
#ifdef XR_USE_PLATFORM_ANDROID
|
||||
|
||||
#define XR_KHR_android_surface_swapchain 1
|
||||
#define XR_KHR_android_surface_swapchain_SPEC_VERSION 4
|
||||
#define XR_KHR_ANDROID_SURFACE_SWAPCHAIN_EXTENSION_NAME "XR_KHR_android_surface_swapchain"
|
||||
typedef XrResult (XRAPI_PTR *PFN_xrCreateSwapchainAndroidSurfaceKHR)(XrSession session, const XrSwapchainCreateInfo* info, XrSwapchain* swapchain, jobject* surface);
|
||||
|
||||
#ifndef XR_NO_PROTOTYPES
|
||||
XRAPI_ATTR XrResult XRAPI_CALL xrCreateSwapchainAndroidSurfaceKHR(
|
||||
XrSession session,
|
||||
const XrSwapchainCreateInfo* info,
|
||||
XrSwapchain* swapchain,
|
||||
jobject* surface);
|
||||
#endif
|
||||
#endif /* XR_USE_PLATFORM_ANDROID */
|
||||
|
||||
#ifdef XR_USE_PLATFORM_ANDROID
|
||||
|
||||
#define XR_KHR_android_create_instance 1
|
||||
#define XR_KHR_android_create_instance_SPEC_VERSION 3
|
||||
#define XR_KHR_ANDROID_CREATE_INSTANCE_EXTENSION_NAME "XR_KHR_android_create_instance"
|
||||
typedef struct XrInstanceCreateInfoAndroidKHR {
|
||||
XrStructureType type;
|
||||
const void* XR_MAY_ALIAS next;
|
||||
void* XR_MAY_ALIAS applicationVM;
|
||||
void* XR_MAY_ALIAS applicationActivity;
|
||||
} XrInstanceCreateInfoAndroidKHR;
|
||||
|
||||
#endif /* XR_USE_PLATFORM_ANDROID */
|
||||
|
||||
#ifdef XR_USE_GRAPHICS_API_VULKAN
|
||||
|
||||
#define XR_KHR_vulkan_swapchain_format_list 1
|
||||
#define XR_KHR_vulkan_swapchain_format_list_SPEC_VERSION 2
|
||||
#define XR_KHR_VULKAN_SWAPCHAIN_FORMAT_LIST_EXTENSION_NAME "XR_KHR_vulkan_swapchain_format_list"
|
||||
typedef struct XrVulkanSwapchainFormatListCreateInfoKHR {
|
||||
XrStructureType type;
|
||||
const void* XR_MAY_ALIAS next;
|
||||
uint32_t viewFormatCount;
|
||||
const VkFormat* viewFormats;
|
||||
} XrVulkanSwapchainFormatListCreateInfoKHR;
|
||||
|
||||
#endif /* XR_USE_GRAPHICS_API_VULKAN */
|
||||
|
||||
#ifdef XR_USE_GRAPHICS_API_OPENGL
|
||||
|
||||
#define XR_KHR_opengl_enable 1
|
||||
#define XR_KHR_opengl_enable_SPEC_VERSION 8
|
||||
#define XR_KHR_OPENGL_ENABLE_EXTENSION_NAME "XR_KHR_opengl_enable"
|
||||
#ifdef XR_USE_PLATFORM_WIN32
|
||||
typedef struct XrGraphicsBindingOpenGLWin32KHR {
|
||||
XrStructureType type;
|
||||
const void* XR_MAY_ALIAS next;
|
||||
HDC hDC;
|
||||
HGLRC hGLRC;
|
||||
} XrGraphicsBindingOpenGLWin32KHR;
|
||||
#endif // XR_USE_PLATFORM_WIN32
|
||||
|
||||
#ifdef XR_USE_PLATFORM_XLIB
|
||||
typedef struct XrGraphicsBindingOpenGLXlibKHR {
|
||||
XrStructureType type;
|
||||
const void* XR_MAY_ALIAS next;
|
||||
Display* xDisplay;
|
||||
uint32_t visualid;
|
||||
GLXFBConfig glxFBConfig;
|
||||
GLXDrawable glxDrawable;
|
||||
GLXContext glxContext;
|
||||
} XrGraphicsBindingOpenGLXlibKHR;
|
||||
#endif // XR_USE_PLATFORM_XLIB
|
||||
|
||||
#ifdef XR_USE_PLATFORM_XCB
|
||||
typedef struct XrGraphicsBindingOpenGLXcbKHR {
|
||||
XrStructureType type;
|
||||
const void* XR_MAY_ALIAS next;
|
||||
xcb_connection_t* connection;
|
||||
uint32_t screenNumber;
|
||||
xcb_glx_fbconfig_t fbconfigid;
|
||||
xcb_visualid_t visualid;
|
||||
xcb_glx_drawable_t glxDrawable;
|
||||
xcb_glx_context_t glxContext;
|
||||
} XrGraphicsBindingOpenGLXcbKHR;
|
||||
#endif // XR_USE_PLATFORM_XCB
|
||||
|
||||
#ifdef XR_USE_PLATFORM_WAYLAND
|
||||
typedef struct XrGraphicsBindingOpenGLWaylandKHR {
|
||||
XrStructureType type;
|
||||
const void* XR_MAY_ALIAS next;
|
||||
struct wl_display* display;
|
||||
} XrGraphicsBindingOpenGLWaylandKHR;
|
||||
#endif // XR_USE_PLATFORM_WAYLAND
|
||||
|
||||
typedef struct XrSwapchainImageOpenGLKHR {
|
||||
XrStructureType type;
|
||||
void* XR_MAY_ALIAS next;
|
||||
uint32_t image;
|
||||
} XrSwapchainImageOpenGLKHR;
|
||||
|
||||
typedef struct XrGraphicsRequirementsOpenGLKHR {
|
||||
XrStructureType type;
|
||||
void* XR_MAY_ALIAS next;
|
||||
XrVersion minApiVersionSupported;
|
||||
XrVersion maxApiVersionSupported;
|
||||
} XrGraphicsRequirementsOpenGLKHR;
|
||||
|
||||
typedef XrResult (XRAPI_PTR *PFN_xrGetOpenGLGraphicsRequirementsKHR)(XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsOpenGLKHR* graphicsRequirements);
|
||||
|
||||
#ifndef XR_NO_PROTOTYPES
|
||||
XRAPI_ATTR XrResult XRAPI_CALL xrGetOpenGLGraphicsRequirementsKHR(
|
||||
XrInstance instance,
|
||||
XrSystemId systemId,
|
||||
XrGraphicsRequirementsOpenGLKHR* graphicsRequirements);
|
||||
#endif
|
||||
#endif /* XR_USE_GRAPHICS_API_OPENGL */
|
||||
|
||||
#ifdef XR_USE_GRAPHICS_API_OPENGL_ES
|
||||
|
||||
#define XR_KHR_opengl_es_enable 1
|
||||
#define XR_KHR_opengl_es_enable_SPEC_VERSION 6
|
||||
#define XR_KHR_OPENGL_ES_ENABLE_EXTENSION_NAME "XR_KHR_opengl_es_enable"
|
||||
#ifdef XR_USE_PLATFORM_ANDROID
|
||||
typedef struct XrGraphicsBindingOpenGLESAndroidKHR {
|
||||
XrStructureType type;
|
||||
const void* XR_MAY_ALIAS next;
|
||||
EGLDisplay display;
|
||||
EGLConfig config;
|
||||
EGLContext context;
|
||||
} XrGraphicsBindingOpenGLESAndroidKHR;
|
||||
#endif // XR_USE_PLATFORM_ANDROID
|
||||
|
||||
typedef struct XrSwapchainImageOpenGLESKHR {
|
||||
XrStructureType type;
|
||||
void* XR_MAY_ALIAS next;
|
||||
uint32_t image;
|
||||
} XrSwapchainImageOpenGLESKHR;
|
||||
|
||||
typedef struct XrGraphicsRequirementsOpenGLESKHR {
|
||||
XrStructureType type;
|
||||
void* XR_MAY_ALIAS next;
|
||||
XrVersion minApiVersionSupported;
|
||||
XrVersion maxApiVersionSupported;
|
||||
} XrGraphicsRequirementsOpenGLESKHR;
|
||||
|
||||
typedef XrResult (XRAPI_PTR *PFN_xrGetOpenGLESGraphicsRequirementsKHR)(XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsOpenGLESKHR* graphicsRequirements);
|
||||
|
||||
#ifndef XR_NO_PROTOTYPES
|
||||
XRAPI_ATTR XrResult XRAPI_CALL xrGetOpenGLESGraphicsRequirementsKHR(
|
||||
XrInstance instance,
|
||||
XrSystemId systemId,
|
||||
XrGraphicsRequirementsOpenGLESKHR* graphicsRequirements);
|
||||
#endif
|
||||
#endif /* XR_USE_GRAPHICS_API_OPENGL_ES */
|
||||
|
||||
#ifdef XR_USE_GRAPHICS_API_VULKAN
|
||||
|
||||
#define XR_KHR_vulkan_enable 1
|
||||
#define XR_KHR_vulkan_enable_SPEC_VERSION 6
|
||||
#define XR_KHR_VULKAN_ENABLE_EXTENSION_NAME "XR_KHR_vulkan_enable"
|
||||
typedef struct XrGraphicsBindingVulkanKHR {
|
||||
XrStructureType type;
|
||||
const void* XR_MAY_ALIAS next;
|
||||
VkInstance instance;
|
||||
VkPhysicalDevice physicalDevice;
|
||||
VkDevice device;
|
||||
uint32_t queueFamilyIndex;
|
||||
uint32_t queueIndex;
|
||||
} XrGraphicsBindingVulkanKHR;
|
||||
|
||||
typedef struct XrSwapchainImageVulkanKHR {
|
||||
XrStructureType type;
|
||||
void* XR_MAY_ALIAS next;
|
||||
VkImage image;
|
||||
} XrSwapchainImageVulkanKHR;
|
||||
|
||||
typedef struct XrGraphicsRequirementsVulkanKHR {
|
||||
XrStructureType type;
|
||||
void* XR_MAY_ALIAS next;
|
||||
XrVersion minApiVersionSupported;
|
||||
XrVersion maxApiVersionSupported;
|
||||
} XrGraphicsRequirementsVulkanKHR;
|
||||
|
||||
typedef XrResult (XRAPI_PTR *PFN_xrGetVulkanInstanceExtensionsKHR)(XrInstance instance, XrSystemId systemId, uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, char* buffer);
|
||||
typedef XrResult (XRAPI_PTR *PFN_xrGetVulkanDeviceExtensionsKHR)(XrInstance instance, XrSystemId systemId, uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, char* buffer);
|
||||
typedef XrResult (XRAPI_PTR *PFN_xrGetVulkanGraphicsDeviceKHR)(XrInstance instance, XrSystemId systemId, VkInstance vkInstance, VkPhysicalDevice* vkPhysicalDevice);
|
||||
typedef XrResult (XRAPI_PTR *PFN_xrGetVulkanGraphicsRequirementsKHR)(XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsVulkanKHR* graphicsRequirements);
|
||||
|
||||
#ifndef XR_NO_PROTOTYPES
|
||||
XRAPI_ATTR XrResult XRAPI_CALL xrGetVulkanInstanceExtensionsKHR(
|
||||
XrInstance instance,
|
||||
XrSystemId systemId,
|
||||
uint32_t bufferCapacityInput,
|
||||
uint32_t* bufferCountOutput,
|
||||
char* buffer);
|
||||
|
||||
XRAPI_ATTR XrResult XRAPI_CALL xrGetVulkanDeviceExtensionsKHR(
|
||||
XrInstance instance,
|
||||
XrSystemId systemId,
|
||||
uint32_t bufferCapacityInput,
|
||||
uint32_t* bufferCountOutput,
|
||||
char* buffer);
|
||||
|
||||
XRAPI_ATTR XrResult XRAPI_CALL xrGetVulkanGraphicsDeviceKHR(
|
||||
XrInstance instance,
|
||||
XrSystemId systemId,
|
||||
VkInstance vkInstance,
|
||||
VkPhysicalDevice* vkPhysicalDevice);
|
||||
|
||||
XRAPI_ATTR XrResult XRAPI_CALL xrGetVulkanGraphicsRequirementsKHR(
|
||||
XrInstance instance,
|
||||
XrSystemId systemId,
|
||||
XrGraphicsRequirementsVulkanKHR* graphicsRequirements);
|
||||
#endif
|
||||
#endif /* XR_USE_GRAPHICS_API_VULKAN */
|
||||
|
||||
#ifdef XR_USE_GRAPHICS_API_D3D11
|
||||
|
||||
#define XR_KHR_D3D11_enable 1
|
||||
#define XR_KHR_D3D11_enable_SPEC_VERSION 4
|
||||
#define XR_KHR_D3D11_ENABLE_EXTENSION_NAME "XR_KHR_D3D11_enable"
|
||||
typedef struct XrGraphicsBindingD3D11KHR {
|
||||
XrStructureType type;
|
||||
const void* XR_MAY_ALIAS next;
|
||||
ID3D11Device* device;
|
||||
} XrGraphicsBindingD3D11KHR;
|
||||
|
||||
typedef struct XrSwapchainImageD3D11KHR {
|
||||
XrStructureType type;
|
||||
void* XR_MAY_ALIAS next;
|
||||
ID3D11Texture2D* texture;
|
||||
} XrSwapchainImageD3D11KHR;
|
||||
|
||||
typedef struct XrGraphicsRequirementsD3D11KHR {
|
||||
XrStructureType type;
|
||||
void* XR_MAY_ALIAS next;
|
||||
LUID adapterLuid;
|
||||
D3D_FEATURE_LEVEL minFeatureLevel;
|
||||
} XrGraphicsRequirementsD3D11KHR;
|
||||
|
||||
typedef XrResult (XRAPI_PTR *PFN_xrGetD3D11GraphicsRequirementsKHR)(XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsD3D11KHR* graphicsRequirements);
|
||||
|
||||
#ifndef XR_NO_PROTOTYPES
|
||||
XRAPI_ATTR XrResult XRAPI_CALL xrGetD3D11GraphicsRequirementsKHR(
|
||||
XrInstance instance,
|
||||
XrSystemId systemId,
|
||||
XrGraphicsRequirementsD3D11KHR* graphicsRequirements);
|
||||
#endif
|
||||
#endif /* XR_USE_GRAPHICS_API_D3D11 */
|
||||
|
||||
#ifdef XR_USE_GRAPHICS_API_D3D12
|
||||
|
||||
#define XR_KHR_D3D12_enable 1
|
||||
#define XR_KHR_D3D12_enable_SPEC_VERSION 6
|
||||
#define XR_KHR_D3D12_ENABLE_EXTENSION_NAME "XR_KHR_D3D12_enable"
|
||||
typedef struct XrGraphicsBindingD3D12KHR {
|
||||
XrStructureType type;
|
||||
const void* XR_MAY_ALIAS next;
|
||||
ID3D12Device* device;
|
||||
ID3D12CommandQueue* queue;
|
||||
} XrGraphicsBindingD3D12KHR;
|
||||
|
||||
typedef struct XrSwapchainImageD3D12KHR {
|
||||
XrStructureType type;
|
||||
void* XR_MAY_ALIAS next;
|
||||
ID3D12Resource* texture;
|
||||
} XrSwapchainImageD3D12KHR;
|
||||
|
||||
typedef struct XrGraphicsRequirementsD3D12KHR {
|
||||
XrStructureType type;
|
||||
void* XR_MAY_ALIAS next;
|
||||
LUID adapterLuid;
|
||||
D3D_FEATURE_LEVEL minFeatureLevel;
|
||||
} XrGraphicsRequirementsD3D12KHR;
|
||||
|
||||
typedef XrResult (XRAPI_PTR *PFN_xrGetD3D12GraphicsRequirementsKHR)(XrInstance instance, XrSystemId systemId, XrGraphicsRequirementsD3D12KHR* graphicsRequirements);
|
||||
|
||||
#ifndef XR_NO_PROTOTYPES
|
||||
XRAPI_ATTR XrResult XRAPI_CALL xrGetD3D12GraphicsRequirementsKHR(
|
||||
XrInstance instance,
|
||||
XrSystemId systemId,
|
||||
XrGraphicsRequirementsD3D12KHR* graphicsRequirements);
|
||||
#endif
|
||||
#endif /* XR_USE_GRAPHICS_API_D3D12 */
|
||||
|
||||
#ifdef XR_USE_PLATFORM_WIN32
|
||||
|
||||
#define XR_KHR_win32_convert_performance_counter_time 1
|
||||
#define XR_KHR_win32_convert_performance_counter_time_SPEC_VERSION 1
|
||||
#define XR_KHR_WIN32_CONVERT_PERFORMANCE_COUNTER_TIME_EXTENSION_NAME "XR_KHR_win32_convert_performance_counter_time"
|
||||
typedef XrResult (XRAPI_PTR *PFN_xrConvertWin32PerformanceCounterToTimeKHR)(XrInstance instance, const LARGE_INTEGER* performanceCounter, XrTime* time);
|
||||
typedef XrResult (XRAPI_PTR *PFN_xrConvertTimeToWin32PerformanceCounterKHR)(XrInstance instance, XrTime time, LARGE_INTEGER* performanceCounter);
|
||||
|
||||
#ifndef XR_NO_PROTOTYPES
|
||||
XRAPI_ATTR XrResult XRAPI_CALL xrConvertWin32PerformanceCounterToTimeKHR(
|
||||
XrInstance instance,
|
||||
const LARGE_INTEGER* performanceCounter,
|
||||
XrTime* time);
|
||||
|
||||
XRAPI_ATTR XrResult XRAPI_CALL xrConvertTimeToWin32PerformanceCounterKHR(
|
||||
XrInstance instance,
|
||||
XrTime time,
|
||||
LARGE_INTEGER* performanceCounter);
|
||||
#endif
|
||||
#endif /* XR_USE_PLATFORM_WIN32 */
|
||||
|
||||
#ifdef XR_USE_TIMESPEC
|
||||
|
||||
#define XR_KHR_convert_timespec_time 1
|
||||
#define XR_KHR_convert_timespec_time_SPEC_VERSION 1
|
||||
#define XR_KHR_CONVERT_TIMESPEC_TIME_EXTENSION_NAME "XR_KHR_convert_timespec_time"
|
||||
typedef XrResult (XRAPI_PTR *PFN_xrConvertTimespecTimeToTimeKHR)(XrInstance instance, const struct timespec* timespecTime, XrTime* time);
|
||||
typedef XrResult (XRAPI_PTR *PFN_xrConvertTimeToTimespecTimeKHR)(XrInstance instance, XrTime time, struct timespec* timespecTime);
|
||||
|
||||
#ifndef XR_NO_PROTOTYPES
|
||||
XRAPI_ATTR XrResult XRAPI_CALL xrConvertTimespecTimeToTimeKHR(
|
||||
XrInstance instance,
|
||||
const struct timespec* timespecTime,
|
||||
XrTime* time);
|
||||
|
||||
XRAPI_ATTR XrResult XRAPI_CALL xrConvertTimeToTimespecTimeKHR(
|
||||
XrInstance instance,
|
||||
XrTime time,
|
||||
struct timespec* timespecTime);
|
||||
#endif
|
||||
#endif /* XR_USE_TIMESPEC */
|
||||
|
||||
#ifdef XR_USE_PLATFORM_EGL
|
||||
|
||||
#define XR_MNDX_egl_enable 1
|
||||
#define XR_MNDX_egl_enable_SPEC_VERSION 1
|
||||
#define XR_MNDX_EGL_ENABLE_EXTENSION_NAME "XR_MNDX_egl_enable"
|
||||
typedef struct XrGraphicsBindingEGLMNDX {
|
||||
XrStructureType type;
|
||||
const void* XR_MAY_ALIAS next;
|
||||
PFNEGLGETPROCADDRESSPROC getProcAddress;
|
||||
EGLDisplay display;
|
||||
EGLConfig config;
|
||||
EGLContext context;
|
||||
} XrGraphicsBindingEGLMNDX;
|
||||
|
||||
#endif /* XR_USE_PLATFORM_EGL */
|
||||
|
||||
#ifdef XR_USE_PLATFORM_WIN32
|
||||
|
||||
#define XR_MSFT_perception_anchor_interop_preview 1
|
||||
#define XR_MSFT_perception_anchor_interop_preview_SPEC_VERSION 1
|
||||
#define XR_MSFT_PERCEPTION_ANCHOR_INTEROP_PREVIEW_EXTENSION_NAME "XR_MSFT_perception_anchor_interop_preview"
|
||||
typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialAnchorFromPerceptionAnchorMSFT)(XrSession session, IUnknown* perceptionAnchor, XrSpatialAnchorMSFT* anchor);
|
||||
typedef XrResult (XRAPI_PTR *PFN_xrTryGetPerceptionAnchorFromSpatialAnchorMSFT)(XrSession session, XrSpatialAnchorMSFT anchor, IUnknown** perceptionAnchor);
|
||||
|
||||
#ifndef XR_NO_PROTOTYPES
|
||||
XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialAnchorFromPerceptionAnchorMSFT(
|
||||
XrSession session,
|
||||
IUnknown* perceptionAnchor,
|
||||
XrSpatialAnchorMSFT* anchor);
|
||||
|
||||
XRAPI_ATTR XrResult XRAPI_CALL xrTryGetPerceptionAnchorFromSpatialAnchorMSFT(
|
||||
XrSession session,
|
||||
XrSpatialAnchorMSFT anchor,
|
||||
IUnknown** perceptionAnchor);
|
||||
#endif
|
||||
#endif /* XR_USE_PLATFORM_WIN32 */
|
||||
|
||||
#ifdef XR_USE_PLATFORM_WIN32
|
||||
|
||||
#define XR_MSFT_holographic_window_attachment 1
|
||||
#define XR_MSFT_holographic_window_attachment_SPEC_VERSION 1
|
||||
#define XR_MSFT_HOLOGRAPHIC_WINDOW_ATTACHMENT_EXTENSION_NAME "XR_MSFT_holographic_window_attachment"
|
||||
#ifdef XR_USE_PLATFORM_WIN32
|
||||
typedef struct XrHolographicWindowAttachmentMSFT {
|
||||
XrStructureType type;
|
||||
const void* XR_MAY_ALIAS next;
|
||||
IUnknown* holographicSpace;
|
||||
IUnknown* coreWindow;
|
||||
} XrHolographicWindowAttachmentMSFT;
|
||||
#endif // XR_USE_PLATFORM_WIN32
|
||||
|
||||
#endif /* XR_USE_PLATFORM_WIN32 */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
110
MsftOpenXRGame/Plugins/MicrosoftOpenXR/Source/MicrosoftOpenXR/Private/External/openxr/openxr_platform_defines.h
поставляемый
Normal file
110
MsftOpenXRGame/Plugins/MicrosoftOpenXR/Source/MicrosoftOpenXR/Private/External/openxr/openxr_platform_defines.h
поставляемый
Normal file
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
** Copyright (c) 2017-2020 The Khronos Group Inc.
|
||||
**
|
||||
** SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*/
|
||||
|
||||
#ifndef OPENXR_PLATFORM_DEFINES_H_
|
||||
#define OPENXR_PLATFORM_DEFINES_H_ 1
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Platform-specific calling convention macros.
|
||||
*
|
||||
* Platforms should define these so that OpenXR clients call OpenXR functions
|
||||
* with the same calling conventions that the OpenXR implementation expects.
|
||||
*
|
||||
* XRAPI_ATTR - Placed before the return type in function declarations.
|
||||
* Useful for C++11 and GCC/Clang-style function attribute syntax.
|
||||
* XRAPI_CALL - Placed after the return type in function declarations.
|
||||
* Useful for MSVC-style calling convention syntax.
|
||||
* XRAPI_PTR - Placed between the '(' and '*' in function pointer types.
|
||||
*
|
||||
* Function declaration: XRAPI_ATTR void XRAPI_CALL xrFunction(void);
|
||||
* Function pointer type: typedef void (XRAPI_PTR *PFN_xrFunction)(void);
|
||||
*/
|
||||
#if defined(_WIN32)
|
||||
#define XRAPI_ATTR
|
||||
// On Windows, functions use the stdcall convention
|
||||
#define XRAPI_CALL __stdcall
|
||||
#define XRAPI_PTR XRAPI_CALL
|
||||
#elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH < 7
|
||||
#error "API not supported for the 'armeabi' NDK ABI"
|
||||
#elif defined(__ANDROID__) && defined(__ARM_ARCH) && __ARM_ARCH >= 7 && defined(__ARM_32BIT_STATE)
|
||||
// On Android 32-bit ARM targets, functions use the "hardfloat"
|
||||
// calling convention, i.e. float parameters are passed in registers. This
|
||||
// is true even if the rest of the application passes floats on the stack,
|
||||
// as it does by default when compiling for the armeabi-v7a NDK ABI.
|
||||
#define XRAPI_ATTR __attribute__((pcs("aapcs-vfp")))
|
||||
#define XRAPI_CALL
|
||||
#define XRAPI_PTR XRAPI_ATTR
|
||||
#else
|
||||
// On other platforms, use the default calling convention
|
||||
#define XRAPI_ATTR
|
||||
#define XRAPI_CALL
|
||||
#define XRAPI_PTR
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
#if !defined(XR_NO_STDINT_H)
|
||||
#if defined(_MSC_VER) && (_MSC_VER < 1600)
|
||||
typedef signed __int8 int8_t;
|
||||
typedef unsigned __int8 uint8_t;
|
||||
typedef signed __int16 int16_t;
|
||||
typedef unsigned __int16 uint16_t;
|
||||
typedef signed __int32 int32_t;
|
||||
typedef unsigned __int32 uint32_t;
|
||||
typedef signed __int64 int64_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
#else
|
||||
#include <stdint.h>
|
||||
#endif
|
||||
#endif // !defined( XR_NO_STDINT_H )
|
||||
|
||||
// XR_PTR_SIZE (in bytes)
|
||||
#if (defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__) ) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(__powerpc64__))
|
||||
#define XR_PTR_SIZE 8
|
||||
#else
|
||||
#define XR_PTR_SIZE 4
|
||||
#endif
|
||||
|
||||
// Needed so we can use clang __has_feature portably.
|
||||
#if !defined(XR_COMPILER_HAS_FEATURE)
|
||||
#if defined(__clang__)
|
||||
#define XR_COMPILER_HAS_FEATURE(x) __has_feature(x)
|
||||
#else
|
||||
#define XR_COMPILER_HAS_FEATURE(x) 0
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Identifies if the current compiler has C++11 support enabled.
|
||||
// Does not by itself identify if any given C++11 feature is present.
|
||||
#if !defined(XR_CPP11_ENABLED) && defined(__cplusplus)
|
||||
#if defined(__GNUC__) && defined(__GXX_EXPERIMENTAL_CXX0X__)
|
||||
#define XR_CPP11_ENABLED 1
|
||||
#elif defined(_MSC_VER) && (_MSC_VER >= 1600)
|
||||
#define XR_CPP11_ENABLED 1
|
||||
#elif (__cplusplus >= 201103L) // 201103 is the first C++11 version.
|
||||
#define XR_CPP11_ENABLED 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Identifies if the current compiler supports C++11 nullptr.
|
||||
#if !defined(XR_CPP_NULLPTR_SUPPORTED)
|
||||
#if defined(XR_CPP11_ENABLED) && \
|
||||
((defined(__clang__) && XR_COMPILER_HAS_FEATURE(cxx_nullptr)) || \
|
||||
(defined(__GNUC__) && (((__GNUC__ * 1000) + __GNUC_MINOR__) >= 4006)) || \
|
||||
(defined(_MSC_VER) && (_MSC_VER >= 1600)) || \
|
||||
(defined(__EDG_VERSION__) && (__EDG_VERSION__ >= 403)))
|
||||
#define XR_CPP_NULLPTR_SUPPORTED 1
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
1638
MsftOpenXRGame/Plugins/MicrosoftOpenXR/Source/MicrosoftOpenXR/Private/External/openxr/openxr_reflection.h
поставляемый
Normal file
1638
MsftOpenXRGame/Plugins/MicrosoftOpenXR/Source/MicrosoftOpenXR/Private/External/openxr/openxr_reflection.h
поставляемый
Normal file
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,392 @@
|
|||
#include "HandMeshPlugin.h"
|
||||
#include "InputCoreTypes.h"
|
||||
#include "OpenXRCore.h"
|
||||
#include "IXRTrackingSystem.h"
|
||||
#include "IOpenXRARModule.h"
|
||||
#include "IOpenXRARTrackedGeometryHolder.h"
|
||||
#include "ARSessionConfig.h"
|
||||
#include "HeadMountedDisplayTypes.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FMicrosoftOpenXRModule"
|
||||
|
||||
|
||||
namespace MicrosoftOpenXR
|
||||
{
|
||||
FHandMeshPlugin::FHandState::FHandState()
|
||||
{
|
||||
}
|
||||
|
||||
bool FHandMeshPlugin::GetRequiredExtensions(TArray<const ANSICHAR*>& OutExtensions)
|
||||
{
|
||||
OutExtensions.Add(XR_EXT_HAND_TRACKING_EXTENSION_NAME);
|
||||
OutExtensions.Add(XR_MSFT_HAND_TRACKING_MESH_EXTENSION_NAME);
|
||||
return true;
|
||||
}
|
||||
|
||||
const void* FHandMeshPlugin::OnCreateSession(XrInstance InInstance, XrSystemId InSystem, const void* InNext)
|
||||
{
|
||||
XrSystemHandTrackingMeshPropertiesMSFT HandMeshTrackingSystemProperties{ XR_TYPE_SYSTEM_HAND_TRACKING_MESH_PROPERTIES_MSFT };
|
||||
XrSystemHandTrackingPropertiesEXT HandTrackingSystemProperties{ XR_TYPE_SYSTEM_HAND_TRACKING_PROPERTIES_EXT, &HandMeshTrackingSystemProperties };
|
||||
XrSystemProperties systemProperties{ XR_TYPE_SYSTEM_PROPERTIES, &HandTrackingSystemProperties };
|
||||
|
||||
XR_ENSURE(xrGetSystemProperties(InInstance, InSystem, &systemProperties));
|
||||
|
||||
bool bHandMeshTrackingAvailable = HandTrackingSystemProperties.supportsHandTracking != XR_FALSE && HandMeshTrackingSystemProperties.supportsHandTrackingMesh != XR_FALSE;
|
||||
|
||||
if (bHandMeshTrackingAvailable)
|
||||
{
|
||||
XR_ENSURE(xrGetInstanceProcAddr(InInstance, "xrCreateHandTrackerEXT", (PFN_xrVoidFunction*)&xrCreateHandTrackerEXT));
|
||||
XR_ENSURE(xrGetInstanceProcAddr(InInstance, "xrCreateHandMeshSpaceMSFT", (PFN_xrVoidFunction*)&xrCreateHandMeshSpaceMSFT));
|
||||
XR_ENSURE(xrGetInstanceProcAddr(InInstance, "xrUpdateHandMeshMSFT", (PFN_xrVoidFunction*)&xrUpdateHandMeshMSFT));
|
||||
|
||||
for (int i=0; i<HandCount; ++i)
|
||||
{
|
||||
FHandState& HandState = HandStates[i];
|
||||
|
||||
HandState.IndicesCount = 0;
|
||||
HandState.VerticesCount = 0;
|
||||
|
||||
HandState.VerticesMaxAmount = HandMeshTrackingSystemProperties.maxHandMeshVertexCount;
|
||||
HandState.IndicesMaxAmount = HandMeshTrackingSystemProperties.maxHandMeshIndexCount;
|
||||
|
||||
HandState.Guid = FGuid::NewGuid();
|
||||
|
||||
HandState.Vertices.resize(HandState.VerticesMaxAmount);
|
||||
HandState.Indices.resize(HandState.IndicesMaxAmount);
|
||||
}
|
||||
|
||||
HandMeshStatus = EHandMeshStatus::Disabled;
|
||||
}
|
||||
else
|
||||
{
|
||||
HandMeshStatus = EHandMeshStatus::NotInitialised;
|
||||
}
|
||||
|
||||
return InNext;
|
||||
}
|
||||
|
||||
const void* FHandMeshPlugin::OnBeginSession(XrSession InSession, const void* InNext)
|
||||
{
|
||||
if (HandMeshStatus == EHandMeshStatus::NotInitialised)
|
||||
{
|
||||
return InNext;
|
||||
}
|
||||
static FName SystemName(TEXT("OpenXR"));
|
||||
if (GEngine->XRSystem.IsValid() && (GEngine->XRSystem->GetSystemName() == SystemName))
|
||||
{
|
||||
XRTrackingSystem = GEngine->XRSystem.Get();
|
||||
}
|
||||
else
|
||||
{
|
||||
HandMeshStatus = EHandMeshStatus::NotInitialised;
|
||||
return InNext;
|
||||
}
|
||||
|
||||
if (IOpenXRARModule::IsAvailable())
|
||||
{
|
||||
TrackedMeshHolder = IOpenXRARModule::Get().GetTrackedMeshHolder();
|
||||
}
|
||||
else
|
||||
{
|
||||
HandMeshStatus = EHandMeshStatus::NotInitialised;
|
||||
return InNext;
|
||||
}
|
||||
|
||||
for (int i = 0; i < HandCount; ++i)
|
||||
{
|
||||
FHandState& HandState = HandStates[i];
|
||||
|
||||
XrHandTrackerCreateInfoEXT CreateInfo{ XR_TYPE_HAND_TRACKER_CREATE_INFO_EXT };
|
||||
CreateInfo.hand = XrHandEXT(XR_HAND_LEFT_EXT + i);
|
||||
CreateInfo.handJointSet = XR_HAND_JOINT_SET_DEFAULT_EXT;
|
||||
XR_ENSURE(xrCreateHandTrackerEXT(InSession, &CreateInfo, &HandState.HandTracker));
|
||||
|
||||
XrHandMeshSpaceCreateInfoMSFT SpaceCreateInfo{ XR_TYPE_HAND_MESH_SPACE_CREATE_INFO_MSFT };
|
||||
SpaceCreateInfo.handPoseType = XR_HAND_POSE_TYPE_TRACKED_MSFT;
|
||||
SpaceCreateInfo.poseInHandMeshSpace = ToXrPose(FTransform::Identity, XRTrackingSystem->GetWorldToMetersScale());
|
||||
XR_ENSURE(xrCreateHandMeshSpaceMSFT(HandState.HandTracker, &SpaceCreateInfo, &HandState.Space));
|
||||
}
|
||||
|
||||
return InNext;
|
||||
}
|
||||
|
||||
void FHandMeshPlugin::UpdateDeviceLocations(XrSession InSession, XrTime DisplayTime, XrSpace TrackingSpace)
|
||||
{
|
||||
check(IsInGameThread());
|
||||
|
||||
if (HandMeshStatus <= EHandMeshStatus::Disabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < HandCount; ++i)
|
||||
{
|
||||
FHandState& HandState = HandStates[i];
|
||||
|
||||
XrSpaceLocation SpaceLocation { XR_TYPE_SPACE_LOCATION };
|
||||
|
||||
XR_ENSURE(xrLocateSpace(HandState.Space, TrackingSpace, DisplayTime, &SpaceLocation));
|
||||
const XrSpaceLocationFlags ValidFlags = XR_SPACE_LOCATION_ORIENTATION_VALID_BIT | XR_SPACE_LOCATION_POSITION_VALID_BIT;
|
||||
|
||||
if ((SpaceLocation.locationFlags & ValidFlags) == ValidFlags)
|
||||
{
|
||||
HandState.TrackingState = EARTrackingState::Tracking;
|
||||
HandState.LocalToTrackingTransform = ToFTransform(SpaceLocation.pose, XRTrackingSystem->GetWorldToMetersScale());
|
||||
|
||||
XrHandMeshUpdateInfoMSFT HandMeshUpdateInfo { XR_TYPE_HAND_MESH_UPDATE_INFO_MSFT };
|
||||
HandMeshUpdateInfo.time = DisplayTime;
|
||||
HandMeshUpdateInfo.handPoseType = XR_HAND_POSE_TYPE_TRACKED_MSFT;
|
||||
|
||||
XrHandMeshMSFT HandMesh { XR_TYPE_HAND_MESH_MSFT };
|
||||
HandMesh.indexBuffer.indexCapacityInput = HandState.Indices.size();
|
||||
HandMesh.indexBuffer.indices = HandState.Indices.data();
|
||||
|
||||
HandMesh.vertexBuffer.vertexCapacityInput = HandState.Vertices.size();
|
||||
HandMesh.vertexBuffer.vertices = HandState.Vertices.data();
|
||||
|
||||
XR_ENSURE(xrUpdateHandMeshMSFT(HandState.HandTracker, &HandMeshUpdateInfo, &HandMesh));
|
||||
|
||||
|
||||
if (HandMesh.indexBufferChanged)
|
||||
{
|
||||
HandState.IndicesCount = HandMesh.indexBuffer.indexCountOutput;
|
||||
//changes in index buffer can't be independent, if index buffer changes, vertex buffers changes as well
|
||||
}
|
||||
|
||||
if (HandMesh.vertexBufferChanged)
|
||||
{
|
||||
HandState.VerticesCount = HandMesh.vertexBuffer.vertexCountOutput;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
//tracking lost
|
||||
HandState.TrackingState = EARTrackingState::NotTracking;
|
||||
|
||||
HandState.IndicesCount = 0;
|
||||
HandState.VerticesCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (HandMeshStatus != EHandMeshStatus::EnabledTrackingGeometry)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
TrackedMeshHolder->StartMeshUpdates();
|
||||
for (int i = 0; i < HandCount; ++i)
|
||||
{
|
||||
FHandState& HandState = HandStates[i];
|
||||
|
||||
FOpenXRMeshUpdate* MeshUpdate = TrackedMeshHolder->AllocateMeshUpdate(HandState.Guid);
|
||||
MeshUpdate->Type = EARObjectClassification::HandMesh;
|
||||
MeshUpdate->TrackingState = HandState.TrackingState;
|
||||
if (HandState.TrackingState != EARTrackingState::Tracking)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
MeshUpdate->LocalToTrackingTransform = HandState.LocalToTrackingTransform;
|
||||
MeshUpdate->Indices.AddUninitialized(HandState.IndicesCount * 2);
|
||||
|
||||
size_t TriangleCount = (size_t)HandState.IndicesCount / 3;
|
||||
|
||||
auto DestIndices = MeshUpdate->Indices.GetData();
|
||||
auto RawIndices = HandState.Indices.data();
|
||||
|
||||
for (size_t j = 0; j < TriangleCount; ++j)
|
||||
{
|
||||
//forward face
|
||||
DestIndices[0] = RawIndices[2];
|
||||
DestIndices[1] = RawIndices[1];
|
||||
DestIndices[2] = RawIndices[0];
|
||||
|
||||
//backward face
|
||||
DestIndices[3] = RawIndices[0];
|
||||
DestIndices[4] = RawIndices[1];
|
||||
DestIndices[5] = RawIndices[2];
|
||||
|
||||
DestIndices += 6;
|
||||
RawIndices += 3;
|
||||
}
|
||||
|
||||
MeshUpdate->Vertices.AddUninitialized(HandState.VerticesCount);
|
||||
for (size_t j = 0; j < HandState.VerticesCount; ++j)
|
||||
{
|
||||
auto& destVert = MeshUpdate->Vertices[j];
|
||||
const auto& srcVert = HandState.Vertices[j];
|
||||
|
||||
destVert = ToFVector(srcVert.position, XRTrackingSystem->GetWorldToMetersScale());
|
||||
}
|
||||
}
|
||||
TrackedMeshHolder->EndMeshUpdates();
|
||||
}
|
||||
|
||||
void FHandMeshPlugin::Register()
|
||||
{
|
||||
IModularFeatures::Get().RegisterModularFeature(IHandTracker::GetModularFeatureName(), static_cast<IHandTracker*>(this));
|
||||
IModularFeatures::Get().RegisterModularFeature(IOpenXRExtensionPlugin::GetModularFeatureName(), static_cast<IOpenXRExtensionPlugin*>(this));
|
||||
}
|
||||
|
||||
void FHandMeshPlugin::Unregister()
|
||||
{
|
||||
IModularFeatures::Get().UnregisterModularFeature(IHandTracker::GetModularFeatureName(), static_cast<IHandTracker*>(this));
|
||||
IModularFeatures::Get().UnregisterModularFeature(IOpenXRExtensionPlugin::GetModularFeatureName(), static_cast<IOpenXRExtensionPlugin*>(this));
|
||||
}
|
||||
|
||||
bool FHandMeshPlugin::Turn(EHandMeshStatus Mode)
|
||||
{
|
||||
check(IsInGameThread());
|
||||
|
||||
if (HandMeshStatus == EHandMeshStatus::NotInitialised)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Mode == EHandMeshStatus::Disabled)
|
||||
{
|
||||
for (int i = 0; i < HandCount; ++i)
|
||||
{
|
||||
FHandState& HandState = HandStates[i];
|
||||
|
||||
HandState.IndicesCount = 0;
|
||||
HandState.VerticesCount = 0;
|
||||
HandState.LocalToTrackingTransform.SetIdentity();
|
||||
HandState.TrackingState = EARTrackingState::NotTracking;
|
||||
|
||||
if (HandMeshStatus == EHandMeshStatus::EnabledTrackingGeometry)
|
||||
{
|
||||
TrackedMeshHolder->RemoveMesh(HandState.Guid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HandMeshStatus = Mode;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FHandMeshPlugin::IsHandTrackingStateValid() const
|
||||
{
|
||||
if (HandMeshStatus <= EHandMeshStatus::Disabled)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < HandCount; ++i)
|
||||
{
|
||||
const FHandState& HandState = HandStates[i];
|
||||
if (HandState.TrackingState == EARTrackingState::Tracking)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FHandMeshPlugin::HasHandMeshData() const
|
||||
{
|
||||
if (HandMeshStatus <= EHandMeshStatus::Disabled)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < HandCount; ++i)
|
||||
{
|
||||
const FHandState& HandState = HandStates[i];
|
||||
if (HandState.VerticesCount > 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FHandMeshPlugin::GetHandMeshData(EControllerHand Hand, TArray<struct FVector>& OutVertices, TArray<struct FVector>& OutNormals, TArray<int32>& OutIndices, FTransform& OutHandMeshTransform) const
|
||||
{
|
||||
check(IsInGameThread());
|
||||
|
||||
if (HandMeshStatus <= EHandMeshStatus::Disabled)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const FHandState* HandState = nullptr;
|
||||
if (Hand == EControllerHand::Left)
|
||||
{
|
||||
HandState = HandStates + Left;
|
||||
}
|
||||
else if (Hand == EControllerHand::Right)
|
||||
{
|
||||
HandState = HandStates + Right;
|
||||
}
|
||||
else
|
||||
{
|
||||
//do nothing for an unknown controller
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if (HandMeshStatus != EHandMeshStatus::EnabledXRVisualization)
|
||||
{
|
||||
OutIndices.Empty();
|
||||
OutVertices.Empty();
|
||||
OutNormals.Empty();
|
||||
OutHandMeshTransform = FTransform::Identity;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//clear the data without deallocation
|
||||
OutIndices.Reset(HandState->IndicesCount * 2);
|
||||
OutVertices.Reset(HandState->VerticesCount);
|
||||
OutNormals.Reset(HandState->VerticesCount);
|
||||
|
||||
if (HandState->TrackingState != EARTrackingState::Tracking)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
OutHandMeshTransform = HandState->LocalToTrackingTransform * XRTrackingSystem->GetTrackingToWorldTransform();
|
||||
|
||||
OutIndices.AddUninitialized(HandState->IndicesCount * 2);
|
||||
|
||||
size_t TriangleCount = (size_t)HandState->IndicesCount / 3;
|
||||
|
||||
auto DestIndices = OutIndices.GetData();
|
||||
auto RawIndices = HandState->Indices.data();
|
||||
|
||||
for (size_t j = 0; j < TriangleCount; ++j)
|
||||
{
|
||||
//forward face
|
||||
DestIndices[0] = RawIndices[2];
|
||||
DestIndices[1] = RawIndices[1];
|
||||
DestIndices[2] = RawIndices[0];
|
||||
|
||||
//backward face
|
||||
DestIndices[3] = RawIndices[0];
|
||||
DestIndices[4] = RawIndices[1];
|
||||
DestIndices[5] = RawIndices[2];
|
||||
|
||||
DestIndices += 6;
|
||||
RawIndices += 3;
|
||||
}
|
||||
|
||||
OutVertices.AddUninitialized(HandState->VerticesCount);
|
||||
OutNormals.AddUninitialized(HandState->VerticesCount);
|
||||
for (size_t j = 0; j < HandState->VerticesCount; ++j)
|
||||
{
|
||||
auto& destVert = OutVertices[j];
|
||||
auto& destNorm = OutNormals[j];
|
||||
const auto& srcVert = HandState->Vertices[j];
|
||||
|
||||
destVert = ToFVector(srcVert.position, XRTrackingSystem->GetWorldToMetersScale());
|
||||
destNorm = -ToFVector(srcVert.normal);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace MicrosoftOpenXR
|
|
@ -0,0 +1,86 @@
|
|||
#pragma once
|
||||
|
||||
#include "OpenXRCommon.h"
|
||||
#include "ARTypes.h"
|
||||
#include "MicrosoftOpenXR.h"
|
||||
#include "IHandTracker.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
class IOpenXRARTrackedMeshHolder;
|
||||
|
||||
namespace MicrosoftOpenXR
|
||||
{
|
||||
class FHandMeshPlugin : public IOpenXRExtensionPlugin, public IHandTracker
|
||||
{
|
||||
public:
|
||||
static const int HandCount = 2;
|
||||
enum Hand {Left = 0, Right = 1};
|
||||
|
||||
struct FHandState : public FNoncopyable
|
||||
{
|
||||
FHandState();
|
||||
|
||||
XrHandTrackerEXT HandTracker{};
|
||||
XrSpace Space{};
|
||||
|
||||
std::vector<uint32_t> Indices;
|
||||
std::vector<XrHandMeshVertexMSFT> Vertices;
|
||||
|
||||
size_t VerticesCount = 0;
|
||||
size_t IndicesCount = 0;
|
||||
|
||||
size_t VerticesMaxAmount = 0;
|
||||
size_t IndicesMaxAmount = 0;
|
||||
|
||||
FGuid Guid;
|
||||
|
||||
EARTrackingState TrackingState = EARTrackingState::Unknown;
|
||||
FTransform LocalToTrackingTransform;
|
||||
};
|
||||
|
||||
void Register();
|
||||
void Unregister();
|
||||
|
||||
bool GetRequiredExtensions(TArray<const ANSICHAR*>& OutExtensions) override;
|
||||
const void* OnCreateSession(XrInstance InInstance, XrSystemId InSystem, const void* InNext) override;
|
||||
const void* OnBeginSession(XrSession InSession, const void* InNext) override;
|
||||
void UpdateDeviceLocations(XrSession InSession, XrTime DisplayTime, XrSpace TrackingSpace) override;
|
||||
|
||||
bool Turn(EHandMeshStatus Mode);
|
||||
private:
|
||||
FHandState HandStates[HandCount];
|
||||
|
||||
EHandMeshStatus HandMeshStatus = EHandMeshStatus::NotInitialised;
|
||||
|
||||
PFN_xrCreateHandTrackerEXT xrCreateHandTrackerEXT = nullptr;
|
||||
|
||||
PFN_xrCreateHandMeshSpaceMSFT xrCreateHandMeshSpaceMSFT = nullptr;
|
||||
PFN_xrUpdateHandMeshMSFT xrUpdateHandMeshMSFT = nullptr;
|
||||
|
||||
class IXRTrackingSystem* XRTrackingSystem = nullptr;
|
||||
IOpenXRARTrackedMeshHolder* TrackedMeshHolder = nullptr;
|
||||
|
||||
// Inherited via IHandTracker
|
||||
virtual FName GetHandTrackerDeviceTypeName() const override
|
||||
{
|
||||
return FName(TEXT("MicrosoftOpenXRHandTracking"));
|
||||
}
|
||||
|
||||
virtual bool IsHandTrackingStateValid() const override;
|
||||
|
||||
virtual bool GetKeypointState(EControllerHand Hand, EHandKeypoint Keypoint, FTransform& OutTransform, float& OutRadius) const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool GetAllKeypointStates(EControllerHand Hand, TArray<struct FVector>& OutPositions, TArray<struct FQuat>& OutRotations, TArray<float>& OutRadii) const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual bool HasHandMeshData() const override;
|
||||
|
||||
virtual bool GetHandMeshData(EControllerHand Hand, TArray<struct FVector>& OutVertices, TArray<struct FVector>& OutNormals, TArray<int32>& OutIndices, FTransform& OutHandMeshTransform) const override;
|
||||
};
|
||||
} // namespace MicrosoftOpenXR
|
|
@ -0,0 +1,408 @@
|
|||
#include "HolographicRemotingPlugin.h"
|
||||
|
||||
#if SUPPORTS_REMOTING
|
||||
|
||||
#include "InputCoreTypes.h"
|
||||
|
||||
namespace MicrosoftOpenXR
|
||||
{
|
||||
void FHolographicRemotingPlugin::Register()
|
||||
{
|
||||
IModularFeatures::Get().RegisterModularFeature(GetModularFeatureName(), this);
|
||||
}
|
||||
|
||||
void FHolographicRemotingPlugin::Unregister()
|
||||
{
|
||||
IModularFeatures::Get().UnregisterModularFeature(GetModularFeatureName(), this);
|
||||
|
||||
if (UMicrosoftOpenXRRuntimeSettings::Get())
|
||||
{
|
||||
UMicrosoftOpenXRRuntimeSettings::Get()->OnRemotingConnect.Unbind();
|
||||
UMicrosoftOpenXRRuntimeSettings::Get()->OnRemotingDisconnect.Unbind();
|
||||
}
|
||||
}
|
||||
|
||||
bool FHolographicRemotingPlugin::GetCustomLoader(PFN_xrGetInstanceProcAddr* OutGetProcAddr)
|
||||
{
|
||||
remotingEnabled = ParseRemotingCmdArgs();
|
||||
|
||||
if (remotingEnabled)
|
||||
{
|
||||
FString platformName = FPlatformProperties::IniPlatformName();
|
||||
const FString RemotingJsonPath = FPaths::ProjectPluginsDir() / "MicrosoftOpenXR/ThirdParty/HolographicAppRemoting" /
|
||||
platformName / "Win64/RemotingXR.json";
|
||||
|
||||
if (FPaths::FileExists(RemotingJsonPath))
|
||||
{
|
||||
// Set the XR_RUNTIME_JSON environment variable to point to the app remoting dll.
|
||||
// This must be done before loading openxr_loader.dll.
|
||||
SetEnvironmentVariableW(L"XR_RUNTIME_JSON", *RemotingJsonPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
// RemotingXR.json could not be found. Do not attempt to use remoting.
|
||||
remotingEnabled = false;
|
||||
UE_LOG(LogHMD, Warning,
|
||||
TEXT("Remoting is desired, but RemotingXR.json could not be found at expected location: %s"),
|
||||
*RemotingJsonPath);
|
||||
}
|
||||
}
|
||||
|
||||
// Return false to use the intended OpenXR loader.
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FHolographicRemotingPlugin::GetRequiredExtensions(TArray<const ANSICHAR*>& OutExtensions)
|
||||
{
|
||||
if (remotingEnabled)
|
||||
{
|
||||
OutExtensions.Add(XR_MSFT_HOLOGRAPHIC_REMOTING_EXTENSION_NAME);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FHolographicRemotingPlugin::OnEvent(XrSession InSession, const XrEventDataBaseHeader* InHeader)
|
||||
{
|
||||
switch ((XrRemotingStructureType)InHeader->type)
|
||||
{
|
||||
case XR_TYPE_REMOTING_EVENT_DATA_CONNECTED_MSFT:
|
||||
SetRemotingStatusText(FString("Connected"), FLinearColor::Green);
|
||||
break;
|
||||
case XR_TYPE_REMOTING_EVENT_DATA_DISCONNECTED_MSFT:
|
||||
UpdateDisconnectedText();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to create a connection between getting the system and the OpenXRHMD constructor.
|
||||
// The OpenXRHMD ctor calls some OpenXR API's which will return default values for HoloLens if a connection has not been made.
|
||||
// When connecting to a VR HMD, using default values will be fatal.
|
||||
void FHolographicRemotingPlugin::PostGetSystem(XrInstance InInstance, XrSystemId InSystem)
|
||||
{
|
||||
Instance = InInstance;
|
||||
System = InSystem;
|
||||
|
||||
// An app remoting player must be running on the remote device for this call to succeed.
|
||||
// Otherwise a disconnect will fire and a connection can happen in the editor at runtime,
|
||||
// but default HoloLens values will be used in the remoting runtime.
|
||||
//
|
||||
// When connecting from a packaged exe, the app remoting player must be running at this time.
|
||||
// Otherwise there is no suitable fallback for connecting, since xrLocateSpace will be called
|
||||
// before any other event gets a chance to make another connection, and will crash without a valid connection.
|
||||
ConnectToRemoteDevice(remotingConnectionData);
|
||||
}
|
||||
|
||||
// Bind delegates here, since UMicrosoftOpenXRRuntimeSettings will now be initialized.
|
||||
const void* FHolographicRemotingPlugin::OnCreateSession(XrInstance InInstance, XrSystemId InSystem, const void* InNext)
|
||||
{
|
||||
#if WITH_EDITOR
|
||||
// If automatically connecting to the remote device, establish the connection now.
|
||||
GConfig->GetBool(TEXT("/Script/MicrosoftOpenXRRuntimeSettings.MicrosoftOpenXRRuntimeSettings"), TEXT("bAutoConnectRemoting"),
|
||||
autoConnectRemoting, GEditorPerProjectIni);
|
||||
if (autoConnectRemoting)
|
||||
{
|
||||
ParseRemotingConfig();
|
||||
ConnectToRemoteDevice(remotingConnectionData);
|
||||
}
|
||||
|
||||
if (UMicrosoftOpenXRRuntimeSettings::Get())
|
||||
{
|
||||
UMicrosoftOpenXRRuntimeSettings::Get()->OnRemotingConnect.BindSP(this, &FHolographicRemotingPlugin::ConnectToRemoteDevice);
|
||||
UMicrosoftOpenXRRuntimeSettings::Get()->OnRemotingDisconnect.BindSP(
|
||||
this, &FHolographicRemotingPlugin::DisconnectFromRemoteDevice);
|
||||
}
|
||||
#endif
|
||||
|
||||
return InNext;
|
||||
}
|
||||
|
||||
XrRemotingConnectionStateMSFT FHolographicRemotingPlugin::GetRemotingConnectionState()
|
||||
{
|
||||
if (!remotingEnabled
|
||||
|| System == XR_NULL_SYSTEM_ID)
|
||||
{
|
||||
return XrRemotingConnectionStateMSFT::XR_REMOTING_CONNECTION_STATE_DISCONNECTED_MSFT;
|
||||
}
|
||||
|
||||
PFN_xrRemotingGetConnectionStateMSFT getConnectionState;
|
||||
if (XR_FAILED(
|
||||
xrGetInstanceProcAddr(Instance, "xrRemotingGetConnectionStateMSFT", (PFN_xrVoidFunction*)&getConnectionState)))
|
||||
{
|
||||
return XrRemotingConnectionStateMSFT::XR_REMOTING_CONNECTION_STATE_DISCONNECTED_MSFT;
|
||||
}
|
||||
|
||||
XrRemotingConnectionStateMSFT connectionState;
|
||||
if (XR_FAILED(getConnectionState(Instance, System, &connectionState, nullptr)))
|
||||
{
|
||||
return XrRemotingConnectionStateMSFT::XR_REMOTING_CONNECTION_STATE_DISCONNECTED_MSFT;
|
||||
}
|
||||
|
||||
return connectionState;
|
||||
}
|
||||
|
||||
void FHolographicRemotingPlugin::ConnectToRemoteDevice(RemotingConnectionData data)
|
||||
{
|
||||
if (!remotingEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetRemotingConnectionState() !=
|
||||
XrRemotingConnectionStateMSFT::XR_REMOTING_CONNECTION_STATE_DISCONNECTED_MSFT)
|
||||
{
|
||||
// Already connected.
|
||||
return;
|
||||
}
|
||||
|
||||
SetRemotingStatusText("Connecting...", FLinearColor::Yellow);
|
||||
if (data.IP.IsEmpty())
|
||||
{
|
||||
SetRemotingStatusText("Cancelled, IP is not set.", FLinearColor::Red);
|
||||
return;
|
||||
}
|
||||
|
||||
PFN_xrRemotingSetContextPropertiesMSFT setContextProperties;
|
||||
XR_ENSURE(
|
||||
xrGetInstanceProcAddr(Instance, "xrRemotingSetContextPropertiesMSFT", (PFN_xrVoidFunction*)&setContextProperties));
|
||||
|
||||
// Set remoting properties - this must be done while disconnected.
|
||||
XrRemotingRemoteContextPropertiesMSFT remoteContext;
|
||||
remoteContext.type = (XrStructureType)XR_TYPE_REMOTING_REMOTE_CONTEXT_PROPERTIES_MSFT;
|
||||
remoteContext.next = nullptr;
|
||||
remoteContext.enableAudio = data.EnableAudio;
|
||||
// Protect against negative or unsuitably low bitrates.
|
||||
const int minBitrate = 1000;
|
||||
if (data.Bitrate < minBitrate)
|
||||
{
|
||||
data.Bitrate = minBitrate;
|
||||
}
|
||||
remoteContext.maxBitrateKbps = data.Bitrate;
|
||||
remoteContext.videoCodec = (XrRemotingVideoCodecMSFT)data.ConnectionCodec;
|
||||
if (XR_FAILED(setContextProperties(Instance, System, &remoteContext)))
|
||||
{
|
||||
SetRemotingStatusText("xrRemotingSetContextPropertiesMSFT failed, check remoting inputs.", FLinearColor::Red);
|
||||
return;
|
||||
}
|
||||
|
||||
// Listen
|
||||
if (data.ConnectionType == RemotingConnectionType::Listen)
|
||||
{
|
||||
XrRemotingListenInfoMSFT listenInfo{ static_cast<XrStructureType>(XR_TYPE_REMOTING_LISTEN_INFO_MSFT) };
|
||||
listenInfo.listenInterface = TCHAR_TO_ANSI(*data.IP.TrimStartAndEnd());
|
||||
listenInfo.handshakeListenPort = data.Port;
|
||||
listenInfo.transportListenPort = data.Port + 1;
|
||||
listenInfo.secureConnection = false;
|
||||
|
||||
PFN_xrRemotingListenMSFT remotingListen;
|
||||
XR_ENSURE(xrGetInstanceProcAddr(Instance, "xrRemotingListenMSFT", (PFN_xrVoidFunction*)&remotingListen));
|
||||
|
||||
// Connect for remote connection.
|
||||
if (XR_FAILED(remotingListen(Instance, System, &listenInfo)))
|
||||
{
|
||||
SetRemotingStatusText("xrRemotingListenMSFT failed.", FLinearColor::Red);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else // Connect
|
||||
{
|
||||
XrRemotingConnectInfoMSFT connectInfo{ static_cast<XrStructureType>(XR_TYPE_REMOTING_CONNECT_INFO_MSFT) };
|
||||
connectInfo.remoteHostName = TCHAR_TO_ANSI(*data.IP.TrimStartAndEnd());
|
||||
connectInfo.remotePort = data.Port;
|
||||
connectInfo.secureConnection = false;
|
||||
|
||||
PFN_xrRemotingConnectMSFT remotingConnect;
|
||||
XR_ENSURE(xrGetInstanceProcAddr(Instance, "xrRemotingConnectMSFT", (PFN_xrVoidFunction*)&remotingConnect));
|
||||
|
||||
// Connect to remote device.
|
||||
if (XR_FAILED(remotingConnect(Instance, System, &connectInfo)))
|
||||
{
|
||||
SetRemotingStatusText("xrRemotingConnectMSFT failed.", FLinearColor::Red);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FHolographicRemotingPlugin::DisconnectFromRemoteDevice()
|
||||
{
|
||||
if (!remotingEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetRemotingConnectionState() ==
|
||||
XrRemotingConnectionStateMSFT::XR_REMOTING_CONNECTION_STATE_DISCONNECTED_MSFT)
|
||||
{
|
||||
// Already disconnected.
|
||||
return;
|
||||
}
|
||||
|
||||
PFN_xrRemotingDisconnectMSFT remotingDisconnect;
|
||||
XR_ENSURE(xrGetInstanceProcAddr(Instance, "xrRemotingDisconnectMSFT", (PFN_xrVoidFunction*)&remotingDisconnect));
|
||||
|
||||
// Disconnect from remote device.
|
||||
XrRemotingDisconnectInfoMSFT disconnectInfo{ static_cast<XrStructureType>(XR_TYPE_REMOTING_DISCONNECT_INFO_MSFT) };
|
||||
if (XR_FAILED(remotingDisconnect(Instance, System, &disconnectInfo)))
|
||||
{
|
||||
SetRemotingStatusText("xrRemotingDisconnectMSFT failed.", FLinearColor::Red);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void FHolographicRemotingPlugin::SetRemotingStatusText(FString message, FLinearColor statusColor)
|
||||
{
|
||||
#if WITH_EDITOR
|
||||
if (UMicrosoftOpenXRRuntimeSettings::Get() != nullptr)
|
||||
{
|
||||
UMicrosoftOpenXRRuntimeSettings::Get()->OnRemotingStatusChanged.ExecuteIfBound(message, statusColor);
|
||||
}
|
||||
#endif
|
||||
|
||||
UE_LOG(LogHMD, Log, TEXT("HolographicRemotingPlugin::SetRemotingStatusText: %s"), *message);
|
||||
}
|
||||
|
||||
void FHolographicRemotingPlugin::UpdateDisconnectedText()
|
||||
{
|
||||
PFN_xrRemotingGetConnectionStateMSFT getConnectionState;
|
||||
|
||||
FString disconnectText = FString("Disconnected");
|
||||
|
||||
if (System != XR_NULL_SYSTEM_ID && XR_SUCCEEDED(xrGetInstanceProcAddr(Instance, "xrRemotingGetConnectionStateMSFT",
|
||||
(PFN_xrVoidFunction*)&getConnectionState)))
|
||||
{
|
||||
XrRemotingConnectionStateMSFT connectionState;
|
||||
XrRemotingDisconnectReasonMSFT disconnectReason;
|
||||
if (XR_SUCCEEDED(getConnectionState(Instance, System, &connectionState, &disconnectReason)))
|
||||
{
|
||||
disconnectText += FString(": ") + GetDisconnectedReason((int)disconnectReason);
|
||||
}
|
||||
}
|
||||
|
||||
SetRemotingStatusText(disconnectText, FLinearColor::Red);
|
||||
}
|
||||
|
||||
FString FHolographicRemotingPlugin::GetDisconnectedReason(int index)
|
||||
{
|
||||
// Copied from the XrRemotingDisconnectReasonMSFT enum.
|
||||
TArray<FString> disconnectedReasons
|
||||
{
|
||||
FString("XR_REMOTING_DISCONNECT_REASON_NONE_MSFT"),
|
||||
FString("XR_REMOTING_DISCONNECT_REASON_UNKNOWN_MSFT"),
|
||||
FString("XR_REMOTING_DISCONNECT_REASON_NO_SERVER_CERTIFICATE_MSFT"),
|
||||
FString("XR_REMOTING_DISCONNECT_REASON_HANDSHAKE_PORT_BUSY_MSFT"),
|
||||
FString("XR_REMOTING_DISCONNECT_REASON_HANDSHAKE_UNREACHABLE_MSFT"),
|
||||
FString("XR_REMOTING_DISCONNECT_REASON_HANDSHAKE_CONNECTION_FAILED_MSFT"),
|
||||
FString("XR_REMOTING_DISCONNECT_REASON_AUTHENTICATION_FAILED_MSFT"),
|
||||
FString("XR_REMOTING_DISCONNECT_REASON_REMOTING_VERSION_MISMATCH_MSFT"),
|
||||
FString("XR_REMOTING_DISCONNECT_REASON_INCOMPATIBLE_TRANSPORT_PROTOCOLS_MSFT"),
|
||||
FString("XR_REMOTING_DISCONNECT_REASON_HANDSHAKE_FAILED_MSFT"),
|
||||
FString("XR_REMOTING_DISCONNECT_REASON_TRANSPORT_PORT_BUSY_MSFT"),
|
||||
FString("XR_REMOTING_DISCONNECT_REASON_TRANSPORT_UNREACHABLE_MSFT"),
|
||||
FString("XR_REMOTING_DISCONNECT_REASON_TRANSPORT_CONNECTION_FAILED_MSFT"),
|
||||
FString("XR_REMOTING_DISCONNECT_REASON_PROTOCOL_VERSION_MISMATCH_MSFT"),
|
||||
FString("XR_REMOTING_DISCONNECT_REASON_PROTOCOL_ERROR_MSFT"),
|
||||
FString("XR_REMOTING_DISCONNECT_REASON_VIDEO_CODEC_NOT_AVAILABLE_MSFT"),
|
||||
FString("XR_REMOTING_DISCONNECT_REASON_CANCELED_MSFT"),
|
||||
FString("XR_REMOTING_DISCONNECT_REASON_CONNECTION_LOST_MSFT"),
|
||||
FString("XR_REMOTING_DISCONNECT_REASON_DEVICE_LOST_MSFT"),
|
||||
FString("XR_REMOTING_DISCONNECT_REASON_DISCONNECT_REQUEST_MSFT"),
|
||||
FString("XR_REMOTING_DISCONNECT_REASON_HANDSHAKE_NETWORK_UNREACHABLE_MSFT"),
|
||||
FString("XR_REMOTING_DISCONNECT_REASON_HANDSHAKE_CONNECTION_REFUSED_MSFT"),
|
||||
FString("XR_REMOTING_DISCONNECT_REASON_VIDEO_FORMAT_NOT_AVAILABLE_MSFT"),
|
||||
FString("XR_REMOTING_DISCONNECT_REASON_PEER_DISCONNECT_REQUEST_MSFT"),
|
||||
FString("XR_REMOTING_DISCONNECT_REASON_PEER_DISCONNECT_TIMEOUT_MSFT"),
|
||||
FString("XR_REMOTING_DISCONNECT_REASON_SESSION_OPEN_TIMEOUT_MSFT"),
|
||||
FString("XR_REMOTING_DISCONNECT_REASON_REMOTING_HANDSHAKE_TIMEOUT_MSFT"),
|
||||
FString("XR_REMOTING_DISCONNECT_REASON_INTERNAL_ERROR_MSFT")
|
||||
};
|
||||
|
||||
if (!disconnectedReasons.IsValidIndex(index))
|
||||
{
|
||||
return FString("disconnectedReason index out of bounds: ") + FString::FromInt(index);
|
||||
}
|
||||
|
||||
return FString::Format(TEXT("{0} ({1})"), { disconnectedReasons[index], index });
|
||||
}
|
||||
|
||||
void FHolographicRemotingPlugin::ParseRemotingConfig()
|
||||
{
|
||||
#if WITH_EDITOR
|
||||
GConfig->GetBool(TEXT("/Script/MicrosoftOpenXRRuntimeSettings.MicrosoftOpenXRRuntimeSettings"), TEXT("bEnableRemotingForEditor"),
|
||||
remotingEnabled, GEditorPerProjectIni);
|
||||
|
||||
GConfig->GetString(TEXT("/Script/MicrosoftOpenXRRuntimeSettings.MicrosoftOpenXRRuntimeSettings"), TEXT("RemoteHoloLensIP"),
|
||||
remotingConnectionData.IP, GEditorPerProjectIni);
|
||||
|
||||
GConfig->GetInt(TEXT("/Script/MicrosoftOpenXRRuntimeSettings.MicrosoftOpenXRRuntimeSettings"), TEXT("MaxBitrate"),
|
||||
remotingConnectionData.Bitrate, GEditorPerProjectIni);
|
||||
|
||||
GConfig->GetBool(TEXT("/Script/MicrosoftOpenXRRuntimeSettings.MicrosoftOpenXRRuntimeSettings"), TEXT("EnableAudio"),
|
||||
remotingConnectionData.EnableAudio, GEditorPerProjectIni);
|
||||
|
||||
int connectionType;
|
||||
GConfig->GetInt(TEXT("/Script/MicrosoftOpenXRRuntimeSettings.MicrosoftOpenXRRuntimeSettings"), TEXT("ConnectionType"),
|
||||
connectionType, GEditorPerProjectIni);
|
||||
remotingConnectionData.ConnectionType = (RemotingConnectionType)connectionType;
|
||||
|
||||
int connectionCodec;
|
||||
GConfig->GetInt(TEXT("/Script/MicrosoftOpenXRRuntimeSettings.MicrosoftOpenXRRuntimeSettings"), TEXT("ConnectionCodec"),
|
||||
connectionCodec, GEditorPerProjectIni);
|
||||
remotingConnectionData.ConnectionCodec = (RemotingCodec)connectionCodec;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool FHolographicRemotingPlugin::ParseRemotingCmdArgs()
|
||||
{
|
||||
#if WITH_EDITOR
|
||||
ParseRemotingConfig();
|
||||
#elif !SUPPORTS_REMOTING_IN_PACKAGED_BUILD
|
||||
return false;
|
||||
#endif
|
||||
|
||||
// Use cmd args for opting into remoting from a packaged exe.
|
||||
// These can also be used from the editor, and will supersede values in MicrosoftOpenXRRuntimeSettings
|
||||
// when connecting from OnGetSystem. Any connections at runtime will use the editor settings.
|
||||
TArray<FString> tokens, switches;
|
||||
FCommandLine::Parse(FCommandLine::Get(), tokens, switches);
|
||||
|
||||
remotingConnectionData.EnableAudio =
|
||||
switches.Contains(FString("EnableAudio"));
|
||||
|
||||
remotingConnectionData.ConnectionType =
|
||||
switches.Contains(FString("Listen")) ? RemotingConnectionType::Listen : RemotingConnectionType::Connect;
|
||||
|
||||
FString codecString;
|
||||
FParse::Value(FCommandLine::Get(), TEXT("RemotingCodec="), codecString);
|
||||
codecString = codecString.TrimStartAndEnd().ToLower();
|
||||
RemotingCodec codec = RemotingCodec::Any;
|
||||
if (codecString == FString("h264"))
|
||||
{
|
||||
codec = RemotingCodec::H264;
|
||||
}
|
||||
else if (codecString == FString("h265"))
|
||||
{
|
||||
codec = RemotingCodec::H265;
|
||||
}
|
||||
else
|
||||
{
|
||||
codec = RemotingCodec::Any;
|
||||
}
|
||||
remotingConnectionData.ConnectionCodec = codec;
|
||||
|
||||
FParse::Value(FCommandLine::Get(), TEXT("RemotingBitrate="), remotingConnectionData.Bitrate);
|
||||
|
||||
FString StringToParse;
|
||||
if (FParse::Value(FCommandLine::Get(), TEXT("HoloLensRemoting="), StringToParse) &&
|
||||
UMicrosoftOpenXRRuntimeSettings::ParseAddress(
|
||||
StringToParse, remotingConnectionData.IP, remotingConnectionData.Port))
|
||||
{
|
||||
// An IP to remote to was set, remoting is desired.
|
||||
return true;
|
||||
}
|
||||
|
||||
return remotingEnabled;
|
||||
}
|
||||
|
||||
} // namespace MicrosoftOpenXR
|
||||
|
||||
#endif
|
|
@ -0,0 +1,64 @@
|
|||
#pragma once
|
||||
|
||||
// Currently remoting only supports x64 Windows: Editor and Packaged Exe
|
||||
#define SUPPORTS_REMOTING (PLATFORM_WINDOWS && PLATFORM_64BITS)
|
||||
|
||||
#if SUPPORTS_REMOTING
|
||||
|
||||
#include "OpenXRCommon.h"
|
||||
#include "openxr_msft_holographic_remoting.h "
|
||||
|
||||
#include "Windows/AllowWindowsPlatformTypes.h"
|
||||
#include <Windows.h>
|
||||
#include "Windows/HideWindowsPlatformTypes.h"
|
||||
|
||||
#include "OpenXRCore.h"
|
||||
#include "HeadMountedDisplayTypes.h"
|
||||
|
||||
#if WITH_EDITOR
|
||||
#include "Editor.h"
|
||||
#endif
|
||||
|
||||
#include "MicrosoftOpenXRRuntimeSettings.h"
|
||||
|
||||
// Microsoft.Holographic.AppRemoting binaries only exist for Win64.
|
||||
#define SUPPORTS_REMOTING_IN_PACKAGED_BUILD (!WITH_EDITOR && PLATFORM_DESKTOP && PLATFORM_64BITS)
|
||||
|
||||
namespace MicrosoftOpenXR
|
||||
{
|
||||
class FHolographicRemotingPlugin : public IOpenXRExtensionPlugin, public TSharedFromThis<FHolographicRemotingPlugin>
|
||||
{
|
||||
public:
|
||||
void Register();
|
||||
void Unregister();
|
||||
|
||||
bool GetCustomLoader(PFN_xrGetInstanceProcAddr* OutGetProcAddr) override;
|
||||
bool GetRequiredExtensions(TArray<const ANSICHAR*>& OutExtensions) override;
|
||||
void OnEvent(XrSession InSession, const XrEventDataBaseHeader* InHeader) override;
|
||||
void PostGetSystem(XrInstance InInstance, XrSystemId InSystem) override;
|
||||
const void* OnCreateSession(XrInstance InInstance, XrSystemId InSystem, const void* InNext) override;
|
||||
|
||||
private:
|
||||
void* LoaderHandle;
|
||||
bool remotingEnabled = false;
|
||||
bool autoConnectRemoting = false;
|
||||
|
||||
XrInstance Instance;
|
||||
XrSystemId System = XR_NULL_SYSTEM_ID;
|
||||
|
||||
RemotingConnectionData remotingConnectionData;
|
||||
|
||||
XrRemotingConnectionStateMSFT GetRemotingConnectionState();
|
||||
void ConnectToRemoteDevice(RemotingConnectionData data);
|
||||
void DisconnectFromRemoteDevice();
|
||||
|
||||
void SetRemotingStatusText(FString message, FLinearColor statusColor);
|
||||
void UpdateDisconnectedText();
|
||||
FString GetDisconnectedReason(int index);
|
||||
|
||||
void ParseRemotingConfig();
|
||||
bool ParseRemotingCmdArgs();
|
||||
};
|
||||
} // namespace MicrosoftOpenXR
|
||||
|
||||
#endif
|
|
@ -0,0 +1,52 @@
|
|||
#include "HolographicWindowAttachmentPlugin.h"
|
||||
|
||||
#if PLATFORM_HOLOLENS
|
||||
#include "HoloLens/HoloLensApplication.h"
|
||||
|
||||
#include <Unknwn.h>
|
||||
|
||||
namespace MicrosoftOpenXR
|
||||
{
|
||||
void FHolographicWindowAttachmentPlugin::Register()
|
||||
{
|
||||
IModularFeatures::Get().RegisterModularFeature(GetModularFeatureName(), this);
|
||||
}
|
||||
|
||||
void FHolographicWindowAttachmentPlugin::Unregister()
|
||||
{
|
||||
IModularFeatures::Get().UnregisterModularFeature(GetModularFeatureName(), this);
|
||||
}
|
||||
|
||||
bool FHolographicWindowAttachmentPlugin::GetOptionalExtensions(TArray<const ANSICHAR*>& OutExtensions)
|
||||
{
|
||||
OutExtensions.Add(XR_MSFT_HOLOGRAPHIC_WINDOW_ATTACHMENT_EXTENSION_NAME);
|
||||
return true;
|
||||
}
|
||||
|
||||
const void* FHolographicWindowAttachmentPlugin::OnCreateSession(XrInstance InInstance, XrSystemId InSystem, const void* InNext)
|
||||
{
|
||||
// Ensure xrCreateSession is called from the game thread which is the main app view CoreWindow thread.
|
||||
check(IsInGameThread());
|
||||
if (IOpenXRHMDPlugin::Get().IsExtensionEnabled(XR_MSFT_HOLOGRAPHIC_WINDOW_ATTACHMENT_EXTENSION_NAME))
|
||||
{
|
||||
// If the app requests to start in VR then the XrSession should take over the main app view's CoreWindow and
|
||||
// HolographicSpace. This ensures keyboard input and other things will still route to the engine. Otherwise the
|
||||
// XrSession will create its own CoreWindow and HolographicSpace and it will take all focus. The HolographicSpace will
|
||||
// only be available if start in VR is enabled.
|
||||
Windows::Graphics::Holographic::HolographicSpace ^ holographicSpace =
|
||||
FHoloLensApplication::GetHoloLensApplication()->GetHolographicSpace();
|
||||
if (holographicSpace)
|
||||
{
|
||||
HolographicWindowAttachment.next = InNext;
|
||||
HolographicWindowAttachment.holographicSpace = reinterpret_cast<::IUnknown*>(holographicSpace);
|
||||
HolographicWindowAttachment.coreWindow =
|
||||
reinterpret_cast<::IUnknown*>(Windows::UI::Core::CoreWindow::GetForCurrentThread());
|
||||
return &HolographicWindowAttachment;
|
||||
}
|
||||
}
|
||||
|
||||
return InNext;
|
||||
}
|
||||
} // namespace MicrosoftOpenXR
|
||||
|
||||
#endif
|
|
@ -0,0 +1,23 @@
|
|||
#pragma once
|
||||
|
||||
#if PLATFORM_HOLOLENS
|
||||
|
||||
#include "OpenXRCommon.h"
|
||||
|
||||
namespace MicrosoftOpenXR
|
||||
{
|
||||
class FHolographicWindowAttachmentPlugin : public IOpenXRExtensionPlugin
|
||||
{
|
||||
public:
|
||||
void Register();
|
||||
void Unregister();
|
||||
|
||||
bool GetOptionalExtensions(TArray<const ANSICHAR*>& OutExtensions) override;
|
||||
const void* OnCreateSession(XrInstance InInstance, XrSystemId InSystem, const void* InNext) override;
|
||||
|
||||
private:
|
||||
XrHolographicWindowAttachmentMSFT HolographicWindowAttachment{XR_TYPE_HOLOGRAPHIC_WINDOW_ATTACHMENT_MSFT};
|
||||
};
|
||||
} // namespace MicrosoftOpenXR
|
||||
|
||||
#endif
|
|
@ -0,0 +1,565 @@
|
|||
#if PLATFORM_WINDOWS || PLATFORM_HOLOLENS
|
||||
|
||||
#include "LocatableCamPlugin.h"
|
||||
#include "InputCoreTypes.h"
|
||||
#include "OpenXRCore.h"
|
||||
#include "IOpenXRARModule.h"
|
||||
#include "IOpenXRARTrackedGeometryHolder.h"
|
||||
#include "IXRTrackingSystem.h"
|
||||
#include "OpenXRCameraImageTexture.h"
|
||||
#include "ARSessionConfig.h"
|
||||
|
||||
#include "WindowsMixedRealityInteropUtility.h"
|
||||
|
||||
#include "Windows/AllowWindowsPlatformTypes.h"
|
||||
#include "Windows/AllowWindowsPlatformAtomics.h"
|
||||
#include "Windows/PreWindowsApi.h"
|
||||
|
||||
#include <DXGI1_4.h>
|
||||
#include <mfapi.h>
|
||||
#include <Windows.Graphics.DirectX.Direct3D11.interop.h>
|
||||
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
#include <winrt/Windows.Foundation.Collections.h>
|
||||
#include <winrt/Windows.Graphics.DirectX.Direct3D11.h>
|
||||
#include <winrt/windows.Perception.Spatial.Preview.h>
|
||||
#include <winrt/Windows.Media.Devices.h>
|
||||
#include <winrt/Windows.Media.Devices.Core.h>
|
||||
|
||||
#include "Windows/PostWindowsApi.h"
|
||||
#include "Windows/HideWindowsPlatformAtomics.h"
|
||||
#include "Windows/HideWindowsPlatformTypes.h"
|
||||
|
||||
|
||||
using namespace winrt::Windows::Foundation::Collections;
|
||||
using namespace winrt::Windows::Foundation::Numerics;
|
||||
using namespace winrt::Windows::Perception::Spatial;
|
||||
|
||||
using namespace winrt::Windows::Media::Capture;
|
||||
using namespace winrt::Windows::Media::Capture::Frames;
|
||||
|
||||
|
||||
namespace MicrosoftOpenXR
|
||||
{
|
||||
|
||||
FLocatableCamPlugin::FLocatableCamPlugin()
|
||||
{
|
||||
}
|
||||
|
||||
FLocatableCamPlugin::~FLocatableCamPlugin()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(RefsLock);
|
||||
if (AsyncInfo)
|
||||
{
|
||||
AsyncInfo.Cancel();
|
||||
}
|
||||
}
|
||||
|
||||
bool FLocatableCamPlugin::GetRequiredExtensions(TArray<const ANSICHAR*>& OutExtensions)
|
||||
{
|
||||
OutExtensions.Add(XR_MSFT_SPATIAL_GRAPH_BRIDGE_EXTENSION_NAME);
|
||||
return true;
|
||||
}
|
||||
|
||||
const void* FLocatableCamPlugin::OnCreateSession(XrInstance InInstance, XrSystemId InSystem, const void* InNext)
|
||||
{
|
||||
XR_ENSURE(xrGetInstanceProcAddr(InInstance, "xrCreateSpatialGraphNodeSpaceMSFT", (PFN_xrVoidFunction*)&xrCreateSpatialGraphNodeSpaceMSFT));
|
||||
|
||||
static FName SystemName(TEXT("OpenXR"));
|
||||
if (GEngine->XRSystem.IsValid() && (GEngine->XRSystem->GetSystemName() == SystemName))
|
||||
{
|
||||
XRTrackingSystem = GEngine->XRSystem.Get();
|
||||
}
|
||||
|
||||
ensure(XRTrackingSystem != nullptr);
|
||||
|
||||
return InNext;
|
||||
}
|
||||
|
||||
void FLocatableCamPlugin::UpdateDeviceLocations(XrSession InSession, XrTime DisplayTime, XrSpace TrackingSpace)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(RefsLock);
|
||||
check(IsInGameThread());
|
||||
|
||||
if (!SharedDXTexture)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!SharedTextureHolder)
|
||||
{
|
||||
UE_LOG(LogHMD, Log, TEXT("ARSession isn't started, can't capture frames"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (Space == XR_NULL_HANDLE && DynamicNode.IsSet())
|
||||
{
|
||||
XrSpatialGraphNodeSpaceCreateInfoMSFT SpatialGraphNodeSpaceCreateInfo{ XR_TYPE_SPATIAL_GRAPH_NODE_SPACE_CREATE_INFO_MSFT };
|
||||
SpatialGraphNodeSpaceCreateInfo.nodeType = XR_SPATIAL_GRAPH_NODE_TYPE_DYNAMIC_MSFT;
|
||||
SpatialGraphNodeSpaceCreateInfo.pose = ToXrPose(DynamicNode.GetValue().Value, XRTrackingSystem->GetWorldToMetersScale());
|
||||
|
||||
check(sizeof(SpatialGraphNodeSpaceCreateInfo.nodeId) == sizeof(FGuid));
|
||||
FMemory::Memcpy(&SpatialGraphNodeSpaceCreateInfo.nodeId, &DynamicNode.GetValue().Key, sizeof(SpatialGraphNodeSpaceCreateInfo.nodeId));
|
||||
|
||||
XR_ENSURE(xrCreateSpatialGraphNodeSpaceMSFT(InSession, &SpatialGraphNodeSpaceCreateInfo, &Space));
|
||||
}
|
||||
|
||||
// We leave our pointer null until there's an image to wrap around, so create on demand
|
||||
if (SharedTextureHolder->CameraImage == nullptr)
|
||||
{
|
||||
SharedTextureHolder->CameraImage = NewObject<UOpenXRCameraImageTexture>();
|
||||
}
|
||||
// This will start the async update process
|
||||
SharedTextureHolder->CameraImage->Init(SharedDXTexture);
|
||||
SharedDXTexture = nullptr;
|
||||
|
||||
PVCameraToWorldMatrix = FTransform::Identity;
|
||||
|
||||
if (Space != XR_NULL_HANDLE)
|
||||
{
|
||||
XrSpaceLocation SpaceLocation{ XR_TYPE_SPACE_LOCATION };
|
||||
XR_ENSURE(xrLocateSpace(Space, TrackingSpace, DisplayTime, &SpaceLocation));
|
||||
const XrSpaceLocationFlags ValidFlags = XR_SPACE_LOCATION_ORIENTATION_VALID_BIT | XR_SPACE_LOCATION_POSITION_VALID_BIT;
|
||||
if ((SpaceLocation.locationFlags & ValidFlags) == ValidFlags)
|
||||
{
|
||||
PVCameraToWorldMatrix = ToFTransform(SpaceLocation.pose, XRTrackingSystem->GetWorldToMetersScale()) * XRTrackingSystem->GetTrackingToWorldTransform();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FLocatableCamPlugin::Register()
|
||||
{
|
||||
IModularFeatures::Get().RegisterModularFeature(IOpenXRExtensionPlugin::GetModularFeatureName(), static_cast<IOpenXRExtensionPlugin*>(this));
|
||||
}
|
||||
|
||||
void FLocatableCamPlugin::Unregister()
|
||||
{
|
||||
StopCameraCapture();
|
||||
IModularFeatures::Get().UnregisterModularFeature(IOpenXRExtensionPlugin::GetModularFeatureName(), static_cast<IOpenXRExtensionPlugin*>(this));
|
||||
}
|
||||
|
||||
void FLocatableCamPlugin::StartCameraCapture(int DesiredWidth, int DesiredHeight, int DesiredFPS)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(RefsLock);
|
||||
check(IsInGameThread());
|
||||
if (CameraFrameReader)
|
||||
{
|
||||
UE_LOG(LogHMD, Log, TEXT("Camera is already capturing frames. Aborting."));
|
||||
return;
|
||||
}
|
||||
|
||||
auto FindAllAsyncOp = MediaFrameSourceGroup::FindAllAsync();
|
||||
AsyncInfo = FindAllAsyncOp;
|
||||
FindAllAsyncOp.Completed([=](auto&& asyncInfo, auto&& asyncStatus)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(RefsLock);
|
||||
if (AsyncInfo.Status() != winrt::Windows::Foundation::AsyncStatus::Completed)
|
||||
{
|
||||
AsyncInfo = nullptr;
|
||||
return;
|
||||
}
|
||||
AsyncInfo = nullptr;
|
||||
|
||||
auto DiscoveredGroups = asyncInfo.GetResults();
|
||||
MediaFrameSourceGroup ChosenSourceGroup = nullptr;
|
||||
MediaFrameSourceInfo ChosenSourceInfo = nullptr;
|
||||
|
||||
MediaCaptureInitializationSettings CaptureSettings = MediaCaptureInitializationSettings();
|
||||
CaptureSettings.StreamingCaptureMode(StreamingCaptureMode::Video);
|
||||
CaptureSettings.MemoryPreference(MediaCaptureMemoryPreference::Auto); // For GPU
|
||||
CaptureSettings.VideoProfile(nullptr);
|
||||
|
||||
for(auto&& Group : DiscoveredGroups)
|
||||
{
|
||||
// For HoloLens, use the video conferencing video profile - this will give the best power consumption.
|
||||
auto profileList = MediaCapture::FindKnownVideoProfiles(Group.Id(), KnownVideoProfile::VideoConferencing);
|
||||
if (profileList.Size() == 0)
|
||||
{
|
||||
// No video conferencing profiles in this group, move to the next one.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Cache the first valid group and profile in case we do not find a profile that matches the input description.
|
||||
if (ChosenSourceGroup == nullptr)
|
||||
{
|
||||
ChosenSourceGroup = Group;
|
||||
|
||||
CaptureSettings.SourceGroup(ChosenSourceGroup);
|
||||
CaptureSettings.VideoProfile(profileList.GetAt(0));
|
||||
}
|
||||
|
||||
if (DesiredWidth > 0 && DesiredHeight > 0 && DesiredFPS > 0)
|
||||
{
|
||||
bool found = false;
|
||||
for (auto&& profile : profileList)
|
||||
{
|
||||
for (auto&& desc : profile.SupportedRecordMediaDescription())
|
||||
{
|
||||
// Check for a profile that matches our desired dimensions.
|
||||
if (desc.Width() == DesiredWidth && desc.Height() == DesiredHeight && desc.FrameRate() == DesiredFPS)
|
||||
{
|
||||
ChosenSourceGroup = Group;
|
||||
|
||||
CaptureSettings.SourceGroup(Group);
|
||||
CaptureSettings.VideoProfile(profile);
|
||||
CaptureSettings.RecordMediaDescription(desc);
|
||||
found = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If there was no camera available, then log it and bail
|
||||
if (ChosenSourceGroup == nullptr)
|
||||
{
|
||||
UE_LOG(LogHMD, Log, TEXT("No media frame source found, so no camera images will be delivered"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (DesiredWidth > 0 && DesiredHeight > 0 && DesiredFPS > 0 && CaptureSettings.RecordMediaDescription() == nullptr)
|
||||
{
|
||||
UE_LOG(LogHMD, Log, TEXT("No matching video format found, using default profile instead."));
|
||||
}
|
||||
|
||||
// Find the color camera source
|
||||
// Search through the infos to determine if this is the color camera source
|
||||
for (auto&& Info : ChosenSourceGroup.SourceInfos())
|
||||
{
|
||||
if (Info.SourceKind() == MediaFrameSourceKind::Color)
|
||||
{
|
||||
ChosenSourceInfo = Info;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If there was no camera available, then log it and bail
|
||||
if (ChosenSourceInfo == nullptr)
|
||||
{
|
||||
UE_LOG(LogHMD, Log, TEXT("No media frame source info found, so no camera images will be delivered"));
|
||||
return;
|
||||
}
|
||||
|
||||
// Create our capture object with our settings
|
||||
winrt::agile_ref < winrt::Windows::Media::Capture::MediaCapture > Capture{ MediaCapture() };
|
||||
auto InitializeAsyncOp = Capture.get().InitializeAsync(CaptureSettings);
|
||||
AsyncInfo = InitializeAsyncOp;
|
||||
InitializeAsyncOp.Completed([=](auto&& asyncInfo, auto&& asyncStatus)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(RefsLock);
|
||||
if (AsyncInfo.Status() != winrt::Windows::Foundation::AsyncStatus::Completed)
|
||||
{
|
||||
AsyncInfo = nullptr;
|
||||
UE_LOG(LogHMD, Log, TEXT("Failed to open camera, please check Webcam capability"));
|
||||
return;
|
||||
}
|
||||
AsyncInfo = nullptr;
|
||||
|
||||
// Get the frame source from the source info we got earlier
|
||||
MediaFrameSource FrameSource = Capture.get().FrameSources().Lookup(ChosenSourceInfo.Id());
|
||||
|
||||
// Now create and start the frame reader
|
||||
auto CreateFrameReaderAsyncOp = Capture.get().CreateFrameReaderAsync(FrameSource);
|
||||
AsyncInfo = CreateFrameReaderAsyncOp;
|
||||
CreateFrameReaderAsyncOp.Completed([=](auto&& asyncInfo, auto&& asyncStatus)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(RefsLock);
|
||||
if (AsyncInfo.Status() != winrt::Windows::Foundation::AsyncStatus::Completed)
|
||||
{
|
||||
AsyncInfo = nullptr;
|
||||
return;
|
||||
}
|
||||
AsyncInfo = nullptr;
|
||||
|
||||
MediaFrameReader FrameReader = asyncInfo.GetResults();
|
||||
auto StartAsyncOp = FrameReader.StartAsync();
|
||||
AsyncInfo = StartAsyncOp;
|
||||
StartAsyncOp.Completed([=](auto&& asyncInfo, auto&& asyncStatus)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(RefsLock);
|
||||
// Finally, copy to our object
|
||||
if (AsyncInfo.Status() != winrt::Windows::Foundation::AsyncStatus::Completed)
|
||||
{
|
||||
AsyncInfo = nullptr;
|
||||
return;
|
||||
}
|
||||
AsyncInfo = nullptr;
|
||||
|
||||
MediaFrameReaderStartStatus StartStatus = asyncInfo.GetResults();
|
||||
if (StartStatus == MediaFrameReaderStartStatus::Success)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(RefsLock);
|
||||
CameraCapture = std::move(Capture);
|
||||
CameraFrameReader = std::move(FrameReader);
|
||||
CameraFrameSource = std::move(FrameSource);
|
||||
UE_LOG(LogHMD, Log, TEXT("Successfully created the camera reader"));
|
||||
|
||||
// Subscribe the inbound frame event
|
||||
OnFrameArrivedEvent = CameraFrameReader.FrameArrived(winrt::auto_revoke, [this](auto&& sender, auto&& args) { OnFrameArrived(sender, args); });
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogHMD, Log, TEXT("Failed to start the frame reader with status = %d"), static_cast<int>(StartStatus));
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
void FLocatableCamPlugin::StopCameraCapture()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(RefsLock);
|
||||
check(IsInGameThread());
|
||||
|
||||
OnFrameArrivedEvent.revoke();
|
||||
if (AsyncInfo)
|
||||
{
|
||||
AsyncInfo.Cancel();
|
||||
}
|
||||
|
||||
CameraIntrinsics = nullptr;
|
||||
DynamicNode.Reset();
|
||||
SharedDXTexture = nullptr;
|
||||
if (Space != XR_NULL_HANDLE)
|
||||
{
|
||||
xrDestroySpace(Space);
|
||||
Space = XR_NULL_HANDLE;
|
||||
}
|
||||
|
||||
if (CameraFrameReader)
|
||||
{
|
||||
auto StopAsyncOp = CameraFrameReader.StopAsync();
|
||||
AsyncInfo = StopAsyncOp;
|
||||
StopAsyncOp.Completed([=](auto&& asyncInfo, auto&& asyncStatus)
|
||||
{
|
||||
if (asyncStatus == winrt::Windows::Foundation::AsyncStatus::Completed)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(RefsLock);
|
||||
AsyncInfo = nullptr;
|
||||
CameraCapture = nullptr;
|
||||
CameraFrameReader = nullptr;
|
||||
CameraFrameSource = nullptr;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FLocatableCamPlugin::OnStartARSession(class UARSessionConfig* SessionConfig)
|
||||
{
|
||||
Format = SessionConfig->GetDesiredVideoFormat();
|
||||
SharedTextureHolder = MakeShared<FSharedTextureHolder>();
|
||||
}
|
||||
|
||||
void FLocatableCamPlugin::OnStopARSession()
|
||||
{
|
||||
SharedTextureHolder = nullptr;
|
||||
}
|
||||
|
||||
|
||||
TOptional<TPair<FGuid, FTransform>> FindDynamicNode(const MediaFrameReference& frame, float WorldToMetersScale)
|
||||
{
|
||||
// Copied MFStreamExtension_CameraExtrinsics's value from mfapi.h so that we don't have to link against Mfplat.lib.
|
||||
// mfapi.h is still needed for the MFCameraExtrinsics struct.
|
||||
constexpr winrt::guid CameraExtrinsicsGuid(0x6b761658, 0xb7ec, 0x4c3b, { 0x82, 0x25, 0x86, 0x23, 0xca, 0xbe, 0xc3, 0x1d });
|
||||
if (auto inspectable = frame.Properties().TryLookup(CameraExtrinsicsGuid))
|
||||
{
|
||||
auto propertyValue = inspectable.try_as<winrt::Windows::Foundation::IPropertyValue>();
|
||||
if (propertyValue && propertyValue.Type() == winrt::Windows::Foundation::PropertyType::UInt8Array)
|
||||
{
|
||||
winrt::com_array<uint8_t> bytes;
|
||||
propertyValue.GetUInt8Array(bytes);
|
||||
if (bytes.size() >= sizeof(MFCameraExtrinsics))
|
||||
{
|
||||
const auto* const extrinsics = reinterpret_cast<const MFCameraExtrinsics*>(bytes.data());
|
||||
if (extrinsics->TransformCount > 0)
|
||||
{
|
||||
const MFCameraExtrinsic_CalibratedTransform& transform = extrinsics->CalibratedTransforms[0];
|
||||
|
||||
DirectX::XMFLOAT4 rot(transform.Orientation.x, transform.Orientation.y, transform.Orientation.z, transform.Orientation.w);
|
||||
DirectX::XMFLOAT3 pos(transform.Position.x, transform.Position.y, transform.Position.z);
|
||||
|
||||
FTransform TrackingSpaceTransform(WMRUtility::FromMixedRealityQuaternion(rot), WMRUtility::FromMixedRealityVector(pos) * WorldToMetersScale);
|
||||
|
||||
return MakeTuple(WMRUtility::GUIDToFGuid(transform.CalibrationId), TrackingSpaceTransform);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void FLocatableCamPlugin::OnFrameArrived(MediaFrameReader SendingFrameReader, MediaFrameArrivedEventArgs FrameArrivedArgs)
|
||||
{
|
||||
MediaFrameReference CurrentFrame = SendingFrameReader.TryAcquireLatestFrame();
|
||||
if (CurrentFrame == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Drill down through the objects to get the underlying D3D texture
|
||||
VideoMediaFrame VideoFrame = CurrentFrame.VideoMediaFrame();
|
||||
auto ManagedSurface = VideoFrame.Direct3DSurface();
|
||||
|
||||
if (ManagedSurface == nullptr)
|
||||
{
|
||||
UE_LOG(LogHMD, Log, TEXT("OnFrameArrived() : VideoMediaFrame->Direct3DSurface was null, so no image to process"));
|
||||
return;
|
||||
}
|
||||
|
||||
winrt::com_ptr<IDXGIResource1> srcResource = nullptr;
|
||||
winrt::com_ptr<Windows::Graphics::DirectX::Direct3D11::IDirect3DDxgiInterfaceAccess> DxgiInterfaceAccess =
|
||||
ManagedSurface.as<Windows::Graphics::DirectX::Direct3D11::IDirect3DDxgiInterfaceAccess>();
|
||||
|
||||
if (DxgiInterfaceAccess == nullptr)
|
||||
{
|
||||
UE_LOG(LogHMD, Log, TEXT("OnFrameArrived() : Failed to get DxgiInterfaceAccess from ManagedSurface. Cannot process image."));
|
||||
return;
|
||||
}
|
||||
|
||||
DxgiInterfaceAccess->GetInterface(IID_PPV_ARGS(srcResource.put()));
|
||||
|
||||
if (srcResource == nullptr)
|
||||
{
|
||||
UE_LOG(LogHMD, Log, TEXT("Unable to get the underlying video texture"));
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(RefsLock);
|
||||
if (CameraFrameReader == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Get camera intrinsics, since we just have the one camera, cache the intrinsics.
|
||||
if (CameraIntrinsics == nullptr)
|
||||
{
|
||||
CameraIntrinsics = VideoFrame.CameraIntrinsics();
|
||||
}
|
||||
|
||||
// Find current frame's tracking information from the frame's coordinate system.
|
||||
if (!DynamicNode)
|
||||
{
|
||||
DynamicNode = FindDynamicNode(CurrentFrame, XRTrackingSystem->GetWorldToMetersScale());
|
||||
}
|
||||
auto OutSharedDXTexture = std::make_shared<winrt::handle>();
|
||||
if (FAILED(srcResource->CreateSharedHandle(NULL, DXGI_SHARED_RESOURCE_READ, NULL, OutSharedDXTexture->put())))
|
||||
{
|
||||
UE_LOG(LogHMD, Log, TEXT("Unable to create shared handler of the video texture"));
|
||||
return;
|
||||
}
|
||||
SharedDXTexture = OutSharedDXTexture;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FLocatableCamPlugin::FSharedTextureHolder::AddReferencedObjects(FReferenceCollector& Collector)
|
||||
{
|
||||
Collector.AddReferencedObject(CameraImage);
|
||||
}
|
||||
|
||||
|
||||
FTransform FLocatableCamPlugin::GetCameraTransform() const
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(RefsLock);
|
||||
return PVCameraToWorldMatrix;
|
||||
}
|
||||
|
||||
|
||||
bool FLocatableCamPlugin::GetPVCameraIntrinsics(FVector2D& focalLength, int& width, int& height, FVector2D& principalPoint, FVector& radialDistortion, FVector2D& tangentialDistortion) const
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(RefsLock);
|
||||
if (CameraIntrinsics == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
focalLength = WMRUtility::FromFloat2(CameraIntrinsics.FocalLength());
|
||||
width = CameraIntrinsics.ImageWidth();
|
||||
height = CameraIntrinsics.ImageHeight();
|
||||
principalPoint = WMRUtility::FromFloat2(CameraIntrinsics.PrincipalPoint());
|
||||
radialDistortion = WMRUtility::FromFloat3(CameraIntrinsics.RadialDistortion(), XRTrackingSystem->GetWorldToMetersScale());
|
||||
tangentialDistortion = WMRUtility::FromFloat2(CameraIntrinsics.TangentialDistortion());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
FVector FLocatableCamPlugin::GetWorldSpaceRayFromCameraPoint(FVector2D pixelCoordinate) const
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(RefsLock);
|
||||
if (CameraIntrinsics == nullptr)
|
||||
{
|
||||
return FVector::ZeroVector;
|
||||
}
|
||||
|
||||
auto unprojectedPointAtUnitDepth = CameraIntrinsics.UnprojectAtUnitDepth(winrt::Windows::Foundation::Point(pixelCoordinate.X, pixelCoordinate.Y));
|
||||
|
||||
FVector ray = WMRUtility::FromFloat3(
|
||||
winrt::Windows::Foundation::Numerics::float3(
|
||||
unprojectedPointAtUnitDepth,
|
||||
-1.0f // Unprojection happened at 1 meter
|
||||
)
|
||||
, XRTrackingSystem->GetWorldToMetersScale());
|
||||
|
||||
ray.Normalize();
|
||||
|
||||
return PVCameraToWorldMatrix.TransformVector(ray);
|
||||
}
|
||||
|
||||
bool FLocatableCamPlugin::OnGetCameraIntrinsics(FARCameraIntrinsics& OutCameraIntrinsics) const
|
||||
{
|
||||
FVector radialDistortion;
|
||||
FVector2D tangentialDistortion;
|
||||
return GetPVCameraIntrinsics(OutCameraIntrinsics.FocalLength, OutCameraIntrinsics.ImageResolution.X, OutCameraIntrinsics.ImageResolution.Y, OutCameraIntrinsics.PrincipalPoint, radialDistortion, tangentialDistortion);
|
||||
}
|
||||
|
||||
UARTexture* FLocatableCamPlugin::OnGetARTexture(EARTextureType TextureType) const
|
||||
{
|
||||
if (TextureType == EARTextureType::CameraImage)
|
||||
{
|
||||
return SharedTextureHolder->CameraImage;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool FLocatableCamPlugin::OnToggleARCapture(const bool bOnOff)
|
||||
{
|
||||
if (bOnOff)
|
||||
{
|
||||
StartCameraCapture(Format.Width, Format.Height, Format.FPS);
|
||||
}
|
||||
else
|
||||
{
|
||||
StopCameraCapture();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FLocatableCamPlugin::IsEnabled() const
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(RefsLock);
|
||||
return CameraFrameReader != nullptr;
|
||||
}
|
||||
|
||||
|
||||
IOpenXRCustomCaptureSupport* FLocatableCamPlugin::GetCustomCaptureSupport(const EARCaptureType CaptureType)
|
||||
{
|
||||
if (CaptureType == EARCaptureType::Camera)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
} // namespace MicrosoftOpenXR
|
||||
|
||||
#endif //PLATFORM_WINDOWS || PLATFORM_HOLOLENS
|
|
@ -0,0 +1,105 @@
|
|||
#pragma once
|
||||
#if PLATFORM_WINDOWS || PLATFORM_HOLOLENS
|
||||
|
||||
#include "OpenXRCommon.h"
|
||||
#include "ARTypes.h"
|
||||
#include "MicrosoftOpenXR.h"
|
||||
#include "ARTextures.h"
|
||||
|
||||
#include "Windows/AllowWindowsPlatformTypes.h"
|
||||
#include "Windows/AllowWindowsPlatformAtomics.h"
|
||||
#include "Windows/PreWindowsApi.h"
|
||||
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
|
||||
#include <unknwn.h>
|
||||
#include <winrt/Windows.Media.Capture.h>
|
||||
#include <winrt/Windows.Media.Capture.Frames.h>
|
||||
#include <winrt/Windows.Perception.Spatial.h>
|
||||
|
||||
#include "Windows/PostWindowsApi.h"
|
||||
#include "Windows/HideWindowsPlatformAtomics.h"
|
||||
#include "Windows/HideWindowsPlatformTypes.h"
|
||||
|
||||
class UOpenXRCameraImageTexture;
|
||||
|
||||
namespace MicrosoftOpenXR
|
||||
{
|
||||
class FLocatableCamPlugin : public IOpenXRExtensionPlugin, public IOpenXRCustomCaptureSupport
|
||||
{
|
||||
public:
|
||||
|
||||
FLocatableCamPlugin();
|
||||
~FLocatableCamPlugin();
|
||||
|
||||
void Register();
|
||||
void Unregister();
|
||||
|
||||
bool GetRequiredExtensions(TArray<const ANSICHAR*>& OutExtensions) override;
|
||||
const void* OnCreateSession(XrInstance InInstance, XrSystemId InSystem, const void* InNext) override;
|
||||
void UpdateDeviceLocations(XrSession InSession, XrTime DisplayTime, XrSpace TrackingSpace) override;
|
||||
|
||||
virtual void OnStartARSession(class UARSessionConfig* SessionConfig) override;
|
||||
virtual void OnStopARSession() override;
|
||||
|
||||
virtual IOpenXRCustomCaptureSupport* GetCustomCaptureSupport(const EARCaptureType CaptureType) override;
|
||||
|
||||
|
||||
FTransform GetCameraTransform() const override;
|
||||
|
||||
bool GetPVCameraIntrinsics(FVector2D& focalLength, int& width, int& height, FVector2D& principalPoint, FVector& radialDistortion, FVector2D& tangentialDistortion) const;
|
||||
|
||||
FVector GetWorldSpaceRayFromCameraPoint(FVector2D pixelCoordinate) const override;
|
||||
|
||||
virtual bool OnGetCameraIntrinsics(FARCameraIntrinsics& OutCameraIntrinsics) const override;
|
||||
virtual class UARTexture* OnGetARTexture(EARTextureType TextureType) const override;
|
||||
virtual bool OnToggleARCapture(const bool bOnOff) override;
|
||||
bool IsEnabled() const override;
|
||||
|
||||
private:
|
||||
|
||||
class FSharedTextureHolder : public FGCObject
|
||||
{
|
||||
public:
|
||||
virtual void AddReferencedObjects(FReferenceCollector& Collector) override;
|
||||
|
||||
UOpenXRCameraImageTexture* CameraImage = nullptr;
|
||||
};
|
||||
|
||||
PFN_xrCreateSpatialGraphNodeSpaceMSFT xrCreateSpatialGraphNodeSpaceMSFT;
|
||||
|
||||
void StartCameraCapture(int DesiredWidth, int DesiredHeight, int DesiredFPS);
|
||||
void StopCameraCapture();
|
||||
|
||||
void OnFrameArrived(winrt::Windows::Media::Capture::Frames::MediaFrameReader SendingFrameReader, winrt::Windows::Media::Capture::Frames::MediaFrameArrivedEventArgs FrameArrivedArgs);
|
||||
|
||||
|
||||
/** Controls access to our references */
|
||||
mutable std::recursive_mutex RefsLock;
|
||||
/** The objects we need in order to receive frames of camera data */
|
||||
winrt::agile_ref<winrt::Windows::Media::Capture::MediaCapture> CameraCapture = nullptr;
|
||||
winrt::Windows::Media::Capture::Frames::MediaFrameReader CameraFrameReader = nullptr;
|
||||
winrt::Windows::Media::Capture::Frames::MediaFrameSource CameraFrameSource = nullptr;
|
||||
winrt::Windows::Media::Devices::Core::CameraIntrinsics CameraIntrinsics = nullptr;
|
||||
|
||||
TOptional<TPair<FGuid, FTransform>> DynamicNode;
|
||||
|
||||
winrt::Windows::Media::Capture::Frames::MediaFrameReader::FrameArrived_revoker OnFrameArrivedEvent;
|
||||
|
||||
winrt::Windows::Foundation::IAsyncInfo AsyncInfo;
|
||||
|
||||
std::shared_ptr<winrt::handle> SharedDXTexture;
|
||||
XrSpace Space = XR_NULL_HANDLE;
|
||||
|
||||
class IXRTrackingSystem* XRTrackingSystem = nullptr;
|
||||
FARVideoFormat Format;
|
||||
|
||||
FTransform PVCameraToWorldMatrix;
|
||||
TSharedPtr< FSharedTextureHolder> SharedTextureHolder;
|
||||
};
|
||||
} // namespace MicrosoftOpenXR
|
||||
|
||||
#endif //PLATFORM_WINDOWS || PLATFORM_HOLOLENS
|
|
@ -0,0 +1,159 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
|
||||
#include "MicrosoftOpenXR.h"
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "HolographicWindowAttachmentPlugin.h"
|
||||
#include "HolographicRemotingPlugin.h"
|
||||
#include "SpatialAnchorPlugin.h"
|
||||
#include "Modules/ModuleManager.h"
|
||||
#include "HandMeshPlugin.h"
|
||||
#include "QRTrackingPlugin.h"
|
||||
#include "LocatableCamPlugin.h"
|
||||
#include "Interfaces/IPluginManager.h"
|
||||
#include "ShaderCore.h"
|
||||
#include "SpeechPlugin.h"
|
||||
#include "SpatialMappingPlugin.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FMicrosoftOpenXRModule"
|
||||
|
||||
namespace MicrosoftOpenXR
|
||||
{
|
||||
static class FMicrosoftOpenXRModule * g_MicrosoftOpenXRModule;
|
||||
|
||||
class FMicrosoftOpenXRModule : public IModuleInterface
|
||||
{
|
||||
public:
|
||||
void StartupModule() override
|
||||
{
|
||||
SpatialAnchorPlugin.Register();
|
||||
HandMeshPlugin.Register();
|
||||
#if PLATFORM_WINDOWS || PLATFORM_HOLOLENS
|
||||
QRTrackingPlugin.Register();
|
||||
LocatableCamPlugin.Register();
|
||||
SpeechPlugin.Register();
|
||||
SpatialMappingPlugin.Register();
|
||||
#endif
|
||||
|
||||
#if SUPPORTS_REMOTING
|
||||
HolographicRemotingPlugin = MakeShared<FHolographicRemotingPlugin>();
|
||||
HolographicRemotingPlugin->Register();
|
||||
#endif
|
||||
|
||||
#if PLATFORM_HOLOLENS
|
||||
HolographicWindowAttachmentPlugin.Register();
|
||||
#endif
|
||||
|
||||
g_MicrosoftOpenXRModule = this;
|
||||
}
|
||||
|
||||
void ShutdownModule() override
|
||||
{
|
||||
g_MicrosoftOpenXRModule = nullptr;
|
||||
|
||||
SpatialAnchorPlugin.Unregister();
|
||||
HandMeshPlugin.Unregister();
|
||||
#if PLATFORM_WINDOWS || PLATFORM_HOLOLENS
|
||||
QRTrackingPlugin.Unregister();
|
||||
LocatableCamPlugin.Unregister();
|
||||
SpeechPlugin.Unregister();
|
||||
SpatialMappingPlugin.Unregister();
|
||||
#endif
|
||||
|
||||
#if SUPPORTS_REMOTING
|
||||
HolographicRemotingPlugin->Unregister();
|
||||
#endif
|
||||
|
||||
#if PLATFORM_HOLOLENS
|
||||
HolographicWindowAttachmentPlugin.Unregister();
|
||||
#endif
|
||||
}
|
||||
|
||||
FHandMeshPlugin HandMeshPlugin;
|
||||
FSpatialAnchorPlugin SpatialAnchorPlugin;
|
||||
#if PLATFORM_WINDOWS || PLATFORM_HOLOLENS
|
||||
FQRTrackingPlugin QRTrackingPlugin;
|
||||
FLocatableCamPlugin LocatableCamPlugin;
|
||||
FSpeechPlugin SpeechPlugin;
|
||||
FSpatialMappingPlugin SpatialMappingPlugin;
|
||||
#endif
|
||||
|
||||
#if SUPPORTS_REMOTING
|
||||
TSharedPtr<FHolographicRemotingPlugin> HolographicRemotingPlugin;
|
||||
#endif
|
||||
|
||||
#if PLATFORM_HOLOLENS
|
||||
FHolographicWindowAttachmentPlugin HolographicWindowAttachmentPlugin;
|
||||
#endif
|
||||
};
|
||||
} // namespace MicrosoftOpenXR
|
||||
|
||||
|
||||
bool UMicrosoftOpenXRFunctionLibrary::SetUseHandMesh(EHandMeshStatus Mode)
|
||||
{
|
||||
return MicrosoftOpenXR::g_MicrosoftOpenXRModule->HandMeshPlugin.Turn(Mode);
|
||||
}
|
||||
|
||||
bool UMicrosoftOpenXRFunctionLibrary::IsQREnabled()
|
||||
{
|
||||
#if PLATFORM_WINDOWS || PLATFORM_HOLOLENS
|
||||
return MicrosoftOpenXR::g_MicrosoftOpenXRModule->QRTrackingPlugin.IsEnabled();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
FTransform UMicrosoftOpenXRFunctionLibrary::GetPVCameraToWorldTransform()
|
||||
{
|
||||
#if PLATFORM_WINDOWS || PLATFORM_HOLOLENS
|
||||
return MicrosoftOpenXR::g_MicrosoftOpenXRModule->LocatableCamPlugin.GetCameraTransform();
|
||||
#else
|
||||
return FTransform::Identity;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool UMicrosoftOpenXRFunctionLibrary::GetPVCameraIntrinsics(FVector2D& focalLength, int& width, int& height, FVector2D& principalPoint, FVector& radialDistortion, FVector2D& tangentialDistortion)
|
||||
{
|
||||
#if PLATFORM_WINDOWS || PLATFORM_HOLOLENS
|
||||
return MicrosoftOpenXR::g_MicrosoftOpenXRModule->LocatableCamPlugin.GetPVCameraIntrinsics(focalLength, width, height, principalPoint, radialDistortion, tangentialDistortion);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
FVector UMicrosoftOpenXRFunctionLibrary::GetWorldSpaceRayFromCameraPoint(FVector2D pixelCoordinate)
|
||||
{
|
||||
#if PLATFORM_WINDOWS || PLATFORM_HOLOLENS
|
||||
return MicrosoftOpenXR::g_MicrosoftOpenXRModule->LocatableCamPlugin.GetWorldSpaceRayFromCameraPoint(pixelCoordinate);
|
||||
#else
|
||||
return FVector::ZeroVector;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool UMicrosoftOpenXRFunctionLibrary::IsSpeechRecognitionAvailable()
|
||||
{
|
||||
#if PLATFORM_WINDOWS || PLATFORM_HOLOLENS
|
||||
return true;
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void UMicrosoftOpenXRFunctionLibrary::AddKeywords(TArray<FKeywordInput> Keywords)
|
||||
{
|
||||
#if PLATFORM_WINDOWS || PLATFORM_HOLOLENS
|
||||
MicrosoftOpenXR::g_MicrosoftOpenXRModule->SpeechPlugin.AddKeywords(Keywords);
|
||||
#endif
|
||||
}
|
||||
|
||||
void UMicrosoftOpenXRFunctionLibrary::RemoveKeywords(TArray<FString> Keywords)
|
||||
{
|
||||
#if PLATFORM_WINDOWS || PLATFORM_HOLOLENS
|
||||
MicrosoftOpenXR::g_MicrosoftOpenXRModule->SpeechPlugin.RemoveKeywords(Keywords);
|
||||
#endif
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
||||
|
||||
IMPLEMENT_MODULE(MicrosoftOpenXR::FMicrosoftOpenXRModule, MicrosoftOpenXR)
|
|
@ -0,0 +1,308 @@
|
|||
#include "OpenXRCameraImageTexture.h"
|
||||
|
||||
#include "GlobalShader.h"
|
||||
#include "RenderUtils.h"
|
||||
#include "RHIStaticStates.h"
|
||||
#include "PipelineStateCache.h"
|
||||
#include "ShaderParameterUtils.h"
|
||||
#include "SceneUtils.h"
|
||||
#include "MediaShaders.h"
|
||||
#include "HeadMountedDisplayTypes.h"
|
||||
|
||||
#if PLATFORM_WINDOWS || PLATFORM_HOLOLENS
|
||||
#include "Windows/AllowWindowsPlatformTypes.h"
|
||||
|
||||
THIRD_PARTY_INCLUDES_START
|
||||
#include <windows.h>
|
||||
#include <D3D11.h>
|
||||
#include <d3d11_1.h>
|
||||
#include <dxgi1_2.h>
|
||||
THIRD_PARTY_INCLUDES_END
|
||||
|
||||
#include "Windows/COMPointer.h"
|
||||
#include "Windows/HideWindowsPlatformTypes.h"
|
||||
#endif
|
||||
|
||||
|
||||
#if PLATFORM_WINDOWS || PLATFORM_HOLOLENS
|
||||
|
||||
/** Resource class to do all of the setup work on the render thread */
|
||||
class FOpenXRCameraImageResource :
|
||||
public FTextureResource
|
||||
{
|
||||
public:
|
||||
FOpenXRCameraImageResource(UOpenXRCameraImageTexture* InOwner)
|
||||
: LastFrameNumber(0)
|
||||
, Owner(InOwner)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~FOpenXRCameraImageResource()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the resource is initialized. This is only called by the rendering thread.
|
||||
*/
|
||||
virtual void InitRHI() override
|
||||
{
|
||||
check(IsInRenderingThread());
|
||||
|
||||
FString RHIString = FApp::GetGraphicsRHI();
|
||||
if (RHIString.IsEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (RHIString != TEXT("DirectX 11"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
FSamplerStateInitializerRHI SamplerStateInitializer(SF_Bilinear, AM_Clamp, AM_Clamp, AM_Clamp);
|
||||
SamplerStateRHI = RHICreateSamplerState(SamplerStateInitializer);
|
||||
|
||||
bool bDidConvert = false;
|
||||
if (CameraImageHandle)
|
||||
{
|
||||
// Open the shared texture from the HoloLens camera on Unreal's d3d device.
|
||||
ID3D11Device* D3D11Device = static_cast<ID3D11Device*>(GDynamicRHI->RHIGetNativeDevice());
|
||||
TComPtr<ID3D11DeviceContext> D3D11DeviceContext = nullptr;
|
||||
D3D11Device->GetImmediateContext(&D3D11DeviceContext);
|
||||
if (D3D11DeviceContext == nullptr)
|
||||
{
|
||||
CameraImageHandle = nullptr;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
TComPtr<ID3D11Texture2D> cameraImageTexture;
|
||||
TComPtr<IDXGIResource1> cameraImageResource(NULL);
|
||||
if (FAILED(((ID3D11Device1*)D3D11Device)->OpenSharedResource1(CameraImageHandle->get(), __uuidof(IDXGIResource1), (void**)&cameraImageResource)))
|
||||
{
|
||||
UE_LOG(LogHMD, Log, TEXT("ID3D11Device1::OpenSharedResource1 failed in FOpenXRCameraImageResource::InitRHI"));
|
||||
return;
|
||||
}
|
||||
if (FAILED(cameraImageResource->QueryInterface(__uuidof(ID3D11Texture2D), (void**)(&cameraImageTexture))))
|
||||
{
|
||||
UE_LOG(LogHMD, Log, TEXT("IDXGIResource1::QueryInterface failed in FOpenXRCameraImageResource::InitRHI"));
|
||||
return;
|
||||
}
|
||||
|
||||
D3D11_TEXTURE2D_DESC Desc;
|
||||
cameraImageTexture->GetDesc(&Desc);
|
||||
|
||||
Size.X = Desc.Width;
|
||||
Size.Y = Desc.Height;
|
||||
|
||||
// Create the copy target
|
||||
{
|
||||
FRHIResourceCreateInfo CreateInfo;
|
||||
CopyTextureRef = RHICreateTexture2D(Size.X, Size.Y, PF_NV12, 1, 1, TexCreate_Dynamic | TexCreate_ShaderResource, CreateInfo);
|
||||
}
|
||||
// Create the render target
|
||||
{
|
||||
FRHIResourceCreateInfo CreateInfo;
|
||||
TRefCountPtr<FRHITexture2D> DummyTexture2DRHI;
|
||||
// Create our render target that we'll convert to
|
||||
RHICreateTargetableShaderResource2D(Size.X, Size.Y, PF_B8G8R8A8, 1, TexCreate_Dynamic, TexCreate_RenderTargetable, false, CreateInfo, DecodedTextureRef, DummyTexture2DRHI);
|
||||
}
|
||||
|
||||
if (PerformCopy(cameraImageTexture, D3D11DeviceContext))
|
||||
{
|
||||
PerformConversion();
|
||||
bDidConvert = true;
|
||||
}
|
||||
// Now that the conversion is done, we can get rid of our refs
|
||||
CameraImageHandle = nullptr;
|
||||
CopyTextureRef.SafeRelease();
|
||||
}
|
||||
|
||||
// Default to an empty 1x1 texture if we don't have a camera image or failed to convert
|
||||
if (!bDidConvert)
|
||||
{
|
||||
FRHIResourceCreateInfo CreateInfo;
|
||||
Size.X = Size.Y = 1;
|
||||
DecodedTextureRef = RHICreateTexture2D(Size.X, Size.Y, PF_B8G8R8A8, 1, 1, TexCreate_ShaderResource, CreateInfo);
|
||||
}
|
||||
|
||||
TextureRHI = DecodedTextureRef;
|
||||
TextureRHI->SetName(Owner->GetFName());
|
||||
RHIBindDebugLabelName(TextureRHI, *Owner->GetName());
|
||||
RHIUpdateTextureReference(Owner->TextureReference.TextureReferenceRHI, TextureRHI);
|
||||
}
|
||||
|
||||
virtual void ReleaseRHI() override
|
||||
{
|
||||
RHIUpdateTextureReference(Owner->TextureReference.TextureReferenceRHI, nullptr);
|
||||
CameraImageHandle = nullptr;
|
||||
CopyTextureRef.SafeRelease();
|
||||
DecodedTextureRef.SafeRelease();
|
||||
FTextureResource::ReleaseRHI();
|
||||
}
|
||||
|
||||
/** Returns the width of the texture in pixels. */
|
||||
virtual uint32 GetSizeX() const override
|
||||
{
|
||||
return Size.X;
|
||||
}
|
||||
|
||||
/** Returns the height of the texture in pixels. */
|
||||
virtual uint32 GetSizeY() const override
|
||||
{
|
||||
return Size.Y;
|
||||
}
|
||||
|
||||
/** Render thread update of the texture so we don't get 2 updates per frame on the render thread */
|
||||
void Init_RenderThread(std::shared_ptr<winrt::handle> handle)
|
||||
{
|
||||
check(IsInRenderingThread());
|
||||
if (LastFrameNumber != GFrameNumber)
|
||||
{
|
||||
LastFrameNumber = GFrameNumber;
|
||||
ReleaseRHI();
|
||||
CameraImageHandle = handle;
|
||||
InitRHI();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
/** Copy CameraImage to our CopyTextureRef using the GPU */
|
||||
bool PerformCopy(const TComPtr<ID3D11Texture2D>& texture, const TComPtr<ID3D11DeviceContext>& context)
|
||||
{
|
||||
// These must already be prepped
|
||||
if (texture == nullptr
|
||||
|| context == nullptr
|
||||
|| !CopyTextureRef.IsValid())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// Get the underlying interface for the texture we are copying to
|
||||
TComPtr<ID3D11Resource> CopyTexture = reinterpret_cast<ID3D11Resource*>(CopyTextureRef->GetNativeResource());
|
||||
if (CopyTexture == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
context->CopyResource(CopyTexture.Get(), texture);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Runs a shader to convert YUV to RGB */
|
||||
void PerformConversion()
|
||||
{
|
||||
FRHICommandListImmediate& CommandList = FRHICommandListExecutor::GetImmediateCommandList();
|
||||
SCOPED_DRAW_EVENT(CommandList, HoloLensCameraImageConversion);
|
||||
|
||||
FGraphicsPipelineStateInitializer GraphicsPSOInit;
|
||||
FRHITexture* RenderTarget = DecodedTextureRef.GetReference();
|
||||
CommandList.Transition(FRHITransitionInfo(RenderTarget, ERHIAccess::Unknown, ERHIAccess::RTV));
|
||||
|
||||
FIntPoint OutputDim(RenderTarget->GetSizeXYZ().X, RenderTarget->GetSizeXYZ().Y);
|
||||
|
||||
FRHIRenderPassInfo RPInfo(RenderTarget, ERenderTargetActions::DontLoad_Store);
|
||||
CommandList.BeginRenderPass(RPInfo, TEXT("HoloLensCameraImageConversion"));
|
||||
{
|
||||
CommandList.ApplyCachedRenderTargets(GraphicsPSOInit);
|
||||
CommandList.SetViewport(0, 0, 0.0f, OutputDim.X, OutputDim.Y, 1.0f);
|
||||
|
||||
GraphicsPSOInit.DepthStencilState = TStaticDepthStencilState<false, CF_Always>::GetRHI();
|
||||
GraphicsPSOInit.RasterizerState = TStaticRasterizerState<>::GetRHI();
|
||||
GraphicsPSOInit.BlendState = TStaticBlendStateWriteMask<CW_RGBA, CW_NONE, CW_NONE, CW_NONE, CW_NONE, CW_NONE, CW_NONE, CW_NONE>::GetRHI();
|
||||
GraphicsPSOInit.PrimitiveType = PT_TriangleStrip;
|
||||
|
||||
// configure media shaders
|
||||
auto ShaderMap = GetGlobalShaderMap(GMaxRHIFeatureLevel);
|
||||
TShaderMapRef<FMediaShadersVS> VertexShader(ShaderMap);
|
||||
|
||||
GraphicsPSOInit.BoundShaderState.VertexDeclarationRHI = GMediaVertexDeclaration.VertexDeclarationRHI;
|
||||
GraphicsPSOInit.BoundShaderState.VertexShaderRHI = VertexShader.GetVertexShader();
|
||||
|
||||
// Use the sample format to choose the conversion path
|
||||
TShaderMapRef<FNV12ConvertPS> ConvertShader(ShaderMap);
|
||||
GraphicsPSOInit.BoundShaderState.PixelShaderRHI = ConvertShader.GetPixelShader();
|
||||
SetGraphicsPipelineState(CommandList, GraphicsPSOInit);
|
||||
|
||||
FShaderResourceViewRHIRef Y_SRV = RHICreateShaderResourceView(CopyTextureRef, 0, 1, PF_G8);
|
||||
FShaderResourceViewRHIRef UV_SRV = RHICreateShaderResourceView(CopyTextureRef, 0, 1, PF_R8G8);
|
||||
|
||||
ConvertShader->SetParameters(CommandList, CopyTextureRef->GetSizeXY(), Y_SRV, UV_SRV, OutputDim, MediaShaders::YuvToSrgbDefault, MediaShaders::YUVOffset8bits, false);
|
||||
|
||||
// draw full size quad into render target
|
||||
FVertexBufferRHIRef VertexBuffer = CreateTempMediaVertexBuffer();
|
||||
CommandList.SetStreamSource(0, VertexBuffer, 0);
|
||||
// set viewport to RT size
|
||||
CommandList.SetViewport(0, 0, 0.0f, OutputDim.X, OutputDim.Y, 1.0f);
|
||||
|
||||
CommandList.DrawPrimitive(0, 2, 1);
|
||||
}
|
||||
CommandList.EndRenderPass();
|
||||
CommandList.Transition(FRHITransitionInfo(RenderTarget, ERHIAccess::RTV, ERHIAccess::SRVGraphics));
|
||||
|
||||
}
|
||||
|
||||
/** The size we get from the incoming camera image */
|
||||
FIntPoint Size;
|
||||
|
||||
/** The raw camera image from the HoloLens which we copy to our texture to allow it to be quickly released */
|
||||
std::shared_ptr<winrt::handle> CameraImageHandle;
|
||||
/** The nv12 texture that we copy into so we don't block the camera from being able to send frames */
|
||||
FTexture2DRHIRef CopyTextureRef;
|
||||
/** The texture that we actually render with which is populated via a shader that converts nv12 to rgba */
|
||||
FTexture2DRHIRef DecodedTextureRef;
|
||||
/** The last frame we were updated on */
|
||||
uint32 LastFrameNumber;
|
||||
|
||||
const UOpenXRCameraImageTexture* Owner;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
UOpenXRCameraImageTexture::UOpenXRCameraImageTexture(const FObjectInitializer& ObjectInitializer)
|
||||
: Super(ObjectInitializer)
|
||||
, LastUpdateFrame(0)
|
||||
{
|
||||
}
|
||||
|
||||
void UOpenXRCameraImageTexture::BeginDestroy()
|
||||
{
|
||||
Super::BeginDestroy();
|
||||
}
|
||||
|
||||
FTextureResource* UOpenXRCameraImageTexture::CreateResource()
|
||||
{
|
||||
#if PLATFORM_WINDOWS || PLATFORM_HOLOLENS
|
||||
return new FOpenXRCameraImageResource(this);
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if PLATFORM_WINDOWS || PLATFORM_HOLOLENS
|
||||
/** Forces the reconstruction of the texture data and conversion from Nv12 to RGB */
|
||||
void UOpenXRCameraImageTexture::Init(std::shared_ptr<winrt::handle> handle)
|
||||
{
|
||||
// It's possible that we get more than one queued thread update per game frame
|
||||
// Skip any additional frames because it will cause the recursive flush rendering commands ensure
|
||||
if (LastUpdateFrame != GFrameCounter)
|
||||
{
|
||||
LastUpdateFrame = GFrameCounter;
|
||||
if (Resource != nullptr)
|
||||
{
|
||||
FOpenXRCameraImageResource* LambdaResource = static_cast<FOpenXRCameraImageResource*>(Resource);
|
||||
ENQUEUE_RENDER_COMMAND(Init_RenderThread)(
|
||||
[LambdaResource, handle](FRHICommandListImmediate&)
|
||||
{
|
||||
LambdaResource->Init_RenderThread(handle);
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
// This should end up only being called once, the first time we get a texture update
|
||||
UpdateResource();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
|
||||
#include "ARTextures.h"
|
||||
#if PLATFORM_WINDOWS || PLATFORM_HOLOLENS
|
||||
#include <memory>
|
||||
#include <winrt/base.h>
|
||||
#endif
|
||||
|
||||
|
||||
#include "OpenXRCameraImageTexture.generated.h"
|
||||
|
||||
/**
|
||||
* Provides access to the camera's image data as a texture
|
||||
*/
|
||||
UCLASS(NotBlueprintType)
|
||||
class UOpenXRCameraImageTexture :
|
||||
public UARTextureCameraImage
|
||||
{
|
||||
GENERATED_UCLASS_BODY()
|
||||
|
||||
public:
|
||||
// UTexture interface implementation
|
||||
virtual void BeginDestroy() override;
|
||||
virtual FTextureResource* CreateResource() override;
|
||||
virtual EMaterialValueType GetMaterialType() const override { return MCT_Texture2D; }
|
||||
// End UTexture interface
|
||||
|
||||
#if PLATFORM_WINDOWS || PLATFORM_HOLOLENS
|
||||
/** Forces the reconstruction of the texture data and conversion from Nv12 to RGB */
|
||||
virtual void Init(std::shared_ptr<winrt::handle> handle);
|
||||
#endif
|
||||
|
||||
friend class FOpenXRCameraImageResource;
|
||||
|
||||
private:
|
||||
/** Used to prevent two updates of the texture in the same game frame */
|
||||
uint64 LastUpdateFrame;
|
||||
};
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
#include "OpenXRCommon.h"
|
||||
#include "CoreMinimal.h"
|
||||
#include "OpenXRCore.h"
|
||||
|
||||
namespace MicrosoftOpenXR
|
||||
{
|
||||
XrPath GetXrPath(XrInstance Instance, const char* PathString)
|
||||
{
|
||||
XrPath Path = XR_NULL_PATH;
|
||||
XrResult Result = xrStringToPath(Instance, PathString, &Path);
|
||||
check(XR_SUCCEEDED(Result));
|
||||
return Path;
|
||||
}
|
||||
} // namespace MicrosoftOpenXR
|
|
@ -0,0 +1,35 @@
|
|||
#pragma once
|
||||
|
||||
#include <openxr/openxr.h>
|
||||
|
||||
#include "HAL/Platform.h"
|
||||
|
||||
#if PLATFORM_WINDOWS || PLATFORM_HOLOLENS
|
||||
|
||||
#pragma warning(disable : 5205 4265 4268 4946)
|
||||
|
||||
#define XR_USE_PLATFORM_WIN32 1
|
||||
#include "Windows/AllowWindowsPlatformTypes.h"
|
||||
#include "Windows/AllowWindowsPlatformAtomics.h"
|
||||
#include "Windows/PreWindowsApi.h"
|
||||
|
||||
#include <unknwn.h>
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
#include <winrt/Windows.Foundation.Collections.h>
|
||||
#endif
|
||||
|
||||
#include <openxr/openxr_platform.h>
|
||||
|
||||
#if PLATFORM_WINDOWS || PLATFORM_HOLOLENS
|
||||
#include "Windows/PostWindowsApi.h"
|
||||
#include "Windows/HideWindowsPlatformAtomics.h"
|
||||
#include "Windows/HideWindowsPlatformTypes.h"
|
||||
#endif
|
||||
|
||||
#include "IOpenXRHMDPlugin.h"
|
||||
#include "IOpenXRExtensionPlugin.h"
|
||||
|
||||
namespace MicrosoftOpenXR
|
||||
{
|
||||
XrPath GetXrPath(XrInstance Instance, const char* PathString);
|
||||
}
|
|
@ -0,0 +1,292 @@
|
|||
#include "QRTrackingPlugin.h"
|
||||
#if PLATFORM_WINDOWS || PLATFORM_HOLOLENS
|
||||
#include "OpenXRCore.h"
|
||||
#include "IOpenXRARTrackedGeometryHolder.h"
|
||||
#include "IXRTrackingSystem.h"
|
||||
|
||||
#include "WindowsMixedRealityInteropUtility.h"
|
||||
|
||||
#include <winrt/Windows.Foundation.Collections.h>
|
||||
|
||||
using namespace winrt::Windows::Foundation;
|
||||
using namespace winrt::Windows::Foundation::Collections;
|
||||
using namespace winrt::Windows::Perception::Spatial;
|
||||
using namespace winrt::Windows::Perception::Spatial::Surfaces;
|
||||
using namespace winrt::Microsoft::MixedReality::QR;
|
||||
|
||||
namespace MicrosoftOpenXR
|
||||
{
|
||||
void FQRTrackingPlugin::Register()
|
||||
{
|
||||
IModularFeatures::Get().RegisterModularFeature(IOpenXRExtensionPlugin::GetModularFeatureName(), this);
|
||||
#if PLATFORM_WINDOWS
|
||||
const FString DllName = "Microsoft.MixedReality.QR.dll";
|
||||
const FString LibrariesDir = FPaths::ProjectPluginsDir() / "MicrosoftOpenXR"/ THIRDPARTY_BINARY_SUBFOLDER;
|
||||
|
||||
FPlatformProcess::PushDllDirectory(*LibrariesDir);
|
||||
if (!FPlatformProcess::GetDllHandle(*DllName))
|
||||
{
|
||||
UE_LOG(LogHMD, Warning, TEXT("Dll \'%s\' can't be loaded from \'%s\'"), *DllName, *LibrariesDir);
|
||||
}
|
||||
FPlatformProcess::PopDllDirectory(*LibrariesDir);
|
||||
#endif
|
||||
}
|
||||
|
||||
void FQRTrackingPlugin::Unregister()
|
||||
{
|
||||
IModularFeatures::Get().UnregisterModularFeature(IOpenXRExtensionPlugin::GetModularFeatureName(), this);
|
||||
StopQRCodeWatcher();
|
||||
}
|
||||
|
||||
bool FQRTrackingPlugin::GetRequiredExtensions(TArray<const ANSICHAR*>& OutExtensions)
|
||||
{
|
||||
OutExtensions.Add(XR_MSFT_SPATIAL_GRAPH_BRIDGE_EXTENSION_NAME);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
const void* FQRTrackingPlugin::OnCreateSession(XrInstance InInstance, XrSystemId InSystem, const void* InNext)
|
||||
{
|
||||
XR_ENSURE(xrGetInstanceProcAddr(InInstance, "xrCreateSpatialGraphNodeSpaceMSFT", (PFN_xrVoidFunction*)&xrCreateSpatialGraphNodeSpaceMSFT));
|
||||
|
||||
QRCodeHolder = IModularFeatures::Get().GetModularFeatureImplementations<IOpenXRARTrackedGeometryHolder>(IOpenXRARTrackedGeometryHolder::GetModularFeatureName())[0];
|
||||
|
||||
ensure(QRCodeHolder != nullptr);
|
||||
|
||||
static FName SystemName(TEXT("OpenXR"));
|
||||
if (GEngine->XRSystem.IsValid() && (GEngine->XRSystem->GetSystemName() == SystemName))
|
||||
{
|
||||
XRTrackingSystem = GEngine->XRSystem.Get();
|
||||
}
|
||||
|
||||
ensure(XRTrackingSystem != nullptr);
|
||||
|
||||
return InNext;
|
||||
}
|
||||
|
||||
bool FQRTrackingPlugin::OnToggleARCapture(const bool On)
|
||||
{
|
||||
if (On)
|
||||
{
|
||||
return StartQRCodeWatcher();
|
||||
}
|
||||
else
|
||||
{
|
||||
StopQRCodeWatcher();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FQRTrackingPlugin::IsEnabled() const
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(QRCodeRefsLock);
|
||||
return QRTrackerInstance != nullptr;
|
||||
}
|
||||
|
||||
bool FQRTrackingPlugin::StartQRCodeWatcher()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(QRCodeRefsLock);
|
||||
|
||||
// Create the tracker and register the callbacks
|
||||
if (QRTrackerInstance == nullptr)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!QRCodeWatcher::IsSupported())
|
||||
{
|
||||
UE_LOG(LogHMD, Log, TEXT("QRCodeWatcher is not supported."));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (winrt::hresult_error e)
|
||||
{
|
||||
UE_LOG(LogHMD, Error, TEXT("QRCodeWatcher::IsSupported failed with error: %d"), e.code().value);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_QRTrackerAsyncOperation = QRCodeWatcher::RequestAccessAsync();
|
||||
m_QRTrackerAsyncOperation.Completed([=](auto&& asyncInfo, auto&& asyncStatus)
|
||||
{
|
||||
if (asyncStatus == winrt::Windows::Foundation::AsyncStatus::Completed)
|
||||
{
|
||||
if (asyncInfo.GetResults() == QRCodeWatcherAccessStatus::Allowed)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(QRCodeRefsLock);
|
||||
|
||||
QRTrackerInstance = QRCodeWatcher();
|
||||
OnAddedEventToken = QRTrackerInstance.Added(winrt::auto_revoke, [=](auto&& sender, auto&& args) { OnAdded(sender, args); });
|
||||
OnUpdatedEventToken = QRTrackerInstance.Updated(winrt::auto_revoke, [=](auto&& sender, auto&& args) { OnUpdated(sender, args); });
|
||||
OnRemovedEventToken = QRTrackerInstance.Removed(winrt::auto_revoke, [=](auto&& sender, auto&& args) { OnRemoved(sender, args); });
|
||||
OnEnumerationCompletedToken = QRTrackerInstance.EnumerationCompleted(winrt::auto_revoke, [=](auto&& sender, auto&& args) { OnEnumerationCompleted(sender, args); });
|
||||
|
||||
// Start the tracker
|
||||
QRTrackerInstance.Start();
|
||||
|
||||
m_QRTrackerAsyncOperation = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogHMD, Log, TEXT("QRTracker access request returns error: %d"), asyncInfo.GetResults());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void FQRTrackingPlugin::StopQRCodeWatcher()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(QRCodeRefsLock);
|
||||
if (m_QRTrackerAsyncOperation != nullptr && m_QRTrackerAsyncOperation.Status() != winrt::Windows::Foundation::AsyncStatus::Completed)
|
||||
{
|
||||
m_QRTrackerAsyncOperation.Cancel();
|
||||
}
|
||||
|
||||
if (QRTrackerInstance != nullptr)
|
||||
{
|
||||
OnAddedEventToken.revoke();
|
||||
OnUpdatedEventToken.revoke();
|
||||
OnRemovedEventToken.revoke();
|
||||
OnEnumerationCompletedToken.revoke();
|
||||
|
||||
// Stop the tracker
|
||||
QRTrackerInstance.Stop();
|
||||
QRTrackerInstance = nullptr;
|
||||
}
|
||||
|
||||
{
|
||||
FScopeLock Lock(&QRCodeContextsMutex);
|
||||
QRCodeContexts.Empty();
|
||||
}
|
||||
}
|
||||
|
||||
void FQRTrackingPlugin::OnAdded(QRCodeWatcher sender, QRCodeAddedEventArgs args)
|
||||
{
|
||||
auto QRCode = new FOpenXRQRCodeData;
|
||||
auto InCode = args.Code();
|
||||
|
||||
QRCode->Id = WMRUtility::GUIDToFGuid(InCode.Id());
|
||||
QRCode->Version = (int32_t)InCode.Version();
|
||||
QRCode->QRCode = InCode.Data().c_str();
|
||||
QRCode->Size = FVector2D(InCode.PhysicalSideLength()) * XRTrackingSystem->GetWorldToMetersScale();
|
||||
QRCode->Timestamp = FPlatformTime::Seconds();
|
||||
QRCode->TrackingState = EARTrackingState::NotTracking;
|
||||
QRCode->LocalToTrackingTransform = FTransform::Identity; //OnAdded returns no actual pose
|
||||
|
||||
auto Context = MakeShared<QRCodeContext, ESPMode::ThreadSafe>();
|
||||
Context->SpatialGraphNodeId = WMRUtility::GUIDToFGuid(InCode.SpatialGraphNodeId());
|
||||
|
||||
{
|
||||
FScopeLock Lock(&QRCodeContextsMutex);
|
||||
QRCodeContexts.FindOrAdd(QRCode->Id) = Context;
|
||||
}
|
||||
|
||||
QRCodeHolder->ARTrackedGeometryAdded(QRCode);
|
||||
}
|
||||
|
||||
void FQRTrackingPlugin::OnUpdated(QRCodeWatcher sender, QRCodeUpdatedEventArgs args)
|
||||
{
|
||||
//OnUpdated works not good, so it's replaced by loop in UpdateDeviceLocations
|
||||
}
|
||||
|
||||
void FQRTrackingPlugin::OnRemoved(QRCodeWatcher sender, QRCodeRemovedEventArgs args)
|
||||
{
|
||||
auto QRCode = new FOpenXRQRCodeData;
|
||||
auto InCode = args.Code();
|
||||
|
||||
QRCode->Id = WMRUtility::GUIDToFGuid(InCode.Id());
|
||||
QRCode->Timestamp = FPlatformTime::Seconds();
|
||||
|
||||
{
|
||||
FScopeLock Lock(&QRCodeContextsMutex);
|
||||
QRCodeContexts.Remove(QRCode->Id);
|
||||
}
|
||||
|
||||
|
||||
QRCodeHolder->ARTrackedGeometryRemoved(QRCode);
|
||||
}
|
||||
|
||||
void FQRTrackingPlugin::OnEnumerationCompleted(QRCodeWatcher sender, winrt::Windows::Foundation::IInspectable args)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void FQRTrackingPlugin::UpdateDeviceLocations(XrSession InSession, XrTime DisplayTime, XrSpace TrackingSpace)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(QRCodeRefsLock);
|
||||
if (QRTrackerInstance == nullptr) { return; }
|
||||
|
||||
for(auto&& InCode : QRTrackerInstance.GetList())
|
||||
{
|
||||
FGuid OutGuid = WMRUtility::GUIDToFGuid(InCode.Id());
|
||||
QRCodeContextPtr Context;
|
||||
{
|
||||
FScopeLock Lock(&QRCodeContextsMutex);
|
||||
if (auto PtrPtr = QRCodeContexts.Find(OutGuid))
|
||||
{
|
||||
Context = *PtrPtr;
|
||||
}
|
||||
else
|
||||
{
|
||||
continue; //QRCode is being deleted
|
||||
}
|
||||
}
|
||||
|
||||
auto OutCode = new FOpenXRQRCodeData;
|
||||
|
||||
OutCode->Id = OutGuid;
|
||||
OutCode->Version = (int32_t)InCode.Version();
|
||||
OutCode->QRCode = InCode.Data().c_str();
|
||||
OutCode->Size = FVector2D(InCode.PhysicalSideLength()) * XRTrackingSystem->GetWorldToMetersScale();
|
||||
OutCode->Timestamp = FPlatformTime::Seconds();
|
||||
|
||||
if (Context->Space == XR_NULL_HANDLE)
|
||||
{
|
||||
XrSpatialGraphNodeSpaceCreateInfoMSFT SpatialGraphNodeSpaceCreateInfo{ XR_TYPE_SPATIAL_GRAPH_NODE_SPACE_CREATE_INFO_MSFT };
|
||||
SpatialGraphNodeSpaceCreateInfo.nodeType = XR_SPATIAL_GRAPH_NODE_TYPE_STATIC_MSFT;
|
||||
SpatialGraphNodeSpaceCreateInfo.pose = ToXrPose(FTransform::Identity, XRTrackingSystem->GetWorldToMetersScale());
|
||||
|
||||
check(sizeof(SpatialGraphNodeSpaceCreateInfo.nodeId) == sizeof(FGuid));
|
||||
FMemory::Memcpy(&SpatialGraphNodeSpaceCreateInfo.nodeId, &Context->SpatialGraphNodeId, sizeof(SpatialGraphNodeSpaceCreateInfo.nodeId));
|
||||
|
||||
XR_ENSURE(xrCreateSpatialGraphNodeSpaceMSFT(InSession, &SpatialGraphNodeSpaceCreateInfo, &Context->Space));
|
||||
}
|
||||
|
||||
XrSpaceLocation SpaceLocation{ XR_TYPE_SPACE_LOCATION };
|
||||
XR_ENSURE(xrLocateSpace(Context->Space, TrackingSpace, DisplayTime, &SpaceLocation));
|
||||
const XrSpaceLocationFlags ValidFlags = XR_SPACE_LOCATION_ORIENTATION_VALID_BIT | XR_SPACE_LOCATION_POSITION_VALID_BIT;
|
||||
if ((SpaceLocation.locationFlags & ValidFlags) == ValidFlags)
|
||||
{
|
||||
OutCode->LocalToTrackingTransform = ToFTransform(SpaceLocation.pose, XRTrackingSystem->GetWorldToMetersScale());
|
||||
OutCode->TrackingState = EARTrackingState::Tracking;
|
||||
}
|
||||
else
|
||||
{
|
||||
OutCode->TrackingState = EARTrackingState::NotTracking;
|
||||
}
|
||||
|
||||
QRCodeHolder->ARTrackedGeometryUpdated(OutCode);
|
||||
}
|
||||
}
|
||||
|
||||
IOpenXRCustomCaptureSupport* FQRTrackingPlugin::GetCustomCaptureSupport(const EARCaptureType CaptureType)
|
||||
{
|
||||
if (CaptureType == EARCaptureType::QRCode)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
FQRTrackingPlugin::QRCodeContext::~QRCodeContext()
|
||||
{
|
||||
if (Space != XR_NULL_HANDLE)
|
||||
{
|
||||
xrDestroySpace(Space);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace MicrosoftOpenXR
|
||||
|
||||
#endif //PLATFORM_WINDOWS || PLATFORM_HOLOLENS
|
|
@ -0,0 +1,87 @@
|
|||
#pragma once
|
||||
|
||||
#if PLATFORM_WINDOWS || PLATFORM_HOLOLENS
|
||||
#include "OpenXRCommon.h"
|
||||
#include "ARTypes.h"
|
||||
|
||||
#include "Windows/AllowWindowsPlatformTypes.h"
|
||||
#include "Windows/AllowWindowsPlatformAtomics.h"
|
||||
#include "Windows/PreWindowsApi.h"
|
||||
|
||||
#include <unknwn.h>
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
#include <winrt/Windows.Perception.Spatial.h>
|
||||
#include <winrt/Windows.Perception.Spatial.Surfaces.h>
|
||||
#include <winrt/Microsoft.MixedReality.QR.h>
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include "Windows/PostWindowsApi.h"
|
||||
#include "Windows/HideWindowsPlatformAtomics.h"
|
||||
#include "Windows/HideWindowsPlatformTypes.h"
|
||||
|
||||
class IOpenXRARTrackedGeometryHolder;
|
||||
struct FOpenXRQRCodeData;
|
||||
|
||||
|
||||
namespace MicrosoftOpenXR
|
||||
{
|
||||
class FQRTrackingPlugin : public IOpenXRExtensionPlugin, public IOpenXRCustomCaptureSupport
|
||||
{
|
||||
public:
|
||||
void Register();
|
||||
void Unregister();
|
||||
|
||||
virtual bool GetRequiredExtensions(TArray<const ANSICHAR*>& OutExtensions) override;
|
||||
|
||||
virtual const void* OnCreateSession(XrInstance InInstance, XrSystemId InSystem, const void* InNext) override;
|
||||
virtual void UpdateDeviceLocations(XrSession InSession, XrTime DisplayTime, XrSpace TrackingSpace) override;
|
||||
|
||||
virtual IOpenXRCustomCaptureSupport* GetCustomCaptureSupport(const EARCaptureType CaptureType) override;
|
||||
|
||||
bool OnToggleARCapture(const bool bOnOff);
|
||||
bool IsEnabled() const;
|
||||
private:
|
||||
|
||||
PFN_xrCreateSpatialGraphNodeSpaceMSFT xrCreateSpatialGraphNodeSpaceMSFT;
|
||||
|
||||
bool StartQRCodeWatcher();
|
||||
void StopQRCodeWatcher();
|
||||
|
||||
// WinRT handlers
|
||||
void OnAdded(winrt::Microsoft::MixedReality::QR::QRCodeWatcher sender, winrt::Microsoft::MixedReality::QR::QRCodeAddedEventArgs args);
|
||||
void OnUpdated(winrt::Microsoft::MixedReality::QR::QRCodeWatcher sender, winrt::Microsoft::MixedReality::QR::QRCodeUpdatedEventArgs args);
|
||||
void OnRemoved(winrt::Microsoft::MixedReality::QR::QRCodeWatcher sender, winrt::Microsoft::MixedReality::QR::QRCodeRemovedEventArgs args);
|
||||
void OnEnumerationCompleted(winrt::Microsoft::MixedReality::QR::QRCodeWatcher sender, winrt::Windows::Foundation::IInspectable args);
|
||||
|
||||
winrt::Microsoft::MixedReality::QR::QRCodeWatcher QRTrackerInstance = nullptr;
|
||||
winrt::Windows::Foundation::IAsyncOperation < winrt::Microsoft::MixedReality::QR::QRCodeWatcherAccessStatus > m_QRTrackerAsyncOperation = nullptr;
|
||||
|
||||
winrt::Microsoft::MixedReality::QR::QRCodeWatcher::Added_revoker OnAddedEventToken;
|
||||
winrt::Microsoft::MixedReality::QR::QRCodeWatcher::Updated_revoker OnUpdatedEventToken;
|
||||
winrt::Microsoft::MixedReality::QR::QRCodeWatcher::Removed_revoker OnRemovedEventToken;
|
||||
winrt::Microsoft::MixedReality::QR::QRCodeWatcher::EnumerationCompleted_revoker OnEnumerationCompletedToken;
|
||||
|
||||
mutable std::recursive_mutex QRCodeRefsLock;
|
||||
|
||||
struct QRCodeContext
|
||||
{
|
||||
EARTrackingState TrackingState = EARTrackingState::Unknown;
|
||||
FGuid SpatialGraphNodeId;
|
||||
XrSpace Space = XR_NULL_HANDLE;
|
||||
|
||||
~QRCodeContext();
|
||||
};
|
||||
|
||||
typedef TSharedPtr<QRCodeContext, ESPMode::ThreadSafe> QRCodeContextPtr;
|
||||
|
||||
IOpenXRARTrackedGeometryHolder* QRCodeHolder;
|
||||
|
||||
class IXRTrackingSystem* XRTrackingSystem = nullptr;
|
||||
|
||||
TMap<FGuid, QRCodeContextPtr > QRCodeContexts;
|
||||
FCriticalSection QRCodeContextsMutex;
|
||||
};
|
||||
} // namespace MicrosoftOpenXR
|
||||
|
||||
#endif //PLATFORM_WINDOWS || PLATFORM_HOLOLENS
|
|
@ -0,0 +1,287 @@
|
|||
#include "SpatialAnchorPlugin.h"
|
||||
#include "OpenXRCore.h"
|
||||
#include "ARPin.h"
|
||||
|
||||
#if HL_ANCHOR_STORE_AVAILABLE
|
||||
|
||||
using namespace winrt::Windows::Perception::Spatial;
|
||||
using namespace winrt::Windows::Foundation;
|
||||
|
||||
#endif
|
||||
|
||||
namespace MicrosoftOpenXR
|
||||
{
|
||||
void FSpatialAnchorPlugin::Register()
|
||||
{
|
||||
IModularFeatures::Get().RegisterModularFeature(GetModularFeatureName(), this);
|
||||
}
|
||||
|
||||
void FSpatialAnchorPlugin::Unregister()
|
||||
{
|
||||
#if HL_ANCHOR_STORE_AVAILABLE
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_spatialAnchorStoreLock);
|
||||
if (m_spatialAnchorStoreAsyncOperation != nullptr && m_spatialAnchorStoreAsyncOperation.Status() != winrt::Windows::Foundation::AsyncStatus::Completed)
|
||||
{
|
||||
m_spatialAnchorStoreAsyncOperation.Cancel();
|
||||
}
|
||||
m_spatialAnchorStore = nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
IModularFeatures::Get().UnregisterModularFeature(GetModularFeatureName(), this);
|
||||
}
|
||||
|
||||
bool FSpatialAnchorPlugin::GetRequiredExtensions(TArray<const ANSICHAR*>& OutExtensions)
|
||||
{
|
||||
OutExtensions.Add(XR_MSFT_SPATIAL_ANCHOR_EXTENSION_NAME);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool FSpatialAnchorPlugin::GetOptionalExtensions(TArray<const ANSICHAR*>& OutExtensions)
|
||||
{
|
||||
#if HL_ANCHOR_STORE_AVAILABLE
|
||||
OutExtensions.Add(XR_MSFT_PERCEPTION_ANCHOR_INTEROP_PREVIEW_EXTENSION_NAME);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
const void* FSpatialAnchorPlugin::OnCreateSession(XrInstance InInstance, XrSystemId InSystem, const void* InNext)
|
||||
{
|
||||
XR_ENSURE(xrGetInstanceProcAddr(InInstance, "xrCreateSpatialAnchorMSFT", (PFN_xrVoidFunction*) &xrCreateSpatialAnchorMSFT));
|
||||
XR_ENSURE(xrGetInstanceProcAddr(InInstance, "xrCreateSpatialAnchorSpaceMSFT",(PFN_xrVoidFunction*) &xrCreateSpatialAnchorSpaceMSFT));
|
||||
XR_ENSURE(xrGetInstanceProcAddr(InInstance, "xrDestroySpatialAnchorMSFT", (PFN_xrVoidFunction*) &xrDestroySpatialAnchorMSFT));
|
||||
|
||||
#if HL_ANCHOR_STORE_AVAILABLE
|
||||
bIsLocalAnchorStoreSupported = IOpenXRHMDPlugin::Get().IsExtensionEnabled(XR_MSFT_PERCEPTION_ANCHOR_INTEROP_PREVIEW_EXTENSION_NAME);
|
||||
|
||||
if (bIsLocalAnchorStoreSupported)
|
||||
{
|
||||
XR_ENSURE(xrGetInstanceProcAddr(InInstance, "xrCreateSpatialAnchorFromPerceptionAnchorMSFT", (PFN_xrVoidFunction*) &xrCreateSpatialAnchorFromPerceptionAnchorMSFT));
|
||||
XR_ENSURE(xrGetInstanceProcAddr(InInstance, "xrTryGetPerceptionAnchorFromSpatialAnchorMSFT", (PFN_xrVoidFunction*) &xrTryGetPerceptionAnchorFromSpatialAnchorMSFT));
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_spatialAnchorStoreLock);
|
||||
m_spatialAnchorStoreAsyncOperation = SpatialAnchorManager::RequestStoreAsync();
|
||||
m_spatialAnchorStoreAsyncOperation.Completed([this](IAsyncOperation<SpatialAnchorStore> asyncOperation, AsyncStatus status)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_spatialAnchorStoreLock);
|
||||
if (asyncOperation.Status() == AsyncStatus::Completed)
|
||||
{
|
||||
m_spatialAnchorStore = asyncOperation.GetResults();
|
||||
}
|
||||
|
||||
m_spatialAnchorStoreAsyncOperation = nullptr;
|
||||
});
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return InNext;
|
||||
}
|
||||
|
||||
IOpenXRCustomAnchorSupport* FSpatialAnchorPlugin::GetCustomAnchorSupport()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
bool FSpatialAnchorPlugin::OnPinComponent(class UARPin* NewPin, XrSession InSession, XrSpace TrackingSpace, XrTime DisplayTime, float worldToMeterScale)
|
||||
{
|
||||
XrResult result;
|
||||
|
||||
XrSpatialAnchorCreateInfoMSFT AnchorCreateDesc = {};
|
||||
AnchorCreateDesc.type = XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_MSFT;
|
||||
AnchorCreateDesc.next = nullptr;
|
||||
AnchorCreateDesc.pose = ToXrPose(NewPin->GetLocalToTrackingTransform(), worldToMeterScale);
|
||||
AnchorCreateDesc.space = TrackingSpace;
|
||||
AnchorCreateDesc.time = DisplayTime;
|
||||
|
||||
XrSpatialAnchorMSFT AnchorId = {};
|
||||
result = xrCreateSpatialAnchorMSFT(InSession, &AnchorCreateDesc, &AnchorId);
|
||||
|
||||
if (XR_FAILED(result))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
XrSpatialAnchorSpaceCreateInfoMSFT AnchorSpaceCreateDesc = {};
|
||||
AnchorSpaceCreateDesc.type = XR_TYPE_SPATIAL_ANCHOR_SPACE_CREATE_INFO_MSFT;
|
||||
AnchorSpaceCreateDesc.next = nullptr;
|
||||
AnchorSpaceCreateDesc.poseInAnchorSpace = ToXrPose(FTransform::Identity);
|
||||
AnchorSpaceCreateDesc.anchor = AnchorId;
|
||||
|
||||
XrSpace AnchorSpace = {};
|
||||
result = xrCreateSpatialAnchorSpaceMSFT(InSession, &AnchorSpaceCreateDesc, &AnchorSpace);
|
||||
|
||||
if (XR_FAILED(result))
|
||||
{
|
||||
xrDestroySpatialAnchorMSFT(AnchorId);
|
||||
return false;
|
||||
}
|
||||
|
||||
SAnchorMSFT* AnchorMSFT = new SAnchorMSFT;
|
||||
AnchorMSFT->AnchorId = AnchorId;
|
||||
AnchorMSFT->Space = AnchorSpace;
|
||||
|
||||
NewPin->SetNativeResource(reinterpret_cast<void*>(AnchorMSFT));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FSpatialAnchorPlugin::OnRemovePin(class UARPin* Pin)
|
||||
{
|
||||
if (void* nativeResource = Pin->GetNativeResource())
|
||||
{
|
||||
SAnchorMSFT* AnchorMSFT = reinterpret_cast<SAnchorMSFT*>(nativeResource);
|
||||
xrDestroySpatialAnchorMSFT(AnchorMSFT->AnchorId);
|
||||
xrDestroySpace(AnchorMSFT->Space);
|
||||
delete AnchorMSFT;
|
||||
}
|
||||
}
|
||||
|
||||
void FSpatialAnchorPlugin::OnUpdatePin(class UARPin* Pin, XrSession InSession, XrSpace TrackingSpace, XrTime DisplayTime, float worldToMeterScale)
|
||||
{
|
||||
if (void* nativeResource = Pin->GetNativeResource())
|
||||
{
|
||||
SAnchorMSFT* AnchorMSFT = reinterpret_cast<SAnchorMSFT*>(nativeResource);
|
||||
XrResult result;
|
||||
XrSpaceLocation SpaceLocation = {};
|
||||
SpaceLocation.type = XR_TYPE_SPACE_LOCATION;
|
||||
SpaceLocation.next = nullptr;
|
||||
|
||||
result = xrLocateSpace(AnchorMSFT->Space, TrackingSpace, DisplayTime, &SpaceLocation);
|
||||
|
||||
const XrSpaceLocationFlags ValidFlags = XR_SPACE_LOCATION_ORIENTATION_VALID_BIT | XR_SPACE_LOCATION_POSITION_VALID_BIT;
|
||||
if (XR_SUCCEEDED(result) && ((SpaceLocation.locationFlags & ValidFlags) == ValidFlags))
|
||||
{
|
||||
FTransform Transform = ToFTransform(SpaceLocation.pose, worldToMeterScale);
|
||||
|
||||
Pin->OnTransformUpdated(Transform);
|
||||
Pin->OnTrackingStateChanged(EARTrackingState::Tracking);
|
||||
}
|
||||
else
|
||||
{
|
||||
Pin->OnTrackingStateChanged(EARTrackingState::NotTracking);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool FSpatialAnchorPlugin::IsLocalPinSaveSupported() const
|
||||
{
|
||||
return bIsLocalAnchorStoreSupported;
|
||||
}
|
||||
|
||||
bool FSpatialAnchorPlugin::ArePinsReadyToLoad()
|
||||
{
|
||||
#if HL_ANCHOR_STORE_AVAILABLE
|
||||
std::lock_guard<std::mutex> lock(m_spatialAnchorStoreLock);
|
||||
return m_spatialAnchorStore != nullptr;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void FSpatialAnchorPlugin::LoadARPins(XrSession InSession, TFunction<UARPin*(FName)> OnCreatePin)
|
||||
{
|
||||
#if HL_ANCHOR_STORE_AVAILABLE
|
||||
std::lock_guard<std::mutex> lock(m_spatialAnchorStoreLock);
|
||||
if (m_spatialAnchorStore == nullptr) { return; }
|
||||
|
||||
for(auto p : m_spatialAnchorStore.GetAllSavedAnchors())
|
||||
{
|
||||
XrResult result;
|
||||
|
||||
auto name = p.Key();
|
||||
auto wmrAnchor = p.Value();
|
||||
|
||||
auto NewPin = OnCreatePin(FName(name.c_str()));
|
||||
|
||||
if (NewPin == nullptr)
|
||||
{
|
||||
//the anchor is already loaded
|
||||
continue;
|
||||
}
|
||||
|
||||
XrSpatialAnchorMSFT AnchorId = {};
|
||||
result = xrCreateSpatialAnchorFromPerceptionAnchorMSFT(InSession, winrt::get_unknown(wmrAnchor), &AnchorId);
|
||||
|
||||
if (XR_FAILED(result))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
XrSpatialAnchorSpaceCreateInfoMSFT AnchorSpaceCreateDesc = {};
|
||||
AnchorSpaceCreateDesc.type = XR_TYPE_SPATIAL_ANCHOR_SPACE_CREATE_INFO_MSFT;
|
||||
AnchorSpaceCreateDesc.next = nullptr;
|
||||
AnchorSpaceCreateDesc.poseInAnchorSpace = ToXrPose(FTransform::Identity);
|
||||
AnchorSpaceCreateDesc.anchor = AnchorId;
|
||||
|
||||
XrSpace AnchorSpace = {};
|
||||
result = xrCreateSpatialAnchorSpaceMSFT(InSession, &AnchorSpaceCreateDesc, &AnchorSpace);
|
||||
|
||||
if (XR_FAILED(result))
|
||||
{
|
||||
xrDestroySpatialAnchorMSFT(AnchorId);
|
||||
continue;
|
||||
}
|
||||
|
||||
SAnchorMSFT* AnchorMSFT = new SAnchorMSFT;
|
||||
AnchorMSFT->AnchorId = AnchorId;
|
||||
AnchorMSFT->Space = AnchorSpace;
|
||||
|
||||
NewPin->SetNativeResource(reinterpret_cast<void*>(AnchorMSFT));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool FSpatialAnchorPlugin::SaveARPin(XrSession InSession, FName InName, UARPin* Pin)
|
||||
{
|
||||
#if HL_ANCHOR_STORE_AVAILABLE
|
||||
std::lock_guard<std::mutex> lock(m_spatialAnchorStoreLock);
|
||||
if (m_spatialAnchorStore == nullptr) { return false; }
|
||||
|
||||
void* nativeResource = Pin->GetNativeResource();
|
||||
if (nativeResource == nullptr) { return false; }
|
||||
|
||||
SAnchorMSFT* AnchorMSFT = reinterpret_cast<SAnchorMSFT*>(nativeResource);
|
||||
XrResult result;
|
||||
|
||||
SpatialAnchor wmrAnchor = nullptr;
|
||||
result = xrTryGetPerceptionAnchorFromSpatialAnchorMSFT(InSession, AnchorMSFT->AnchorId, reinterpret_cast<::IUnknown**>(winrt::put_abi(wmrAnchor)));
|
||||
if (XR_FAILED(result))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const FString SaveId = InName.ToString().ToLower();
|
||||
return m_spatialAnchorStore.TrySave(*SaveId, wmrAnchor);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void FSpatialAnchorPlugin::RemoveSavedARPin(XrSession InSession, FName InName)
|
||||
{
|
||||
#if HL_ANCHOR_STORE_AVAILABLE
|
||||
std::lock_guard<std::mutex> lock(m_spatialAnchorStoreLock);
|
||||
if (m_spatialAnchorStore == nullptr) { return; }
|
||||
|
||||
const FString SaveId = InName.ToString().ToLower();
|
||||
m_spatialAnchorStore.Remove(*SaveId);
|
||||
#endif
|
||||
}
|
||||
|
||||
void FSpatialAnchorPlugin::RemoveAllSavedARPins(XrSession InSession)
|
||||
{
|
||||
#if HL_ANCHOR_STORE_AVAILABLE
|
||||
std::lock_guard<std::mutex> lock(m_spatialAnchorStoreLock);
|
||||
if (m_spatialAnchorStore == nullptr) { return; }
|
||||
|
||||
m_spatialAnchorStore.Clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace MicrosoftOpenXR
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
#pragma once
|
||||
|
||||
#include "OpenXRCommon.h"
|
||||
|
||||
#if (PLATFORM_WINDOWS || PLATFORM_HOLOLENS)
|
||||
#include "Windows/AllowWindowsPlatformTypes.h"
|
||||
#include "Windows/AllowWindowsPlatformAtomics.h"
|
||||
#include "Windows/PreWindowsApi.h"
|
||||
|
||||
#include <unknwn.h>
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
#include <winrt/Windows.Perception.Spatial.h>
|
||||
#include <winrt/Windows.Foundation.Collections.h>
|
||||
|
||||
#include "Windows/PostWindowsApi.h"
|
||||
#include "Windows/HideWindowsPlatformAtomics.h"
|
||||
#include "Windows/HideWindowsPlatformTypes.h"
|
||||
|
||||
#include <mutex>
|
||||
#define HL_ANCHOR_STORE_AVAILABLE 1
|
||||
#else
|
||||
#define HL_ANCHOR_STORE_AVAILABLE 0
|
||||
#endif
|
||||
|
||||
namespace MicrosoftOpenXR
|
||||
{
|
||||
class FSpatialAnchorPlugin : public IOpenXRExtensionPlugin, public IOpenXRCustomAnchorSupport
|
||||
{
|
||||
public:
|
||||
void Register();
|
||||
void Unregister();
|
||||
|
||||
virtual bool GetRequiredExtensions(TArray<const ANSICHAR*>& OutExtensions) override;
|
||||
|
||||
virtual bool GetOptionalExtensions(TArray<const ANSICHAR*>& OutExtensions) override;
|
||||
|
||||
virtual const void* OnCreateSession(XrInstance InInstance, XrSystemId InSystem, const void* InNext) override;
|
||||
|
||||
virtual IOpenXRCustomAnchorSupport* GetCustomAnchorSupport() override;
|
||||
|
||||
virtual bool OnPinComponent(class UARPin* Pin, XrSession InSession, XrSpace TrackingSpace, XrTime DisplayTime, float worldToMeterScale) override;
|
||||
|
||||
virtual void OnRemovePin(class UARPin* Pin) override;
|
||||
|
||||
virtual void OnUpdatePin(class UARPin* Pin, XrSession InSession, XrSpace TrackingSpace, XrTime DisplayTime, float worldToMeterScale) override;
|
||||
|
||||
virtual bool IsLocalPinSaveSupported() const override;
|
||||
|
||||
virtual bool ArePinsReadyToLoad() override;
|
||||
|
||||
virtual void LoadARPins(XrSession InSession, TFunction<UARPin*(FName)> OnCreatePin) override;
|
||||
|
||||
virtual bool SaveARPin(XrSession InSession, FName InName, UARPin* InPin) override;
|
||||
|
||||
virtual void RemoveSavedARPin(XrSession InSession, FName InName) override;
|
||||
|
||||
virtual void RemoveAllSavedARPins(XrSession InSession) override;
|
||||
|
||||
private:
|
||||
|
||||
struct SAnchorMSFT
|
||||
{
|
||||
XrSpatialAnchorMSFT AnchorId;
|
||||
XrSpace Space;
|
||||
};
|
||||
|
||||
PFN_xrCreateSpatialAnchorMSFT xrCreateSpatialAnchorMSFT;
|
||||
PFN_xrDestroySpatialAnchorMSFT xrDestroySpatialAnchorMSFT;
|
||||
PFN_xrCreateSpatialAnchorSpaceMSFT xrCreateSpatialAnchorSpaceMSFT;
|
||||
|
||||
bool bIsLocalAnchorStoreSupported = false;
|
||||
|
||||
#if HL_ANCHOR_STORE_AVAILABLE
|
||||
PFN_xrCreateSpatialAnchorFromPerceptionAnchorMSFT xrCreateSpatialAnchorFromPerceptionAnchorMSFT;
|
||||
PFN_xrTryGetPerceptionAnchorFromSpatialAnchorMSFT xrTryGetPerceptionAnchorFromSpatialAnchorMSFT;
|
||||
|
||||
std::mutex m_spatialAnchorStoreLock;
|
||||
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::Perception::Spatial::SpatialAnchorStore> m_spatialAnchorStoreAsyncOperation;
|
||||
winrt::Windows::Perception::Spatial::SpatialAnchorStore m_spatialAnchorStore{ nullptr };
|
||||
#endif
|
||||
|
||||
};
|
||||
} // namespace MicrosoftOpenXR
|
|
@ -0,0 +1,570 @@
|
|||
#if PLATFORM_WINDOWS || PLATFORM_HOLOLENS
|
||||
#include "SpatialMappingPlugin.h"
|
||||
#include "ARBlueprintLibrary.h"
|
||||
|
||||
using namespace winrt::Windows::Perception::Spatial;
|
||||
using namespace winrt::Windows::Perception::Spatial::Surfaces;
|
||||
|
||||
namespace MicrosoftOpenXR
|
||||
{
|
||||
void FSpatialMappingPlugin::Register()
|
||||
{
|
||||
IModularFeatures::Get().RegisterModularFeature(IOpenXRExtensionPlugin::GetModularFeatureName(), static_cast<IOpenXRExtensionPlugin*>(this));
|
||||
|
||||
MeshOptions = SpatialSurfaceMeshOptions();
|
||||
MeshOptions.IncludeVertexNormals(false);
|
||||
MeshOptions.VertexPositionFormat(winrt::Windows::Graphics::DirectX::DirectXPixelFormat::R16G16B16A16IntNormalized);
|
||||
MeshOptions.TriangleIndexFormat(winrt::Windows::Graphics::DirectX::DirectXPixelFormat::R16UInt);
|
||||
}
|
||||
|
||||
void FSpatialMappingPlugin::Unregister()
|
||||
{
|
||||
IModularFeatures::Get().UnregisterModularFeature(IOpenXRExtensionPlugin::GetModularFeatureName(), static_cast<IOpenXRExtensionPlugin*>(this));
|
||||
|
||||
StopMeshObserver();
|
||||
AnchorLocalizationData.clear();
|
||||
}
|
||||
|
||||
bool FSpatialMappingPlugin::GetRequiredExtensions(TArray<const ANSICHAR*>& OutExtensions)
|
||||
{
|
||||
OutExtensions.Add(XR_MSFT_PERCEPTION_ANCHOR_INTEROP_PREVIEW_EXTENSION_NAME);
|
||||
OutExtensions.Add(XR_MSFT_SPATIAL_ANCHOR_EXTENSION_NAME);
|
||||
return true;
|
||||
}
|
||||
|
||||
const void* FSpatialMappingPlugin::OnCreateSession(XrInstance InInstance, XrSystemId InSystem, const void* InNext)
|
||||
{
|
||||
XR_ENSURE(xrGetInstanceProcAddr(InInstance, "xrCreateSpatialAnchorFromPerceptionAnchorMSFT", (PFN_xrVoidFunction*)&xrCreateSpatialAnchorFromPerceptionAnchorMSFT));
|
||||
XR_ENSURE(xrGetInstanceProcAddr(InInstance, "xrCreateSpatialAnchorSpaceMSFT", (PFN_xrVoidFunction*)&xrCreateSpatialAnchorSpaceMSFT));
|
||||
XR_ENSURE(xrGetInstanceProcAddr(InInstance, "xrDestroySpatialAnchorMSFT", (PFN_xrVoidFunction*)&xrDestroySpatialAnchorMSFT));
|
||||
|
||||
return InNext;
|
||||
}
|
||||
|
||||
const void* FSpatialMappingPlugin::OnBeginSession(XrSession InSession, const void* InNext)
|
||||
{
|
||||
static FName SystemName(TEXT("OpenXR"));
|
||||
if (GEngine->XRSystem.IsValid() && (GEngine->XRSystem->GetSystemName() == SystemName))
|
||||
{
|
||||
XRTrackingSystem = GEngine->XRSystem.Get();
|
||||
}
|
||||
else
|
||||
{
|
||||
return InNext;
|
||||
}
|
||||
|
||||
if (IOpenXRARModule::IsAvailable())
|
||||
{
|
||||
TrackedMeshHolder = IOpenXRARModule::Get().GetTrackedMeshHolder();
|
||||
}
|
||||
else
|
||||
{
|
||||
return InNext;
|
||||
}
|
||||
|
||||
return InNext;
|
||||
}
|
||||
|
||||
bool FSpatialMappingPlugin::FindMeshTransform(XrSpace AnchorSpace, XrTime DisplayTime, XrSpace TrackingSpace, FTransform MeshToCachedAnchorTransform, FTransform& Transform, bool& IsTracking)
|
||||
{
|
||||
auto Scale = XRTrackingSystem->GetWorldToMetersScale();
|
||||
|
||||
IsTracking = false;
|
||||
|
||||
XrSpaceLocation SpaceLocation{ XR_TYPE_SPACE_LOCATION };
|
||||
XrResult result = xrLocateSpace(AnchorSpace, TrackingSpace, DisplayTime, &SpaceLocation);
|
||||
if (XR_FAILED(result))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
constexpr XrSpaceLocationFlags TrackingFlags =
|
||||
XR_SPACE_LOCATION_ORIENTATION_TRACKED_BIT | XR_SPACE_LOCATION_POSITION_TRACKED_BIT;
|
||||
IsTracking = ((SpaceLocation.locationFlags & TrackingFlags) == TrackingFlags);
|
||||
|
||||
constexpr XrSpaceLocationFlags ValidFlags =
|
||||
XR_SPACE_LOCATION_ORIENTATION_VALID_BIT | XR_SPACE_LOCATION_POSITION_VALID_BIT;
|
||||
if ((SpaceLocation.locationFlags & ValidFlags) == ValidFlags)
|
||||
{
|
||||
FTransform LocalToTrackingTransform = ToFTransform(SpaceLocation.pose, Scale);
|
||||
Transform = MeshToCachedAnchorTransform * LocalToTrackingTransform;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool FSpatialMappingPlugin::GetTransformBetweenCoordinateSystems(SpatialCoordinateSystem From, SpatialCoordinateSystem To, FTransform& Transform)
|
||||
{
|
||||
auto transform = From.TryGetTransformTo(To);
|
||||
if (!transform)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto meshToCached = transform.Value();
|
||||
Transform = WMRUtility::FromMixedRealityTransform(DirectX::XMLoadFloat4x4(&meshToCached), XRTrackingSystem->GetWorldToMetersScale());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FSpatialMappingPlugin::LocateSpatialMeshInTrackingSpace(const FGuid& MeshID, SpatialCoordinateSystem MeshCoordinateSystem, XrSession InSession, XrTime DisplayTime, XrSpace TrackingSpace, FTransform& Transform)
|
||||
{
|
||||
bool IsTracking = false;
|
||||
|
||||
// If a known anchor exists for this mesh, use it to locate the mesh transform.
|
||||
auto cachedAnchorDataForThisMesh = AnchorLocalizationData.find(MeshID);
|
||||
if (cachedAnchorDataForThisMesh != AnchorLocalizationData.end())
|
||||
{
|
||||
// This mesh could be localizing against another mesh's coordinate system, apply the offset when getting the mesh transform.
|
||||
FTransform MeshToCachedAnchorTransform;
|
||||
if (GetTransformBetweenCoordinateSystems(MeshCoordinateSystem, cachedAnchorDataForThisMesh->second->CoordinateSystem, MeshToCachedAnchorTransform)
|
||||
&& FindMeshTransform(cachedAnchorDataForThisMesh->second->AnchorSpace, DisplayTime, TrackingSpace, MeshToCachedAnchorTransform, Transform, IsTracking))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// If we failed to locate the mesh and are not tracking, return now. The meshes will continue to be localized after tracking is regained.
|
||||
// However, if we do have tracking, we may have a new coordinate system for the mesh and should recreate it's localization data.
|
||||
if (!IsTracking)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// If we get here with cached anchor data, The device is tracking, but we have failed to localize against the known cached data.
|
||||
// This likely happens when the mesh gets a new coordinate system. Skip looking at existing localization data, and create new data instead.
|
||||
if (cachedAnchorDataForThisMesh == AnchorLocalizationData.end())
|
||||
{
|
||||
// Otherwise, attempt to locate the SpatialSurfaceMesh from all cached coordinate systems.
|
||||
for (const auto& data : AnchorLocalizationData)
|
||||
{
|
||||
// This mesh will be localizing against another mesh's coordinate system, apply the offset when getting the mesh transform.
|
||||
FTransform MeshToCachedAnchorTransform;
|
||||
if (!GetTransformBetweenCoordinateSystems(MeshCoordinateSystem, data.second->CoordinateSystem, MeshToCachedAnchorTransform))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (FindMeshTransform(data.second->AnchorSpace, DisplayTime, TrackingSpace, MeshToCachedAnchorTransform, Transform, IsTracking))
|
||||
{
|
||||
// Add to the anchor localization data, so the next time this mesh is updated it will have a direct entry in the map.
|
||||
AnchorLocalizationData.insert({ MeshID, data.second });
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If no cached anchor exists that can locate the mesh, create a new anchor.
|
||||
// Each spatial mapping mesh is positioned in its own space relative to its coordinate system.
|
||||
// Create a temporary anchor at this coordinate system to localize against the TrackingSpace.
|
||||
auto wmrAnchor = SpatialAnchor::TryCreateRelativeTo(MeshCoordinateSystem);
|
||||
if (wmrAnchor == nullptr)
|
||||
{
|
||||
// If the new anchor and all cached anchors cannot find the mesh, ignore it.
|
||||
return false;
|
||||
}
|
||||
|
||||
XrSpatialAnchorMSFT Anchor;
|
||||
XrResult result = xrCreateSpatialAnchorFromPerceptionAnchorMSFT(InSession, winrt::get_unknown(wmrAnchor), &Anchor);
|
||||
if (XR_FAILED(result))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
XrSpatialAnchorSpaceCreateInfoMSFT AnchorSpaceCreateDesc{ XR_TYPE_SPATIAL_ANCHOR_SPACE_CREATE_INFO_MSFT };
|
||||
AnchorSpaceCreateDesc.poseInAnchorSpace = ToXrPose(FTransform::Identity);
|
||||
AnchorSpaceCreateDesc.anchor = Anchor;
|
||||
|
||||
bool MeshLocated = true;
|
||||
XrSpace AnchorSpace = XR_NULL_HANDLE;
|
||||
result = xrCreateSpatialAnchorSpaceMSFT(InSession, &AnchorSpaceCreateDesc, &AnchorSpace);
|
||||
if (XR_FAILED(result))
|
||||
{
|
||||
wmrAnchor = nullptr;
|
||||
xrDestroySpatialAnchorMSFT(Anchor);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!FindMeshTransform(AnchorSpace, DisplayTime, TrackingSpace, FTransform::Identity, Transform, IsTracking))
|
||||
{
|
||||
// The mesh has not been located in tracking space, but this anchor space may still be locatable in the future.
|
||||
MeshLocated = false;
|
||||
}
|
||||
|
||||
if (cachedAnchorDataForThisMesh == AnchorLocalizationData.end())
|
||||
{
|
||||
// Cache localization data to compare against future meshes.
|
||||
AnchorLocalizationData.insert({ MeshID, MakeShared<WMRAnchorLocalizationData>(AnchorSpace, MeshCoordinateSystem) });
|
||||
}
|
||||
else
|
||||
{
|
||||
// Overwrite existing anchor localization data.
|
||||
cachedAnchorDataForThisMesh->second = MakeShared<WMRAnchorLocalizationData>(AnchorSpace, MeshCoordinateSystem);
|
||||
}
|
||||
|
||||
if (AnchorLocalizationData.size() > WarnAfterThisManyMeshes)
|
||||
{
|
||||
UE_LOG(LogHMD, Warning,
|
||||
TEXT("Spatial mapping has recognized %d spaces, performance may be impacted."),
|
||||
AnchorLocalizationData.size());
|
||||
}
|
||||
|
||||
wmrAnchor = nullptr;
|
||||
xrDestroySpatialAnchorMSFT(Anchor);
|
||||
return MeshLocated;
|
||||
}
|
||||
|
||||
void FSpatialMappingPlugin::UpdateDeviceLocations(XrSession InSession, XrTime DisplayTime, XrSpace TrackingSpace)
|
||||
{
|
||||
std::map<FGuid, MeshLocalizationData>::iterator Mesh;
|
||||
|
||||
{
|
||||
// Since UpdateDeviceLocations is performed on the game thread, update a single spatial mesh per frame.
|
||||
// This prevents stalls when the number of spatial meshes is large.
|
||||
std::lock_guard<std::mutex> lock(MeshRefsLock);
|
||||
if (UniqueMeshes.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (MeshToLocalizeThisFrame >= UniqueMeshes.size())
|
||||
{
|
||||
MeshToLocalizeThisFrame = 0;
|
||||
}
|
||||
|
||||
Mesh = UniqueMeshes.begin();
|
||||
std::advance(Mesh, MeshToLocalizeThisFrame);
|
||||
MeshToLocalizeThisFrame++;
|
||||
|
||||
if (Mesh == UniqueMeshes.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
auto MeshUpdate = new FOpenXRMeshUpdate;
|
||||
MeshUpdate->TrackingState = EARTrackingState::Tracking;
|
||||
|
||||
FTransform Transform;
|
||||
if (!LocateSpatialMeshInTrackingSpace(Mesh->first, Mesh->second.CoordinateSystem, InSession, DisplayTime, TrackingSpace, Transform))
|
||||
{
|
||||
Transform = Mesh->second.LastKnownTransform;
|
||||
MeshUpdate->TrackingState = EARTrackingState::NotTracking;
|
||||
}
|
||||
|
||||
Mesh->second.LastKnownTrackingState = MeshUpdate->TrackingState;
|
||||
|
||||
MeshUpdate->Id = Mesh->first;
|
||||
MeshUpdate->LocalToTrackingTransform = Transform;
|
||||
TrackedMeshHolder->ObjectUpdated(MeshUpdate);
|
||||
|
||||
Mesh->second.LastKnownTransform = Transform;
|
||||
|
||||
// Move the SurfaceObserver's bounding volume so it is always centered on the user.
|
||||
UpdateBoundingVolume();
|
||||
}
|
||||
|
||||
IOpenXRCustomCaptureSupport* FSpatialMappingPlugin::GetCustomCaptureSupport(const EARCaptureType CaptureType)
|
||||
{
|
||||
if (CaptureType == EARCaptureType::SpatialMapping)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool FSpatialMappingPlugin::OnToggleARCapture(const bool On)
|
||||
{
|
||||
if (On)
|
||||
{
|
||||
if (!bGenerateSRMeshes)
|
||||
{
|
||||
UE_LOG(LogHMD, Log, TEXT("Must enable Generate Mesh Data From Tracked Geometry in the ARSessionConfig to use Spatial Mapping."));
|
||||
return false;
|
||||
}
|
||||
|
||||
return StartMeshObserver();
|
||||
}
|
||||
else
|
||||
{
|
||||
StopMeshObserver();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Perform a raycast against the spatial meshes.
|
||||
TArray<FARTraceResult> FSpatialMappingPlugin::OnLineTraceTrackedObjects(const TSharedPtr<FARSupportInterface, ESPMode::ThreadSafe> ARCompositionComponent, const FVector Start, const FVector End, const EARLineTraceChannels TraceChannels)
|
||||
{
|
||||
TArray<FARTraceResult> Results;
|
||||
// Iterate over the tracked MeshGeometries rather than UniqueMeshes since the output TraceResult needs the MeshGeometry.
|
||||
TArray<UARMeshGeometry*> Meshes = UARBlueprintLibrary::GetAllGeometriesByClass<UARMeshGeometry>();
|
||||
|
||||
std::lock_guard<std::mutex> lock(MeshRefsLock);
|
||||
for (UARMeshGeometry* Mesh : Meshes)
|
||||
{
|
||||
// Get the saved mesh data from the tracked mesh Guid.
|
||||
const auto& it = UniqueMeshes.find(Mesh->UniqueId);
|
||||
if (it != UniqueMeshes.end())
|
||||
{
|
||||
FVector HitPoint, HitNormal;
|
||||
float HitDistance;
|
||||
if (it->second.CollisionInfo.Collides(Start, End, Mesh->GetLocalToWorldTransform(), HitPoint, HitNormal, HitDistance))
|
||||
{
|
||||
// Append a hit. The calling function will then sort by HitDistance.
|
||||
Results.Add(FARTraceResult(ARCompositionComponent,
|
||||
HitDistance,
|
||||
TraceChannels,
|
||||
FTransform(HitNormal.ToOrientationQuat(), HitPoint),
|
||||
Mesh));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Results;
|
||||
}
|
||||
|
||||
void FSpatialMappingPlugin::OnStartARSession(class UARSessionConfig* SessionConfig)
|
||||
{
|
||||
GConfig->GetFloat(TEXT("/Script/HoloLensPlatformEditor.HoloLensTargetSettings"), TEXT("SpatialMeshingVolumeSize"), VolumeSize, GEngineIni);
|
||||
GConfig->GetFloat(TEXT("/Script/HoloLensPlatformEditor.HoloLensTargetSettings"), TEXT("MaxTrianglesPerCubicMeter"), TriangleDensity, GEngineIni);
|
||||
|
||||
bGenerateSRMeshes = SessionConfig->bGenerateMeshDataFromTrackedGeometry;
|
||||
}
|
||||
|
||||
bool FSpatialMappingPlugin::StartMeshObserver()
|
||||
{
|
||||
if (TrackedMeshHolder == nullptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(MeshRefsLock);
|
||||
if (SurfaceObserver != nullptr ||
|
||||
(RequestAccessOperation != nullptr &&
|
||||
RequestAccessOperation.Status() == winrt::Windows::Foundation::AsyncStatus::Started))
|
||||
{
|
||||
UE_LOG(LogHMD, Log, TEXT("Attempting to call StartMeshObserver more than once."));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!SpatialSurfaceObserver::IsSupported())
|
||||
{
|
||||
UE_LOG(LogHMD, Warning, TEXT("SpatialSurfaceObserver::IsSupported() returned false. No updates will occur."));
|
||||
return false;
|
||||
}
|
||||
|
||||
RequestAccessOperation = SpatialSurfaceObserver::RequestAccessAsync();
|
||||
RequestAccessOperation.Completed([=](auto&& asyncInfo, auto&& asyncStatus)
|
||||
{
|
||||
if (asyncStatus == winrt::Windows::Foundation::AsyncStatus::Completed &&
|
||||
asyncInfo.GetResults() == SpatialPerceptionAccessStatus::Allowed)
|
||||
{
|
||||
SurfaceObserver = SpatialSurfaceObserver();
|
||||
if (SurfaceObserver != nullptr)
|
||||
{
|
||||
UpdateBoundingVolume();
|
||||
|
||||
check(!OnChangeEventToken);
|
||||
OnChangeEventToken = SurfaceObserver.ObservedSurfacesChanged([this](auto&& sender, auto&& obj) { OnSurfacesChanged(sender, obj); });
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogHMD, Warning, TEXT("Failed to create spatial observer. No updates will occur."));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogHMD, Warning, TEXT("User denied permission for spatial mapping. No updates will occur."));
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FSpatialMappingPlugin::StopMeshObserver()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(MeshRefsLock);
|
||||
|
||||
if (SurfaceObserver != nullptr)
|
||||
{
|
||||
SurfaceObserver.ObservedSurfacesChanged(OnChangeEventToken);
|
||||
OnChangeEventToken = winrt::event_token();
|
||||
|
||||
SurfaceObserver = nullptr;
|
||||
}
|
||||
|
||||
if (RequestAccessOperation != nullptr && RequestAccessOperation.Status() != winrt::Windows::Foundation::AsyncStatus::Completed)
|
||||
{
|
||||
RequestAccessOperation.Cancel();
|
||||
}
|
||||
RequestAccessOperation = nullptr;
|
||||
}
|
||||
|
||||
void FSpatialMappingPlugin::UpdateBoundingVolume()
|
||||
{
|
||||
if (SurfaceObserver == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SpatialBoundingBox AABB =
|
||||
{
|
||||
{ 0.0f, 0.0f, 0.0f },
|
||||
{ VolumeSize, VolumeSize, VolumeSize }
|
||||
};
|
||||
|
||||
const auto coordinateSystem = SpatialLocator::GetDefault().CreateStationaryFrameOfReferenceAtCurrentLocation().CoordinateSystem();
|
||||
if (coordinateSystem == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SpatialBoundingVolume BoundingVolume = SpatialBoundingVolume::FromBox(coordinateSystem, AABB);
|
||||
if (BoundingVolume == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SurfaceObserver.SetBoundingVolume(BoundingVolume);
|
||||
}
|
||||
|
||||
void FSpatialMappingPlugin::CopyMeshData(FOpenXRMeshUpdate* MeshUpdate, SpatialSurfaceMesh SurfaceMesh)
|
||||
{
|
||||
int VertexCount = SurfaceMesh.VertexPositions().ElementCount();
|
||||
int IndexCount = SurfaceMesh.TriangleIndices().ElementCount();
|
||||
MeshUpdate->Vertices.AddUninitialized(VertexCount);
|
||||
MeshUpdate->Indices.AddUninitialized(IndexCount);
|
||||
|
||||
FVector MeshScale = WMRUtility::FromMixedRealityScale(SurfaceMesh.VertexPositionScale());
|
||||
|
||||
DirectX::PackedVector::XMSHORTN4* RawVertices = reinterpret_cast<DirectX::PackedVector::XMSHORTN4*>(SurfaceMesh.VertexPositions().Data().data());
|
||||
check(SurfaceMesh.TriangleIndices().Format() == winrt::Windows::Graphics::DirectX::DirectXPixelFormat::R16UInt);
|
||||
unsigned short* RawIndices = reinterpret_cast<unsigned short*>(SurfaceMesh.TriangleIndices().Data().data());
|
||||
|
||||
float Scale = XRTrackingSystem->GetWorldToMetersScale();
|
||||
for (int Index = 0; Index < VertexCount; Index++)
|
||||
{
|
||||
// Match alignment with MeshOptions->VertexPositionFormat
|
||||
DirectX::PackedVector::XMSHORTN4 packedSource = RawVertices[Index];
|
||||
DirectX::XMVECTOR Source = DirectX::PackedVector::XMLoadShortN4(&packedSource);
|
||||
|
||||
MeshUpdate->Vertices[Index] = WMRUtility::FromXMVectorTranslation(Source, Scale) * MeshScale;
|
||||
}
|
||||
|
||||
int TriangleCount = IndexCount / 3;
|
||||
MRMESH_INDEX_TYPE* DestIndices = (MRMESH_INDEX_TYPE*)MeshUpdate->Indices.GetData();
|
||||
|
||||
// Reverse triangle order
|
||||
for (int Index = 0; Index < TriangleCount; Index++)
|
||||
{
|
||||
DestIndices[0] = RawIndices[2];
|
||||
DestIndices[1] = RawIndices[1];
|
||||
DestIndices[2] = RawIndices[0];
|
||||
DestIndices += 3;
|
||||
RawIndices += 3;
|
||||
}
|
||||
}
|
||||
|
||||
void FSpatialMappingPlugin::OnSurfacesChanged(SpatialSurfaceObserver Observer, winrt::Windows::Foundation::IInspectable)
|
||||
{
|
||||
auto SurfaceCollection = Observer.GetObservedSurfaces();
|
||||
std::vector<FGuid> ObservedSurfacesThisUpdate;
|
||||
|
||||
ObservedSurfacesThisUpdate.clear();
|
||||
for (auto Iter = SurfaceCollection.First(); Iter.HasCurrent(); Iter.MoveNext())
|
||||
{
|
||||
auto KVPair = Iter.Current();
|
||||
|
||||
winrt::guid Id = KVPair.Key();
|
||||
FGuid MeshId = WMRUtility::GUIDToFGuid(Id);
|
||||
ObservedSurfacesThisUpdate.push_back(MeshId);
|
||||
SpatialSurfaceInfo SurfaceInfo = KVPair.Value();
|
||||
|
||||
winrt::Windows::Foundation::IAsyncOperation<SpatialSurfaceMesh> ComputeMeshAsyncOperation =
|
||||
SurfaceInfo.TryComputeLatestMeshAsync(TriangleDensity, MeshOptions);
|
||||
if (ComputeMeshAsyncOperation == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ComputeMeshAsyncOperation.Completed([this, MeshId](winrt::Windows::Foundation::IAsyncOperation<SpatialSurfaceMesh> asyncOperation, winrt::Windows::Foundation::AsyncStatus status)
|
||||
{
|
||||
if (asyncOperation.Status() == winrt::Windows::Foundation::AsyncStatus::Completed)
|
||||
{
|
||||
auto SurfaceMesh = asyncOperation.GetResults();
|
||||
if (SurfaceMesh != nullptr)
|
||||
{
|
||||
TrackedMeshHolder->StartMeshUpdates();
|
||||
|
||||
FOpenXRMeshUpdate* MeshUpdate = TrackedMeshHolder->AllocateMeshUpdate(MeshId);
|
||||
MeshUpdate->Type = EARObjectClassification::World;
|
||||
CopyMeshData(MeshUpdate, SurfaceMesh);
|
||||
|
||||
const auto& it = UniqueMeshes.find(MeshId);
|
||||
if (it != UniqueMeshes.end())
|
||||
{
|
||||
// If a mesh entry already existed for this spatial mesh, use the last known transform for the first update to keep it close to where it was previously.
|
||||
MeshUpdate->LocalToTrackingTransform = it->second.LastKnownTransform;
|
||||
MeshUpdate->TrackingState = it->second.LastKnownTrackingState;
|
||||
|
||||
// Update the cached mesh data
|
||||
std::lock_guard<std::mutex> lock(MeshRefsLock);
|
||||
it->second.CoordinateSystem = SurfaceMesh.CoordinateSystem();
|
||||
it->second.CollisionInfo.UpdateVertices(MeshUpdate->Vertices, MeshUpdate->Indices);
|
||||
}
|
||||
else
|
||||
{
|
||||
// This is the first time observing this mesh
|
||||
std::lock_guard<std::mutex> lock(MeshRefsLock);
|
||||
// Guarantee the mesh is not seen until a valid transform has been found.
|
||||
FTransform TempTransform = FTransform::Identity;
|
||||
TempTransform.SetScale3D(FVector::ZeroVector);
|
||||
|
||||
// Don't set the tracking state until the LocalToTrackingTransform is identified in UpdateDeviceLocations.
|
||||
MeshUpdate->TrackingState = EARTrackingState::NotTracking;
|
||||
|
||||
UniqueMeshes.insert({ MeshId, MeshLocalizationData {
|
||||
TempTransform,
|
||||
EARTrackingState::NotTracking,
|
||||
SurfaceMesh.CoordinateSystem(),
|
||||
TrackedGeometryCollision(MeshUpdate->Vertices, MeshUpdate->Indices)
|
||||
} });
|
||||
|
||||
MeshUpdate->LocalToTrackingTransform = TempTransform;
|
||||
}
|
||||
|
||||
TrackedMeshHolder->EndMeshUpdates();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Check for removed meshes
|
||||
if (UniqueMeshes.size() != ObservedSurfacesThisUpdate.size())
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(MeshRefsLock);
|
||||
|
||||
std::vector<FGuid> MeshesToRemove = std::vector<FGuid>();
|
||||
for (const auto& Mesh : UniqueMeshes)
|
||||
{
|
||||
if (std::find(ObservedSurfacesThisUpdate.begin(),
|
||||
ObservedSurfacesThisUpdate.end(), Mesh.first) == ObservedSurfacesThisUpdate.end())
|
||||
{
|
||||
// Cached mesh was not found, it has been disposed.
|
||||
MeshesToRemove.push_back(Mesh.first);
|
||||
|
||||
TrackedMeshHolder->RemoveMesh(Mesh.first);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& MeshId : MeshesToRemove)
|
||||
{
|
||||
UniqueMeshes.erase(MeshId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif //PLATFORM_WINDOWS || PLATFORM_HOLOLENS
|
|
@ -0,0 +1,124 @@
|
|||
#pragma once
|
||||
|
||||
#if PLATFORM_WINDOWS || PLATFORM_HOLOLENS
|
||||
#include "OpenXRCommon.h"
|
||||
#include "OpenXRCore.h"
|
||||
#include "IOpenXRARModule.h"
|
||||
#include "IOpenXRARTrackedGeometryHolder.h"
|
||||
|
||||
#include "HeadMountedDisplayTypes.h"
|
||||
#include "ARTypes.h"
|
||||
#include "ARSessionConfig.h"
|
||||
#include "WindowsMixedRealityInteropUtility.h"
|
||||
|
||||
#include "IXRTrackingSystem.h"
|
||||
#include "TrackedGeometryCollision.h"
|
||||
|
||||
#include "Windows/AllowWindowsPlatformTypes.h"
|
||||
#include "Windows/AllowWindowsPlatformAtomics.h"
|
||||
#include "Windows/PreWindowsApi.h"
|
||||
|
||||
#include <mutex>
|
||||
#include <map>
|
||||
|
||||
#include <winrt/windows.foundation.Collections.h>
|
||||
|
||||
#include <DirectXMath.h>
|
||||
#include <DirectXPackedVector.h>
|
||||
|
||||
#include <unknwn.h>
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
#include <winrt/Windows.Perception.Spatial.h>
|
||||
#include <winrt/Windows.Perception.Spatial.Surfaces.h>
|
||||
|
||||
#include "Windows/PostWindowsApi.h"
|
||||
#include "Windows/HideWindowsPlatformAtomics.h"
|
||||
#include "Windows/HideWindowsPlatformTypes.h"
|
||||
|
||||
namespace MicrosoftOpenXR
|
||||
{
|
||||
class WMRAnchorLocalizationData : public TSharedFromThis<WMRAnchorLocalizationData>
|
||||
{
|
||||
public:
|
||||
WMRAnchorLocalizationData(XrSpace AnchorSpace, winrt::Windows::Perception::Spatial::SpatialCoordinateSystem CoordinateSystem)
|
||||
: AnchorSpace(AnchorSpace)
|
||||
, CoordinateSystem(CoordinateSystem)
|
||||
{
|
||||
}
|
||||
|
||||
~WMRAnchorLocalizationData()
|
||||
{
|
||||
xrDestroySpace(AnchorSpace);
|
||||
}
|
||||
|
||||
XrSpace AnchorSpace;
|
||||
winrt::Windows::Perception::Spatial::SpatialCoordinateSystem CoordinateSystem;
|
||||
};
|
||||
|
||||
struct MeshLocalizationData
|
||||
{
|
||||
FTransform LastKnownTransform = FTransform::Identity;
|
||||
EARTrackingState LastKnownTrackingState = EARTrackingState::NotTracking;
|
||||
winrt::Windows::Perception::Spatial::SpatialCoordinateSystem CoordinateSystem;
|
||||
TrackedGeometryCollision CollisionInfo;
|
||||
};
|
||||
|
||||
class FSpatialMappingPlugin : public IOpenXRExtensionPlugin, public IOpenXRCustomCaptureSupport
|
||||
{
|
||||
public:
|
||||
void Register();
|
||||
void Unregister();
|
||||
|
||||
bool GetRequiredExtensions(TArray<const ANSICHAR*>& OutExtensions) override;
|
||||
|
||||
const void* OnCreateSession(XrInstance InInstance, XrSystemId InSystem, const void* InNext) override;
|
||||
const void* OnBeginSession(XrSession InSession, const void* InNext) override;
|
||||
void UpdateDeviceLocations(XrSession InSession, XrTime DisplayTime, XrSpace TrackingSpace) override;
|
||||
|
||||
bool OnToggleARCapture(const bool On) override;
|
||||
TArray<FARTraceResult> OnLineTraceTrackedObjects(const TSharedPtr<FARSupportInterface, ESPMode::ThreadSafe> ARCompositionComponent, const FVector Start, const FVector End, const EARLineTraceChannels TraceChannels) override;
|
||||
IOpenXRCustomCaptureSupport* GetCustomCaptureSupport(const EARCaptureType CaptureType) override;
|
||||
|
||||
private:
|
||||
std::mutex MeshRefsLock;
|
||||
winrt::event_token OnChangeEventToken;
|
||||
|
||||
bool bGenerateSRMeshes = false;
|
||||
float VolumeSize = 20.0f;
|
||||
float TriangleDensity = 500.0f;
|
||||
unsigned int MeshToLocalizeThisFrame = 0;
|
||||
|
||||
const int WarnAfterThisManyMeshes = 100;
|
||||
|
||||
PFN_xrCreateSpatialAnchorFromPerceptionAnchorMSFT xrCreateSpatialAnchorFromPerceptionAnchorMSFT;
|
||||
PFN_xrCreateSpatialAnchorSpaceMSFT xrCreateSpatialAnchorSpaceMSFT;
|
||||
PFN_xrDestroySpatialAnchorMSFT xrDestroySpatialAnchorMSFT;
|
||||
|
||||
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::Perception::Spatial::SpatialPerceptionAccessStatus> RequestAccessOperation = nullptr;
|
||||
|
||||
// Map of all observed mesh ID's to their coordinate systems. Used for localizing previously observed meshes.
|
||||
std::map<FGuid, MeshLocalizationData> UniqueMeshes;
|
||||
// Map of anchor data to use when localizing new spatial mapping meshes.
|
||||
std::map<FGuid, TSharedPtr<WMRAnchorLocalizationData>> AnchorLocalizationData;
|
||||
|
||||
IXRTrackingSystem* XRTrackingSystem = nullptr;
|
||||
IOpenXRARTrackedMeshHolder* TrackedMeshHolder = nullptr;
|
||||
winrt::Windows::Perception::Spatial::Surfaces::SpatialSurfaceObserver SurfaceObserver = nullptr;
|
||||
winrt::Windows::Perception::Spatial::Surfaces::SpatialSurfaceMeshOptions MeshOptions = nullptr;
|
||||
|
||||
void OnStartARSession(class UARSessionConfig* SessionConfig) override;
|
||||
|
||||
bool StartMeshObserver();
|
||||
void StopMeshObserver();
|
||||
|
||||
bool FindMeshTransform(XrSpace AnchorSpace, XrTime DisplayTime, XrSpace TrackingSpace, FTransform MeshToCachedAnchorTransform, FTransform& Transform, bool& IsTracking);
|
||||
bool GetTransformBetweenCoordinateSystems(winrt::Windows::Perception::Spatial::SpatialCoordinateSystem From, winrt::Windows::Perception::Spatial::SpatialCoordinateSystem To, FTransform& Transform);
|
||||
bool LocateSpatialMeshInTrackingSpace(const FGuid& MeshID, winrt::Windows::Perception::Spatial::SpatialCoordinateSystem MeshCoordinateSystem, XrSession InSession, XrTime DisplayTime, XrSpace TrackingSpace, FTransform& Transform);
|
||||
|
||||
void UpdateBoundingVolume();
|
||||
void CopyMeshData(FOpenXRMeshUpdate* MeshUpdate, winrt::Windows::Perception::Spatial::Surfaces::SpatialSurfaceMesh SurfaceMesh);
|
||||
void OnSurfacesChanged(winrt::Windows::Perception::Spatial::Surfaces::SpatialSurfaceObserver Observer, winrt::Windows::Foundation::IInspectable);
|
||||
};
|
||||
} // namespace MicrosoftOpenXR
|
||||
|
||||
#endif //PLATFORM_WINDOWS || PLATFORM_HOLOLENS
|
|
@ -0,0 +1,290 @@
|
|||
#include "SpeechPlugin.h"
|
||||
|
||||
#if PLATFORM_WINDOWS || PLATFORM_HOLOLENS
|
||||
|
||||
#include "Engine\Engine.h"
|
||||
|
||||
using namespace winrt::Windows::Media::SpeechRecognition;
|
||||
|
||||
namespace MicrosoftOpenXR
|
||||
{
|
||||
void FSpeechPlugin::Register()
|
||||
{
|
||||
IModularFeatures::Get().RegisterModularFeature(GetModularFeatureName(), this);
|
||||
}
|
||||
|
||||
void FSpeechPlugin::Unregister()
|
||||
{
|
||||
IModularFeatures::Get().UnregisterModularFeature(GetModularFeatureName(), this);
|
||||
}
|
||||
|
||||
void FSpeechPlugin::OnStartARSession(class UARSessionConfig* SessionConfig)
|
||||
{
|
||||
// Add all speech keywords from the input system
|
||||
const TArray <FInputActionSpeechMapping>& SpeechMappings = GetDefault<UInputSettings>()->GetSpeechMappings();
|
||||
for (const FInputActionSpeechMapping& SpeechMapping : SpeechMappings)
|
||||
{
|
||||
FKey Key(SpeechMapping.GetKeyName());
|
||||
FString Keyword = SpeechMapping.GetSpeechKeyword().ToString();
|
||||
|
||||
RegisterKeyword(Key, Keyword);
|
||||
}
|
||||
|
||||
if (SpeechMappings.Num() > 0)
|
||||
{
|
||||
StartSpeechRecognizer();
|
||||
}
|
||||
}
|
||||
|
||||
void FSpeechPlugin::OnStopARSession()
|
||||
{
|
||||
//remove keys from "speech" namespace
|
||||
EKeys::RemoveKeysWithCategory(FInputActionSpeechMapping::GetKeyCategory());
|
||||
|
||||
StopSpeechRecognizer();
|
||||
|
||||
KeywordMap.Empty();
|
||||
}
|
||||
|
||||
void FSpeechPlugin::AddKeywords(TArray<FKeywordInput> KeywordsToAdd)
|
||||
{
|
||||
APlayerController* PlayerController = nullptr;
|
||||
UInputSettings* InputSettings = nullptr;
|
||||
UInputComponent* InputComponent = nullptr;
|
||||
|
||||
if (KeywordsToAdd.Num() == 0
|
||||
|| (PlayerController = GetPlayerController()) == nullptr
|
||||
|| (InputSettings = GetMutableDefault<UInputSettings>()) == nullptr
|
||||
|| (InputComponent = PlayerController->InputComponent) == nullptr)
|
||||
{
|
||||
FString WarningText;
|
||||
KeywordsToAdd.Num() == 0 ?
|
||||
WarningText = FString("FSpeechPlugin::AddKeywords failed: No keywords to register.") :
|
||||
WarningText = FString("FSpeechPlugin::AddKeywords failed: Required engine components are null.");
|
||||
|
||||
UE_LOG(LogHMD, Warning, TEXT("%s"), *WarningText);
|
||||
return;
|
||||
}
|
||||
|
||||
StopSpeechRecognizer();
|
||||
|
||||
for (FKeywordInput InputKeyword : KeywordsToAdd)
|
||||
{
|
||||
if (InputKeyword.Keyword.IsEmpty() || !InputKeyword.Callback.GetFunctionName().IsValid())
|
||||
{
|
||||
UE_LOG(LogHMD, Warning, TEXT("Attempting to add an invalid Keyword: %s or Function: %s"), *InputKeyword.Keyword, *InputKeyword.Callback.GetFunctionName().ToString());
|
||||
continue;
|
||||
}
|
||||
|
||||
FString Keyword = InputKeyword.Keyword;
|
||||
// Key name must match FInputActionSpeechMapping::GetKeyName to cleanup correctly.
|
||||
FKey Key(FName(*FString::Printf(TEXT("%s_%s"), *FInputActionSpeechMapping::GetKeyCategory().ToString(), *Keyword)));
|
||||
|
||||
// Bind speech delegate to the player's UInputComponent to use the same key events as the input system.
|
||||
FInputActionUnifiedDelegate KeywordHandler;
|
||||
KeywordHandler.BindDelegate(InputKeyword.Callback.GetUObject(), InputKeyword.Callback.GetFunctionName());
|
||||
|
||||
// Trigger keywords on pressed events.
|
||||
FInputActionBinding ActionBinding(Key.GetFName(), IE_Pressed);
|
||||
ActionBinding.ActionDelegate = (FInputActionUnifiedDelegate)KeywordHandler;
|
||||
|
||||
// Update input system with new binding.
|
||||
InputComponent->AddActionBinding(ActionBinding);
|
||||
InputSettings->AddActionMapping(FInputActionKeyMapping(Key.GetFName(), Key));
|
||||
|
||||
RegisterKeyword(Key, Keyword);
|
||||
}
|
||||
|
||||
StartSpeechRecognizer();
|
||||
}
|
||||
|
||||
void FSpeechPlugin::RemoveKeywords(TArray<FString> KeywordsToRemove)
|
||||
{
|
||||
if (KeywordsToRemove.Num() == 0)
|
||||
{
|
||||
UE_LOG(LogHMD, Warning, TEXT("FSpeechPlugin::RemoveKeywords failed: No keywords to remove."));
|
||||
return;
|
||||
}
|
||||
|
||||
StopSpeechRecognizer();
|
||||
|
||||
for (FString InputKeyword : KeywordsToRemove)
|
||||
{
|
||||
InputKeyword = InputKeyword.ToLower();
|
||||
|
||||
// Remove local keyword so it is not included in the next recognizer.
|
||||
bool KeywordRemoved = false;
|
||||
for (int i = 0; i < Keywords.size(); i++)
|
||||
{
|
||||
if (Keywords.at(i) == winrt::hstring(*InputKeyword))
|
||||
{
|
||||
Keywords.erase(Keywords.begin() + i);
|
||||
KeywordRemoved = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!KeywordRemoved)
|
||||
{
|
||||
UE_LOG(LogHMD, Warning, TEXT("FSpeechPlugin::RemoveKeywords failed to remove Keyword: %s"), *InputKeyword);
|
||||
}
|
||||
|
||||
// Remove from the keyword map.
|
||||
if (KeywordMap.Contains(InputKeyword))
|
||||
{
|
||||
KeywordMap.Remove(InputKeyword);
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogHMD, Warning, TEXT("FSpeechPlugin::RemoveKeywords failed to remove Keyword from Key Map: %s"), *InputKeyword);
|
||||
}
|
||||
}
|
||||
|
||||
StartSpeechRecognizer();
|
||||
}
|
||||
|
||||
APlayerController* FSpeechPlugin::GetPlayerController()
|
||||
{
|
||||
for (const FWorldContext& Context : GEngine->GetWorldContexts())
|
||||
{
|
||||
if (Context.WorldType == EWorldType::Game || Context.WorldType == EWorldType::PIE)
|
||||
{
|
||||
UWorld* World = Context.World();
|
||||
if (World && World->GetGameInstance())
|
||||
{
|
||||
return World->GetGameInstance()->GetFirstLocalPlayerController();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void FSpeechPlugin::RegisterKeyword(FKey Key, FString Keyword)
|
||||
{
|
||||
TArray<FKey> Keys;
|
||||
EKeys::GetAllKeys(Keys);
|
||||
|
||||
// Only add key if it doesn't already exist.
|
||||
if (!Keys.Contains(Key))
|
||||
{
|
||||
EKeys::AddKey(FKeyDetails(Key, FText(), FKeyDetails::NotBlueprintBindableKey, FInputActionSpeechMapping::GetKeyCategory()));
|
||||
}
|
||||
|
||||
Keyword = Keyword.ToLower();
|
||||
if (KeywordMap.Contains(Keyword))
|
||||
{
|
||||
UE_LOG(LogHMD, Warning, TEXT("Adding duplicate keyword: %s. Multiple events may be called for this keyword."), *Keyword);
|
||||
}
|
||||
|
||||
Keywords.push_back(winrt::hstring(*Keyword));
|
||||
KeywordMap.Add(Keyword, Key);
|
||||
}
|
||||
|
||||
void FSpeechPlugin::CallSpeechCallback(FKey InKey)
|
||||
{
|
||||
APlayerController* PlayerController = GetPlayerController();
|
||||
if (PlayerController == nullptr)
|
||||
{
|
||||
UE_LOG(LogHMD, Warning, TEXT("Attempting to call speech keyword, but PlayerController is not valid"));
|
||||
return;
|
||||
}
|
||||
|
||||
AsyncTask(ENamedThreads::GameThread, [InKey, PlayerController]()
|
||||
{
|
||||
PlayerController->InputKey(InKey, IE_Pressed, 1.0f, false);
|
||||
});
|
||||
}
|
||||
|
||||
void FSpeechPlugin::StartSpeechRecognizer()
|
||||
{
|
||||
SpeechRecognitionListConstraint constraint = SpeechRecognitionListConstraint(Keywords);
|
||||
SpeechRecognizer = winrt::Windows::Media::SpeechRecognition::SpeechRecognizer();
|
||||
SpeechRecognizer.Constraints().Clear();
|
||||
SpeechRecognizer.Constraints().Append(constraint);
|
||||
|
||||
CompileConstraintsAsyncOperation = SpeechRecognizer.CompileConstraintsAsync();
|
||||
|
||||
CompileConstraintsAsyncOperation.Completed([this](winrt::Windows::Foundation::IAsyncOperation<SpeechRecognitionCompilationResult> asyncOperation, winrt::Windows::Foundation::AsyncStatus status)
|
||||
{
|
||||
if (asyncOperation.Status() == winrt::Windows::Foundation::AsyncStatus::Completed)
|
||||
{
|
||||
SpeechRecognitionCompilationResult result = asyncOperation.GetResults();
|
||||
if (result.Status() == SpeechRecognitionResultStatus::Success)
|
||||
{
|
||||
try
|
||||
{
|
||||
SpeechContinuousRecognitionSession session = SpeechRecognizer.ContinuousRecognitionSession();
|
||||
SessionStartAction = session.StartAsync();
|
||||
}
|
||||
catch (winrt::hresult_error e)
|
||||
{
|
||||
// We may see an exception if the microphone capability is not enabled.
|
||||
UE_LOG(LogHMD, Warning, TEXT("SpeechRecognizer failed to start with error: %s"), e.message().c_str());
|
||||
StopSpeechRecognizer();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UE_LOG(LogHMD, Warning, TEXT("SpeechRecognizer access request returns error: %d"), result.Status());
|
||||
StopSpeechRecognizer();
|
||||
}
|
||||
}
|
||||
else if (asyncOperation.Status() != winrt::Windows::Foundation::AsyncStatus::Canceled)
|
||||
{
|
||||
UE_LOG(LogHMD, Warning, TEXT("SpeechRecognizer.CompileConstraintsAsync returns error: %d"), asyncOperation.Status());
|
||||
StopSpeechRecognizer();
|
||||
}
|
||||
});
|
||||
|
||||
ResultsGeneratedToken = SpeechRecognizer.ContinuousRecognitionSession().ResultGenerated(
|
||||
[&](SpeechContinuousRecognitionSession sender, SpeechContinuousRecognitionResultGeneratedEventArgs args)
|
||||
{
|
||||
if (args.Result().Status() == SpeechRecognitionResultStatus::Success &&
|
||||
args.Result().Confidence() != SpeechRecognitionConfidence::Rejected)
|
||||
{
|
||||
FString keyword = FString(args.Result().Text().c_str());
|
||||
CallSpeechCallback(KeywordMap.FindRef(keyword));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void FSpeechPlugin::StopSpeechRecognizer()
|
||||
{
|
||||
if (CompileConstraintsAsyncOperation && CompileConstraintsAsyncOperation.Status() != winrt::Windows::Foundation::AsyncStatus::Completed)
|
||||
{
|
||||
CompileConstraintsAsyncOperation.Cancel();
|
||||
}
|
||||
|
||||
if (SpeechRecognizer != nullptr &&
|
||||
ResultsGeneratedToken.value != 0)
|
||||
{
|
||||
SpeechRecognizer.ContinuousRecognitionSession().ResultGenerated(ResultsGeneratedToken);
|
||||
ResultsGeneratedToken.value = 0;
|
||||
}
|
||||
|
||||
if (SpeechRecognizer != nullptr &&
|
||||
CompileConstraintsAsyncOperation.Status() == winrt::Windows::Foundation::AsyncStatus::Completed)
|
||||
{
|
||||
// If the SpeechRecognizer is idle, it is not capturing. Stopping while idle will throw an exception.
|
||||
if ((SessionStartAction != nullptr && SessionStartAction.Status() == winrt::Windows::Foundation::AsyncStatus::Completed) &&
|
||||
SpeechRecognizer.State() != winrt::Windows::Media::SpeechRecognition::SpeechRecognizerState::Idle)
|
||||
{
|
||||
try
|
||||
{
|
||||
SpeechRecognizer.ContinuousRecognitionSession().StopAsync();
|
||||
}
|
||||
catch (winrt::hresult_error e)
|
||||
{
|
||||
// We may see an exception if no microphone was attached.
|
||||
UE_LOG(LogHMD, Warning, TEXT("ContinuousRecognitionSession failed to stop with error: %d"), e.code().value);
|
||||
}
|
||||
}
|
||||
|
||||
SpeechRecognizer.Constraints().Clear();
|
||||
SpeechRecognizer.Close();
|
||||
SpeechRecognizer = nullptr;
|
||||
}
|
||||
}
|
||||
} // namespace MicrosoftOpenXR
|
||||
|
||||
#endif //PLATFORM_WINDOWS || PLATFORM_HOLOLENS
|
|
@ -0,0 +1,61 @@
|
|||
#pragma once
|
||||
|
||||
#if PLATFORM_WINDOWS || PLATFORM_HOLOLENS
|
||||
|
||||
#include "OpenXRCommon.h"
|
||||
#include "MicrosoftOpenXR.h"
|
||||
#include "HeadMountedDisplayTypes.h"
|
||||
|
||||
#include "GameFramework/PlayerInput.h"
|
||||
#include "GameFramework/PlayerController.h"
|
||||
#include "GameFramework/InputSettings.h"
|
||||
|
||||
#include "UObject/NameTypes.h"
|
||||
#include "UObject/UObjectGlobals.h"
|
||||
#include "Async/Async.h"
|
||||
|
||||
#include "Windows/AllowWindowsPlatformTypes.h"
|
||||
#include "Windows/AllowWindowsPlatformAtomics.h"
|
||||
#include "Windows/PreWindowsApi.h"
|
||||
|
||||
#include <unknwn.h>
|
||||
#include <winrt/Windows.Media.SpeechRecognition.h>
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
#include <winrt/Windows.Foundation.Collections.h>
|
||||
|
||||
#include "Windows/PostWindowsApi.h"
|
||||
#include "Windows/HideWindowsPlatformAtomics.h"
|
||||
#include "Windows/HideWindowsPlatformTypes.h"
|
||||
|
||||
namespace MicrosoftOpenXR
|
||||
{
|
||||
class FSpeechPlugin : public IOpenXRExtensionPlugin
|
||||
{
|
||||
public:
|
||||
void Register();
|
||||
void Unregister();
|
||||
|
||||
void OnStartARSession(class UARSessionConfig* SessionConfig) override;
|
||||
void OnStopARSession() override;
|
||||
|
||||
void AddKeywords(TArray<FKeywordInput> KeywordsToAdd);
|
||||
void RemoveKeywords(TArray<FString> KeywordsToRemove);
|
||||
|
||||
private:
|
||||
winrt::Windows::Media::SpeechRecognition::SpeechRecognizer SpeechRecognizer = nullptr;
|
||||
winrt::Windows::Foundation::IAsyncOperation<winrt::Windows::Media::SpeechRecognition::SpeechRecognitionCompilationResult> CompileConstraintsAsyncOperation;
|
||||
winrt::Windows::Foundation::IAsyncAction SessionStartAction;
|
||||
winrt::event_token ResultsGeneratedToken;
|
||||
std::vector<winrt::hstring> Keywords;
|
||||
TMap<FString, FKey> KeywordMap;
|
||||
|
||||
APlayerController* GetPlayerController();
|
||||
|
||||
void RegisterKeyword(FKey Key, FString Keyword);
|
||||
void CallSpeechCallback(FKey InKey);
|
||||
void StartSpeechRecognizer();
|
||||
void StopSpeechRecognizer();
|
||||
};
|
||||
} // namespace MicrosoftOpenXR
|
||||
|
||||
#endif //PLATFORM_WINDOWS || PLATFORM_HOLOLENS
|
|
@ -0,0 +1,65 @@
|
|||
#include "TrackedGeometryCollision.h"
|
||||
|
||||
namespace MicrosoftOpenXR
|
||||
{
|
||||
TrackedGeometryCollision::TrackedGeometryCollision(const TArray<FVector> InVertices, const TArray<MRMESH_INDEX_TYPE> InIndices)
|
||||
{
|
||||
if (InVertices.Num() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Vertices = std::move(InVertices);
|
||||
Indices = std::move(InIndices);
|
||||
|
||||
// Create a bounding box from the input vertices to reduce the number of full meshes that need to be hit-tested.
|
||||
BoundingBox = FBox(&Vertices[0], Vertices.Num());
|
||||
}
|
||||
|
||||
void TrackedGeometryCollision::UpdateVertices(const TArray<FVector> InVertices, const TArray<MRMESH_INDEX_TYPE> InIndices)
|
||||
{
|
||||
Vertices = InVertices;
|
||||
Indices = InIndices;
|
||||
|
||||
// Create a bounding box from the input vertices to reduce the number of full meshes that need to be hit-tested.
|
||||
BoundingBox = FBox(&Vertices[0], Vertices.Num());
|
||||
}
|
||||
|
||||
bool TrackedGeometryCollision::Collides(const FVector Start, const FVector End, const FTransform MeshToWorld, FVector& OutHitPoint, FVector& OutHitNormal, float& OutHitDistance)
|
||||
{
|
||||
if (MeshToWorld.GetScale3D().IsNearlyZero())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check bounding box collision first so we don't check triangles for meshes we definitely won't collide with.
|
||||
if (!FMath::LineBoxIntersection(BoundingBox.TransformBy(MeshToWorld), Start, End, End - Start))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for triangle collision and set the output hit position, normal, and distance.
|
||||
for (int i = 0; i < Indices.Num(); i += 3)
|
||||
{
|
||||
// Ignore this triangle if it has indices out of range.
|
||||
if ((unsigned int)Indices[i] > (unsigned int)Vertices.Num()
|
||||
|| (unsigned int)Indices[i + 1] > (unsigned int)Vertices.Num()
|
||||
|| (unsigned int)Indices[i + 2] > (unsigned int)Vertices.Num())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (FMath::SegmentTriangleIntersection(Start, End,
|
||||
MeshToWorld.TransformPosition(Vertices[Indices[i]]),
|
||||
MeshToWorld.TransformPosition(Vertices[Indices[i + 1]]),
|
||||
MeshToWorld.TransformPosition(Vertices[Indices[i + 2]]),
|
||||
OutHitPoint, OutHitNormal))
|
||||
{
|
||||
OutHitDistance = (OutHitPoint - Start).Size();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
} // namespace MicrosoftOpenXR
|
|
@ -0,0 +1,37 @@
|
|||
#pragma once
|
||||
#include "OpenXRCommon.h"
|
||||
#include "OpenXRCore.h"
|
||||
#include "IOpenXRARModule.h"
|
||||
#include "IOpenXRARTrackedGeometryHolder.h"
|
||||
|
||||
#include "HeadMountedDisplayTypes.h"
|
||||
#include "ARTypes.h"
|
||||
|
||||
namespace MicrosoftOpenXR
|
||||
{
|
||||
class TrackedGeometryCollision
|
||||
{
|
||||
public:
|
||||
TrackedGeometryCollision(const TArray<FVector> InVertices, const TArray<MRMESH_INDEX_TYPE> InIndices);
|
||||
|
||||
void UpdateVertices(const TArray<FVector> InVertices, const TArray<MRMESH_INDEX_TYPE> InIndices);
|
||||
|
||||
/// <summary>
|
||||
/// Hit test a ray against tracked mesh data.
|
||||
/// </summary>
|
||||
/// <param name="Start">Start of collision ray in world space</param>
|
||||
/// <param name="End">End of collision ray in world space</param>
|
||||
/// <param name="TrackingToWorld">Transform from mesh local space to world space. The mesh may not be in tracking space.</param>
|
||||
/// <param name="OutHitPoint">Position of hit in world space</param>
|
||||
/// <param name="OutHitNormal">Normal of hit in world space</param>
|
||||
/// <param name="OutHitDistance">Distance from ray start</param>
|
||||
/// <returns>True if the input ray collides with this mesh.</returns>
|
||||
bool Collides(const FVector Start, const FVector End, const FTransform MeshToWorld, FVector& OutHitPoint, FVector& OutHitNormal, float& OutHitDistance);
|
||||
|
||||
private:
|
||||
TArray<FVector> Vertices;
|
||||
TArray<MRMESH_INDEX_TYPE> Indices;
|
||||
|
||||
FBox BoundingBox;
|
||||
};
|
||||
} // namespace MicrosoftOpenXR
|
|
@ -0,0 +1,184 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
|
||||
// Simple type conversion functions for working with the WindowsMixedRealityInterop
|
||||
|
||||
#pragma once
|
||||
|
||||
#if PLATFORM_WINDOWS || PLATFORM_HOLOLENS
|
||||
#include "CoreMinimal.h"
|
||||
#include "HAL\UnrealMemory.h"
|
||||
|
||||
#include "Windows/WindowsHWrapper.h"
|
||||
#include "HeadMountedDisplayTypes.h"
|
||||
|
||||
#include <DirectXMath.h>
|
||||
#include <unknwn.h>
|
||||
#include <winrt/base.h>
|
||||
|
||||
|
||||
namespace MicrosoftOpenXR
|
||||
{
|
||||
class WMRUtility
|
||||
{
|
||||
public:
|
||||
// Convert between DirectX XMMatrix to Unreal FMatrix.
|
||||
static FORCEINLINE FMatrix ToFMatrix(DirectX::XMMATRIX& M)
|
||||
{
|
||||
DirectX::XMFLOAT4X4 dst;
|
||||
DirectX::XMStoreFloat4x4(&dst, M);
|
||||
|
||||
return FMatrix(
|
||||
FPlane(dst._11, dst._21, dst._31, dst._41),
|
||||
FPlane(dst._12, dst._22, dst._32, dst._42),
|
||||
FPlane(dst._13, dst._23, dst._33, dst._43),
|
||||
FPlane(dst._14, dst._24, dst._34, dst._44));
|
||||
}
|
||||
|
||||
static FORCEINLINE FMatrix ToFMatrix(DirectX::XMFLOAT4X4& M)
|
||||
{
|
||||
return FMatrix(
|
||||
FPlane(M._11, M._21, M._31, M._41),
|
||||
FPlane(M._12, M._22, M._32, M._42),
|
||||
FPlane(M._13, M._23, M._33, M._43),
|
||||
FPlane(M._14, M._24, M._34, M._44));
|
||||
}
|
||||
|
||||
static FORCEINLINE FTransform FromMixedRealityTransform(const DirectX::XMMATRIX& M, float InScale = 1.0f)
|
||||
{
|
||||
DirectX::XMVECTOR Scale;
|
||||
DirectX::XMVECTOR Rotation;
|
||||
DirectX::XMVECTOR Translation;
|
||||
DirectX::XMMatrixDecompose(&Scale, &Rotation, &Translation, M);
|
||||
|
||||
return FTransform(FromXMVectorRotation(Rotation), FromXMVectorTranslation(Translation, InScale), FromXMVectorScale(Scale));
|
||||
}
|
||||
|
||||
static FORCEINLINE FVector FromMixedRealityVector(DirectX::XMFLOAT3 pos)
|
||||
{
|
||||
return FVector(
|
||||
-1.0f * pos.z,
|
||||
pos.x,
|
||||
pos.y);
|
||||
}
|
||||
|
||||
static FORCEINLINE DirectX::XMFLOAT3 ToMixedRealityVector(FVector pos)
|
||||
{
|
||||
return DirectX::XMFLOAT3(
|
||||
pos.Y,
|
||||
pos.Z,
|
||||
-1.0f * pos.X);
|
||||
}
|
||||
|
||||
static FORCEINLINE FVector FromMixedRealityScale(DirectX::XMFLOAT3 pos)
|
||||
{
|
||||
return FVector(
|
||||
pos.z,
|
||||
pos.x,
|
||||
pos.y);
|
||||
}
|
||||
|
||||
static FORCEINLINE FVector FromMixedRealityScale(winrt::Windows::Foundation::Numerics::float3 pos)
|
||||
{
|
||||
return FVector(
|
||||
pos.z,
|
||||
pos.x,
|
||||
pos.y);
|
||||
}
|
||||
|
||||
static FORCEINLINE DirectX::XMFLOAT3 ToMixedRealityScale(FVector pos)
|
||||
{
|
||||
return DirectX::XMFLOAT3(
|
||||
pos.Y,
|
||||
pos.Z,
|
||||
pos.X);
|
||||
}
|
||||
|
||||
|
||||
static FORCEINLINE FQuat FromMixedRealityQuaternion(DirectX::XMFLOAT4 rot)
|
||||
{
|
||||
FQuat quaternion(
|
||||
-1.0f * rot.z,
|
||||
rot.x,
|
||||
rot.y,
|
||||
-1.0f * rot.w);
|
||||
quaternion.Normalize();
|
||||
|
||||
return quaternion;
|
||||
}
|
||||
|
||||
static FORCEINLINE DirectX::XMFLOAT4 ToMixedRealityQuaternion(FQuat rot)
|
||||
{
|
||||
// Windows api IsNormalized checks fail on a negative identity quaternion.
|
||||
if (rot == FQuat::Identity)
|
||||
{
|
||||
return DirectX::XMFLOAT4(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
|
||||
DirectX::XMVECTOR v = DirectX::XMVectorSet(
|
||||
rot.Y,
|
||||
rot.Z,
|
||||
-1.0f * rot.X,
|
||||
-1.0f * rot.W);
|
||||
DirectX::XMQuaternionNormalize(v);
|
||||
|
||||
DirectX::XMFLOAT4 quatf4out;
|
||||
DirectX::XMStoreFloat4(&quatf4out, v);
|
||||
return quatf4out;
|
||||
}
|
||||
|
||||
static FORCEINLINE FVector FromFloat3(winrt::Windows::Foundation::Numerics::float3 pos, float scale = 1.0f)
|
||||
{
|
||||
return FVector(
|
||||
-1.0f * pos.z,
|
||||
pos.x,
|
||||
pos.y) * scale;
|
||||
}
|
||||
|
||||
static FORCEINLINE FVector FromXMVectorTranslation(DirectX::XMVECTOR InValue, float scale = 1.0f)
|
||||
{
|
||||
InValue = DirectX::XMVectorMultiply(InValue, DirectX::XMVectorSet(scale, scale, -1 * scale, scale));
|
||||
InValue = DirectX::XMVectorSwizzle(InValue, 2, 0, 1, 3);
|
||||
|
||||
DirectX::XMFLOAT3 Dest;
|
||||
DirectX::XMStoreFloat3(&Dest, InValue);
|
||||
return FVector(Dest.x, Dest.y, Dest.z);
|
||||
}
|
||||
|
||||
static FORCEINLINE FQuat FromXMVectorRotation(DirectX::XMVECTOR InValue)
|
||||
{
|
||||
DirectX::XMFLOAT4 Dest;
|
||||
DirectX::XMStoreFloat4(&Dest, InValue);
|
||||
|
||||
return FromMixedRealityQuaternion(Dest);
|
||||
}
|
||||
|
||||
static FORCEINLINE FVector FromXMVectorScale(DirectX::XMVECTOR InValue)
|
||||
{
|
||||
InValue = DirectX::XMVectorSwizzle(InValue, 2, 0, 1, 3);
|
||||
|
||||
DirectX::XMFLOAT3 Dest;
|
||||
DirectX::XMStoreFloat3(&Dest, InValue);
|
||||
return FVector(Dest.x, Dest.y, Dest.z);
|
||||
}
|
||||
|
||||
static FORCEINLINE FVector2D FromFloat2(winrt::Windows::Foundation::Numerics::float2 pos)
|
||||
{
|
||||
return FVector2D(pos.x, pos.y);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static FORCEINLINE FGuid GUIDToFGuid(const winrt::guid& InGuid)
|
||||
{
|
||||
check(sizeof(FGuid) == sizeof(winrt::guid));
|
||||
|
||||
FGuid OutGuid;
|
||||
FMemory::Memcpy(&OutGuid, &InGuid, sizeof(FGuid));
|
||||
return OutGuid;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
// Copyright (c) Microsoft Corporation.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/ObjectMacros.h"
|
||||
#include "Kismet/BlueprintFunctionLibrary.h"
|
||||
#include "Components/InputComponent.h"
|
||||
|
||||
#include "MicrosoftOpenXR.generated.h"
|
||||
|
||||
USTRUCT(BlueprintType, Category = "MicrosoftOpenXR|OpenXR")
|
||||
struct FKeywordInput
|
||||
{
|
||||
GENERATED_USTRUCT_BODY()
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
FString Keyword;
|
||||
|
||||
UPROPERTY(EditAnywhere, BlueprintReadWrite)
|
||||
FInputActionHandlerDynamicSignature Callback;
|
||||
};
|
||||
|
||||
UENUM(BlueprintType, Category = "MicrosoftOpenXR|OpenXR")
|
||||
enum class EHandMeshStatus : uint8
|
||||
{
|
||||
NotInitialised = 0 UMETA(Hidden),
|
||||
Disabled = 1,
|
||||
EnabledTrackingGeometry = 2,
|
||||
EnabledXRVisualization = 3
|
||||
};
|
||||
|
||||
UCLASS(ClassGroup = OpenXR)
|
||||
class MICROSOFTOPENXR_API UMicrosoftOpenXRFunctionLibrary :
|
||||
public UBlueprintFunctionLibrary
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
Turn Hand Mesh
|
||||
|
||||
@param On true if enable
|
||||
@return true if the command successes
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "MicrosoftOpenXR|OpenXR")
|
||||
static bool SetUseHandMesh(EHandMeshStatus Mode);
|
||||
|
||||
/**
|
||||
Is QR Tracking enabled
|
||||
|
||||
@return true if the command successes
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "MicrosoftOpenXR|OpenXR")
|
||||
static bool IsQREnabled();
|
||||
|
||||
/**
|
||||
* Get the transform from PV camera space to Unreal world space.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "MicrosoftOpenXR|OpenXR")
|
||||
static FTransform GetPVCameraToWorldTransform();
|
||||
|
||||
/**
|
||||
* Get the PV Camera intrinsics.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "MicrosoftOpenXR|OpenXR")
|
||||
static bool GetPVCameraIntrinsics(FVector2D& focalLength, int& width, int& height, FVector2D& principalPoint, FVector& radialDistortion, FVector2D& tangentialDistortion);
|
||||
|
||||
/**
|
||||
* Get a ray into the scene from a camera point.
|
||||
* X is left/right
|
||||
* Y is up/down
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "MicrosoftOpenXR|OpenXR")
|
||||
static FVector GetWorldSpaceRayFromCameraPoint(FVector2D pixelCoordinate);
|
||||
|
||||
/**
|
||||
Check if the current platform supports speech recognition.
|
||||
*/
|
||||
UFUNCTION(BlueprintPure, Category = "MicrosoftOpenXR|OpenXR")
|
||||
static bool IsSpeechRecognitionAvailable();
|
||||
|
||||
/**
|
||||
Add new speech keywords with associated callbacks.
|
||||
|
||||
@param Keywords list of keyword and callbacks to add.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "MicrosoftOpenXR|OpenXR")
|
||||
static void AddKeywords(TArray<FKeywordInput> Keywords);
|
||||
|
||||
/**
|
||||
Remove speech keywords.
|
||||
|
||||
@param Keywords list of keyword to remove.
|
||||
*/
|
||||
UFUNCTION(BlueprintCallable, Category = "MicrosoftOpenXR|OpenXR")
|
||||
static void RemoveKeywords(TArray<FString> Keywords);
|
||||
|
||||
};
|
||||
|
|
@ -0,0 +1,282 @@
|
|||
#pragma once
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Copyright (C) Microsoft Corporation. All Rights Reserved
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define XR_MSFT_holographic_remoting 1
|
||||
#define XR_MSFT_holographic_remoting_SPEC_VERSION 1
|
||||
#define XR_MSFT_HOLOGRAPHIC_REMOTING_EXTENSION_NAME "XR_MSFT_holographic_remoting"
|
||||
|
||||
// Extension number: 66 / Enum subrange base: 65000
|
||||
|
||||
// extends XrStructureType
|
||||
typedef enum XrRemotingStructureType {
|
||||
XR_TYPE_REMOTING_REMOTE_CONTEXT_PROPERTIES_MSFT = 1000065000,
|
||||
XR_TYPE_REMOTING_CONNECT_INFO_MSFT = 1000065001,
|
||||
XR_TYPE_REMOTING_LISTEN_INFO_MSFT = 1000065002,
|
||||
XR_TYPE_REMOTING_DISCONNECT_INFO_MSFT = 1000065003,
|
||||
XR_TYPE_REMOTING_EVENT_DATA_LISTENING_MSFT = 1000065004,
|
||||
XR_TYPE_REMOTING_EVENT_DATA_CONNECTED_MSFT = 1000065005,
|
||||
XR_TYPE_REMOTING_EVENT_DATA_DISCONNECTED_MSFT = 1000065006,
|
||||
XR_TYPE_REMOTING_AUTHENTICATION_TOKEN_REQUEST_MSFT = 1000065007,
|
||||
XR_TYPE_REMOTING_CERTIFICATE_DATA_MSFT = 1000065008,
|
||||
XR_TYPE_REMOTING_CERTIFICATE_VALIDATION_RESULT_MSFT = 1000065009,
|
||||
XR_TYPE_REMOTING_SERVER_CERTIFICATE_VALIDATION_MSFT = 1000065010,
|
||||
XR_TYPE_REMOTING_AUTHENTICATION_TOKEN_VALIDATION_MSFT = 1000065011,
|
||||
XR_TYPE_REMOTING_SERVER_CERTIFICATE_REQUEST_MSFT = 1000065012,
|
||||
XR_TYPE_REMOTING_SECURE_CONNECTION_CLIENT_CALLBACKS_MSFT = 1000065013,
|
||||
XR_TYPE_REMOTING_SECURE_CONNECTION_SERVER_CALLBACKS_MSFT = 1000065014,
|
||||
XR_TYPE_REMOTING_MAX_ENUM = 0x7FFFFFFF
|
||||
} XrRemotingStructureType;
|
||||
|
||||
// extends XrResult
|
||||
typedef enum XrRemotingResult {
|
||||
XR_ERROR_REMOTING_NOT_DISCONNECTED_MSFT = -1000065000,
|
||||
XR_ERROR_REMOTING_CODEC_NOT_FOUND_MSFT = -1000065001,
|
||||
XR_ERROR_REMOTING_CALLBACK_ERROR_MSFT = -1000065002,
|
||||
XR_ERROR_REMOTING_MAX_ENUM = 0x7FFFFFFF
|
||||
} XrRemotingResult;
|
||||
|
||||
typedef enum XrRemotingDisconnectReasonMSFT {
|
||||
XR_REMOTING_DISCONNECT_REASON_NONE_MSFT = 0,
|
||||
XR_REMOTING_DISCONNECT_REASON_UNKNOWN_MSFT = 1,
|
||||
XR_REMOTING_DISCONNECT_REASON_NO_SERVER_CERTIFICATE_MSFT = 2,
|
||||
XR_REMOTING_DISCONNECT_REASON_HANDSHAKE_PORT_BUSY_MSFT = 3,
|
||||
XR_REMOTING_DISCONNECT_REASON_HANDSHAKE_UNREACHABLE_MSFT = 4,
|
||||
XR_REMOTING_DISCONNECT_REASON_HANDSHAKE_CONNECTION_FAILED_MSFT = 5,
|
||||
XR_REMOTING_DISCONNECT_REASON_AUTHENTICATION_FAILED_MSFT = 6,
|
||||
XR_REMOTING_DISCONNECT_REASON_REMOTING_VERSION_MISMATCH_MSFT = 7,
|
||||
XR_REMOTING_DISCONNECT_REASON_INCOMPATIBLE_TRANSPORT_PROTOCOLS_MSFT = 8,
|
||||
XR_REMOTING_DISCONNECT_REASON_HANDSHAKE_FAILED_MSFT = 9,
|
||||
XR_REMOTING_DISCONNECT_REASON_TRANSPORT_PORT_BUSY_MSFT = 10,
|
||||
XR_REMOTING_DISCONNECT_REASON_TRANSPORT_UNREACHABLE_MSFT = 11,
|
||||
XR_REMOTING_DISCONNECT_REASON_TRANSPORT_CONNECTION_FAILED_MSFT = 12,
|
||||
XR_REMOTING_DISCONNECT_REASON_PROTOCOL_VERSION_MISMATCH_MSFT = 13,
|
||||
XR_REMOTING_DISCONNECT_REASON_PROTOCOL_ERROR_MSFT = 14,
|
||||
XR_REMOTING_DISCONNECT_REASON_VIDEO_CODEC_NOT_AVAILABLE_MSFT = 15,
|
||||
XR_REMOTING_DISCONNECT_REASON_CANCELED_MSFT = 16,
|
||||
XR_REMOTING_DISCONNECT_REASON_CONNECTION_LOST_MSFT = 17,
|
||||
XR_REMOTING_DISCONNECT_REASON_DEVICE_LOST_MSFT = 18,
|
||||
XR_REMOTING_DISCONNECT_REASON_DISCONNECT_REQUEST_MSFT = 19,
|
||||
XR_REMOTING_DISCONNECT_REASON_HANDSHAKE_NETWORK_UNREACHABLE_MSFT = 20,
|
||||
XR_REMOTING_DISCONNECT_REASON_HANDSHAKE_CONNECTION_REFUSED_MSFT = 21,
|
||||
XR_REMOTING_DISCONNECT_REASON_VIDEO_FORMAT_NOT_AVAILABLE_MSFT = 22,
|
||||
XR_REMOTING_DISCONNECT_REASON_PEER_DISCONNECT_REQUEST_MSFT = 23,
|
||||
XR_REMOTING_DISCONNECT_REASON_PEER_DISCONNECT_TIMEOUT_MSFT = 24,
|
||||
XR_REMOTING_DISCONNECT_REASON_SESSION_OPEN_TIMEOUT_MSFT = 25,
|
||||
XR_REMOTING_DISCONNECT_REASON_REMOTING_HANDSHAKE_TIMEOUT_MSFT = 26,
|
||||
XR_REMOTING_DISCONNECT_REASON_INTERNAL_ERROR_MSFT = 27,
|
||||
XR_REMOTING_DISCONNECT_REASON_MAX_ENUM = 0x7FFFFFFF
|
||||
} XrRemotingDisconnectReasonMSFT;
|
||||
|
||||
typedef enum XrRemotingConnectionStateMSFT {
|
||||
XR_REMOTING_CONNECTION_STATE_DISCONNECTED_MSFT = 0,
|
||||
XR_REMOTING_CONNECTION_STATE_CONNECTING_MSFT = 1,
|
||||
XR_REMOTING_CONNECTION_STATE_CONNECTED_MSFT = 2,
|
||||
XR_REMOTING_CONNECTION_STATE_MAX_ENUM = 0x7FFFFFFF
|
||||
} XrRemotingConnectionStateMSFT;
|
||||
|
||||
typedef enum XrRemotingVideoCodecMSFT {
|
||||
XR_REMOTING_VIDEO_CODEC_ANY_MSFT = 0,
|
||||
XR_REMOTING_VIDEO_CODEC_H264_MSFT = 1,
|
||||
XR_REMOTING_VIDEO_CODEC_H265_MSFT = 2,
|
||||
XR_REMOTING_VIDEO_CODEC_MAX_ENUM = 0x7FFFFFFF
|
||||
} XrRemotingVideoCodecMSFT;
|
||||
|
||||
typedef enum XrRemotingCertificateNameValidationResultMSFT {
|
||||
XR_REMOTING_CERTIFICATE_NAME_VALIDATION_RESULT_NOT_CHECKED_MSFT = 0,
|
||||
XR_REMOTING_CERTIFICATE_NAME_VALIDATION_RESULT_MATCH_MSFT = 1,
|
||||
XR_REMOTING_CERTIFICATE_NAME_VALIDATION_RESULT_MISMATCH_MSFT = 2,
|
||||
XR_REMOTING_CERTIFICATE_NAME_VALIDATION_RESULT_MAX_ENUM = 0x7FFFFFFF
|
||||
} XrRemotingCertificateNameValidationResultMSFT;
|
||||
|
||||
typedef struct XrRemotingRemoteContextPropertiesMSFT {
|
||||
XrStructureType type;
|
||||
void* next;
|
||||
uint32_t maxBitrateKbps;
|
||||
XrBool32 enableAudio;
|
||||
XrRemotingVideoCodecMSFT videoCodec;
|
||||
} XrRemotingRemoteContextPropertiesMSFT;
|
||||
|
||||
typedef struct XrRemotingConnectInfoMSFT {
|
||||
XrStructureType type;
|
||||
void* next;
|
||||
const char* remoteHostName;
|
||||
uint16_t remotePort;
|
||||
XrBool32 secureConnection;
|
||||
} XrRemotingConnectInfoMSFT;
|
||||
|
||||
typedef struct XrRemotingListenInfoMSFT {
|
||||
XrStructureType type;
|
||||
void* next;
|
||||
const char* listenInterface;
|
||||
uint16_t handshakeListenPort;
|
||||
uint16_t transportListenPort;
|
||||
XrBool32 secureConnection;
|
||||
} XrRemotingListenInfoMSFT;
|
||||
|
||||
typedef struct XrRemotingDisconnectInfoMSFT {
|
||||
XrStructureType type;
|
||||
const void* next;
|
||||
} XrRemotingDisconnectInfoMSFT;
|
||||
|
||||
typedef struct XrRemotingEventDataListeningMSFT {
|
||||
XrStructureType type;
|
||||
const void* next;
|
||||
uint16_t listeningPort;
|
||||
} XrEventDataRemoteContextListeningMSFT;
|
||||
|
||||
typedef struct XrRemotingEventDataConnectedMSFT {
|
||||
XrStructureType type;
|
||||
const void* next;
|
||||
} XrEventDataRemoteContextConnectedMSFT;
|
||||
|
||||
typedef struct XrRemotingEventDataDisconnectedMSFT {
|
||||
XrStructureType type;
|
||||
const void* next;
|
||||
XrRemotingDisconnectReasonMSFT disconnectReason;
|
||||
} XrEventDataRemoteContextDisconnectedMSFT;
|
||||
|
||||
typedef struct XrRemotingAuthenticationTokenRequestMSFT {
|
||||
XrStructureType type;
|
||||
const void* next;
|
||||
void* context;
|
||||
uint32_t tokenCapacityIn;
|
||||
uint32_t tokenSizeOut;
|
||||
char* tokenBuffer;
|
||||
} XrRemotingAuthenticationTokenRequestMSFT;
|
||||
|
||||
typedef struct XrRemotingCertificateDataMSFT {
|
||||
XrStructureType type;
|
||||
const void* next;
|
||||
uint32_t size;
|
||||
const uint8_t* data;
|
||||
} XrRemotingCertificateDataMSFT;
|
||||
|
||||
typedef struct XrRemotingCertificateValidationResultMSFT {
|
||||
XrStructureType type;
|
||||
const void* next;
|
||||
XrBool32 trustedRoot;
|
||||
XrBool32 revoked;
|
||||
XrBool32 expired;
|
||||
XrBool32 wrongUsage;
|
||||
XrRemotingCertificateNameValidationResultMSFT nameValidationResult;
|
||||
XrBool32 revocationCheckFailed;
|
||||
XrBool32 invalidCertOrChain;
|
||||
} XrRemotingCertificateValidationResultMSFT;
|
||||
|
||||
typedef struct XrRemotingServerCertificateValidationMSFT {
|
||||
XrStructureType type;
|
||||
const void* next;
|
||||
void* context;
|
||||
const char* hostName;
|
||||
XrBool32 forceRevocationCheck;
|
||||
uint32_t numCertificates;
|
||||
const XrRemotingCertificateDataMSFT* certificates;
|
||||
XrRemotingCertificateValidationResultMSFT* systemValidationResult;
|
||||
XrRemotingCertificateValidationResultMSFT validationResultOut;
|
||||
} XrRemotingServerCertificateValidationMSFT;
|
||||
|
||||
typedef struct XrRemotingAuthenticationTokenValidationMSFT {
|
||||
XrStructureType type;
|
||||
const void* next;
|
||||
void* context;
|
||||
const char* token;
|
||||
XrBool32 tokenValidOut;
|
||||
} XrRemotingAuthenticationTokenValidationMSFT;
|
||||
|
||||
typedef struct XrRemotingServerCertificateRequestMSFT {
|
||||
XrStructureType type;
|
||||
const void* next;
|
||||
void* context;
|
||||
uint32_t certStoreCapacityIn;
|
||||
uint32_t certStoreSizeOut;
|
||||
uint8_t* certStoreBuffer;
|
||||
uint32_t keyPassphraseCapacityIn;
|
||||
uint32_t keyPassphraseSizeOut;
|
||||
char* keyPassphraseBuffer;
|
||||
uint32_t subjectNameCapacityIn;
|
||||
uint32_t subjectNameSizeOut;
|
||||
char* subjectNameBuffer;
|
||||
} XrRemotingServerCertificateRequestMSFT;
|
||||
|
||||
// Secure connection callback functions (typedef only, no prototype)
|
||||
typedef XrResult(XRAPI_PTR* PFN_xrRemotingRequestAuthenticationTokenCallbackMSFT)(XrRemotingAuthenticationTokenRequestMSFT* authenticationTokenRequest);
|
||||
typedef XrResult(XRAPI_PTR* PFN_xrRemotingValidateServerCertificateCallbackMSFT)(XrRemotingServerCertificateValidationMSFT* serverCertificateValidation);
|
||||
typedef XrResult(XRAPI_PTR* PFN_xrRemotingValidateAuthenticationTokenCallbackMSFT)(XrRemotingAuthenticationTokenValidationMSFT* authenticationTokenValidation);
|
||||
typedef XrResult(XRAPI_PTR* PFN_xrRemotingRequestServerCertificateCallbackMSFT)(XrRemotingServerCertificateRequestMSFT* serverCertificateRequest);
|
||||
|
||||
typedef struct XrRemotingSecureConnectionClientCallbacksMSFT {
|
||||
XrStructureType type;
|
||||
void* next;
|
||||
void* context;
|
||||
PFN_xrRemotingRequestAuthenticationTokenCallbackMSFT requestAuthenticationTokenCallback;
|
||||
PFN_xrRemotingValidateServerCertificateCallbackMSFT validateServerCertificateCallback;
|
||||
XrBool32 performSystemValidation;
|
||||
} XrRemotingSecureConnectionClientCallbacksMSFT;
|
||||
|
||||
typedef struct XrRemotingSecureConnectionServerCallbacksMSFT {
|
||||
XrStructureType type;
|
||||
void* next;
|
||||
void* context;
|
||||
PFN_xrRemotingRequestServerCertificateCallbackMSFT requestServerCertificateCallback;
|
||||
PFN_xrRemotingValidateAuthenticationTokenCallbackMSFT validateAuthenticationTokenCallback;
|
||||
const char* authenticationRealm;
|
||||
} XrRemotingSecureConnectionServerCallbacksMSFT;
|
||||
|
||||
// Remoting extension callable functions
|
||||
typedef XrResult(XRAPI_PTR* PFN_xrRemotingSetContextPropertiesMSFT)(XrInstance instance, XrSystemId systemId, const XrRemotingRemoteContextPropertiesMSFT* contextProperties);
|
||||
typedef XrResult(XRAPI_PTR* PFN_xrRemotingConnectMSFT)(XrInstance instance, XrSystemId systemId, const XrRemotingConnectInfoMSFT* connectInfo);
|
||||
typedef XrResult(XRAPI_PTR* PFN_xrRemotingListenMSFT)(XrInstance instance, XrSystemId systemId, const XrRemotingListenInfoMSFT* listenInfo);
|
||||
typedef XrResult(XRAPI_PTR* PFN_xrRemotingDisconnectMSFT)(XrInstance instance, XrSystemId systemId, const XrRemotingDisconnectInfoMSFT* disconnectInfo);
|
||||
typedef XrResult(XRAPI_PTR* PFN_xrRemotingGetConnectionStateMSFT)(XrInstance instance, XrSystemId systemId, XrRemotingConnectionStateMSFT* connectionState, XrRemotingDisconnectReasonMSFT* lastDisconnectReason);
|
||||
|
||||
typedef XrResult(XRAPI_PTR* PFN_xrRemotingSetSecureConnectionClientCallbacksMSFT)(XrInstance instance, XrSystemId systemId, const XrRemotingSecureConnectionClientCallbacksMSFT* secureConnectionClientCallbacks);
|
||||
typedef XrResult(XRAPI_PTR* PFN_xrRemotingSetSecureConnectionServerCallbacksMSFT)(XrInstance instance, XrSystemId systemId, const XrRemotingSecureConnectionServerCallbacksMSFT* secureConnectionServerCallbacks);
|
||||
|
||||
#ifndef XR_NO_PROTOTYPES
|
||||
XRAPI_ATTR XrResult XRAPI_CALL xrRemotingSetContextPropertiesMSFT(
|
||||
XrInstance instance,
|
||||
XrSystemId systemId,
|
||||
const XrRemotingRemoteContextPropertiesMSFT* contextProperties);
|
||||
|
||||
XRAPI_ATTR XrResult XRAPI_CALL xrRemotingConnectMSFT(
|
||||
XrInstance instance,
|
||||
XrSystemId systemId,
|
||||
const XrRemotingConnectInfoMSFT* connectInfo);
|
||||
|
||||
XRAPI_ATTR XrResult XRAPI_CALL xrRemotingListenMSFT(
|
||||
XrInstance instance,
|
||||
XrSystemId systemId,
|
||||
const XrRemotingListenInfoMSFT* listenInfo);
|
||||
|
||||
XRAPI_ATTR XrResult XRAPI_CALL xrRemotingDisconnectMSFT(
|
||||
XrInstance instance,
|
||||
XrSystemId systemId,
|
||||
const XrRemotingDisconnectInfoMSFT* disconnectInfo);
|
||||
|
||||
XRAPI_ATTR XrResult XRAPI_CALL xrRemotingGetConnectionStateMSFT(
|
||||
XrInstance instance,
|
||||
XrSystemId systemId,
|
||||
XrRemotingConnectionStateMSFT* connectionState,
|
||||
XrRemotingDisconnectReasonMSFT* lastDisconnectReason);
|
||||
|
||||
XRAPI_ATTR XrResult XRAPI_CALL xrRemotingSetSecureConnectionClientCallbacksMSFT(
|
||||
XrInstance instance,
|
||||
XrSystemId systemId,
|
||||
const XrRemotingSecureConnectionClientCallbacksMSFT* secureConnectionClientCallbacks);
|
||||
|
||||
XRAPI_ATTR XrResult XRAPI_CALL xrRemotingSetSecureConnectionServerCallbacksMSFT(
|
||||
XrInstance instance,
|
||||
XrSystemId systemId,
|
||||
const XrRemotingSecureConnectionServerCallbacksMSFT* secureConnectionServerCallbacks);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Microsoft.MixedReality.QR" version="0.5.2102" targetFramework="native" />
|
||||
<package id="Microsoft.VCRTForwarders.140" version="1.0.6" targetFramework="native" />
|
||||
<package id="Microsoft.Windows.CppWinRT" version="2.0.200729.8" targetFramework="native" />
|
||||
</packages>
|
|
@ -0,0 +1,35 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
|
||||
using UnrealBuildTool;
|
||||
|
||||
public class MicrosoftOpenXREditor : ModuleRules
|
||||
{
|
||||
public MicrosoftOpenXREditor(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[] {
|
||||
"Core",
|
||||
"CoreUObject",
|
||||
"InputCore",
|
||||
"Engine",
|
||||
"Slate",
|
||||
"SlateCore",
|
||||
"EditorStyle",
|
||||
"EditorWidgets",
|
||||
"DesktopWidgets",
|
||||
"PropertyEditor",
|
||||
"UnrealEd",
|
||||
"SharedSettingsWidgets",
|
||||
"TargetPlatform",
|
||||
"RenderCore",
|
||||
"MicrosoftOpenXRRuntimeSettings"
|
||||
}
|
||||
);
|
||||
|
||||
PrivateIncludePathModuleNames.AddRange(
|
||||
new string[] {
|
||||
"Settings"
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
|
||||
#include "MicrosoftOpenXRDetails.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FMicrosoftOpenXRDetails"
|
||||
|
||||
TSharedRef<IDetailCustomization> FMicrosoftOpenXRDetails::MakeInstance()
|
||||
{
|
||||
return MakeShareable(new FMicrosoftOpenXRDetails);
|
||||
}
|
||||
|
||||
void FMicrosoftOpenXRDetails::CustomizeDetails(IDetailLayoutBuilder& DetailBuilder)
|
||||
{
|
||||
statusTextWidget = SNew(STextBlock);
|
||||
|
||||
UMicrosoftOpenXRRuntimeSettings::Get()->OnRemotingStatusChanged.BindSP(this, &FMicrosoftOpenXRDetails::SetStatusText);
|
||||
|
||||
IDetailCategoryBuilder& remotingCategory = DetailBuilder.EditCategory(TEXT("OpenXR Holographic Remoting"));
|
||||
remotingCategory.AddCustomRow(LOCTEXT("Connect Button", "Connect Button"))
|
||||
[
|
||||
SNew(SButton)
|
||||
.Text(LOCTEXT("Connect", "Connect"))
|
||||
.OnClicked_Raw(this, &FMicrosoftOpenXRDetails::OnConnectButtonClicked)
|
||||
.IsEnabled_Raw(this, &FMicrosoftOpenXRDetails::AreButtonsEnabled)
|
||||
];
|
||||
|
||||
remotingCategory.AddCustomRow(LOCTEXT("Disconnect Button", "Disconnect Button"))
|
||||
[
|
||||
SNew(SButton)
|
||||
.Text(LOCTEXT("Disconnect", "Disconnect"))
|
||||
.OnClicked_Raw(this, &FMicrosoftOpenXRDetails::OnDisconnectButtonClicked)
|
||||
.IsEnabled_Raw(this, &FMicrosoftOpenXRDetails::AreButtonsEnabled)
|
||||
];
|
||||
|
||||
remotingCategory.AddCustomRow(LOCTEXT("Status Text", "Status Text"))[statusTextWidget.ToSharedRef()];
|
||||
}
|
||||
|
||||
void FMicrosoftOpenXRDetails::SetStatusText(FString message, FLinearColor statusColor)
|
||||
{
|
||||
if (statusTextWidget == nullptr)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
statusTextWidget->SetText(FText::FromString(message));
|
||||
statusTextWidget->SetColorAndOpacity(FSlateColor(statusColor));
|
||||
}
|
||||
|
||||
FReply FMicrosoftOpenXRDetails::OnConnectButtonClicked()
|
||||
{
|
||||
MicrosoftOpenXR::RemotingConnectionData data;
|
||||
UMicrosoftOpenXRRuntimeSettings::ParseAddress(
|
||||
UMicrosoftOpenXRRuntimeSettings::Get()->RemoteHoloLensIP,
|
||||
data.IP, data.Port);
|
||||
data.Bitrate = UMicrosoftOpenXRRuntimeSettings::Get()->MaxBitrate;
|
||||
data.EnableAudio = UMicrosoftOpenXRRuntimeSettings::Get()->EnableAudio;
|
||||
data.ConnectionType = UMicrosoftOpenXRRuntimeSettings::Get()->ConnectionType;
|
||||
data.ConnectionCodec = UMicrosoftOpenXRRuntimeSettings::Get()->ConnectionCodec;
|
||||
|
||||
UMicrosoftOpenXRRuntimeSettings::Get()->OnRemotingConnect.ExecuteIfBound(data);
|
||||
|
||||
return FReply::Handled();
|
||||
}
|
||||
|
||||
FReply FMicrosoftOpenXRDetails::OnDisconnectButtonClicked()
|
||||
{
|
||||
UMicrosoftOpenXRRuntimeSettings::Get()->OnRemotingDisconnect.ExecuteIfBound();
|
||||
|
||||
return FReply::Handled();
|
||||
}
|
||||
|
||||
bool FMicrosoftOpenXRDetails::AreButtonsEnabled() const
|
||||
{
|
||||
UMicrosoftOpenXRRuntimeSettings* settings = UMicrosoftOpenXRRuntimeSettings::Get();
|
||||
return settings->bEnableRemotingForEditor;
|
||||
}
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
|
@ -0,0 +1,37 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IDetailCustomNodeBuilder.h"
|
||||
#include "PropertyHandle.h"
|
||||
#include "IDetailCustomization.h"
|
||||
#include "IPropertyTypeCustomization.h"
|
||||
#include "PropertyCustomizationHelpers.h"
|
||||
|
||||
#include "Widgets/Text/STextBlock.h"
|
||||
#include "Widgets/Input/SButton.h"
|
||||
|
||||
#include "DetailLayoutBuilder.h"
|
||||
#include "DetailCategoryBuilder.h"
|
||||
#include "IDetailPropertyRow.h"
|
||||
#include "DetailWidgetRow.h"
|
||||
#include "IDetailGroup.h"
|
||||
|
||||
#include "MicrosoftOpenXRRuntimeSettings.h"
|
||||
|
||||
class FMicrosoftOpenXRDetails : public IDetailCustomization
|
||||
{
|
||||
public:
|
||||
static TSharedRef<IDetailCustomization> MakeInstance();
|
||||
virtual void CustomizeDetails(IDetailLayoutBuilder& DetailBuilder) override;
|
||||
|
||||
private:
|
||||
FString StatusText;
|
||||
TSharedPtr<STextBlock> statusTextWidget;
|
||||
|
||||
void SetStatusText(FString message, FLinearColor statusColor);
|
||||
FReply OnConnectButtonClicked();
|
||||
FReply OnDisconnectButtonClicked();
|
||||
bool AreButtonsEnabled() const;
|
||||
};
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
|
||||
#include "MicrosoftOpenXRRuntimeSettings.h"
|
||||
#include "Modules/ModuleInterface.h"
|
||||
#include "ISettingsModule.h"
|
||||
#include "Modules/ModuleManager.h"
|
||||
#include "PropertyEditorModule.h"
|
||||
#include "MicrosoftOpenXRDetails.h"
|
||||
|
||||
#define LOCTEXT_NAMESPACE "FMicrosoftOpenXREditorModule"
|
||||
|
||||
|
||||
/**
|
||||
* Module for MicrosoftOpenXR platform editor utilities
|
||||
*/
|
||||
class FMicrosoftOpenXREditorModule
|
||||
: public IModuleInterface
|
||||
{
|
||||
virtual void StartupModule() override
|
||||
{
|
||||
FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked<FPropertyEditorModule>("PropertyEditor");
|
||||
PropertyModule.RegisterCustomClassLayout(FName("MicrosoftOpenXRRuntimeSettings"), FOnGetDetailCustomizationInstance::CreateStatic(&FMicrosoftOpenXRDetails::MakeInstance));
|
||||
PropertyModule.NotifyCustomizationModuleChanged();
|
||||
|
||||
// register settings
|
||||
ISettingsModule* SettingsModule = FModuleManager::GetModulePtr<ISettingsModule>("Settings");
|
||||
|
||||
if (SettingsModule != nullptr)
|
||||
{
|
||||
SettingsModule->RegisterSettings("Project", "Platforms", "MicrosoftOpenXR",
|
||||
// Using "Windows Mixed Reality" here to preserve the remoting location from legacy WMR in Project Settings/ Windows Mixed Reality
|
||||
LOCTEXT("RuntimeSettingsName", "Windows Mixed Reality"),
|
||||
LOCTEXT("RuntimeSettingsDescription", "Project settings for Mixed Reality Platform Extensions"),
|
||||
GetMutableDefault<UMicrosoftOpenXRRuntimeSettings>()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void ShutdownModule() override
|
||||
{
|
||||
ISettingsModule* SettingsModule = FModuleManager::GetModulePtr<ISettingsModule>("Settings");
|
||||
|
||||
if (SettingsModule != nullptr)
|
||||
{
|
||||
SettingsModule->UnregisterSettings("Project", "Platforms", "MicrosoftOpenXR");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
IMPLEMENT_MODULE(FMicrosoftOpenXREditorModule, MicrosoftOpenXREditor);
|
||||
|
||||
#undef LOCTEXT_NAMESPACE
|
|
@ -0,0 +1,28 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
|
||||
using UnrealBuildTool;
|
||||
|
||||
public class MicrosoftOpenXRRuntimeSettings : ModuleRules
|
||||
{
|
||||
public MicrosoftOpenXRRuntimeSettings(ReadOnlyTargetRules Target) : base(Target)
|
||||
{
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"Core",
|
||||
"CoreUObject",
|
||||
"Engine"
|
||||
}
|
||||
);
|
||||
|
||||
if (Target.Type == TargetRules.TargetType.Editor || Target.Type == TargetRules.TargetType.Program)
|
||||
{
|
||||
PrivateDependencyModuleNames.AddRange(
|
||||
new string[]
|
||||
{
|
||||
"TargetPlatform"
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
|
||||
#include "MicrosoftOpenXRRuntimeSettings.h"
|
||||
#include "Misc/ConfigCacheIni.h"
|
||||
#include "CoreGlobals.h"
|
||||
#include "UObject/Package.h"
|
||||
|
||||
UMicrosoftOpenXRRuntimeSettings* UMicrosoftOpenXRRuntimeSettings::MicrosoftOpenXRSettingsSingleton = nullptr;
|
||||
|
||||
#if WITH_EDITOR
|
||||
void UMicrosoftOpenXRRuntimeSettings::PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent)
|
||||
{
|
||||
GConfig->Flush(false);
|
||||
}
|
||||
#endif
|
||||
|
||||
UMicrosoftOpenXRRuntimeSettings* UMicrosoftOpenXRRuntimeSettings::Get()
|
||||
{
|
||||
if (MicrosoftOpenXRSettingsSingleton == nullptr && GetTransientPackage() != nullptr)
|
||||
{
|
||||
static const TCHAR* SettingsContainerName = TEXT("MicrosoftOpenXRRuntimeSettingsContainer");
|
||||
|
||||
MicrosoftOpenXRSettingsSingleton = FindObject<UMicrosoftOpenXRRuntimeSettings>(GetTransientPackage(), SettingsContainerName);
|
||||
|
||||
if (MicrosoftOpenXRSettingsSingleton == nullptr)
|
||||
{
|
||||
MicrosoftOpenXRSettingsSingleton = NewObject<UMicrosoftOpenXRRuntimeSettings>(
|
||||
GetTransientPackage(), UMicrosoftOpenXRRuntimeSettings::StaticClass(), SettingsContainerName);
|
||||
MicrosoftOpenXRSettingsSingleton->AddToRoot();
|
||||
}
|
||||
}
|
||||
return MicrosoftOpenXRSettingsSingleton;
|
||||
}
|
||||
|
||||
bool UMicrosoftOpenXRRuntimeSettings::ParseAddress(const FString& StringToParse, FString& Address, uint32& Port)
|
||||
{
|
||||
FString PortStr;
|
||||
|
||||
if (StringToParse.Len() == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (StringToParse.Split(TEXT(":"), &Address, &PortStr))
|
||||
{
|
||||
// Parse the input in format "IP:Port"
|
||||
Port = FCString::Atoi(*PortStr);
|
||||
}
|
||||
else if (!StringToParse.Contains("."))
|
||||
{
|
||||
// If the given ip is not valid, try using it as a port for a listen connection to the remoting player.
|
||||
Address = TEXT("0.0.0.0");
|
||||
Port = FCString::Atoi(*StringToParse);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise only an IP is set. Use the default port.
|
||||
Address = StringToParse;
|
||||
Port = 8265;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
|
||||
#include "Modules/ModuleInterface.h"
|
||||
#include "Modules/ModuleManager.h"
|
||||
|
||||
IMPLEMENT_MODULE(FDefaultModuleImpl, MicrosoftOpenXRRuntimeSettings);
|
|
@ -0,0 +1,90 @@
|
|||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "UObject/ObjectMacros.h"
|
||||
#include "UObject/Object.h"
|
||||
#include "Engine/EngineTypes.h"
|
||||
|
||||
#include "MicrosoftOpenXRRuntimeSettings.generated.h"
|
||||
|
||||
UENUM()
|
||||
enum class RemotingConnectionType
|
||||
{
|
||||
Connect = 0,
|
||||
Listen = 1
|
||||
};
|
||||
|
||||
// This will be cast to XrRemotingVideoCodecMSFT, IDs must match
|
||||
UENUM()
|
||||
enum class RemotingCodec
|
||||
{
|
||||
Any = 0,
|
||||
H264 = 1,
|
||||
H265 = 2
|
||||
};
|
||||
|
||||
namespace MicrosoftOpenXR
|
||||
{
|
||||
struct RemotingConnectionData
|
||||
{
|
||||
FString IP;
|
||||
uint32 Port = 8265;
|
||||
int Bitrate = 8000;
|
||||
bool EnableAudio = false;
|
||||
RemotingConnectionType ConnectionType = RemotingConnectionType::Connect;
|
||||
RemotingCodec ConnectionCodec = RemotingCodec::Any;
|
||||
};
|
||||
} // namespace MicrosoftOpenXR
|
||||
|
||||
DECLARE_DELEGATE_TwoParams(FMicrosoftOpenXRRemotingStatusChanged, FString /*RemotingMessage*/, FLinearColor /*StatusColor*/);
|
||||
DECLARE_DELEGATE_OneParam(FMicrosoftOpenXRRemotingConnect, MicrosoftOpenXR::RemotingConnectionData);
|
||||
DECLARE_DELEGATE(FMicrosoftOpenXRRemotingDisconnect);
|
||||
|
||||
/**
|
||||
* Implements the settings for the WindowsMixedReality runtime platform.
|
||||
*/
|
||||
UCLASS(config=EditorPerProjectUserSettings)
|
||||
class MICROSOFTOPENXRRUNTIMESETTINGS_API UMicrosoftOpenXRRuntimeSettings : public UObject
|
||||
{
|
||||
public:
|
||||
GENERATED_BODY()
|
||||
|
||||
FMicrosoftOpenXRRemotingStatusChanged OnRemotingStatusChanged;
|
||||
FMicrosoftOpenXRRemotingConnect OnRemotingConnect;
|
||||
FMicrosoftOpenXRRemotingDisconnect OnRemotingDisconnect;
|
||||
|
||||
static UMicrosoftOpenXRRuntimeSettings* Get();
|
||||
|
||||
static bool ParseAddress(const FString& StringToParse, FString& Address, uint32& Port);
|
||||
|
||||
#if WITH_EDITOR
|
||||
virtual void PostEditChangeProperty(struct FPropertyChangedEvent& PropertyChangedEvent) override;
|
||||
#endif // WITH_EDITOR
|
||||
|
||||
UPROPERTY(GlobalConfig, EditAnywhere, Category = "OpenXR Holographic Remoting", Meta = (ConfigRestartRequired = true, DisplayName = "Enable Remoting For Editor (Requires Restart)", Tooltip = "If true, start with a valid HMD to enable connecting via remoting. Editor restart required."))
|
||||
bool bEnableRemotingForEditor = false;
|
||||
|
||||
/** The IP of the HoloLens to remote to. */
|
||||
UPROPERTY(GlobalConfig, EditAnywhere, Category = "OpenXR Holographic Remoting", Meta = (EditCondition = "bEnableRemotingForEditor", DisplayName = "IP of HoloLens to remote to."))
|
||||
FString RemoteHoloLensIP;
|
||||
|
||||
UPROPERTY(GlobalConfig, EditAnywhere, Category = "OpenXR Holographic Remoting", Meta = (EditCondition = "bEnableRemotingForEditor", DisplayName = "Automatically connect to remote device."))
|
||||
bool bAutoConnectRemoting = false;
|
||||
|
||||
UPROPERTY(GlobalConfig, EditAnywhere, Category = "OpenXR Holographic Remoting", Meta = (EditCondition = "bEnableRemotingForEditor", DisplayName = "Max network transfer rate (kb/s)."))
|
||||
unsigned int MaxBitrate = 8000;
|
||||
|
||||
UPROPERTY(GlobalConfig, EditAnywhere, Category = "OpenXR Holographic Remoting", Meta = (EditCondition = "bEnableRemotingForEditor", DisplayName = "Use audio from PC when remoting."))
|
||||
bool EnableAudio = false;
|
||||
|
||||
UPROPERTY(GlobalConfig, EditAnywhere, Category = "OpenXR Holographic Remoting", Meta = (EditCondition = "bEnableRemotingForEditor", DisplayName = "Connection Type."))
|
||||
RemotingConnectionType ConnectionType = RemotingConnectionType::Connect;
|
||||
|
||||
UPROPERTY(GlobalConfig, EditAnywhere, Category = "OpenXR Holographic Remoting", Meta = (EditCondition = "bEnableRemotingForEditor", DisplayName = "Connection Codec."))
|
||||
RemotingCodec ConnectionCodec = RemotingCodec::Any;
|
||||
|
||||
private:
|
||||
static class UMicrosoftOpenXRRuntimeSettings* MicrosoftOpenXRSettingsSingleton;
|
||||
};
|
|
@ -0,0 +1,223 @@
|
|||
MICROSOFT SOFTWARE LICENSE TERMS
|
||||
|
||||
MICROSOFT HOLOGRAPHIC REMOTING
|
||||
|
||||
IF YOU LIVE IN (OR ARE A BUSINESS WITH YOUR PRINCIPAL PLACE OF BUSINESS IN) THE
|
||||
UNITED STATES, PLEASE READ THE “BINDING ARBITRATION AND CLASS ACTION WAIVER”
|
||||
SECTION BELOW. IT AFFECTS HOW DISPUTES ARE RESOLVED.
|
||||
|
||||
These license terms are an agreement between you and Microsoft Corporation (or one of its affiliates). They
|
||||
apply to the software named above and any Microsoft services or software updates (except to the extent such
|
||||
services or updates are accompanied by new or additional terms, in which case those different terms apply
|
||||
prospectively and do not alter your or Microsoft’s rights relating to pre-updated software or services). IF YOU
|
||||
COMPLY WITH THESE LICENSE TERMS, YOU HAVE THE RIGHTS BELOW. BY USING THE SOFTWARE, YOU ACCEPT THESE TERMS.
|
||||
|
||||
1. INSTALLATION AND USE RIGHTS.
|
||||
|
||||
a) General. You may install and use any number of copies of the software to develop and test your
|
||||
applications.
|
||||
|
||||
b) Third Party Software. The software may include third party applications that are licensed to you
|
||||
under this agreement or under their own terms. License terms, notices, and acknowledgements, if
|
||||
any, for the third party applications may be accessible online at http://aka.ms/thirdpartynotices or in
|
||||
an accompanying notices file. Even if such applications are governed by other agreements, the
|
||||
disclaimer, limitations on, and exclusions of damages below also apply to the extent allowed by
|
||||
applicable law.
|
||||
|
||||
c) Competitive Benchmarking. If you are a direct competitor, and you access or use the software for
|
||||
purposes of competitive benchmarking, analysis, or intelligence gathering, you waive as against
|
||||
Microsoft, its subsidiaries, and its affiliated companies (including prospectively) any competitive use,
|
||||
access, and benchmarking test restrictions in the terms governing your software to the extent your
|
||||
terms of use are, or purport to be, more restrictive than Microsoft’s terms. If you do not waive any
|
||||
such purported restrictions in the terms governing your software, you are not allowed to access or use
|
||||
this software, and will not do so.
|
||||
|
||||
2. DISTRIBUTABLE CODE. The software may contain code you are permitted to distribute (i.e. make
|
||||
available for third parties) in applications you develop, as described in this Section.
|
||||
|
||||
a) Distribution Rights. The code and test files described below are distributable if included with the
|
||||
software.
|
||||
|
||||
i. REDIST.TXT Files. You may copy and distribute the object code form of code listed on the REDIST
|
||||
list in the software, if any, or listed at redist.txt;
|
||||
|
||||
ii. Sample Code, Templates, and Styles. You may copy, modify, and distribute the source and object
|
||||
code form of code marked as “sample”, “template”, “simple styles”, and “sketch styles”; and
|
||||
|
||||
iii. Third Party Distribution. You may permit distributors of your applications to copy and distribute
|
||||
any of this distributable code you elect to distribute with your applications.
|
||||
|
||||
b) Distribution Requirements. For any code you distribute, you must:
|
||||
|
||||
i. add significant primary functionality to it in your applications;
|
||||
|
||||
ii. require distributors and external end users to agree to terms that protect it and Microsoft at least
|
||||
as much as this agreement; and
|
||||
|
||||
iii. indemnify, defend, and hold harmless Microsoft from any claims, including attorneys’ fees, related
|
||||
to the distribution or use of your applications, except to the extent that any claim is based solely
|
||||
on the unmodified distributable code.
|
||||
|
||||
c) Distribution Restrictions. You may not:
|
||||
|
||||
i. use Microsoft’s trademarks or trade dress in your application in any way that suggests your
|
||||
application comes from or is endorsed by Microsoft; or
|
||||
|
||||
ii. modify or distribute the source code of any distributable code so that any part of it becomes
|
||||
subject to any license that requires that the distributable code, any other part of the software, or
|
||||
any of Microsoft’s other intellectual property be disclosed or distributed in source code form, or
|
||||
that others have the right to modify it.
|
||||
|
||||
3. DATA COLLECTION. The software may collect information about you and your use of the software and
|
||||
send that to Microsoft. Microsoft may use this information to provide services and improve Microsoft’s
|
||||
products and services. Your opt-out rights, if any, are described in the product documentation. Some
|
||||
features in the software may enable collection of data from users of your applications that access or use
|
||||
the software. If you use these features to enable data collection in your applications, you must comply
|
||||
with applicable law, including getting any required user consent, and maintain a prominent privacy policy
|
||||
that accurately informs users about how you use, collect, and share their data. You can learn more about
|
||||
Microsoft’s data collection and use in the product documentation and the Microsoft Privacy Statement at https://go.microsoft.com/fwlink/?LinkId=521839. You agree to comply with all applicable provisions of the
|
||||
Microsoft Privacy Statement.
|
||||
|
||||
4. SCOPE OF LICENSE. The software is licensed, not sold. Microsoft reserves all other rights. Unless
|
||||
applicable law gives you more rights despite this limitation, you will not (and have no right to):
|
||||
|
||||
a) work around any technical limitations in the software that only allow you to use it in certain ways;
|
||||
|
||||
b) reverse engineer, decompile, or disassemble the software, or attempt to do so, except and only to the
|
||||
extent permitted by licensing terms governing the use of open-source components that may be
|
||||
included with the software;
|
||||
|
||||
c) remove, minimize, block, or modify any notices of Microsoft or its suppliers in the software;
|
||||
|
||||
d) use the software in any way that is against the law or to create or propagate malware; or
|
||||
|
||||
e) share, publish, distribute, or lend the software (except for any distributable code, subject to the terms
|
||||
above), provide the software as a stand-alone hosted solution for others to use, or transfer the
|
||||
software or this agreement to any third party.
|
||||
|
||||
5. EXPORT RESTRICTIONS. You must comply with all domestic and international export laws and
|
||||
regulations that apply to the software, which include restrictions on destinations, end users, and end use.
|
||||
For further information on export restrictions, visit http://aka.ms/exporting.
|
||||
|
||||
6. SUPPORT SERVICES. Microsoft is not obligated under this agreement to provide any support services for
|
||||
the software. Any support provided is “as is”, “with all faults”, and without warranty of any kind.
|
||||
|
||||
7. UPDATES. The software may periodically check for updates, and download and install them for you. You
|
||||
may obtain updates only from Microsoft or authorized sources. Microsoft may need to update your system
|
||||
to provide you with updates. You agree to receive these automatic updates without any additional notice.
|
||||
Updates may not include or support all existing software features, services, or peripheral devices.
|
||||
|
||||
8. BINDING ARBITRATION AND CLASS ACTION WAIVER. This Section applies if you live in (or, if
|
||||
a business, your principal place of business is in) the United States. If you and Microsoft have a
|
||||
dispute, you and Microsoft agree to try for 60 days to resolve it informally. If you and Microsoft can’t, you
|
||||
and Microsoft agree to binding individual arbitration before the American Arbitration Association
|
||||
under the Federal Arbitration Act (“FAA”), and not to sue in court in front of a judge or jury. Instead,
|
||||
a neutral arbitrator will decide. Class action lawsuits, class-wide arbitrations, private attorney-
|
||||
general actions, and any other proceeding where someone acts in a representative capacity are not
|
||||
allowed; nor is combining individual proceedings without the consent of all parties. The complete
|
||||
Arbitration Agreement contains more terms and is at http://aka.ms/arb-agreement-1. You and Microsoft
|
||||
agree to these terms.
|
||||
|
||||
9. ENTIRE AGREEMENT. This agreement, and any other terms Microsoft may provide for supplements,
|
||||
updates, or third-party applications, is the entire agreement for the software.
|
||||
|
||||
10. APPLICABLE LAW AND PLACE TO RESOLVE DISPUTES. If you acquired the software in the United
|
||||
States or Canada, the laws of the state or province where you live (or, if a business, where your principal
|
||||
place of business is located) govern the interpretation of this agreement, claims for its breach, and all
|
||||
other claims (including consumer protection, unfair competition, and tort claims), regardless of conflict of
|
||||
laws principles, except that the FAA governs everything related to arbitration. If you acquired the software
|
||||
in any other country, its laws apply, except that the FAA governs everything related to arbitration. If U.S.
|
||||
federal jurisdiction exists, you and Microsoft consent to exclusive jurisdiction and venue in the federal court
|
||||
in King County, Washington for all disputes heard in court (excluding arbitration). If not, you and Microsoft
|
||||
consent to exclusive jurisdiction and venue in the Superior Court of King County, Washington for all
|
||||
disputes heard in court (excluding arbitration).
|
||||
|
||||
11. CONSUMER RIGHTS; REGIONAL VARIATIONS. This agreement describes certain legal rights. You
|
||||
may have other rights, including consumer rights, under the laws of your state or country. Separate and
|
||||
apart from your relationship with Microsoft, you may also have rights with respect to the party from which
|
||||
you acquired the software. This agreement does not change those other rights if the laws of your state or
|
||||
country do not permit it to do so. For example, if you acquired the software in one of the below regions, or
|
||||
mandatory country law applies, then the following provisions apply to you:
|
||||
|
||||
a) Australia. You have statutory guarantees under the Australian Consumer Law and nothing in this
|
||||
agreement is intended to affect those rights.
|
||||
|
||||
b) Canada. If you acquired this software in Canada, you may stop receiving updates by turning off the
|
||||
automatic update feature, disconnecting your device from the Internet (if and when you re-connect to
|
||||
the Internet, however, the software will resume checking for and installing updates), or uninstalling
|
||||
the software. The product documentation, if any, may also specify how to turn off updates for your
|
||||
specific device or software.
|
||||
|
||||
c) Germany and Austria.
|
||||
|
||||
i. Warranty. The properly licensed software will perform substantially as described in any Microsoft
|
||||
materials that accompany the software. However, Microsoft gives no contractual guarantee in
|
||||
relation to the licensed software.
|
||||
|
||||
ii. Limitation of Liability. In case of intentional conduct, gross negligence, claims based on the
|
||||
Product Liability Act, as well as, in case of death or personal or physical injury, Microsoft is liable
|
||||
according to the statutory law.
|
||||
|
||||
Subject to the foregoing clause ii., Microsoft will only be liable for slight negligence if Microsoft is in
|
||||
breach of such material contractual obligations, the fulfillment of which facilitate the due performance
|
||||
of this agreement, the breach of which would endanger the purpose of this agreement and the
|
||||
compliance with which a party may constantly trust in (so-called "cardinal obligations"). In other cases
|
||||
of slight negligence, Microsoft will not be liable for slight negligence.
|
||||
|
||||
12. DISCLAIMER OF WARRANTY. THE SOFTWARE IS LICENSED “AS IS.” YOU BEAR THE RISK OF
|
||||
USING IT. MICROSOFT GIVES NO EXPRESS WARRANTIES, GUARANTEES, OR CONDITIONS.
|
||||
TO THE EXTENT PERMITTED UNDER APPLICABLE LAWS, MICROSOFT EXCLUDES ALL IMPLIED
|
||||
WARRANTIES, INCLUDING MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND
|
||||
NON-INFRINGEMENT.
|
||||
|
||||
13. LIMITATION ON AND EXCLUSION OF DAMAGES. IF YOU HAVE ANY BASIS FOR RECOVERING
|
||||
DAMAGES DESPITE THE PRECEDING DISCLAIMER OF WARRANTY, YOU CAN RECOVER FROM
|
||||
MICROSOFT AND ITS SUPPLIERS ONLY DIRECT DAMAGES UP TO U.S. $5.00. YOU CANNOT
|
||||
RECOVER ANY OTHER DAMAGES, INCLUDING CONSEQUENTIAL, LOST PROFITS, SPECIAL,
|
||||
INDIRECT, OR INCIDENTAL DAMAGES.
|
||||
|
||||
This limitation applies to (a) anything related to the software, services, content (including
|
||||
code) on third party Internet sites, or third party applications; and (b) claims for breach of
|
||||
contract, warranty, guarantee, or condition; strict liability, negligence, or other tort; or any
|
||||
other claim; in each case to the extent permitted by applicable law.
|
||||
|
||||
It also applies even if Microsoft knew or should have known about the possibility of the
|
||||
damages. The above limitation or exclusion may not apply to you because your state,
|
||||
province, or country may not allow the exclusion or limitation of incidental, consequential, or
|
||||
other damages.
|
||||
|
||||
Please note: As this software is distributed in Canada, some of the clauses in this agreement are
|
||||
provided below in French.
|
||||
|
||||
Remarque: Ce logiciel étant distribué au Canada, certaines des clauses dans ce contrat sont
|
||||
fournies ci-dessous en français.
|
||||
|
||||
EXONÉRATION DE GARANTIE. Le logiciel visé par une licence est offert « tel quel ». Toute
|
||||
utilisation de ce logiciel est à votre seule risque et péril. Microsoft n’accorde aucune autre
|
||||
garantie expresse. Vous pouvez bénéficier de droits additionnels en vertu du droit local sur la
|
||||
protection des consommateurs, que ce contrat ne peut modifier. La ou elles sont permises par le
|
||||
droit locale, les garanties implicites de qualité marchande, d’adéquation à un usage particulier et
|
||||
d’absence de contrefaçon sont exclues.
|
||||
|
||||
LIMITATION DES DOMMAGES-INTÉRÊTS ET EXCLUSION DE RESPONSABILITÉ POUR LES
|
||||
DOMMAGES. Vous pouvez obtenir de Microsoft et de ses fournisseurs une indemnisation en cas de
|
||||
dommages directs uniquement à hauteur de 5,00 $ US. Vous ne pouvez prétendre à aucune
|
||||
indemnisation pour les autres dommages, y compris les dommages spéciaux, indirects ou
|
||||
accessoires et pertes de bénéfices.
|
||||
|
||||
Cette limitation concerne:
|
||||
|
||||
• tout ce qui est relié au logiciel, aux services ou au contenu (y compris le code) figurant sur
|
||||
des sites Internet tiers ou dans des programmes tiers; et
|
||||
|
||||
• les réclamations au titre de violation de contrat ou de garantie, ou au titre de responsabilité
|
||||
stricte, de négligence ou d’une autre faute dans la limite autorisée par la loi en vigueur.
|
||||
|
||||
Elle s’applique également, même si Microsoft connaissait ou devrait connaître l’éventualité d’un
|
||||
tel dommage. Si votre pays n’autorise pas l’exclusion ou la limitation de responsabilité pour les
|
||||
dommages indirects, accessoires ou de quelque nature que ce soit, il se peut que la limitation ou
|
||||
l’exclusion ci-dessus ne s’appliquera pas à votre égard.
|
||||
EFFET JURIDIQUE. Le présent contrat décrit certains droits juridiques. Vous pourriez avoir
|
||||
d’autres droits prévus par les lois de votre pays. Le présent contrat ne modifie pas les droits que
|
||||
vous confèrent les lois de votre pays si celles-ci ne le permettent pas.
|
||||
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче