FoveHMD.h 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. #pragma once
  2. // Unreal headeers
  3. #include "Engine.h"
  4. #include "HeadMountedDisplay.h"
  5. #include "SceneViewExtension.h"
  6. #include "Templates/RefCounting.h"
  7. #include <Runtime/Launch/Resources/Version.h>
  8. // Fove headers
  9. #include "FoveTypes.h"
  10. // Make sure that we have the macros needed to specialize the build for different versions of Unreal Engine
  11. #if !defined(ENGINE_MAJOR_VERSION) || !defined(ENGINE_MINOR_VERSION)
  12. static_assert(false, "Unable to find Unreal version macros");
  13. #endif
  14. // Check the allowed version
  15. // If this check fails, this version of unreal is not supported, but patches are welcome!
  16. #if ENGINE_MAJOR_VERSION != 4 || ENGINE_MINOR_VERSION >= 18 || ENGINE_MINOR_VERSION < 12
  17. static_assert(false, "This version of the Fove Unreal plugin only supports Unreal 4.12 through 4.17");
  18. #endif
  19. // Determine the base class for FFoveHMD
  20. #if ENGINE_MAJOR_VERSION >= 4 && ENGINE_MINOR_VERSION >= 18
  21. #include "HeadMountedDisplayBase.h"
  22. #define FOVEHMD_BASE_CLASS FHeadMountedDisplayBase
  23. #else
  24. #define FOVEHMD_BASE_CLASS IHeadMountedDisplay
  25. #endif
  26. // Forward declarations
  27. struct ID3D11Texture2D;
  28. class FoveRenderingBridge;
  29. class IRendererModule;
  30. namespace Fove
  31. {
  32. class IFVRHeadset;
  33. class IFVRCompositor;
  34. }
  35. class FOVEHMD_API FFoveHMD : public FOVEHMD_BASE_CLASS, public ISceneViewExtension, public TSharedFromThis<FFoveHMD, ESPMode::ThreadSafe>
  36. {
  37. public: // Generic
  38. // Construction / destruction
  39. FFoveHMD(TSharedRef<Fove::IFVRHeadset, ESPMode::ThreadSafe> headset, TUniquePtr<Fove::IFVRCompositor> compositor, Fove::SFVR_CompositorLayer layer);
  40. ~FFoveHMD() override;
  41. // Helper to return the global FFoveHMD object
  42. // Returns null if there is no HMD device, or if the current HMD device is not an FFoveHMD
  43. static FFoveHMD* Get();
  44. public: // General FOVE-Specific Functions
  45. // Getters for the FOVE C++ API objects
  46. // The full FOVE C++ API can be accessed via these helpers
  47. // Most of the commonly needed functions within the C++ API are exposed through other helpers in this class
  48. // Such helpers deal with coordinate conversions and Unreal types, so they are more convenient
  49. Fove::IFVRHeadset& GetHeadset() { return *FoveHeadset; }
  50. Fove::IFVRHeadset const& GetHeadset() const { return *FoveHeadset; }
  51. Fove::IFVRCompositor& GetCompositor() { return *FoveCompositor; }
  52. Fove::IFVRCompositor const& GetCompositor() const { return *FoveCompositor; }
  53. //! Returns whether the FOVE headset is connected
  54. bool IsHardwareConnected() const;
  55. // Returns true if all the FOVE hardware has been started correctly
  56. bool IsHardwareReady() const;
  57. public: // Eye tracking
  58. // Returns true if eye calibration is currently running
  59. // This generally means that any other content in the headset is at least partially obscured by the calibrator
  60. bool IsEyeTrackingCalibrating() const;
  61. // Starts calibration if the current user has no eye tracking calibration
  62. // This should be invoked at a point in your game before eye tracking is needed,
  63. // but while the calibration overlay is not a problem (eg. before a level starts).
  64. // In the event that calibration starts, you can call IsEyeTrackingCalibrating() to determine when it's finished
  65. // Returns false if there was an error
  66. bool EnsureEyeTrackingCalibration();
  67. // Returns the convergence point of the two eye rays via any of non-null out parameters
  68. // The ray returned will be one of the two eye rays
  69. // The distance field is the distance along the ray to the intersection point with the other eye ray
  70. // The accuracy field is an estimatation of the accuracy of the distance field, which may be zero, for example when one eye is blinking/disabled
  71. // This functionality is considered alpha. While you should use this function for eye tracking, it's best to do a raycast in the 3D world,
  72. // and only use the distance field (when available) to disambiguate between multiple hits.
  73. // The coordinates used here are world coordinates with (0,0,0) at the camera point.
  74. bool GetGazeConvergence(bool bRelativeToHMD, FVector* outRayOrigin, FVector* outRayDirection, float* outDistance, float* outAccuracy) const;
  75. // Sets outLeft/outRight to the direction of the eye gaze for that eye, if nonnull
  76. // Returns false if there's an error (output arguments will not be touched in that case)
  77. // if bRelativeToHMD is true, the rotiation of the HMD will be taken into account
  78. bool GetGazeVector(bool bRelativeToHMD, FVector* outLeft, FVector* outRight) const;
  79. // Sets outLeft/outRight to the direction of the eye gaze for that eye, if nonnull
  80. // The output coordinates are in 0 to 1 coordinates, where (0, 0) is the bottom left and (1, 1) is the top right of the screen
  81. // Returns false if there's an error (output arguments will not be touched in that case)
  82. bool GetGazeVector2D(FVector2D* outLeft, FVector2D* outRight) const;
  83. // Manual drift correction. This is experiemental, dont use it yet
  84. bool ManualDriftCorrection3D(FVector Location);
  85. // Sets outLeft/outRigh to true or false based on which eyes are being tracked, if nonnull
  86. // Returns false if there's an error (output arguments will not be touched in that case)
  87. bool CheckEyesTracked(bool* outLeft, bool* outRight);
  88. // Sets outLeft/outRight to true or false based on which eyes are closed, if nonnull
  89. // Returns false if there's an error (output arguments will not be touched in that case)
  90. bool CheckEyesClosed(bool* outLeft, bool* outRight);
  91. public: // FOVE-specific position tracking functions
  92. // Returns true if position tracking hardware has been enabled and initialized
  93. bool IsPositionReady() const;
  94. public: // IHeadMountedDisplay / FHeadMountedDisplayBase interface
  95. // 4.18 and later
  96. #if ENGINE_MAJOR_VERSION >= 4 && ENGINE_MINOR_VERSION >= 18
  97. FName GetSystemName() const override;
  98. bool EnumerateTrackedDevices(TArray<int, FDefaultAllocator>&, EXRTrackedDeviceType) override;
  99. void RefreshPoses() override;
  100. bool GetCurrentPose(int32, FQuat&, FVector&) override;
  101. float GetWorldToMetersScale() const override { return WorldToMetersScale; }
  102. #endif
  103. // 4.13 through 4.17
  104. #if ENGINE_MAJOR_VERSION >= 4 && ENGINE_MINOR_VERSION >= 13 && ENGINE_MINOR_VERSION < 18
  105. FName GetDeviceName() const override;
  106. #endif
  107. // 4.12 and later
  108. bool IsHMDConnected() override;
  109. bool IsHMDEnabled() const override;
  110. void EnableHMD(bool allow = true) override;
  111. EHMDDeviceType::Type GetHMDDeviceType() const override;
  112. bool GetHMDMonitorInfo(MonitorInfo&) override;
  113. void GetFieldOfView(float& OutHFOVInDegrees, float& OutVFOVInDegrees) const override;
  114. bool IsChromaAbCorrectionEnabled() const override;
  115. void SetInterpupillaryDistance(float NewInterpupillaryDistance) override;
  116. float GetInterpupillaryDistance() const override;
  117. bool DoesSupportPositionalTracking() const override;
  118. bool HasValidTrackingPosition() override;
  119. void RebaseObjectOrientationAndPosition(FVector& Position, FQuat& Orientation) const override;
  120. bool IsHeadTrackingAllowed() const override;
  121. void ResetOrientationAndPosition(float yaw = 0.f) override;
  122. void ResetOrientation(float Yaw = 0.f) override;
  123. void ResetPosition() override;
  124. void SetBaseRotation(const FRotator& BaseRot) override;
  125. FRotator GetBaseRotation() const override;
  126. void SetBaseOrientation(const FQuat& BaseOrient) override;
  127. FQuat GetBaseOrientation() const override;
  128. void OnBeginPlay(FWorldContext& InWorldContext) override;
  129. void OnEndPlay(FWorldContext& InWorldContext) override;
  130. void SetTrackingOrigin(EHMDTrackingOrigin::Type NewOrigin) override;
  131. EHMDTrackingOrigin::Type GetTrackingOrigin() override;
  132. #if ENGINE_MAJOR_VERSION >= 4 && ENGINE_MINOR_VERSION < 18 // Removed in 4.18
  133. void GetPositionalTrackingCameraProperties(FVector& OutOrigin, FQuat& OutOrientation, float& OutHFOV, float& OutVFOV, float& OutCameraDistance, float& OutNearPlane, float& OutFarPlane) const override;
  134. void GetCurrentOrientationAndPosition(FQuat& CurrentOrientation, FVector& CurrentPosition) override;
  135. TSharedPtr<ISceneViewExtension, ESPMode::ThreadSafe> GetViewExtension() override;
  136. void ApplyHmdRotation(APlayerController* PC, FRotator& ViewRotation) override;
  137. bool UpdatePlayerCamera(FQuat& CurrentOrientation, FVector& CurrentPosition) override;
  138. bool IsPositionalTrackingEnabled() const override;
  139. #endif
  140. #if ENGINE_MAJOR_VERSION >= 4 && ENGINE_MINOR_VERSION < 16 // Removed in 4.16
  141. bool Exec(UWorld* InWorld, const TCHAR* Cmd, FOutputDevice& Ar) override;
  142. bool EnablePositionalTracking(bool enable) override;
  143. bool IsInLowPersistenceMode() const override;
  144. void EnableLowPersistenceMode(bool Enable = true) override;
  145. #endif
  146. public: // IStereoRendering interface
  147. #if ENGINE_MAJOR_VERSION >= 4 && ENGINE_MINOR_VERSION >= 18
  148. FMatrix GetStereoProjectionMatrix(EStereoscopicPass) const override;
  149. #endif
  150. // 4.12 and later
  151. void SetClippingPlanes(float NCP, float FCP) override;
  152. void GetEyeRenderParams_RenderThread(const FRenderingCompositePassContext& Context, FVector2D& EyeToSrcUVScaleValue, FVector2D& EyeToSrcUVOffsetValue) const override;
  153. bool IsStereoEnabled() const override;
  154. bool EnableStereo(bool stereo = true) override;
  155. void AdjustViewRect(EStereoscopicPass StereoPass, int32& X, int32& Y, uint32& SizeX, uint32& SizeY) const override;
  156. void GetOrthoProjection(int32 RTWidth, int32 RTHeight, float OrthoDistance, FMatrix OrthoProjection[2]) const override;
  157. void InitCanvasFromView(FSceneView* InView, UCanvas* Canvas) override;
  158. #if ENGINE_MAJOR_VERSION >= 4 && ENGINE_MINOR_VERSION < 18 // Removed in 4.18
  159. void CalculateStereoViewOffset(EStereoscopicPass StereoPassType, const FRotator& ViewRotation, const float MetersToWorld, FVector& ViewLocation) override;
  160. FMatrix GetStereoProjectionMatrix(EStereoscopicPass StereoPassType, const float FOV) const override;
  161. void RenderTexture_RenderThread(FRHICommandListImmediate& RHICmdList, FTexture2DRHIParamRef BackBuffer, FTexture2DRHIParamRef SrcTexture) const override;
  162. void CalculateRenderTargetSize(const FViewport& Viewport, uint32& InOutSizeX, uint32& InOutSizeY) override;
  163. bool NeedReAllocateViewportRenderTarget(const FViewport& Viewport) override;
  164. bool ShouldUseSeparateRenderTarget() const override;
  165. void UpdateViewport(bool bUseSeparateRenderTarget, const FViewport& Viewport, SViewport*) override;
  166. #endif
  167. public: // ISceneViewExtension interface
  168. void SetupViewFamily(FSceneViewFamily& InViewFamily) override;
  169. void SetupView(FSceneViewFamily& InViewFamily, FSceneView& InView) override;
  170. void BeginRenderViewFamily(FSceneViewFamily& InViewFamily) override {}
  171. void PreRenderView_RenderThread(FRHICommandListImmediate& RHICmdList, FSceneView& InView) override;
  172. void PreRenderViewFamily_RenderThread(FRHICommandListImmediate& RHICmdList, FSceneViewFamily& InViewFamily) override;
  173. private: // Implementation details
  174. void PrivOrientationAndPosition(FQuat& OutOrientation, FVector& OutPosition);
  175. FMatrix PrivStereoProjectionMatrix(EStereoscopicPass) const;
  176. // Number of "world" units in one meter
  177. float WorldToMetersScale = 1;
  178. // Distance to the clip planes. This can be set by the game
  179. float ZNear = 1.0f;
  180. float ZFar = 1.0f; // If this is equal to ZNear then there is no far clip
  181. FQuat BaseOrientation = FQuat::Identity;
  182. FRotator AppliedHmdOrientation = FRotator(0, 0, 0);
  183. FRotator ControlRotation = FRotator(0, 0, 0);
  184. const TSharedRef<Fove::IFVRHeadset, ESPMode::ThreadSafe> FoveHeadset;
  185. const TSharedRef<Fove::IFVRCompositor, ESPMode::ThreadSafe> FoveCompositor;
  186. Fove::SFVR_CompositorLayer FoveCompositorLayer;
  187. IRendererModule* RendererModule = nullptr;
  188. bool bHmdEnabled = true;
  189. bool bStereoEnabled = false;
  190. int32 WindowMirrorMode = 2; // how to mirror the display contents to the desktop window: 0 - no mirroring, 1 - single eye, 2 - stereo pair
  191. // The rendering bridge used to submit to the FOVE compositor
  192. // This is a reference as a hack around sporatic build fails on 4.17+MSVC due to ~FoveRenderingBridge not being defined yet.
  193. // Even though a forward declaration should be perfectly fine since ~TRefCountPtr<FoveRenderingBridge> is not instanciated until after FoveRenderingBridge is declared...
  194. TRefCountPtr<FoveRenderingBridge>& Bridge;
  195. };
  196. /*
  197. TODO:
  198. Note from Unreal:
  199. The last comment on this file has to do with eye tracking. While there's nothing technically wrong with your implementation, there are a few Unreal-isms that you might be able to take advantage of in order to get a more naturally integrated plugin.
  200. Right now, you expose the eye tracking parameters to the player through some functions, which return locations and rotations. That's useful, but for many users, especially ones that use Blueprint more extensively, it's often more natural and useful for them to think in terms of component based design. In Unreal, you can have something called SceneComponent, which basically means something that you can add to your Actor, which has a transformation associated with it. Most things that you can see in the game are components...meshes, sprites, etc. You can composite them together in Actors in order to create more complex Actors, and they follow a parenting hierarchy. Because of that, it makes it very easy to deal with them in whichever space you want...component, actor, or world space.
  201. For your eye tracking interface, I think it's fine to leave accessors to get the transforms directly, but you might also consider making a new class based off of SceneComponent, whose job it is to simply modify it's orientation and position to match that of the user's eyes. That will give users something physical in the world to represent the eye position, and all their standard functions (GetComponentPosition and Orientation, GetComponentWorldOrientation, etc) will also still work. It also lets them attach things directly to the eye, if they wanted to do a gaze cursor! All they'd have to do is attach to the eye tracking component, and the rest would be automatically updated.
  202. In the typical set up, I'd imagine that the player would have their character actor, which would in turn have its normal camera, which gets updated by the location and orientation of the HMD. That part works now! To do eye tracking, all they would have to do was attach two of your new FoveEyeTrackingComponents, one for each eye, to the camera, and then everything is done!
  203. In order to do this, I suggest looking at MotionControllerComponent, which updates its relative position and orientation constantly based on the position and orientation of the motion controller. The same could be used for the eye tracking component. The relative position of the component would be updated to be half of the IPD offset to the left, and the orientation would be the face-space orientation of the eye. That's it, and it would be a very natural extension of the Unreal component system.
  204. */