diff --git a/Settings/AllSettings.ini b/Settings/AllSettings.ini index a0207d85..4d7b3dbf 100644 --- a/Settings/AllSettings.ini +++ b/Settings/AllSettings.ini @@ -90,6 +90,14 @@ DdrawOverrideHeight = 0 DdrawOverrideStencilFormat = 0 DdrawIntegerScalingClamp = 0 DdrawMaintainAspectRatio = 0 +DdrawDisableLighting = 0 +DdrawConvertHomogeneousW = 0 +DdrawConvertHomogeneousToWorld = 0 +DdrawConvertHomogeneousToWorldUseGameCamera = 0 +DdrawConvertHomogeneousToWorldFOV = 90.0 +DdrawConvertHomogeneousToWorldNearPlane = 1.0 +DdrawConvertHomogeneousToWorldFarPlane = 1000.0 +DdrawConvertHomogeneousToWorldDepthOffset = 0.0 [d3d9] AnisotropicFiltering = 0 diff --git a/Settings/Settings.cpp b/Settings/Settings.cpp index ed81bf64..ca960c99 100644 --- a/Settings/Settings.cpp +++ b/Settings/Settings.cpp @@ -495,6 +495,9 @@ void Settings::SetDefaultConfigSettings() Config.PrimaryBufferSamples = 44100; Config.PrimaryBufferChannels = 2; Config.AudioFadeOutDelayMS = 20; + Config.DdrawConvertHomogeneousToWorldFOV = 90.0f; + Config.DdrawConvertHomogeneousToWorldNearPlane = 1.0f; + Config.DdrawConvertHomogeneousToWorldFarPlane = 1000.0f; SetValue("ExcludeProcess", "dxwnd.exe", &Config.ExcludeProcess); SetValue("ExcludeProcess", "dgVoodooSetup.exe", &Config.ExcludeProcess); } diff --git a/Settings/Settings.h b/Settings/Settings.h index 00637929..e84e7ef0 100644 --- a/Settings/Settings.h +++ b/Settings/Settings.h @@ -56,9 +56,17 @@ visit(DdrawOverrideHeight) \ visit(DdrawOverrideStencilFormat) \ visit(DdrawResolutionHack) \ + visit(DdrawConvertHomogeneousW) \ + visit(DdrawConvertHomogeneousToWorld) \ + visit(DdrawConvertHomogeneousToWorldUseGameCamera) \ + visit(DdrawConvertHomogeneousToWorldFOV) \ + visit(DdrawConvertHomogeneousToWorldNearPlane) \ + visit(DdrawConvertHomogeneousToWorldFarPlane) \ + visit(DdrawConvertHomogeneousToWorldDepthOffset) \ visit(DdrawUseDirect3D9Caps) \ visit(DdrawUseNativeResolution) \ visit(DdrawEnableMouseHook) \ + visit(DdrawDisableLighting) \ visit(DdrawHookSystem32) \ visit(D3d8HookSystem32) \ visit(D3d9HookSystem32) \ @@ -251,6 +259,13 @@ struct CONFIG bool DdrawWriteToGDI = false; // Blt surface directly to GDI rather than Direct3D9 bool DdrawIntegerScalingClamp = false; // Scales the screen by an integer value to help preserve video quality bool DdrawMaintainAspectRatio = false; // Keeps the current DirectDraw aspect ratio when overriding the game's resolution + bool DdrawConvertHomogeneousW = false; // Convert primites using D3DFVF_XYZRHW to D3DFVF_XYZW. + bool DdrawConvertHomogeneousToWorld = false; // Convert primitives back into a world space. Needed for RTX. + bool DdrawConvertHomogeneousToWorldUseGameCamera = false; // Use the game's view matrix instead of replacing it with our own. + float DdrawConvertHomogeneousToWorldFOV = 0.0f; // The field of view of the camera used to reconstruct the original 3D world. + float DdrawConvertHomogeneousToWorldNearPlane = 0.0f; // The near plane of the camera used to reconstruct the original 3D world. + float DdrawConvertHomogeneousToWorldFarPlane = 0.0f; // The far plane of the camera used to reconstruct the original 3D world. + float DdrawConvertHomogeneousToWorldDepthOffset = 0.0f; // The offset to add to the geometry so it does not clip into the near plane. bool DdrawUseDirect3D9Caps = false; // Use Direct3D9 (Dd7to9) for GetCaps bool DdrawUseNativeResolution = false; // Uses the current screen resolution for Dd7to9 DWORD DdrawClippedWidth = 0; // Used to scaled Direct3d9 to use this width when using Dd7to9 @@ -269,6 +284,7 @@ struct CONFIG DWORD DdrawFlipFillColor = 0; // Color used to fill the primary surface before flipping bool DdrawForceMipMapAutoGen = false; // Force Direct3d9 to use this AutoStencilFormat when using Dd7to9 bool DdrawEnableMouseHook = false; // Allow to hook into mouse to limit it to the chosen resolution + bool DdrawDisableLighting = false; // Allow to disable lighting DWORD DdrawHookSystem32 = 0; // Hooks the ddraw.dll file in the Windows System32 folder DWORD D3d8HookSystem32 = 0; // Hooks the d3d8.dll file in the Windows System32 folder DWORD D3d9HookSystem32 = 0; // Hooks the d3d9.dll file in the Windows System32 folder diff --git a/ddraw/IDirect3DDeviceX.cpp b/ddraw/IDirect3DDeviceX.cpp index e7e4766b..9f1d9ee5 100644 --- a/ddraw/IDirect3DDeviceX.cpp +++ b/ddraw/IDirect3DDeviceX.cpp @@ -2340,6 +2340,12 @@ HRESULT m_IDirect3DDeviceX::SetRenderState(D3DRENDERSTATETYPE dwRenderStateType, LOG_LIMIT(100, __FUNCTION__ << " Warning: 'D3DRENDERSTATE_STIPPLEPATTERN00' not implemented! " << dwRenderState); } return D3D_OK; + case D3DRENDERSTATE_LIGHTING: // 137 + if (Config.DdrawDisableLighting) + { + dwRenderState = FALSE; + } + break; case D3DRENDERSTATE_EXTENTS: // 138 // ToDo: use this to enable/disable clip plane extents set by SetClipStatus() DeviceStates.RenderState[dwRenderStateType].State = dwRenderState; @@ -2559,17 +2565,122 @@ HRESULT m_IDirect3DDeviceX::SetTransform(D3DTRANSFORMSTATETYPE dtstTransformStat break; } - HRESULT hr = SetD9Transform(dtstTransformStateType, lpD3DMatrix); - - if (SUCCEEDED(hr)) + D3DMATRIX view; + if (Config.DdrawConvertHomogeneousW) { -#ifdef ENABLE_DEBUGOVERLAY - if (Config.EnableImgui) + if (dtstTransformStateType == D3DTS_VIEW) { - DOverlay.SetTransform(dtstTransformStateType, lpD3DMatrix); + D3DVIEWPORT9 Viewport9; + if (SUCCEEDED((*d3d9Device)->GetViewport(&Viewport9))) + { + const float width = (float)Viewport9.Width; + const float height = (float)Viewport9.Height; + + // Replace the matrix with one that handles D3DFVF_XYZRHW geometry + ZeroMemory(&view, sizeof(D3DMATRIX)); + view._11 = 2.0f / width; + view._22 = -2.0f / height; + view._33 = 1.0f; + view._41 = -1.0f; // translate X + view._42 = 1.0f; // translate Y + view._44 = 1.0f; + + // Set flag + ConvertHomogeneous.IsTransformViewSet = true; + + if (Config.DdrawConvertHomogeneousToWorld) + { + DirectX::XMVECTOR position, direction; + float depthOffset = 0.0f; + if (Config.DdrawConvertHomogeneousToWorldUseGameCamera) + { + // To reconstruct the 3D world, we need to know where the camera is and where it is looking + position = DirectX::XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); + + const float x = lpD3DMatrix->_11; + const float y = lpD3DMatrix->_12; + const float z = lpD3DMatrix->_13; + + float pitch = std::atan2(y, z); + if (pitch < 0.0f && y * z > 0.0f) // check if y and z have the same sign + { + // handle flipping of the pitch. This is not because the camera is looking up. + pitch += DirectX::XM_PI; + } + + float yaw = std::asin(x); + if (yaw < 0.0f) + { + yaw += DirectX::XM_2PI; + } + + // mirror the transform + float pitchneg = -pitch; + + float pitch_cos = std::cos(pitchneg); + float x2 = 0.0f; //std::cos(yaw) * pitch_cos; + float y2 = std::sin(pitchneg); + float z2 = /*std::sin(yaw) **/ pitch_cos; + + direction = DirectX::XMVectorSet(x2, y2, z2, 0.0f); + + depthOffset = Config.DdrawConvertHomogeneousToWorldDepthOffset; + + ConvertHomogeneous.ToWorld_GameCameraYaw = yaw; + ConvertHomogeneous.ToWorld_GameCameraPitch = pitch; + } + else + { + position = DirectX::XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f); + direction = DirectX::XMVectorSet(0.0f, 0.0f, 1.0f, 0.0f); + } + + // Store the original matrix so it can be restored + ConvertHomogeneous.ToWorld_ViewMatrixOriginal = view; + + // The Black & White matrix is an ortho camera, so create a perspective one matching the game + const float fov = Config.DdrawConvertHomogeneousToWorldFOV; + const float nearplane = Config.DdrawConvertHomogeneousToWorldNearPlane; + const float farplane = Config.DdrawConvertHomogeneousToWorldFarPlane; + const float ratio = width / height; + DirectX::XMMATRIX proj = DirectX::XMMatrixPerspectiveFovLH(fov * (3.14159265359f / 180.0f), ratio, nearplane, farplane); + + DirectX::XMStoreFloat4x4((DirectX::XMFLOAT4X4*)&ConvertHomogeneous.ToWorld_ProjectionMatrix, proj); + + DirectX::XMVECTOR upVector = DirectX::XMVectorSet(0.0f, 1.0f, 0.0f, 0.0f); + DirectX::XMMATRIX viewMatrix = DirectX::XMMatrixLookToLH(position, direction, upVector); + + // Store the 3D view matrix so it can be set later + DirectX::XMStoreFloat4x4((DirectX::XMFLOAT4X4*)&ConvertHomogeneous.ToWorld_ViewMatrix, viewMatrix); + + // Store the view inverse matrix of the game, so we can transform the geometry with it + DirectX::XMMATRIX toViewSpace = DirectX::XMLoadFloat4x4((DirectX::XMFLOAT4X4*)&view); + DirectX::XMMATRIX vp = DirectX::XMMatrixMultiply(viewMatrix, proj); + DirectX::XMMATRIX vpinv = DirectX::XMMatrixInverse(nullptr, vp); + + DirectX::XMMATRIX depthoffset = DirectX::XMMatrixTranslation(0.0f, 0.0f, depthOffset); + + ConvertHomogeneous.ToWorld_ViewMatrixInverse = DirectX::XMMatrixMultiply(depthoffset, DirectX::XMMatrixMultiply(toViewSpace, vpinv)); + } + + // Override original matrix pointer + lpD3DMatrix = &view; + } } -#endif + else + { + return D3D_OK; + } + } + + HRESULT hr = SetD9Transform(dtstTransformStateType, lpD3DMatrix); + +#ifdef ENABLE_DEBUGOVERLAY + if (SUCCEEDED(hr) && !Config.DdrawConvertHomogeneousW) + { + DOverlay.SetTransform(dtstTransformStateType, lpD3DMatrix); } +#endif return hr; } @@ -2826,6 +2937,103 @@ HRESULT m_IDirect3DDeviceX::DrawIndexedPrimitive(D3DPRIMITIVETYPE dptPrimitiveTy // Update vertices for Direct3D9 (needs to be first) UpdateVertices(dwVertexTypeDesc, lpVertices, 0, dwVertexCount); + // Handle PositionT + if (Config.DdrawConvertHomogeneousW && (dwVertexTypeDesc & 0x0E) == D3DFVF_XYZRHW) + { + if (!ConvertHomogeneous.IsTransformViewSet) + { + D3DMATRIX Matrix = {}; + GetTransform(D3DTS_VIEW, &Matrix); + SetTransform(D3DTS_VIEW, &Matrix); + } + + if (!Config.DdrawConvertHomogeneousToWorld) + { + /*UINT8 *vertex = (UINT8*)lpVertices; + for (UINT x = 0; x < dwVertexCount; x++) + { + float *pos = (float*) vertex; + pos[3] = 1.0f; + vertex += stride; + }*/ + + // Update the FVF + dwVertexTypeDesc = (dwVertexTypeDesc & ~D3DFVF_XYZRHW) | D3DFVF_XYZW; + } + else + { + const UINT stride = GetVertexStride(dwVertexTypeDesc); + + const UINT targetStride = stride - sizeof(float); + const UINT restSize = stride - sizeof(float) * 4; + + ConvertHomogeneous.ToWorld_IntermediateGeometry.resize(targetStride * dwVertexCount); + + UINT8* sourceVertex = (UINT8*)lpVertices; + UINT8* targetVertex = (UINT8*)ConvertHomogeneous.ToWorld_IntermediateGeometry.data(); + + lpVertices = targetVertex; + + for (UINT x = 0; x < dwVertexCount; x++) + { + // Transform the vertices into world space + float* srcpos = (float*)sourceVertex; + float* trgtpos = (float*)targetVertex; + + DirectX::XMVECTOR xpos = DirectX::XMVectorSet(srcpos[0], srcpos[1], srcpos[2], srcpos[3]); + + DirectX::XMVECTOR xpos_global = DirectX::XMVector3TransformCoord(xpos, ConvertHomogeneous.ToWorld_ViewMatrixInverse); + + xpos_global = DirectX::XMVectorDivide(xpos_global, DirectX::XMVectorSplatW(xpos_global)); + + trgtpos[0] = DirectX::XMVectorGetX(xpos_global); + trgtpos[1] = DirectX::XMVectorGetY(xpos_global); + trgtpos[2] = DirectX::XMVectorGetZ(xpos_global); + + // Copy the rest + std::memcpy(targetVertex + sizeof(float) * 3, sourceVertex + sizeof(float) * 4, restSize); + + // Move to next vertex + sourceVertex += stride; + targetVertex += targetStride; + } + + // Set transform + (*d3d9Device)->SetTransform(D3DTS_VIEW, &ConvertHomogeneous.ToWorld_ViewMatrix); + (*d3d9Device)->SetTransform(D3DTS_PROJECTION, &ConvertHomogeneous.ToWorld_ProjectionMatrix); + + // Update the FVF + const DWORD newVertexTypeDesc = (dwVertexTypeDesc & ~D3DFVF_XYZRHW) | D3DFVF_XYZ; + + // Set fixed function vertex type + if (FAILED((*d3d9Device)->SetFVF(newVertexTypeDesc))) + { + LOG_LIMIT(100, __FUNCTION__ << " Error: invalid FVF type: " << Logging::hex(dwVertexTypeDesc)); + return D3DERR_INVALIDVERTEXTYPE; + } + + // Handle dwFlags + SetDrawStates(newVertexTypeDesc, dwFlags, DirectXVersion); + + // Draw indexed primitive UP + HRESULT hr = (*d3d9Device)->DrawIndexedPrimitiveUP(dptPrimitiveType, 0, dwVertexCount, GetNumberOfPrimitives(dptPrimitiveType, dwIndexCount), lpwIndices, D3DFMT_INDEX16, lpVertices, targetStride); + + // Handle dwFlags + RestoreDrawStates(newVertexTypeDesc, dwFlags); + + // Restore transform + D3DMATRIX identityMatrix = {}; + identityMatrix._11 = 1.0f; + identityMatrix._22 = 1.0f; + identityMatrix._33 = 1.0f; + + (*d3d9Device)->SetTransform(D3DTS_VIEW, &ConvertHomogeneous.ToWorld_ViewMatrixOriginal); + (*d3d9Device)->SetTransform(D3DTS_PROJECTION, &identityMatrix); + + return hr; + } + } + // Set fixed function vertex type if (FAILED((*d3d9Device)->SetFVF(dwVertexTypeDesc))) { @@ -5972,6 +6180,10 @@ void m_IDirect3DDeviceX::PrepDevice() { (*d3d9Device)->SetTransform(entry.first, &entry.second); } + if (Config.DdrawDisableLighting) + { + (*d3d9Device)->SetRenderState(D3DRS_LIGHTING, FALSE); + } } } @@ -6095,6 +6307,9 @@ void m_IDirect3DDeviceX::SetDefaults() bSetDefaults = false; RequiresStateRestore = true; + // Reset Homogeneous flag + ConvertHomogeneous.IsTransformViewSet = false; + // Render states if (ClientDirectXVersion > 1) { diff --git a/ddraw/IDirect3DDeviceX.h b/ddraw/IDirect3DDeviceX.h index f29b7a41..13fa75e8 100644 --- a/ddraw/IDirect3DDeviceX.h +++ b/ddraw/IDirect3DDeviceX.h @@ -225,6 +225,9 @@ class m_IDirect3DDeviceX : public IUnknown, public AddressLookupTableDdrawObject // Vector temporary buffer cache std::vector> VertexCache; + // The data used for rendering Homogeneous + CONVERTHOMOGENEOUS ConvertHomogeneous; + // Viewport array std::vector AttachedViewports; diff --git a/ddraw/IDirect3DTypes.h b/ddraw/IDirect3DTypes.h index 01ac26bd..b75d6292 100644 --- a/ddraw/IDirect3DTypes.h +++ b/ddraw/IDirect3DTypes.h @@ -386,6 +386,18 @@ typedef enum _D3DSURFACETYPE { D3DTYPE_DEPTHSTENCIL = 4 } D3DSURFACETYPE; +struct CONVERTHOMOGENEOUS +{ + bool IsTransformViewSet = false; // Remembers if game sets the view matrix + D3DMATRIX ToWorld_ProjectionMatrix; // Store the projection matrix used to transform the geometry on the gpu + D3DMATRIX ToWorld_ViewMatrix; // Store the view matrix used to transform the geometry on the gpu + D3DMATRIX ToWorld_ViewMatrixOriginal; // Store the original view matrix, so we can restore it + DirectX::XMMATRIX ToWorld_ViewMatrixInverse; // Store the inverse view matrix to transform the geometry on the cpu + std::vector ToWorld_IntermediateGeometry; // Intermediate buffer for the geometry conversion + float ToWorld_GameCameraYaw = 0.0f; + float ToWorld_GameCameraPitch = 0.0f; +}; + #define CLAMP(val,zmin,zmax) (max((zmin),min((zmax),(val)))) // Clamp rhw values