MeshData(const string& name) { static Assimp::Importer importer; D3DInfo& d3d = *D3DInfo::CurrentInstance(); // Read the mesh data from file using Asset Importer. const aiScene* scene = importer.ReadFile(config::Meshes + name, ImportSetting); if (!scene || !scene->mNumMeshes) { Warn("Mesh read failed for " + name); return; } const aiMesh* mesh = scene->mMeshes[0]; WarnIf(scene->mNumMeshes > 1, "Mesh " + name + " has more sub-meshes than are currently supported"); // Verify mesh texture coordinates and tangents. bool hasTexCoords = true; if (!mesh->HasTextureCoords(0)) { hasTexCoords = false; Warn("Mesh " + name + " doesn't have texture coordinates"); } bool hasTangents = true; if (!mesh->HasTangentsAndBitangents()) { hasTangents = false; Warn("Mesh " + name + " doesn't have tangents/bitangents"); } float minFloat = numeric_limits<float>::min(); float maxFloat = numeric_limits<float>::max(); boundingBox.max = { minFloat, minFloat, minFloat }; boundingBox.min = { maxFloat, maxFloat, maxFloat }; // Copy all vertices. vertices.resize(mesh->mNumVertices); for (size_t i = 0; i < mesh->mNumVertices; ++i) { vertices[i].position = reinterpret_cast<const float3&>(mesh->mVertices[i]); vertices[i].normal = reinterpret_cast<const float3&>(mesh->mNormals[i]); if (hasTexCoords) { vertices[i].tex = reinterpret_cast<const float2&>(mesh->mTextureCoords[0][i]); } if (hasTangents) { vertices[i].tangent = reinterpret_cast<const float3&>(mesh->mTangents[i]); vertices[i].bitangent = reinterpret_cast<const float3&>(mesh->mBitangents[i]); } // Determine the min and max extents of the mesh. if (vertices[i].position.x < boundingBox.min.x) boundingBox.min.x = vertices[i].position.x; if (vertices[i].position.y < boundingBox.min.y) boundingBox.min.y = vertices[i].position.y; if (vertices[i].position.z < boundingBox.min.z) boundingBox.min.z = vertices[i].position.z; if (vertices[i].position.x > boundingBox.max.x) boundingBox.max.x = vertices[i].position.x; if (vertices[i].position.y > boundingBox.max.y) boundingBox.max.y = vertices[i].position.y; if (vertices[i].position.z > boundingBox.max.z) boundingBox.max.z = vertices[i].position.z; } // Calculate the centroid of the mesh: centroid = min + ((max - min) / 2) XMVECTOR minVector = XMLoadFloat3(&boundingBox.min); XMVECTOR cornerToCorner = XMVectorSubtract(XMLoadFloat3(&boundingBox.max), minVector); XMStoreFloat3( &boundingBox.centroid, XMVectorAdd( minVector, XMVectorScale(cornerToCorner, 0.5f))); // Center the mesh on (0,0,0) by subtracting the centroid position from all vertex positions. for (auto& vertex : vertices) { vertex.position.x -= boundingBox.centroid.x; vertex.position.y -= boundingBox.centroid.y; vertex.position.z -= boundingBox.centroid.z; } // Copy all indices. indices.resize(mesh->mNumFaces * 3); for (size_t i = 0; i < mesh->mNumFaces; ++i) { indices[i * 3 + 0] = mesh->mFaces[i].mIndices[0]; indices[i * 3 + 1] = mesh->mFaces[i].mIndices[1]; indices[i * 3 + 2] = mesh->mFaces[i].mIndices[2]; } // Free the loaded scene. importer.FreeScene(); // Create the index buffer. D3D11_BUFFER_DESC bufferDesc; bufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER; bufferDesc.ByteWidth = indices.size() * sizeof(indices[0]); bufferDesc.CPUAccessFlags = 0; bufferDesc.MiscFlags = 0; bufferDesc.StructureByteStride = 0; bufferDesc.Usage = D3D11_USAGE_IMMUTABLE; D3D11_SUBRESOURCE_DATA initData; initData.pSysMem = indices.data(); initData.SysMemPitch = 0; initData.SysMemSlicePitch = 0; DX(d3d.Device->CreateBuffer(&bufferDesc, &initData, indexBuffer)); // Create the vertex buffer. bufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER; bufferDesc.ByteWidth = vertices.size() * sizeof(vertices[0]); initData.pSysMem = vertices.data(); DX(d3d.Device->CreateBuffer(&bufferDesc, &initData, vertexBuffer)); // Create the constant buffer. D3D11_BUFFER_DESC cbDesc; ZeroMemory(&cbDesc, sizeof(cbDesc)); cbDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; cbDesc.ByteWidth = sizeof(ObjectConstants); cbDesc.Usage = D3D11_USAGE_DEFAULT; DX(d3d.Device->CreateBuffer(&cbDesc, nullptr, constantBuffer)); }
// return true to retry later (e.g. after display lost) static bool MainLoop(bool retryCreate) { // Initialize these to nullptr here to handle device lost failures cleanly ovrTexture * mirrorTexture = nullptr; OculusTexture * pEyeRenderTexture[2] = { nullptr, nullptr }; DepthBuffer * pEyeDepthBuffer[2] = { nullptr, nullptr }; Scene * roomScene = nullptr; Camera * mainCam = nullptr; D3D11_TEXTURE2D_DESC td = {}; ovrHmd HMD; ovrGraphicsLuid luid; ovrResult result = ovr_Create(&HMD, &luid); if (!OVR_SUCCESS(result)) return retryCreate; ovrHmdDesc hmdDesc = ovr_GetHmdDesc(HMD); // ------------------------------------------------------------------- // Add: Make Instance that CL Eye Camera Capture Class CLEyeCameraCapture* cam[2] = { NULL }; // Query for number of connected camera int numCams = CLEyeGetCameraCount(); if (numCams == 0) { printf_s("No PS3Eye Camera detected\n"); goto Done; } printf_s("Found %d cameras\n", numCams); for (int iCam = 0; iCam < numCams; iCam++) { char windowName[64]; // Query unique camera uuid GUID guid = CLEyeGetCameraUUID(iCam); printf("Camera %d GUID: [%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x]\n", iCam + 1, guid.Data1, guid.Data2, guid.Data3, guid.Data4[0], guid.Data4[1], guid.Data4[2], guid.Data4[3], guid.Data4[4], guid.Data4[5], guid.Data4[6], guid.Data4[7]); sprintf_s(windowName, "Camera Window %d", iCam + 1); // Create camera capture object cam[iCam] = new CLEyeCameraCapture(windowName, guid, CLEYE_COLOR_RAW, CLEYE_VGA, 30); cam[iCam]->StartCapture(); } // ------------------------------------------------------------------- // Setup Device and Graphics // Note: the mirror window can be any size, for this sample we use 1/2 the HMD resolution if (!DIRECTX.InitDevice(hmdDesc.Resolution.w / 2, hmdDesc.Resolution.h / 2, reinterpret_cast<LUID*>(&luid))) goto Done; // Make the eye render buffers (caution if actual size < requested due to HW limits). ovrRecti eyeRenderViewport[2]; for (int eye = 0; eye < 2; ++eye) { ovrSizei idealSize = ovr_GetFovTextureSize(HMD, (ovrEyeType)eye, hmdDesc.DefaultEyeFov[eye], 1.0f); pEyeRenderTexture[eye] = new OculusTexture(); if (!pEyeRenderTexture[eye]->Init(HMD, idealSize.w, idealSize.h)) { if (retryCreate) goto Done; VALIDATE(OVR_SUCCESS(result), "Failed to create eye texture."); } pEyeDepthBuffer[eye] = new DepthBuffer(DIRECTX.Device, idealSize.w, idealSize.h); eyeRenderViewport[eye].Pos.x = 0; eyeRenderViewport[eye].Pos.y = 0; eyeRenderViewport[eye].Size = idealSize; if (!pEyeRenderTexture[eye]->TextureSet) { if (retryCreate) goto Done; VALIDATE(false, "Failed to create texture."); } } // Create a mirror to see on the monitor. td.ArraySize = 1; td.Format = DXGI_FORMAT_R8G8B8A8_UNORM_SRGB; td.Width = DIRECTX.WinSizeW; td.Height = DIRECTX.WinSizeH; td.Usage = D3D11_USAGE_DEFAULT; td.SampleDesc.Count = 1; td.MipLevels = 1; result = ovr_CreateMirrorTextureD3D11(HMD, DIRECTX.Device, &td, 0, &mirrorTexture); if (!OVR_SUCCESS(result)) { if (retryCreate) goto Done; VALIDATE(false, "Failed to create mirror texture."); } // Create the room model roomScene = new Scene(false); // Create camera mainCam = new Camera(&XMVectorSet(0.0f, 1.6f, 5.0f, 0), &XMQuaternionIdentity()); // Setup VR components, filling out description ovrEyeRenderDesc eyeRenderDesc[2]; eyeRenderDesc[0] = ovr_GetRenderDesc(HMD, ovrEye_Left, hmdDesc.DefaultEyeFov[0]); eyeRenderDesc[1] = ovr_GetRenderDesc(HMD, ovrEye_Right, hmdDesc.DefaultEyeFov[1]); bool isVisible = true; DCB portConfig; portConfig.BaudRate = 115200; portConfig.Parity = EVENPARITY; g_seriPort.Start("\\\\.\\COM3", &portConfig); // Main loop while (DIRECTX.HandleMessages()) { XMVECTOR forward = XMVector3Rotate(XMVectorSet(0, 0, -0.05f, 0), mainCam->Rot); XMVECTOR right = XMVector3Rotate(XMVectorSet(0.05f, 0, 0, 0), mainCam->Rot); if (DIRECTX.Key['W'] || DIRECTX.Key[VK_UP]) mainCam->Pos = XMVectorAdd(mainCam->Pos, forward); if (DIRECTX.Key['S'] || DIRECTX.Key[VK_DOWN]) mainCam->Pos = XMVectorSubtract(mainCam->Pos, forward); if (DIRECTX.Key['D']) mainCam->Pos = XMVectorAdd(mainCam->Pos, right); if (DIRECTX.Key['A']) mainCam->Pos = XMVectorSubtract(mainCam->Pos, right); static float Yaw = 0; if (DIRECTX.Key[VK_LEFT]) mainCam->Rot = XMQuaternionRotationRollPitchYaw(0, Yaw += 0.02f, 0); if (DIRECTX.Key[VK_RIGHT]) mainCam->Rot = XMQuaternionRotationRollPitchYaw(0, Yaw -= 0.02f, 0); // Animate the cube static float cubeClock = 0; roomScene->Models[0]->Pos = XMFLOAT3(9 * sin(cubeClock), 3, 9 * cos(cubeClock += 0.015f)); // Get both eye poses simultaneously, with IPD offset already included. ovrPosef EyeRenderPose[2]; ovrVector3f HmdToEyeViewOffset[2] = { eyeRenderDesc[0].HmdToEyeViewOffset, eyeRenderDesc[1].HmdToEyeViewOffset }; double frameTime = ovr_GetPredictedDisplayTime(HMD, 0); // Keeping sensorSampleTime as close to ovr_GetTrackingState as possible - fed into the layer double sensorSampleTime = ovr_GetTimeInSeconds(); ovrTrackingState hmdState = ovr_GetTrackingState(HMD, frameTime, ovrTrue); ovr_CalcEyePoses(hmdState.HeadPose.ThePose, HmdToEyeViewOffset, EyeRenderPose); // -------------------------------------------------------------------------- // Add: Get Head Yaw Roll Pitch float hmdPitch = 0.0f; float hmdRoll = 0.0f; float hmdYaw = 0.0f; OVR::Posef HeadPose = hmdState.HeadPose.ThePose; HeadPose.Rotation.GetEulerAngles<OVR::Axis_Y, OVR::Axis_X, OVR::Axis_Z>(&hmdYaw, &hmdPitch, &hmdRoll); SetPos(2, ServoRoll(hmdYaw)); SetPos(3, ServoRoll(hmdPitch)); // -------------------------------------------------------------------------- // Render Scene to Eye Buffers if (isVisible) { for (int eye = 0; eye < 2; ++eye) { // Increment to use next texture, just before writing pEyeRenderTexture[eye]->AdvanceToNextTexture(); // Clear and set up rendertarget int texIndex = pEyeRenderTexture[eye]->TextureSet->CurrentIndex; DIRECTX.SetAndClearRenderTarget(pEyeRenderTexture[eye]->TexRtv[texIndex], pEyeDepthBuffer[eye]); DIRECTX.SetViewport((float)eyeRenderViewport[eye].Pos.x, (float)eyeRenderViewport[eye].Pos.y, (float)eyeRenderViewport[eye].Size.w, (float)eyeRenderViewport[eye].Size.h); //Get the pose information in XM format XMVECTOR eyeQuat = XMVectorSet(EyeRenderPose[eye].Orientation.x, EyeRenderPose[eye].Orientation.y, EyeRenderPose[eye].Orientation.z, EyeRenderPose[eye].Orientation.w); XMVECTOR eyePos = XMVectorSet(EyeRenderPose[eye].Position.x, EyeRenderPose[eye].Position.y, EyeRenderPose[eye].Position.z, 0); // Get view and projection matrices for the Rift camera XMVECTOR CombinedPos = XMVectorAdd(mainCam->Pos, XMVector3Rotate(eyePos, mainCam->Rot)); Camera finalCam(&CombinedPos, &(XMQuaternionMultiply(eyeQuat,mainCam->Rot))); XMMATRIX view = finalCam.GetViewMatrix(); ovrMatrix4f p = ovrMatrix4f_Projection(eyeRenderDesc[eye].Fov, 0.2f, 1000.0f, ovrProjection_RightHanded); XMMATRIX proj = XMMatrixSet(p.M[0][0], p.M[1][0], p.M[2][0], p.M[3][0], p.M[0][1], p.M[1][1], p.M[2][1], p.M[3][1], p.M[0][2], p.M[1][2], p.M[2][2], p.M[3][2], p.M[0][3], p.M[1][3], p.M[2][3], p.M[3][3]); XMMATRIX prod = XMMatrixMultiply(view, proj); roomScene->Render(&prod, 1, 1, 1, 1, true); } } // Initialize our single full screen Fov layer. ovrLayerEyeFov ld = {}; ld.Header.Type = ovrLayerType_EyeFov; ld.Header.Flags = 0; for (int eye = 0; eye < 2; ++eye) { ld.ColorTexture[eye] = pEyeRenderTexture[eye]->TextureSet; ld.Viewport[eye] = eyeRenderViewport[eye]; ld.Fov[eye] = hmdDesc.DefaultEyeFov[eye]; ld.RenderPose[eye] = EyeRenderPose[eye]; ld.SensorSampleTime = sensorSampleTime; } ovrLayerHeader* layers = &ld.Header; result = ovr_SubmitFrame(HMD, 0, nullptr, &layers, 1); // exit the rendering loop if submit returns an error, will retry on ovrError_DisplayLost if (!OVR_SUCCESS(result)) goto Done; isVisible = (result == ovrSuccess); // Render mirror ovrD3D11Texture* tex = (ovrD3D11Texture*)mirrorTexture; DIRECTX.Context->CopyResource(DIRECTX.BackBuffer, tex->D3D11.pTexture); DIRECTX.SwapChain->Present(0, 0); } // Release resources Done: delete mainCam; delete roomScene; if (mirrorTexture) ovr_DestroyMirrorTexture(HMD, mirrorTexture); for (int eye = 0; eye < 2; ++eye) { delete pEyeRenderTexture[eye]; delete pEyeDepthBuffer[eye]; } DIRECTX.ReleaseDevice(); ovr_Destroy(HMD); g_seriPort.End(); for (int iCam = 0; iCam < numCams; iCam++) { cam[iCam]->StopCapture(); delete cam[iCam]; } // Retry on ovrError_DisplayLost return retryCreate || OVR_SUCCESS(result) || (result == ovrError_DisplayLost); }
void Transform::Translate(const XMFLOAT3& translation) { XMStoreFloat3(&position, XMVectorAdd(XMLoadFloat3(&position), XMLoadFloat3(&translation))); isDirty = true; }
// Renders meshes using cascaded shadow mapping void MeshRenderer::RenderShadowMap(ID3D11DeviceContext* context, const Camera& camera, const Float4x4& world) { PIXEvent event(L"Mesh Shadow Map Rendering"); // Get the current render targets + viewport ID3D11RenderTargetView* renderTargets[D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT] = { NULL }; ID3D11DepthStencilView* depthStencil = NULL; context->OMGetRenderTargets(D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT, renderTargets, &depthStencil); uint32 numViewports = D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE; D3D11_VIEWPORT oldViewports[D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE]; context->RSGetViewports(&numViewports, oldViewports); const float sMapSize = static_cast<float>(ShadowMapSize); const float MinDistance = _shadowDepthBounds.x; const float MaxDistance = _shadowDepthBounds.y; // Compute the split distances based on the partitioning mode float CascadeSplits[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; { float lambda = 1.0f; float nearClip = camera.NearClip(); float farClip = camera.FarClip(); float clipRange = farClip - nearClip; float minZ = nearClip + MinDistance * clipRange; float maxZ = nearClip + MaxDistance * clipRange; float range = maxZ - minZ; float ratio = maxZ / minZ; for(uint32 i = 0; i < NumCascades; ++i) { float p = (i + 1) / static_cast<float>(NumCascades); float log = minZ * std::pow(ratio, p); float uniform = minZ + range * p; float d = lambda * (log - uniform) + uniform; CascadeSplits[i] = (d - nearClip) / clipRange; } } Float3 c0Extents; Float4x4 c0Matrix; // Render the meshes to each cascade for(uint32 cascadeIdx = 0; cascadeIdx < NumCascades; ++cascadeIdx) { PIXEvent cascadeEvent((L"Rendering Shadow Map Cascade " + ToString(cascadeIdx)).c_str()); // Set the viewport D3D11_VIEWPORT viewport; viewport.TopLeftX = 0.0f; viewport.TopLeftY = 0.0f; viewport.Width = sMapSize; viewport.Height = sMapSize; viewport.MinDepth = 0.0f; viewport.MaxDepth = 1.0f; context->RSSetViewports(1, &viewport); // Set the shadow map as the depth target ID3D11DepthStencilView* dsv = _shadowMap.DSView; ID3D11RenderTargetView* nullRenderTargets[D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT] = { NULL }; context->OMSetRenderTargets(D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT, nullRenderTargets, dsv); context->ClearDepthStencilView(dsv, D3D11_CLEAR_DEPTH|D3D11_CLEAR_STENCIL, 1.0f, 0); // Get the 8 points of the view frustum in world space XMVECTOR frustumCornersWS[8] = { XMVectorSet(-1.0f, 1.0f, 0.0f, 1.0f), XMVectorSet( 1.0f, 1.0f, 0.0f, 1.0f), XMVectorSet( 1.0f, -1.0f, 0.0f, 1.0f), XMVectorSet(-1.0f, -1.0f, 0.0f, 1.0f), XMVectorSet(-1.0f, 1.0f, 1.0f, 1.0f), XMVectorSet( 1.0f, 1.0f, 1.0f, 1.0f), XMVectorSet( 1.0f, -1.0f, 1.0f, 1.0f), XMVectorSet(-1.0f, -1.0f, 1.0f, 1.0f), }; float prevSplitDist = cascadeIdx == 0 ? MinDistance : CascadeSplits[cascadeIdx - 1]; float splitDist = CascadeSplits[cascadeIdx]; XMVECTOR det; XMMATRIX invViewProj = XMMatrixInverse(&det, camera.ViewProjectionMatrix().ToSIMD()); for(uint32 i = 0; i < 8; ++i) frustumCornersWS[i] = XMVector3TransformCoord(frustumCornersWS[i], invViewProj); // Get the corners of the current cascade slice of the view frustum for(uint32 i = 0; i < 4; ++i) { XMVECTOR cornerRay = XMVectorSubtract(frustumCornersWS[i + 4], frustumCornersWS[i]); XMVECTOR nearCornerRay = XMVectorScale(cornerRay, prevSplitDist); XMVECTOR farCornerRay = XMVectorScale(cornerRay, splitDist); frustumCornersWS[i + 4] = XMVectorAdd(frustumCornersWS[i], farCornerRay); frustumCornersWS[i] = XMVectorAdd(frustumCornersWS[i], nearCornerRay); } // Calculate the centroid of the view frustum slice XMVECTOR frustumCenterVec = XMVectorZero(); for(uint32 i = 0; i < 8; ++i) frustumCenterVec = XMVectorAdd(frustumCenterVec, frustumCornersWS[i]); frustumCenterVec = XMVectorScale(frustumCenterVec, 1.0f / 8.0f); Float3 frustumCenter = frustumCenterVec; // Pick the up vector to use for the light camera Float3 upDir = camera.Right(); Float3 minExtents; Float3 maxExtents; { // Create a temporary view matrix for the light Float3 lightCameraPos = frustumCenter; Float3 lookAt = frustumCenter - AppSettings::LightDirection; XMMATRIX lightView = XMMatrixLookAtLH(lightCameraPos.ToSIMD(), lookAt.ToSIMD(), upDir.ToSIMD()); // Calculate an AABB around the frustum corners XMVECTOR mins = XMVectorSet(REAL_MAX, REAL_MAX, REAL_MAX, REAL_MAX); XMVECTOR maxes = XMVectorSet(-REAL_MAX, -REAL_MAX, -REAL_MAX, -REAL_MAX); for(uint32 i = 0; i < 8; ++i) { XMVECTOR corner = XMVector3TransformCoord(frustumCornersWS[i], lightView); mins = XMVectorMin(mins, corner); maxes = XMVectorMax(maxes, corner); } minExtents = mins; maxExtents = maxes; } // Adjust the min/max to accommodate the filtering size float scale = (ShadowMapSize + FilterSize) / static_cast<float>(ShadowMapSize); minExtents.x *= scale; minExtents.y *= scale; maxExtents.x *= scale; maxExtents.x *= scale; Float3 cascadeExtents = maxExtents - minExtents; // Get position of the shadow camera Float3 shadowCameraPos = frustumCenter + AppSettings::LightDirection.Value() * -minExtents.z; // Come up with a new orthographic camera for the shadow caster OrthographicCamera shadowCamera(minExtents.x, minExtents.y, maxExtents.x, maxExtents.y, 0.0f, cascadeExtents.z); shadowCamera.SetLookAt(shadowCameraPos, frustumCenter, upDir); // Draw the mesh with depth only, using the new shadow camera RenderDepth(context, shadowCamera, world, true); // Apply the scale/offset matrix, which transforms from [-1,1] // post-projection space to [0,1] UV space XMMATRIX texScaleBias; texScaleBias.r[0] = XMVectorSet(0.5f, 0.0f, 0.0f, 0.0f); texScaleBias.r[1] = XMVectorSet(0.0f, -0.5f, 0.0f, 0.0f); texScaleBias.r[2] = XMVectorSet(0.0f, 0.0f, 1.0f, 0.0f); texScaleBias.r[3] = XMVectorSet(0.5f, 0.5f, 0.0f, 1.0f); XMMATRIX shadowMatrix = shadowCamera.ViewProjectionMatrix().ToSIMD(); shadowMatrix = XMMatrixMultiply(shadowMatrix, texScaleBias); // Store the split distance in terms of view space depth const float clipDist = camera.FarClip() - camera.NearClip(); _meshPSConstants.Data.CascadeSplits[cascadeIdx] = camera.NearClip() + splitDist * clipDist; if(cascadeIdx == 0) { c0Extents = cascadeExtents; c0Matrix = shadowMatrix; _meshPSConstants.Data.ShadowMatrix = XMMatrixTranspose(shadowMatrix); _meshPSConstants.Data.CascadeOffsets[0] = Float4(0.0f, 0.0f, 0.0f, 0.0f); _meshPSConstants.Data.CascadeScales[0] = Float4(1.0f, 1.0f, 1.0f, 1.0f); } else { // Calculate the position of the lower corner of the cascade partition, in the UV space // of the first cascade partition Float4x4 invCascadeMat = Float4x4::Invert(shadowMatrix); Float3 cascadeCorner = Float3::Transform(Float3(0.0f, 0.0f, 0.0f), invCascadeMat); cascadeCorner = Float3::Transform(cascadeCorner, c0Matrix); // Do the same for the upper corner Float3 otherCorner = Float3::Transform(Float3(1.0f, 1.0f, 1.0f), invCascadeMat); otherCorner = Float3::Transform(otherCorner, c0Matrix); // Calculate the scale and offset Float3 cascadeScale = Float3(1.0f, 1.0f, 1.f) / (otherCorner - cascadeCorner); _meshPSConstants.Data.CascadeOffsets[cascadeIdx] = Float4(-cascadeCorner, 0.0f); _meshPSConstants.Data.CascadeScales[cascadeIdx] = Float4(cascadeScale, 1.0f); } ConvertToEVSM(context, cascadeIdx, _meshPSConstants.Data.CascadeScales[cascadeIdx].To3D()); } // Restore the previous render targets and viewports context->OMSetRenderTargets(D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT, renderTargets, depthStencil); context->RSSetViewports(D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE, oldViewports); for(uint32 i = 0; i < D3D11_SIMULTANEOUS_RENDER_TARGET_COUNT; ++i) if(renderTargets[i] != NULL) renderTargets[i]->Release(); if(depthStencil != NULL) depthStencil->Release(); }
// Load the sample assets. void D3D12Multithreading::LoadAssets() { // Create the root signature. { D3D12_FEATURE_DATA_ROOT_SIGNATURE featureData = {}; // This is the highest version the sample supports. If CheckFeatureSupport succeeds, the HighestVersion returned will not be greater than this. featureData.HighestVersion = D3D_ROOT_SIGNATURE_VERSION_1_1; if (FAILED(m_device->CheckFeatureSupport(D3D12_FEATURE_ROOT_SIGNATURE, &featureData, sizeof(featureData)))) { featureData.HighestVersion = D3D_ROOT_SIGNATURE_VERSION_1_0; } CD3DX12_DESCRIPTOR_RANGE1 ranges[4]; // Perfomance TIP: Order from most frequent to least frequent. ranges[0].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 2, 1, 0, D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC); // 2 frequently changed diffuse + normal textures - using registers t1 and t2. ranges[1].Init(D3D12_DESCRIPTOR_RANGE_TYPE_CBV, 1, 0, 0, D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC); // 1 frequently changed constant buffer. ranges[2].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0, 0, D3D12_DESCRIPTOR_RANGE_FLAG_DATA_STATIC_WHILE_SET_AT_EXECUTE);// 1 infrequently changed shadow texture - starting in register t0. ranges[3].Init(D3D12_DESCRIPTOR_RANGE_TYPE_SAMPLER, 2, 0); // 2 static samplers. CD3DX12_ROOT_PARAMETER1 rootParameters[4]; rootParameters[0].InitAsDescriptorTable(1, &ranges[0], D3D12_SHADER_VISIBILITY_PIXEL); rootParameters[1].InitAsDescriptorTable(1, &ranges[1], D3D12_SHADER_VISIBILITY_ALL); rootParameters[2].InitAsDescriptorTable(1, &ranges[2], D3D12_SHADER_VISIBILITY_PIXEL); rootParameters[3].InitAsDescriptorTable(1, &ranges[3], D3D12_SHADER_VISIBILITY_PIXEL); CD3DX12_VERSIONED_ROOT_SIGNATURE_DESC rootSignatureDesc; rootSignatureDesc.Init_1_1(_countof(rootParameters), rootParameters, 0, nullptr, D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT); ComPtr<ID3DBlob> signature; ComPtr<ID3DBlob> error; ThrowIfFailed(D3DX12SerializeVersionedRootSignature(&rootSignatureDesc, featureData.HighestVersion, &signature, &error)); ThrowIfFailed(m_device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), IID_PPV_ARGS(&m_rootSignature))); NAME_D3D12_OBJECT(m_rootSignature); } // Create the pipeline state, which includes loading shaders. { ComPtr<ID3DBlob> vertexShader; ComPtr<ID3DBlob> pixelShader; #if defined(_DEBUG) // Enable better shader debugging with the graphics debugging tools. UINT compileFlags = D3DCOMPILE_DEBUG | D3DCOMPILE_SKIP_OPTIMIZATION; #else UINT compileFlags = D3DCOMPILE_OPTIMIZATION_LEVEL3; #endif ThrowIfFailed(D3DCompileFromFile(GetAssetFullPath(L"shaders.hlsl").c_str(), nullptr, nullptr, "VSMain", "vs_5_0", compileFlags, 0, &vertexShader, nullptr)); ThrowIfFailed(D3DCompileFromFile(GetAssetFullPath(L"shaders.hlsl").c_str(), nullptr, nullptr, "PSMain", "ps_5_0", compileFlags, 0, &pixelShader, nullptr)); D3D12_INPUT_LAYOUT_DESC inputLayoutDesc; inputLayoutDesc.pInputElementDescs = SampleAssets::StandardVertexDescription; inputLayoutDesc.NumElements = _countof(SampleAssets::StandardVertexDescription); CD3DX12_DEPTH_STENCIL_DESC depthStencilDesc(D3D12_DEFAULT); depthStencilDesc.DepthEnable = true; depthStencilDesc.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ALL; depthStencilDesc.DepthFunc = D3D12_COMPARISON_FUNC_LESS_EQUAL; depthStencilDesc.StencilEnable = FALSE; // Describe and create the PSO for rendering the scene. D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc = {}; psoDesc.InputLayout = inputLayoutDesc; psoDesc.pRootSignature = m_rootSignature.Get(); psoDesc.VS = CD3DX12_SHADER_BYTECODE(vertexShader.Get()); psoDesc.PS = CD3DX12_SHADER_BYTECODE(pixelShader.Get()); psoDesc.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT); psoDesc.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT); psoDesc.DepthStencilState = depthStencilDesc; psoDesc.SampleMask = UINT_MAX; psoDesc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; psoDesc.NumRenderTargets = 1; psoDesc.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM; psoDesc.DSVFormat = DXGI_FORMAT_D32_FLOAT; psoDesc.SampleDesc.Count = 1; ThrowIfFailed(m_device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&m_pipelineState))); NAME_D3D12_OBJECT(m_pipelineState); // Alter the description and create the PSO for rendering // the shadow map. The shadow map does not use a pixel // shader or render targets. psoDesc.PS = CD3DX12_SHADER_BYTECODE(0, 0); psoDesc.RTVFormats[0] = DXGI_FORMAT_UNKNOWN; psoDesc.NumRenderTargets = 0; ThrowIfFailed(m_device->CreateGraphicsPipelineState(&psoDesc, IID_PPV_ARGS(&m_pipelineStateShadowMap))); NAME_D3D12_OBJECT(m_pipelineStateShadowMap); } // Create temporary command list for initial GPU setup. ComPtr<ID3D12GraphicsCommandList> commandList; ThrowIfFailed(m_device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, m_commandAllocator.Get(), m_pipelineState.Get(), IID_PPV_ARGS(&commandList))); // Create render target views (RTVs). CD3DX12_CPU_DESCRIPTOR_HANDLE rtvHandle(m_rtvHeap->GetCPUDescriptorHandleForHeapStart()); for (UINT i = 0; i < FrameCount; i++) { ThrowIfFailed(m_swapChain->GetBuffer(i, IID_PPV_ARGS(&m_renderTargets[i]))); m_device->CreateRenderTargetView(m_renderTargets[i].Get(), nullptr, rtvHandle); rtvHandle.Offset(1, m_rtvDescriptorSize); NAME_D3D12_OBJECT_INDEXED(m_renderTargets, i); } // Create the depth stencil. { CD3DX12_RESOURCE_DESC shadowTextureDesc( D3D12_RESOURCE_DIMENSION_TEXTURE2D, 0, static_cast<UINT>(m_viewport.Width), static_cast<UINT>(m_viewport.Height), 1, 1, DXGI_FORMAT_D32_FLOAT, 1, 0, D3D12_TEXTURE_LAYOUT_UNKNOWN, D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL | D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE); D3D12_CLEAR_VALUE clearValue; // Performance tip: Tell the runtime at resource creation the desired clear value. clearValue.Format = DXGI_FORMAT_D32_FLOAT; clearValue.DepthStencil.Depth = 1.0f; clearValue.DepthStencil.Stencil = 0; ThrowIfFailed(m_device->CreateCommittedResource( &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT), D3D12_HEAP_FLAG_NONE, &shadowTextureDesc, D3D12_RESOURCE_STATE_DEPTH_WRITE, &clearValue, IID_PPV_ARGS(&m_depthStencil))); NAME_D3D12_OBJECT(m_depthStencil); // Create the depth stencil view. m_device->CreateDepthStencilView(m_depthStencil.Get(), nullptr, m_dsvHeap->GetCPUDescriptorHandleForHeapStart()); } // Load scene assets. UINT fileSize = 0; UINT8* pAssetData; ThrowIfFailed(ReadDataFromFile(GetAssetFullPath(SampleAssets::DataFileName).c_str(), &pAssetData, &fileSize)); // Create the vertex buffer. { ThrowIfFailed(m_device->CreateCommittedResource( &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT), D3D12_HEAP_FLAG_NONE, &CD3DX12_RESOURCE_DESC::Buffer(SampleAssets::VertexDataSize), D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&m_vertexBuffer))); NAME_D3D12_OBJECT(m_vertexBuffer); { ThrowIfFailed(m_device->CreateCommittedResource( &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD), D3D12_HEAP_FLAG_NONE, &CD3DX12_RESOURCE_DESC::Buffer(SampleAssets::VertexDataSize), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&m_vertexBufferUpload))); // Copy data to the upload heap and then schedule a copy // from the upload heap to the vertex buffer. D3D12_SUBRESOURCE_DATA vertexData = {}; vertexData.pData = pAssetData + SampleAssets::VertexDataOffset; vertexData.RowPitch = SampleAssets::VertexDataSize; vertexData.SlicePitch = vertexData.RowPitch; PIXBeginEvent(commandList.Get(), 0, L"Copy vertex buffer data to default resource..."); UpdateSubresources<1>(commandList.Get(), m_vertexBuffer.Get(), m_vertexBufferUpload.Get(), 0, 0, 1, &vertexData); commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_vertexBuffer.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER)); PIXEndEvent(commandList.Get()); } // Initialize the vertex buffer view. m_vertexBufferView.BufferLocation = m_vertexBuffer->GetGPUVirtualAddress(); m_vertexBufferView.SizeInBytes = SampleAssets::VertexDataSize; m_vertexBufferView.StrideInBytes = SampleAssets::StandardVertexStride; } // Create the index buffer. { ThrowIfFailed(m_device->CreateCommittedResource( &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT), D3D12_HEAP_FLAG_NONE, &CD3DX12_RESOURCE_DESC::Buffer(SampleAssets::IndexDataSize), D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&m_indexBuffer))); NAME_D3D12_OBJECT(m_indexBuffer); { ThrowIfFailed(m_device->CreateCommittedResource( &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD), D3D12_HEAP_FLAG_NONE, &CD3DX12_RESOURCE_DESC::Buffer(SampleAssets::IndexDataSize), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&m_indexBufferUpload))); // Copy data to the upload heap and then schedule a copy // from the upload heap to the index buffer. D3D12_SUBRESOURCE_DATA indexData = {}; indexData.pData = pAssetData + SampleAssets::IndexDataOffset; indexData.RowPitch = SampleAssets::IndexDataSize; indexData.SlicePitch = indexData.RowPitch; PIXBeginEvent(commandList.Get(), 0, L"Copy index buffer data to default resource..."); UpdateSubresources<1>(commandList.Get(), m_indexBuffer.Get(), m_indexBufferUpload.Get(), 0, 0, 1, &indexData); commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_indexBuffer.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_INDEX_BUFFER)); PIXEndEvent(commandList.Get()); } // Initialize the index buffer view. m_indexBufferView.BufferLocation = m_indexBuffer->GetGPUVirtualAddress(); m_indexBufferView.SizeInBytes = SampleAssets::IndexDataSize; m_indexBufferView.Format = SampleAssets::StandardIndexFormat; } // Create shader resources. { // Get the CBV SRV descriptor size for the current device. const UINT cbvSrvDescriptorSize = m_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); // Get a handle to the start of the descriptor heap. CD3DX12_CPU_DESCRIPTOR_HANDLE cbvSrvHandle(m_cbvSrvHeap->GetCPUDescriptorHandleForHeapStart()); { // Describe and create 2 null SRVs. Null descriptors are needed in order // to achieve the effect of an "unbound" resource. D3D12_SHADER_RESOURCE_VIEW_DESC nullSrvDesc = {}; nullSrvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; nullSrvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; nullSrvDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; nullSrvDesc.Texture2D.MipLevels = 1; nullSrvDesc.Texture2D.MostDetailedMip = 0; nullSrvDesc.Texture2D.ResourceMinLODClamp = 0.0f; m_device->CreateShaderResourceView(nullptr, &nullSrvDesc, cbvSrvHandle); cbvSrvHandle.Offset(cbvSrvDescriptorSize); m_device->CreateShaderResourceView(nullptr, &nullSrvDesc, cbvSrvHandle); cbvSrvHandle.Offset(cbvSrvDescriptorSize); } // Create each texture and SRV descriptor. const UINT srvCount = _countof(SampleAssets::Textures); PIXBeginEvent(commandList.Get(), 0, L"Copy diffuse and normal texture data to default resources..."); for (UINT i = 0; i < srvCount; i++) { // Describe and create a Texture2D. const SampleAssets::TextureResource &tex = SampleAssets::Textures[i]; CD3DX12_RESOURCE_DESC texDesc( D3D12_RESOURCE_DIMENSION_TEXTURE2D, 0, tex.Width, tex.Height, 1, static_cast<UINT16>(tex.MipLevels), tex.Format, 1, 0, D3D12_TEXTURE_LAYOUT_UNKNOWN, D3D12_RESOURCE_FLAG_NONE); ThrowIfFailed(m_device->CreateCommittedResource( &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT), D3D12_HEAP_FLAG_NONE, &texDesc, D3D12_RESOURCE_STATE_COPY_DEST, nullptr, IID_PPV_ARGS(&m_textures[i]))); NAME_D3D12_OBJECT_INDEXED(m_textures, i); { const UINT subresourceCount = texDesc.DepthOrArraySize * texDesc.MipLevels; UINT64 uploadBufferSize = GetRequiredIntermediateSize(m_textures[i].Get(), 0, subresourceCount); ThrowIfFailed(m_device->CreateCommittedResource( &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_UPLOAD), D3D12_HEAP_FLAG_NONE, &CD3DX12_RESOURCE_DESC::Buffer(uploadBufferSize), D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&m_textureUploads[i]))); // Copy data to the intermediate upload heap and then schedule a copy // from the upload heap to the Texture2D. D3D12_SUBRESOURCE_DATA textureData = {}; textureData.pData = pAssetData + tex.Data->Offset; textureData.RowPitch = tex.Data->Pitch; textureData.SlicePitch = tex.Data->Size; UpdateSubresources(commandList.Get(), m_textures[i].Get(), m_textureUploads[i].Get(), 0, 0, subresourceCount, &textureData); commandList->ResourceBarrier(1, &CD3DX12_RESOURCE_BARRIER::Transition(m_textures[i].Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE)); } // Describe and create an SRV. D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {}; srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; srvDesc.Format = tex.Format; srvDesc.Texture2D.MipLevels = tex.MipLevels; srvDesc.Texture2D.MostDetailedMip = 0; srvDesc.Texture2D.ResourceMinLODClamp = 0.0f; m_device->CreateShaderResourceView(m_textures[i].Get(), &srvDesc, cbvSrvHandle); // Move to the next descriptor slot. cbvSrvHandle.Offset(cbvSrvDescriptorSize); } PIXEndEvent(commandList.Get()); } free(pAssetData); // Create the samplers. { // Get the sampler descriptor size for the current device. const UINT samplerDescriptorSize = m_device->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER); // Get a handle to the start of the descriptor heap. CD3DX12_CPU_DESCRIPTOR_HANDLE samplerHandle(m_samplerHeap->GetCPUDescriptorHandleForHeapStart()); // Describe and create the wrapping sampler, which is used for // sampling diffuse/normal maps. D3D12_SAMPLER_DESC wrapSamplerDesc = {}; wrapSamplerDesc.Filter = D3D12_FILTER_MIN_MAG_MIP_LINEAR; wrapSamplerDesc.AddressU = D3D12_TEXTURE_ADDRESS_MODE_WRAP; wrapSamplerDesc.AddressV = D3D12_TEXTURE_ADDRESS_MODE_WRAP; wrapSamplerDesc.AddressW = D3D12_TEXTURE_ADDRESS_MODE_WRAP; wrapSamplerDesc.MinLOD = 0; wrapSamplerDesc.MaxLOD = D3D12_FLOAT32_MAX; wrapSamplerDesc.MipLODBias = 0.0f; wrapSamplerDesc.MaxAnisotropy = 1; wrapSamplerDesc.ComparisonFunc = D3D12_COMPARISON_FUNC_ALWAYS; wrapSamplerDesc.BorderColor[0] = wrapSamplerDesc.BorderColor[1] = wrapSamplerDesc.BorderColor[2] = wrapSamplerDesc.BorderColor[3] = 0; m_device->CreateSampler(&wrapSamplerDesc, samplerHandle); // Move the handle to the next slot in the descriptor heap. samplerHandle.Offset(samplerDescriptorSize); // Describe and create the point clamping sampler, which is // used for the shadow map. D3D12_SAMPLER_DESC clampSamplerDesc = {}; clampSamplerDesc.Filter = D3D12_FILTER_MIN_MAG_MIP_POINT; clampSamplerDesc.AddressU = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; clampSamplerDesc.AddressV = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; clampSamplerDesc.AddressW = D3D12_TEXTURE_ADDRESS_MODE_CLAMP; clampSamplerDesc.MipLODBias = 0.0f; clampSamplerDesc.MaxAnisotropy = 1; clampSamplerDesc.ComparisonFunc = D3D12_COMPARISON_FUNC_ALWAYS; clampSamplerDesc.BorderColor[0] = clampSamplerDesc.BorderColor[1] = clampSamplerDesc.BorderColor[2] = clampSamplerDesc.BorderColor[3] = 0; clampSamplerDesc.MinLOD = 0; clampSamplerDesc.MaxLOD = D3D12_FLOAT32_MAX; m_device->CreateSampler(&clampSamplerDesc, samplerHandle); } // Create lights. for (int i = 0; i < NumLights; i++) { // Set up each of the light positions and directions (they all start // in the same place). m_lights[i].position = { 0.0f, 15.0f, -30.0f, 1.0f }; m_lights[i].direction = { 0.0, 0.0f, 1.0f, 0.0f }; m_lights[i].falloff = { 800.0f, 1.0f, 0.0f, 1.0f }; m_lights[i].color = { 0.7f, 0.7f, 0.7f, 1.0f }; XMVECTOR eye = XMLoadFloat4(&m_lights[i].position); XMVECTOR at = XMVectorAdd(eye, XMLoadFloat4(&m_lights[i].direction)); XMVECTOR up = { 0, 1, 0 }; m_lightCameras[i].Set(eye, at, up); } // Close the command list and use it to execute the initial GPU setup. ThrowIfFailed(commandList->Close()); ID3D12CommandList* ppCommandLists[] = { commandList.Get() }; m_commandQueue->ExecuteCommandLists(_countof(ppCommandLists), ppCommandLists); // Create frame resources. for (int i = 0; i < FrameCount; i++) { m_frameResources[i] = new FrameResource(m_device.Get(), m_pipelineState.Get(), m_pipelineStateShadowMap.Get(), m_dsvHeap.Get(), m_cbvSrvHeap.Get(), &m_viewport, i); m_frameResources[i]->WriteConstantBuffers(&m_viewport, &m_camera, m_lightCameras, m_lights); } m_currentFrameResourceIndex = 0; m_pCurrentFrameResource = m_frameResources[m_currentFrameResourceIndex]; // Create synchronization objects and wait until assets have been uploaded to the GPU. { ThrowIfFailed(m_device->CreateFence(m_fenceValue, D3D12_FENCE_FLAG_NONE, IID_PPV_ARGS(&m_fence))); m_fenceValue++; // Create an event handle to use for frame synchronization. m_fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); if (m_fenceEvent == nullptr) { ThrowIfFailed(HRESULT_FROM_WIN32(GetLastError())); } // Wait for the command list to execute; we are reusing the same command // list in our main loop but for now, we just want to wait for setup to // complete before continuing. // Signal and increment the fence value. const UINT64 fenceToWaitFor = m_fenceValue; ThrowIfFailed(m_commandQueue->Signal(m_fence.Get(), fenceToWaitFor)); m_fenceValue++; // Wait until the fence is completed. ThrowIfFailed(m_fence->SetEventOnCompletion(fenceToWaitFor, m_fenceEvent)); WaitForSingleObject(m_fenceEvent, INFINITE); } }
// return true to retry later (e.g. after display lost) static bool MainLoop(bool retryCreate) { // Initialize these to nullptr here to handle device lost failures cleanly ovrMirrorTexture mirrorTexture = nullptr; OculusTexture * pEyeRenderTexture = nullptr; DepthBuffer * pEyeDepthBuffer = nullptr; Scene * roomScene = nullptr; Camera * mainCam = nullptr; ovrMirrorTextureDesc desc = {}; bool isVisible = true; long long frameIndex = 0; bool useInstancing = false; const int repeatDrawing = 1; ovrSession session; ovrGraphicsLuid luid; ovrResult result = ovr_Create(&session, &luid); if (!OVR_SUCCESS(result)) return retryCreate; ovrHmdDesc hmdDesc = ovr_GetHmdDesc(session); // Setup Device and Graphics // Note: the mirror window can be any size, for this sample we use 1/2 the HMD resolution if (!DIRECTX.InitDevice(hmdDesc.Resolution.w / 2, hmdDesc.Resolution.h / 2, reinterpret_cast<LUID*>(&luid))) goto Done; ovrRecti eyeRenderViewport[2]; // Make a single eye texture { ovrSizei eyeTexSizeL = ovr_GetFovTextureSize(session, ovrEye_Left, hmdDesc.DefaultEyeFov[0], 1.0f); ovrSizei eyeTexSizeR = ovr_GetFovTextureSize(session, ovrEye_Right, hmdDesc.DefaultEyeFov[1], 1.0f); ovrSizei textureSize; textureSize.w = eyeTexSizeL.w + eyeTexSizeR.w; textureSize.h = max(eyeTexSizeL.h, eyeTexSizeR.h); pEyeRenderTexture = new OculusTexture(); if (!pEyeRenderTexture->Init(session, textureSize.w, textureSize.h)) { if (retryCreate) goto Done; VALIDATE(OVR_SUCCESS(result), "Failed to create eye texture."); } pEyeDepthBuffer = new DepthBuffer(DIRECTX.Device, textureSize.w, textureSize.h); // set viewports eyeRenderViewport[0].Pos.x = 0; eyeRenderViewport[0].Pos.y = 0; eyeRenderViewport[0].Size = eyeTexSizeL; eyeRenderViewport[1].Pos.x = eyeTexSizeL.w; eyeRenderViewport[1].Pos.y = 0; eyeRenderViewport[1].Size = eyeTexSizeR; } if (!pEyeRenderTexture->TextureChain) { if (retryCreate) goto Done; VALIDATE(false, "Failed to create texture."); } // Create a mirror to see on the monitor. desc.Format = OVR_FORMAT_R8G8B8A8_UNORM_SRGB; desc.Width = DIRECTX.WinSizeW; desc.Height = DIRECTX.WinSizeH; result = ovr_CreateMirrorTextureDX(session, DIRECTX.Device, &desc, &mirrorTexture); if (!OVR_SUCCESS(result)) { if (retryCreate) goto Done; VALIDATE(false, "Failed to create mirror texture."); } // Create the room model roomScene = new Scene(false); // Create camera mainCam = new Camera(&XMVectorSet(0.0f, 1.6f, 5.0f, 0), &XMQuaternionIdentity()); // Setup VR components, filling out description ovrEyeRenderDesc eyeRenderDesc[2]; eyeRenderDesc[0] = ovr_GetRenderDesc(session, ovrEye_Left, hmdDesc.DefaultEyeFov[0]); eyeRenderDesc[1] = ovr_GetRenderDesc(session, ovrEye_Right, hmdDesc.DefaultEyeFov[1]); // Main loop while (DIRECTX.HandleMessages()) { XMVECTOR forward = XMVector3Rotate(XMVectorSet(0, 0, -0.05f, 0), mainCam->Rot); XMVECTOR right = XMVector3Rotate(XMVectorSet(0.05f, 0, 0, 0), mainCam->Rot); XMVECTOR up = XMVector3Rotate(XMVectorSet(0, 0.05f, 0, 0), mainCam->Rot); if (DIRECTX.Key['W'] || DIRECTX.Key[VK_UP]) mainCam->Pos = XMVectorAdd(mainCam->Pos, forward); if (DIRECTX.Key['S'] || DIRECTX.Key[VK_DOWN]) mainCam->Pos = XMVectorSubtract(mainCam->Pos, forward); if (DIRECTX.Key['D']) mainCam->Pos = XMVectorAdd(mainCam->Pos, right); if (DIRECTX.Key['A']) mainCam->Pos = XMVectorSubtract(mainCam->Pos, right); if (DIRECTX.Key['Q']) mainCam->Pos = XMVectorAdd(mainCam->Pos, up); if (DIRECTX.Key['E']) mainCam->Pos = XMVectorSubtract(mainCam->Pos, up); static float Yaw = 0; if (DIRECTX.Key[VK_LEFT]) mainCam->Rot = XMQuaternionRotationRollPitchYaw(0, Yaw += 0.02f, 0); if (DIRECTX.Key[VK_RIGHT]) mainCam->Rot = XMQuaternionRotationRollPitchYaw(0, Yaw -= 0.02f, 0); if (DIRECTX.Key['P']) ovr_SetInt(session, OVR_PERF_HUD_MODE, int(ovrPerfHud_AppRenderTiming)); else ovr_SetInt(session, OVR_PERF_HUD_MODE, int(ovrPerfHud_Off)); useInstancing = DIRECTX.Key['I']; // Animate the cube static float cubeClock = 0; roomScene->Models[0]->Pos = XMFLOAT3(9 * sin(cubeClock), 3, 9 * cos(cubeClock += 0.015f)); // Get both eye poses simultaneously, with IPD offset already included. ovrPosef EyeRenderPose[2]; ovrVector3f HmdToEyeOffset[2] = { eyeRenderDesc[0].HmdToEyeOffset, eyeRenderDesc[1].HmdToEyeOffset }; double sensorSampleTime; // sensorSampleTime is fed into the layer later ovr_GetEyePoses(session, frameIndex, ovrTrue, HmdToEyeOffset, EyeRenderPose, &sensorSampleTime); // Render scene to eye texture if (isVisible) { DIRECTX.SetAndClearRenderTarget(pEyeRenderTexture->GetRTV(), pEyeDepthBuffer); // calculate eye transforms XMMATRIX viewProjMatrix[2]; for (int eye = 0; eye < 2; ++eye) { //Get the pose information in XM format XMVECTOR eyeQuat = XMLoadFloat4((XMFLOAT4 *)&EyeRenderPose[eye].Orientation.x); XMVECTOR eyePos = XMVectorSet(EyeRenderPose[eye].Position.x, EyeRenderPose[eye].Position.y, EyeRenderPose[eye].Position.z, 0); // Get view and projection matrices for the Rift camera XMVECTOR CombinedPos = XMVectorAdd(mainCam->Pos, XMVector3Rotate(eyePos, mainCam->Rot)); Camera finalCam(&CombinedPos, &(XMQuaternionMultiply(eyeQuat, mainCam->Rot))); XMMATRIX view = finalCam.GetViewMatrix(); ovrMatrix4f p = ovrMatrix4f_Projection(eyeRenderDesc[eye].Fov, 0.1f, 100.0f, ovrProjection_None); XMMATRIX proj = XMMatrixSet(p.M[0][0], p.M[1][0], p.M[2][0], p.M[3][0], p.M[0][1], p.M[1][1], p.M[2][1], p.M[3][1], p.M[0][2], p.M[1][2], p.M[2][2], p.M[3][2], p.M[0][3], p.M[1][3], p.M[2][3], p.M[3][3]); if (useInstancing) { // scale and offset projection matrix to shift image to correct part of texture for each eye XMMATRIX scale = XMMatrixScaling(0.5f, 1.0f, 1.0f); XMMATRIX translate = XMMatrixTranslation((eye==0) ? -0.5f : 0.5f, 0.0f, 0.0f); proj = XMMatrixMultiply(proj, scale); proj = XMMatrixMultiply(proj, translate); } viewProjMatrix[eye] = XMMatrixMultiply(view, proj); } if (useInstancing) { // use instancing for stereo DIRECTX.SetViewport(0.0f, 0.0f, (float)eyeRenderViewport[0].Size.w + eyeRenderViewport[1].Size.w, (float)eyeRenderViewport[0].Size.h); // render scene for (int i = 0; i < repeatDrawing; i++) roomScene->RenderInstanced(&viewProjMatrix[0], 1, 1, 1, 1, true); } else { // non-instanced path for (int eye = 0; eye < 2; ++eye) { // set viewport DIRECTX.SetViewport((float)eyeRenderViewport[eye].Pos.x, (float)eyeRenderViewport[eye].Pos.y, (float)eyeRenderViewport[eye].Size.w, (float)eyeRenderViewport[eye].Size.h); // render scene for (int i = 0; i < repeatDrawing; i++) roomScene->Render(&viewProjMatrix[eye], 1, 1, 1, 1, true); } } // Commit rendering to the swap chain pEyeRenderTexture->Commit(); } // Initialize our single full screen Fov layer. ovrLayerEyeFov ld = {}; ld.Header.Type = ovrLayerType_EyeFov; ld.Header.Flags = 0; ld.SensorSampleTime = sensorSampleTime; for (int eye = 0; eye < 2; ++eye) { ld.ColorTexture[eye] = pEyeRenderTexture->TextureChain; ld.Viewport[eye] = eyeRenderViewport[eye]; ld.Fov[eye] = hmdDesc.DefaultEyeFov[eye]; ld.RenderPose[eye] = EyeRenderPose[eye]; } ovrLayerHeader* layers = &ld.Header; result = ovr_SubmitFrame(session, frameIndex, nullptr, &layers, 1); // exit the rendering loop if submit returns an error, will retry on ovrError_DisplayLost if (!OVR_SUCCESS(result)) goto Done; isVisible = (result == ovrSuccess); // Render mirror ID3D11Texture2D* tex = nullptr; ovr_GetMirrorTextureBufferDX(session, mirrorTexture, IID_PPV_ARGS(&tex)); DIRECTX.Context->CopyResource(DIRECTX.BackBuffer, tex); tex->Release(); DIRECTX.SwapChain->Present(0, 0); frameIndex++; } // Release resources Done: delete mainCam; delete roomScene; if (mirrorTexture) ovr_DestroyMirrorTexture(session, mirrorTexture); delete pEyeRenderTexture; delete pEyeDepthBuffer; DIRECTX.ReleaseDevice(); ovr_Destroy(session); // Retry on ovrError_DisplayLost return retryCreate || OVR_SUCCESS(result) || (result == ovrError_DisplayLost); }