jmquigs / ModelMod

A system for modifying art in games.
GNU Lesser General Public License v2.1
80 stars 11 forks source link

Update the mmobj IO addon to 3.0+ #36

Open Frosferes2 opened 1 year ago

Frosferes2 commented 1 year ago

The mmobj IO addon scripts have been rewritten using the Blender 3.0+ obj addon as a basis. Important edits to the obj code are preceded by a comment containing either ModelMod or Fros.

Transformation strings are now stored in the Blender object data rather than the vertex group list.

In-game testing has been limited due to GW2 removing DX9 support and the compatibility issues with DX11. I have not been able to create a new mod due to the faulty snapshotting.

jmquigs commented 1 year ago

Thanks very much for this! I'll take a look at it next couple days. I actually haven't worked in MM for a few months now so I need to dust off my own codebase.

Frosferes2 commented 1 year ago

It's no problem, I'm happy to help! I only found out today that GW2 ended DX9 support, so I guess we won't know for certain if everything is working until DX11 is fully functional for MM.

In the meantime, I was looking at the materials imported by obj and now this addon by extension. I think that the alpha channel of the diffuse texture is used as a transparency map by GW2, which is fairly standard for video games. If I link the alpha channel and set the material transparency to clip I can get the mesh looking fairly accurate to the game.

What puzzles me is the lack of normals, or any other map besides the diffuse. I suppose the character models look fairly flat in-game and the engine is very old, but I thought I saw normals used at some point in the game. Maybe they're not on characters for performance reasons shrug.

Frosferes2 commented 1 year ago

I was experimenting with some of my old mods to see if the new IO was working correctly. When I first loaded them, they didn't seem to be working anymore, i.e. nothing changed when the conditions were satisfied. I checked the log file when launching in debug mode and found this error: [ERROR:M:ModDBInterop]: System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection. which was thrown when loading one particular mod.

After some trial and error, I realised that this mod was set to 'mod' weight blend mode. I also determined that the index in question was the named vertex group (#vgn) list that is normally read from the mmobj when mod weight is enabled. I set this mod to use proximity weights instead, and this caused the error to disappear and the mods to load correctly again, including the newly exported mesh.

When I first modified the obj IO, I neglected to port the section that added the #vgn lines, since I wan't aware if they were necessary or not. After adding this to the new export scipt, the group name lines are written as they should, and the previous error does not appear when the mod is in mod weight mode.

Unfortunately, there is another hurdle: when in mod weight mode, the exported mmobj no longer prevents all other mods from working, but it itself doesn't appear. The relevant section of the log file says as follows: ThreadId(1)/39062ms: [M:ModDBInterop]: Vert type 1856284140 contains 7 elements ThreadId(1)/39063ms: [M:ModDBInterop]: Position 0 0 Format R32G32B32_Float ThreadId(1)/39063ms: [M:ModDBInterop]: Normal 0 20 Format R8G8B8A8_UNorm ThreadId(1)/39064ms: [M:ModDBInterop]: Tangent 0 24 Format R8G8B8A8_UNorm ThreadId(1)/39064ms: [M:ModDBInterop]: Binormal 0 28 Format R8G8B8A8_UNorm ThreadId(1)/39064ms: [M:ModDBInterop]: BlendIndices 0 16 Format R8G8B8A8_UInt ThreadId(1)/39065ms: [M:ModDBInterop]: BlendWeight 0 12 Format R8G8B8A8_UNorm ThreadId(1)/39065ms: [M:ModDBInterop]: TextureCoordinate 0 32 Format R32G32_Float ThreadId(1)/39068ms: [M:ModDBInterop]: filling d3d11 vertex buffer stream size: 1492320; vert size: 40, vert type id: 1856284140 ThreadId(1)/39081ms: [ERROR:M:ModDBInterop]: Unsupported format for mod blend index: Format R8G8B8A8_UInt This error occurs regardless of the mesh I use, including snapshotted ones. So I believe that this error is with MM itself. If I am wrong however, let me know.

Both the old and new export scripts generate the same values for the vertex normal (vn) lines. These are different from the vertex normals in the reference mmobj, and I assume this is the cause of the lighting appearing strange in-game. Since these vertex normals are 3d vectors, I suspect that the reason for the discrepancy is that the normals are generated in Blender's axis basis and are not transformed for writing or in-game display. Could the rotation transform data in the mmobj be applied to the vertex normals as well? This may help solve the lighting issue.

As an aside, I have checked and found that GW2 DX11 is compatible with the BC7 DDS compression format, which is nice since it reduces texture size 4-fold whilst appearing virtually lossless.

Frosferes2 commented 1 year ago

Ignore the section of my previous comment about vertex normals. The bug doesn't seem to be related to the MM transformations. It actually appears to be in two parts: mismatching of the vertices with their corresponding normals, and the space the normals are calculated in.

I have managed to fix the former problem in the latest commit. Before, the vertex normals were calculated from the mesh loops, and then compared to a list of previously extracted normals to make sure every normal value was unique. This led to scenarios where the quantity and/or ordering of the vertex normals did not align with the actual vertex list, resulting in glitchy lighting effects. However, in the Blender API, the normals can be called directly from the mesh vertices, ensuring parity between the normals list and vertex indices. Why it was originally coded like it was, I am not sure. It could have something to do with allowing for sharp transitions in vertex colours, or this vertex normal call may have not been part of the API when it was written. Either way, changing this has significantly improved the look of the mesh lighting.

The lighting no longer appears broken. However, it now appears too uniform, as if all vertices in the mesh are facing the same direction relative to the camera. This makes me think the vertex normals are somehow situated in the wrong space (tangent vs object space). This is a problem I will have to tackle another day though. If you know anything more about the calculation or basis of the vertex normals extracted in the ref mmobj, please let me know. The only observation I have at the moment is that all the vector components appear to be negative, which is a little odd.

jmquigs commented 1 year ago

@Frosferes2 thanks for working on this, unfortunately I have several real life issues I'm dealing with now and won't be able to get to modelmod any time soon.

I did read your last comment about your lighting fix, its cool that you found a fix for some of the issues. In terms of your last comment about the lighting appearing to be on a fixed axis, that could be related to this line in the F# code: https://github.com/jmquigs/ModelMod/blob/master/MMManaged/ModDBInterop.fs#L556

That was a hack I put in long ago when I realized the binormals weren't being generated properly. It basically computes new ones using a cross product from two fixed vectors, but that is not technically correct or even close (they are supposed to be generated using vectors derived from the uv coordinates). In the DX11 code I had experimented with using the DXMesh lib to generate those properly but I'm not sure that I ever disabled that cross product hack, so even if that lib did generate working vectors, that code might have broken them.

The vector components must also be written out in the order and format required by the game and this is something that I've had trouble with (I basically had to figure it out with manual testing). If the shader is available and can be decompiled, reading it could shed some insight into the format of the vectors, but I never did this (I had hoped reshade might help with this one day)

Frosferes2 commented 1 year ago

@jmquigs No worries. I hope whatever you're dealing with isn't too taxing on you.

I actually visualised the vertex normals in Matlab. Here is the front X, Y view. The vectors originate from the mesh in the middle and branch outwards. The blue ones are from the ref file and the red are from the mod file exported from Blender vecs You can see that, whils the mod vectors are not biased in any particular direction, the ref vectors are all skewed towards the negative of each axis, causing them all the face roughly the same direction in object space. This is borne out by the observation that all the vn line elements in the ref file are negative. I suspected this was caused by some problem in the MM code, since I was unable to find a single transformation basis that could conform the Blender vectors to the ref vectors.

Perhaps I could have a go at fixing this myself. The main reason for me not digging into the lower-level code is that I've been putting off learning how to use compilers. Are the dependencies for the MM code base listed somewhere?

Besides the normals bug is the snapshotting and mod weight mode not working correctly. I don't know if I could be much help with the former, as I have no idea what the problem is there. But the mod weights blend index exception I mentioned before is invoked by the same script you linked to. Do you know what the cause of this may be?

jmquigs commented 1 year ago

The ref vecs may be a straight dump from the d3d data structures, i'm not sure if I run those through the snapshot transforms. Its possible there is some kind of compression on them decoded by the game shader.

As far as the compiled code though, it might be easiest to start with the F# code as the issues you are troubleshooting are more likely to be there than in the rust code.

The github action that builds the release might be a good place to start. I build the Fsharp code with what used to be called dotnet core. Unfortunately I never made a proper contributing guide. https://github.com/jmquigs/ModelMod/blob/master/.github/workflows/dotnet-desktop.yml You can build new F# assemblies that should be compatible with the MM rust binaries you already have.

If you do try that and have more issues there with building the code, please create a new issue in this repo so that we can discuss, just so this PR isn't cluttered with unrelated comments on how to build the code.

Frosferes2 commented 1 year ago

Thanks, I will give this a try when I can. Unfortunately, Visual Studio flatly refuses to work on my machine. I suspect my Windows installation is corrupted somehow and requires reflashing. To be honest, my drive needs a clear out anyway, but it might take a couple of days to fish through and back up the stuff I want to keep.