Jump to content

Mortal Kombat: Shaolin Monks PS2 models


roocker666
Go to solution Solved by shak-otay,

Recommended Posts

Hi guys, I was trying to extract models from MKSM PS2, it seems like models are compressed in one of these files: GAMEDATA.WAD, GAMEDATA.WAE, GAMEDATA.WAF, GAMEDATA.WAG, GAMEDATA.WAH, GAMEDATA.WAI, GAMEDATA.WAJ(Some are actually .AFS but these have sounds or music) but after extracting the content of GAMEDATA.WAD, I ended with 2K files without names, that sucks, lol. So I just dumped PCSX2 memory and it was easier to find models.

OK, I analyzed the models and I found two different mesh formats, the only difference is in vertex buffer. One is size 32 and the other is size 48, both have a header that looks the same, you can see a tag for meshes there" 04 04 00 01" So with this info is there a way to extract models?  

It looks like vertex buffer 32 does not have Normals, weird..  Here are two model samples: Kitana and Scorpion

And here a few images of vbuf32 and vbuf48 meshes(from Scorpion model): 

BTW, faces are autogenerated so there is no faces data.

scorpion vbuf 32.PNG

scorpion vbuf 48.PNG

Edited by roocker666
  • Thanks 1
Link to comment
Share on other sites

Posted (edited)
22 hours ago, roocker666 said:

So with this info is there a way to extract models?  

 

Hi, isn't it almost always the same with those PS2 models using 4 bytes signature?

Autocreating faces is always a little bit tricky with PS2 models. But I didn't remember to have so many weird superfluous faces.

Kitana_b-point_cloud.png

PS2_autocreated_faces.png

Edited by shak-otay
Link to comment
Share on other sites

Posted (edited)
7 hours ago, shak-otay said:

Hi, isn't it almost always the same with those PS2 models using 4 bytes signature?

A lot of  PS2 models use a 4 bytes signature in submeshes. Just the header changes depending on the game.

Mmmh, maybe" Mesh format1" uses triangles and "Mesh format2" uses tristrip. Just guessing.. but that would be very strange, This is weird

Edited by roocker666
Link to comment
Share on other sites

Posted (edited)

I used tristrips in my previous post (standard triangles don't do). Maybe it's required to remove double vertices before? (I always forget about this.)

Edited by shak-otay
Link to comment
Share on other sites

I did not notice those double vertices, lol. Maybe that is the problem but I really don't know.. I was checking XBOX version but models are in those WAD files too but quickbms script only works on PS2 WAD, so we can't use XBOX files, that sucks.

Link to comment
Share on other sites

32 minutes ago, shak-otay said:

Seems my vStride detection is faulty. How to decide from data what is 32 and what is 48?

 

Exaclty, that is the big question. I don't see a clue on how to identify meshes with 32 and 48. Your image looks like something but maybe is still reading wrong vertex size in some meshes.

I tried to check each mesh just by a quick look, I cut all meshes and made two files, one has all meshes with vertex 32 and the other file has all meshes with vertex 48. But then I noticed that there are two models for each character, Maybe one is used for fatalities. So here is Kitana for fatalities(original model, vertex 32 gorup and vertex 48 group):

model3_kitana_fatality.rar

Link to comment
Share on other sites

Posted (edited)
10 minutes ago, shak-otay said:

Here's the logged obj file for reference (there's 14 error messages inside which I think can be ignored;-)

NIce, that looks A LOT better! thanks. So how do you know what mesh is 32 and what mesh is 48?

Edited by roocker666
Link to comment
Share on other sites

Well, the code for deciding whether vstride is 32 or 48 is ugly. Basically it checks for 0404 at a certain offset:

                pFBuf += 16; j += 16;                                 // start of v-block
                pTmp= pFBuf ; pTmp += vCntB*32 +4;
                if ((*pTmp==0)&&(*(pTmp+1)==0)) pTmp += 24 ;
                cnt2=0;
                while ((*pTmp!=4)&&(cnt2<32)) {pTmp++; cnt2++;}
                if (*pTmp!=4) {
                    pTmp= pFBuf ; pTmp += vCntB*48 +4;
                    if ((*pTmp==0)&&(*(pTmp+1)==0)) pTmp += 24 ;
                    cnt2=0;
                    while ((*pTmp!=4)&&(cnt2<48)) {pTmp++; cnt2++;}
                    if (*pTmp!=4) fprintf( stream, "# error!\n");
                    if (*(pTmp+1)==4) vStride= 48; else fprintf( stream, "# error missing 2nd 4!\n");
                }
                else {
                    if (*(pTmp+1)==4) vStride= 32 ; else fprintf( stream, "# error missing 2nd 4!\n");
                }

Scorpion looks like so:

scorpion.png

  • Like 1
Link to comment
Share on other sites

Posted (edited)
1 hour ago, shak-otay said:

Well, the code for deciding whether vstride is 32 or 48 is ugly. Basically it checks for 0404 at a certain offset...

Ok, ok. I think I found a clue, when a mesh ends(at the end of all its vertex buffers) next bytes are zeros and number 17.

So if mesh has vbuf32 those bytes are 00 00 00 17(4 bytes). If mesh has vbuf48 those bytes are 00 00 00 00 00 00 00 17(8 bytes). That is the only difference that I found, don't know if that helps 🤔

Edited by roocker666
  • Like 1
Link to comment
Share on other sites

Posted (edited)

I see, thanks. Might result in less confusing code but there's 108 findings in kitana of 0000000000000017 (which 4 of them are counts) -> 104.

Atm I've 100x [32] and 83x [48].  I'll check this.

Edited by shak-otay
Link to comment
Share on other sites

Posted (edited)
53 minutes ago, shak-otay said:

I see, thanks. Might result in less confusing code but there's 108 findings in kitana of 0000000000000017 (which 4 of them are counts) -> 104.

Atm I've 100x [32] and 83x [48].  I'll check this.

Got it, just one more thing; At the end of the model there are a lot of tiny submeshes with count 4 and 8, those are the 5 little cubes that appear on top of the model(like in your image of scorpion). So you can delete those submeshes from the file. In Kitana those submeshes start at 0x2A400. So delete the code from 0x2A400 to 0x2BA10, that is useless. It should be same with scorpion.

Edited by roocker666
Link to comment
Share on other sites

Posted (edited)

There are smaller meshes all in the file, for example at # 1. 0x72c 8 [32]

I'm not sure how deleting them would affect the creation of faces.

For the 0000000000000017 thingie, it improves but gives some false results, too, for example for the block at 0x27dd0, which has a stride of 32.

This was easy to correct and I think I can upload the tool after some more checks.

edit: tool for testing (no uv support so far), kitana after extra faces being erased

Make_obj_ShaolinMonksPS2.zip

Kitana_raw.png

Edited by shak-otay
  • Like 1
Link to comment
Share on other sites

Posted (edited)
9 hours ago, shak-otay said:

There are smaller meshes all in the file, for example at # 1. 0x72c 8 [32]

I'm not sure how deleting them would affect the creation of faces.

For the 0000000000000017 thingie, it improves but gives some false results, too, for example for the block at 0x27dd0, which has a stride of 32.

 

Those 5 cubes are always at the end of the model so you don't need to delete small meshes before those.

About block 0x27dd0, the search 0000000000000017 is reading the last four bytes of the last vertex buffer of that specific mesh. SO false results is because of that(I think)

Thanks for tool, I will test it right now

Edited by roocker666
Link to comment
Share on other sites

Posted (edited)

Ok, I used other model "Rayden" it seems like the model is fine(besides those extra faces lol). So the tool works 🙂 I guess you can add UVs later, according to my 2 first images:

UVaddress = Vertaddress + 16(in both vertex formats 32 and 48) The problem are Normals becasue vbuf32 does not have Normals, in vbuf48 Normals address = Vertaddress + 32.

Thanks for the effort man, here is a picture of Rayden:

rayden.PNG

Edited by roocker666
  • Like 1
Link to comment
Share on other sites

Posted (edited)
2 hours ago, shak-otay said:

Well, the uvs. There was always a problem with PS2 uvs, afair. (Maybe I solved a similar problem on Xentax but not for autocreated faces?)

I've created sub meshes in the obj file to get more overview (without them the uvs cannot be separated).

Makeobj_log-kitana_uvs: Well, UVs look good but you need to delete all those extra faces(I did not delete extra faces of the head, that is why UVs look messy). And you need to group specific meshes according to textures.

I grouped all meshes of the head and used the texture;

kitana_Uvs.PNG

kitana_Uvs2.PNG

BTW, here are Kitana textures if you want to check(4 textures):

kitana_textures.rar

Edited by roocker666
  • Like 1
Link to comment
Share on other sites

21 minutes ago, shak-otay said:

If you say so, here's the app expanded by uvs..

 

Yep but you need to delete all extra faces and group meshes. It takes some time but the result is great!

Thanks for the tool and all your help man.  😃

This is the result:

Kitana_Fix.PNG

Link to comment
Share on other sites

Posted (edited)
2 hours ago, roocker666 said:

Yep but you need to delete all extra faces

Maybe the standard tri strips algo isn't the best choice. They probably use a special one.

edit: here another algo is used - don't see an improvement:

Different_triStrips_stillExtraFaces.png

Edited by shak-otay
Link to comment
Share on other sites

Posted (edited)
11 hours ago, shak-otay said:

edit: here another algo is used - don't see an improvement:

I remember that I asked to Leeao about extracting Haunting ground PS2 maps years ago. As you know, PS2 maps don't have faces(like characters) so he wrote a plugin for Noesis. He read vertices buffer(UVs are in other separate block) and then created a Tri List, vertices buffer had this format:

 XXXX YYYY ZZZZ 0000803F

 XXXX YYYY ZZZZ 0000803F

 XXXX YYYY ZZZZ 0000803F

XYZ are 3 Floats then 4 bytes of padding. Here are some images, I know it was made using Python but maybe you can do something similar using C++

 

HG_PS2 vertices.PNG

HG_PS2 Tri list.PNG

Edited by roocker666
  • Thanks 1
Link to comment
Share on other sites

Posted (edited)

Hi, basically the code in the 2nd picture is "standard" tri strips algo (at least I'd call it so:-).

The interesting part is skiplist[ i ] which is filled in the code from the fst picture.

The question is, "can we create a flag from the vertex block values?" I have my doubts when looking at your mesh format pictures above.

But this is the path we need to follow, I guess.

If skipflag is true the face is not written. Thus he skips the superfluous ones, afaics.

 

Edited by shak-otay
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...