mirror of
https://github.com/copyrighttxt/watrbx-game-engine.git
synced 2026-09-05 05:07:48 +00:00
336 lines
9.2 KiB
C++
336 lines
9.2 KiB
C++
#if !defined(RBX_PLATFORM_DURANGO) && !defined(RBX_PLATFORM_UWP)
|
|
#include "DeviceD3D11.h"
|
|
|
|
#include "FramebufferD3D11.h"
|
|
|
|
#include <d3d11.h>
|
|
|
|
LOGGROUP(VR)
|
|
|
|
FASTFLAGVARIABLE(DebugRenderVRHUD, false)
|
|
|
|
#include "../Rendering/LibOVR/Include/OVR_CAPI_D3D.h"
|
|
|
|
#pragma comment(lib, "../Rendering/LibOVR/Lib/Windows/Win32/Release/VS2012/LibOVR.lib")
|
|
|
|
#define OVR_CHECK(call) \
|
|
do { \
|
|
ovrResult vrResult = call; \
|
|
if (OVR_FAILURE(vrResult)) FASTLOG1(FLog::VR, "VR ERROR: " #call " returned %d", vrResult); \
|
|
} while (0)
|
|
|
|
namespace RBX
|
|
{
|
|
namespace Graphics
|
|
{
|
|
typedef HRESULT (WINAPI *TypeCreateDXGIFactory)(REFIID riid, void **ppFactory);
|
|
|
|
static TypeCreateDXGIFactory getFactoryCreationFunction()
|
|
{
|
|
HMODULE module = LoadLibraryA("d3d11.dll");
|
|
if (!module) return NULL;
|
|
|
|
return (TypeCreateDXGIFactory)GetProcAddress(module, "CreateDXGIFactory");
|
|
}
|
|
|
|
static IDXGIAdapter* getAdapterForLuid(LUID luid)
|
|
{
|
|
// Allow to use null/default adapter for applications that may render a window without an HMD.
|
|
if ((luid.HighPart | luid.LowPart) == 0)
|
|
return NULL;
|
|
|
|
TypeCreateDXGIFactory createFactory = getFactoryCreationFunction();
|
|
if (!createFactory)
|
|
return NULL;
|
|
|
|
// Try to find adapter by LUID
|
|
IDXGIFactory* factory = NULL;
|
|
if (FAILED(createFactory(__uuidof(IDXGIFactory), (void**)&factory)))
|
|
return NULL;
|
|
|
|
UINT index = 0;
|
|
|
|
for (;;)
|
|
{
|
|
IDXGIAdapter* adapter = NULL;
|
|
|
|
if (SUCCEEDED(factory->EnumAdapters(index, &adapter)) && adapter)
|
|
{
|
|
DXGI_ADAPTER_DESC desc;
|
|
|
|
if (SUCCEEDED(adapter->GetDesc(&desc)) && desc.AdapterLuid.HighPart == luid.HighPart && desc.AdapterLuid.LowPart == luid.LowPart)
|
|
{
|
|
ReleaseCheck(factory);
|
|
|
|
return adapter;
|
|
}
|
|
}
|
|
else
|
|
break;
|
|
|
|
index++;
|
|
}
|
|
|
|
ReleaseCheck(factory);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static DeviceVR::Pose getPose(const ovrPosef& pose, unsigned int statusFlags)
|
|
{
|
|
DeviceVR::Pose result = {};
|
|
|
|
result.valid = (statusFlags & ovrStatus_OrientationTracked) != 0;
|
|
|
|
result.position[0] = pose.Position.x;
|
|
result.position[1] = pose.Position.y;
|
|
result.position[2] = pose.Position.z;
|
|
|
|
result.orientation[0] = pose.Orientation.x;
|
|
result.orientation[1] = pose.Orientation.y;
|
|
result.orientation[2] = pose.Orientation.z;
|
|
result.orientation[3] = pose.Orientation.w;
|
|
|
|
return result;
|
|
}
|
|
|
|
struct VRTexture
|
|
{
|
|
static const int kMaxCount = 4;
|
|
|
|
shared_ptr<Framebuffer> fb[kMaxCount];
|
|
ovrSwapTextureSet* textureSet;
|
|
|
|
VRTexture(): textureSet(NULL)
|
|
{
|
|
}
|
|
};
|
|
|
|
struct OculusVRD3D11: DeviceVRD3D11
|
|
{
|
|
ovrSession session;
|
|
ovrHmdDesc desc;
|
|
VRTexture textures[2];
|
|
ovrEyeRenderDesc eyeDesc[2];
|
|
|
|
double sensorSampleTime;
|
|
ovrTrackingState trackingState;
|
|
|
|
OculusVRD3D11(): session(NULL), sensorSampleTime(0)
|
|
{
|
|
}
|
|
|
|
~OculusVRD3D11()
|
|
{
|
|
for (int eye = 0; eye < 2; ++eye)
|
|
ovr_DestroySwapTextureSet(session, textures[eye].textureSet);
|
|
|
|
ovr_Destroy(session);
|
|
ovr_Shutdown();
|
|
}
|
|
|
|
void update() override
|
|
{
|
|
double frameTime = ovr_GetPredictedDisplayTime(session, 0);
|
|
|
|
// Keeping sensorSampleTime as close to ovr_GetTrackingState as possible - fed into the layer
|
|
sensorSampleTime = ovr_GetTimeInSeconds();
|
|
trackingState = ovr_GetTrackingState(session, frameTime, ovrTrue);
|
|
}
|
|
|
|
void recenter() override
|
|
{
|
|
ovr_RecenterPose(session);
|
|
}
|
|
|
|
Framebuffer* getEyeFramebuffer(int eye) override
|
|
{
|
|
RBXASSERT(eye == 0 || eye == 1);
|
|
|
|
return textures[eye].fb[textures[eye].textureSet->CurrentIndex].get();
|
|
}
|
|
|
|
State getState() override
|
|
{
|
|
State result = {};
|
|
|
|
result.headPose = getPose(trackingState.HeadPose.ThePose, trackingState.StatusFlags);
|
|
result.handPose[0] = getPose(trackingState.HandPoses[0].ThePose, trackingState.HandStatusFlags[0]);
|
|
result.handPose[1] = getPose(trackingState.HandPoses[1].ThePose, trackingState.HandStatusFlags[1]);
|
|
|
|
for (int eye = 0; eye < 2; ++eye)
|
|
{
|
|
result.eyeOffset[eye][0] = eyeDesc[eye].HmdToEyeViewOffset.x;
|
|
result.eyeOffset[eye][1] = eyeDesc[eye].HmdToEyeViewOffset.y;
|
|
result.eyeOffset[eye][2] = eyeDesc[eye].HmdToEyeViewOffset.z;
|
|
|
|
result.eyeFov[eye][0] = eyeDesc[eye].Fov.UpTan;
|
|
result.eyeFov[eye][1] = eyeDesc[eye].Fov.DownTan;
|
|
result.eyeFov[eye][2] = eyeDesc[eye].Fov.LeftTan;
|
|
result.eyeFov[eye][3] = eyeDesc[eye].Fov.RightTan;
|
|
}
|
|
|
|
result.needsMirror = true;
|
|
|
|
return result;
|
|
}
|
|
|
|
void submitFrame(DeviceContext* context) override
|
|
{
|
|
ovrLayerEyeFov ld = {};
|
|
ld.Header.Type = ovrLayerType_EyeFov;
|
|
ld.Header.Flags = 0;
|
|
|
|
ovrVector3f hmdToEyeViewOffset[2] = { eyeDesc[0].HmdToEyeViewOffset, eyeDesc[1].HmdToEyeViewOffset };
|
|
ovrPosef eyeRenderPoses[2];
|
|
ovr_CalcEyePoses(trackingState.HeadPose.ThePose, hmdToEyeViewOffset, eyeRenderPoses);
|
|
|
|
for (int eye = 0; eye < 2; ++eye)
|
|
{
|
|
ld.ColorTexture[eye] = textures[eye].textureSet;
|
|
ld.Viewport[eye].Pos.x = 0;
|
|
ld.Viewport[eye].Pos.y = 0;
|
|
ld.Viewport[eye].Size.w = textures[eye].fb[0]->getWidth();
|
|
ld.Viewport[eye].Size.h = textures[eye].fb[0]->getHeight();
|
|
ld.Fov[eye] = eyeDesc[eye].Fov;
|
|
ld.RenderPose[eye] = eyeRenderPoses[eye];
|
|
ld.SensorSampleTime = sensorSampleTime;
|
|
}
|
|
|
|
ovrViewScaleDesc viewScaleDesc;
|
|
viewScaleDesc.HmdSpaceToWorldScaleInMeters = 1.0f;
|
|
viewScaleDesc.HmdToEyeViewOffset[0] = eyeDesc[0].HmdToEyeViewOffset;
|
|
viewScaleDesc.HmdToEyeViewOffset[1] = eyeDesc[1].HmdToEyeViewOffset;
|
|
|
|
ovrLayerHeader* layers = &ld.Header;
|
|
OVR_CHECK(ovr_SubmitFrame(session, 0, &viewScaleDesc, &layers, 1));
|
|
|
|
for (int eye = 0; eye < 2; ++eye)
|
|
{
|
|
textures[eye].textureSet->CurrentIndex += 1;
|
|
textures[eye].textureSet->CurrentIndex %= textures[eye].textureSet->TextureCount;
|
|
}
|
|
|
|
if (FFlag::DebugRenderVRHUD)
|
|
{
|
|
static bool keyWasDown = false;
|
|
bool keyIsDown = (GetAsyncKeyState(VK_CONTROL) < 0 && GetAsyncKeyState(VK_F8) < 0);
|
|
|
|
if (keyIsDown && !keyWasDown)
|
|
{
|
|
int perfHudMode = ovr_GetInt(session, OVR_PERF_HUD_MODE, 0);
|
|
|
|
perfHudMode += 1;
|
|
perfHudMode %= ovrPerfHud_Count;
|
|
|
|
ovr_SetInt(session, OVR_PERF_HUD_MODE, perfHudMode);
|
|
}
|
|
|
|
keyWasDown = keyIsDown;
|
|
}
|
|
}
|
|
|
|
void setup(Device* device) override
|
|
{
|
|
ID3D11Device* device11 = static_cast<DeviceD3D11*>(device)->getDevice11();
|
|
|
|
// Work around a bug in LibOVR
|
|
ShowWindow(static_cast<HWND>(static_cast<DeviceD3D11*>(device)->getWindowHandle()), SW_SHOWNORMAL);
|
|
|
|
ovrSizei idealSizeLeft = ovr_GetFovTextureSize(session, ovrEye_Left, desc.DefaultEyeFov[ovrEye_Left], 1.0f);
|
|
ovrSizei idealSizeRight = ovr_GetFovTextureSize(session, ovrEye_Right, desc.DefaultEyeFov[ovrEye_Right], 1.0f);
|
|
|
|
unsigned int width = std::max(idealSizeLeft.w, idealSizeRight.w);
|
|
unsigned int height = std::max(idealSizeLeft.h, idealSizeRight.h);
|
|
|
|
shared_ptr<Renderbuffer> depthStencil = shared_ptr<Renderbuffer>(new RenderbufferD3D11(device, Texture::Format_D24S8, width, height, 1));
|
|
|
|
for (int eye = 0; eye < 2; ++eye)
|
|
{
|
|
D3D11_TEXTURE2D_DESC dsDesc;
|
|
dsDesc.Width = width;
|
|
dsDesc.Height = height;
|
|
dsDesc.MipLevels = 1;
|
|
dsDesc.ArraySize = 1;
|
|
dsDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB;
|
|
dsDesc.SampleDesc.Count = 1;
|
|
dsDesc.SampleDesc.Quality = 0;
|
|
dsDesc.Usage = D3D11_USAGE_DEFAULT;
|
|
dsDesc.CPUAccessFlags = 0;
|
|
dsDesc.MiscFlags = 0;
|
|
dsDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
|
|
|
|
ovrResult result = ovr_CreateSwapTextureSetD3D11(session, device11, &dsDesc, ovrSwapTextureSetD3D11_Typeless, &textures[eye].textureSet);
|
|
if (OVR_FAILURE(result))
|
|
throw RBX::runtime_error("ovr_CreateSwapTextureSetD3D11 failed with %d", result);
|
|
|
|
RBXASSERT(textures[eye].textureSet->TextureCount <= VRTexture::kMaxCount);
|
|
|
|
for (int i = 0; i < textures[eye].textureSet->TextureCount; ++i)
|
|
{
|
|
ovrD3D11Texture* tex = reinterpret_cast<ovrD3D11Texture*>(&textures[eye].textureSet->Textures[i]);
|
|
|
|
tex->D3D11.pTexture->AddRef();
|
|
|
|
shared_ptr<Renderbuffer> colorBuffer(new RenderbufferD3D11(device, Texture::Format_RGBA8, width, height, 1, tex->D3D11.pTexture));
|
|
|
|
textures[eye].fb[i] = device->createFramebuffer(colorBuffer, depthStencil);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
static void logCallback(uintptr_t userData, int level, const char* message)
|
|
{
|
|
FASTLOGS(FLog::VR, "VR: %s", message);
|
|
}
|
|
|
|
DeviceVRD3D11* DeviceVRD3D11::createOculus(IDXGIAdapter** outAdapter)
|
|
{
|
|
ovrInitParams params = {};
|
|
|
|
params.LogCallback = logCallback;
|
|
|
|
ovrResult vrResult = ovr_Initialize(¶ms);
|
|
|
|
if (!OVR_SUCCESS(vrResult))
|
|
{
|
|
FASTLOG1(FLog::VR, "VR: ovr_Initialize returned %d", vrResult);
|
|
return NULL;
|
|
}
|
|
|
|
ovrSession session;
|
|
ovrGraphicsLuid vrLuid;
|
|
|
|
vrResult = ovr_Create(&session, &vrLuid);
|
|
|
|
if (!OVR_SUCCESS(vrResult))
|
|
{
|
|
FASTLOG1(FLog::VR, "VR: ovr_Create returned %d", vrResult);
|
|
|
|
ovr_Shutdown();
|
|
|
|
return NULL;
|
|
}
|
|
|
|
OculusVRD3D11* vr = new OculusVRD3D11();
|
|
|
|
vr->session = session;
|
|
vr->desc = ovr_GetHmdDesc(session);
|
|
|
|
for (int eye = 0; eye < 2; ++eye)
|
|
vr->eyeDesc[eye] = ovr_GetRenderDesc(session, (ovrEyeType)eye, vr->desc.DefaultEyeFov[eye]);
|
|
|
|
if (outAdapter)
|
|
*outAdapter = getAdapterForLuid(*reinterpret_cast<LUID*>(&vrLuid));
|
|
|
|
FASTLOGS(FLog::VR, "VR: Connected to %s", vr->desc.ProductName);
|
|
FASTLOG4(FLog::VR, "VR: Vendor %x Product %x Firmware %d.%d", vr->desc.VendorId, vr->desc.ProductId, vr->desc.FirmwareMajor, vr->desc.FirmwareMinor);
|
|
|
|
return vr;
|
|
}
|
|
|
|
}
|
|
}
|
|
#endif
|