elishacloud / dxwrapper

Fixes compatibility issues with older games running on Windows 10/11 by wrapping DirectX dlls. Also allows loading custom libraries with the file extension .asi into game processes.
zlib License
1.15k stars 82 forks source link

Added support for transforming homogenous W geometry into world geomtry #199

Open dkollmann opened 1 year ago

dkollmann commented 1 year ago

This is an update on the previous pull request https://github.com/elishacloud/dxwrapper/pull/188.

elishacloud commented 1 year ago

Thanks @dkollmann! I will take a look at this soon.

dkollmann commented 1 year ago

Also FYI if you try this code with Black & White, things will have a blue tint. This is because the water plane which should be rendered before all the other geometry, is rendered on top of it instead of behind the other geometry.

dkollmann commented 1 year ago

I made some fixes. Of course lpD3DMatrix has to be replaced, after getting the game camera properties from it. I added an option to disable the lighting which is needed when DdrawConvertHomogeneousW is true but DdrawConvertHomogeneousToWorld is false. At least for Black & White.

The force push was just an update on the commit comment.

elishacloud commented 1 year ago

For some reason when I add this DdrawConvertHomogeneousW = 1 line to the ini file I just get the following:

image

elishacloud commented 1 year ago

For some reason when I add this DdrawConvertHomogeneousW = 1 line to the ini file I just get the following:

Ok, I figured this out. You have to set DdrawDisableLighting = 1 for Black & White.

elishacloud commented 1 year ago

My only last comment is that I prefer to create a struct and add it to the IDirect3DDeviceX.h file rather than a whole new RednerData class for the DdrawConvertHomogeneous settings/matrices. Since the code is in IDirect3DDeviceX.cpp the settings should be in IDirect3DDeviceX.h.

Other than that, I think this is about ready to merge.

dkollmann commented 1 year ago

Ah okay, I created the RenderData.h, because it was not possbile to include the device header in the DebugOverlay.cpp file.

elishacloud commented 1 year ago

I created the RenderData.h, because it was not possbile to include the device header in the DebugOverlay.cpp file.

DebugOverlay.h already has #include "ddraw.h", which includes IDirect3DDeviceX.h. So it is already covered.

elishacloud commented 1 year ago

Now DdrawConvertHomogeneousW seems to be working great. However, I am not able to get DdrawConvertHomogeneousToWorld working at all. I just get a black screen.

PatrickvL commented 1 year ago

If possible, attach a few good looking screenshots (or even link a video) of the resulting rendering in RTX Remix!

dkollmann commented 1 year ago

These are my settings

DdrawOverrideWidth = 1920 DdrawOverrideHeight = 1080 DdrawOverrideRefreshRate = 60 DdrawOverrideStencilFormat = 80 DdrawConvertHomogeneousW = 1 DdrawConvertHomogeneousToWorld = 1 DdrawConvertHomogeneousToWorldUseGameCamera = 1 DdrawConvertHomogeneousToWorldFOV = 90 DdrawConvertHomogeneousToWorldNearPlane = 1 DdrawConvertHomogeneousToWorldFarPlane = 1000 DdrawConvertHomogeneousToWorldDepthOffset = 0.001 DdrawEnableMouseHook = 0 DdrawDisableLighting = 1

dkollmann commented 1 year ago

On my PC with the previous setings...

No RTX, the water plane which should be rendered first is just rendered alogn with the rest of the geometry. https://1drv.ms/v/s!As0-jjvsSN2xgft8Y6R-1Zsvta99uQ?e=453wxe

RTX, I ignore the water plane, so it is not rendered, since it does not serve the purpose of a raytraced surface anyway, since it is a plane facing the camera. https://1drv.ms/v/s!As0-jjvsSN2xgft9BREiXiT7RT6VAg?e=3t0yky

The RTX version is still quite experimental. I am fixing the issues one after another, but there is still work to do.

elishacloud commented 1 year ago

Ok, thanks! I figured it out. With the latest check-in this all you should need in your ini file. The rest should be set by default or auto-detected:

[Compatibility]
Dd7to9                     = 1

[Dd7to9]
DdrawEnableMouseHook       = 0
DdrawDisableLighting       = 1
DdrawConvertHomogeneousW   = 1
DdrawConvertHomogeneousToWorld              = 1
DdrawConvertHomogeneousToWorldUseGameCamera = 1
elishacloud commented 1 year ago

With these settings and the latest build I am not seeing any issues. Try this build with only the included settings.

Latest build: dxwrapper.zip

dkollmann commented 1 year ago

Hmmm it works too well. Something seems off. Let me merge those changes into my branch and let me check.

RTX version crashes, with no explicit stencil format, but that is fine. DdrawOverrideStencilFormat = 80

dkollmann commented 1 year ago

Looks correct in blender though. image

elishacloud commented 1 year ago

RTX version crashes, with no explicit stencil format, but that is fine. DdrawOverrideStencilFormat = 80

Sounds like a bug in RTX. The stencil format is set later. Maybe we should default to setting a stencil and then let the game override that, if needed?

I defaulted to no stencil because 2D DirectDraw games don't need it. But I don't think it will hurt to have a default one set. I prefer having less options and letting dxwrapper figure out things for the user.

Try this one and see if this has the same issue with RTX: dxwrapper.zip

dkollmann commented 1 year ago

I would not worry. To use RTX, you have to install all this as a mod anyway, so you also get the correct overrides and new assets.

I want to replace the water shader as well as the terrain shader. And add lights to the spells and so on.

dkollmann commented 1 year ago

Okay I found the issue. But still, not using DdrawConvertHomogeneousToWorldUseGameCamera currently yields the better results.

Maybe it would be better to just move the light, instead of transforming the geometry into insane values which then generate Z fighting. For the raytracing it should make no difference and the geometry we see is only the visible area anyway, so moving it around the world has no benefit.

dkollmann commented 1 year ago

Try this one and see if this has the same issue with RTX: dxwrapper.zip

Still crashes. Sorry. But as mentioned it does not matter, the user will get an installer anyway, and that will also install the dxwrapper.ini.

Also keep in mind that they just relased RTX Remix 0.2.0 like a week ago. It is still quite an experimental tool.

elishacloud commented 1 year ago

Still crashes. Sorry. [...] it does not matter, the user will get an installer anyway [...] they just relased RTX Remix 0.2.0 like a week ago. It is still quite an experimental tool.

Ok, no problem. Then I will not worry about it right now.

not using DdrawConvertHomogeneousToWorldUseGameCamera currently yields the better results.

Ok. What's the purpose of DdrawConvertHomogeneousToWorldUseGameCamera?

Maybe it would be better to just move the light, instead of transforming the geometry into insane values which then generate Z fighting.

I can't reproduce this issue. I'll wait until you have this resolved before merging the code.

dkollmann commented 1 year ago

Ok. What's the purpose of DdrawConvertHomogeneousToWorldUseGameCamera?

The idea is to transform the geometry back to its "original" world position, so it is correctly placed relative to other world geometry. So when I remove the 2D sky and replace it with a sky dome, so the raytracing has something it can actually hit, the game geometry will be correctly placed.

But it will probably be better to transform the sky dome instead into the homogenous space.

elishacloud commented 1 year ago

But still, not using DdrawConvertHomogeneousToWorldUseGameCamera

I think I fixed this issue. It should now be using the Game Camera. See check-in here: 44d43381e096a1fbff974f593ef68abc4d5c7efd

elishacloud commented 1 year ago

I can't reproduce this issue.

Now I can see the problem with the DdrawConvertHomogeneousToWorldUseGameCamera. It works fine as long as this option is disabled.

elishacloud commented 1 year ago

Shouldn't these lines be changed? FROM:

position = DirectX::XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f);
direction = DirectX::XMVectorSet(0.0f, 0.0f, 1.0f, 0.0f);

TO:

position = DirectX::XMVectorSet(view._41, view._42, view._43, view._44);
direction = DirectX::XMVectorSet(view._31, view._32, view._33, view._34);
dkollmann commented 1 year ago

Shouldn't these lines be changed?

The result of XMMatrixLookToLH should stay the same. So just using fixed values makes it easier for the compiler to optimize this and it simply represents what it does. A matrix which looks down the Z axis.

elishacloud commented 1 year ago

The result of XMMatrixLookToLH should stay the same.

Are you sure? The position sent would be DirectX::XMVectorSet(-1.0f, 1.0f, 0.0f, 1.0f); rather than DirectX::XMVectorSet(0.0f, 0.0f, 0.0f, 0.0f);. Also, if we ever change the view later than having it set by view means it gets updated also. No need to manually change it later.

elishacloud commented 1 year ago

It seems like there may be more than just something wrong with DdrawConvertHomogeneousToWorldUseGameCamera. I am seeing an issue in Dark Reign 2 when just using DdrawConvertHomogeneousToWorld. The sky seems stretched

With DdrawConvertHomogeneousToWorld enable:

image

How it is supposed to look:

image

I can also see it even when just DdrawConvertHomogeneousW is enabled, but it is not quite as pronounced.

dkollmann commented 1 year ago

Okay, here are the things are are wrong here and why "position" is zero.

The result will be the same, but the code should be a bit clearer.

So what happens is...

I hope this all makes sense and is correct. Please take a look.

dkollmann commented 1 year ago

Not sure if it is related, but I noticed something curious with the reconstructed world space in Black & White.

image

The whole world is rotated by 24.4° degreees. But the thing is, if you look at the trees at the bottom left, they are straight, which implies that this is correct. I wonder why this is. Is this due to the angle of the reflection? In game, the camera is completely horizontal. I checked the original view matrix.

But I also noticed that the Y direction seems to be flipped. It is negative when looking up and positive when looking down.

dkollmann commented 1 year ago

Okay this seems to be a Black & White thing. No matter how I look att he view matrix, the angle only seems to be half of the actual angle. MAybe this is related to faking bent normals.

dkollmann commented 1 year ago

Hey Elisha, so I finally got around to take a closer look at this for Black & White. So for me, the practical difference between DdrawConvertHomogeneousToWorldUseGameCamera is that I use the original view matrix to remove the camera's tilt, so the world geometry comes out as horizontal. This way I can place a new water plane and it will always correctly align with the terrain. Of course this also means that the code became quite specialised for Black & White now and it might be a better option to remove DdrawConvertHomogeneousToWorldUseGameCamera, unless you see general value in this special case.

And just as a note, in the following code, yaw and pitch look like they should be the other way around, but this is indeed correct. Yaw is the rotation of X and pitch is the rotation of a vector around X.

Here is what I do now:

DirectX::XMVECTOR position, direction;
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);

    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);
}
elishacloud commented 1 year ago

@dkollmann, sorry for the delay in responding. I updated the code to match your branch. But I am wondering if there is an issue with DdrawConvertHomogeneousW which is getting exacerbated in DdrawConvertHomogeneousToWorld.

Could there be an issue because we are losing data in the DrawIndexedPrimitive() when we overwrite data in lpVertices?

The reason I ask is because I am still seeing distortion in the sky in Dark Reign 2 whenever DdrawConvertHomogeneousToWorld is enabled, even if DdrawConvertHomogeneousToWorldUseGameCamera is disabled. I also see distortion even if just DdrawConvertHomogeneousW is enabled, though the distortion is much smaller in this last case.

dkollmann commented 1 year ago

Hey, I just wanted to say that I am still on this, but I changed my job and am moving to another country, so lately I am super busy.

elishacloud commented 1 year ago

@dkollmann, no problem. Take your time. I understand. Real life has to come first. Thanks for letting me know.

elishacloud commented 1 year ago

Because there are a lot of things added since this PR was created, I updated it to keep it in-sync with the master branch.

elishacloud commented 11 months ago

Keeping PR in-sync with master branch.