XeNTaX Forum Index
Forum MultiEx Commander Tools Tools Home
It is currently Wed May 23, 2018 3:42 pm

All times are UTC + 1 hour




Post new topic Reply to topic  [ 6 posts ] 
Author Message
 Post subject: Make_obj (C source)
PostPosted: Mon Mar 06, 2017 11:01 am 
Offline
M-M-M-Monster veteran
M-M-M-Monster veteran
User avatar

Joined: Fri Apr 20, 2012 9:24 am
Posts: 2250
Location: Nexus, searching for Jim Kirk
Has thanked: 501 times
Have thanks: 1163 times
well, another year has come and gone and I think it's time to share some source (based on my Make_H2O project).

This project shall help to extract multi mesh models so is some kind of follow up to hex2obj.
You don't need to use hex2obj for analysing the model before (but it's recommended).

You can't make too much use of this project if you don't understand 'C', though.
(btw: it doesn't contain the Make_H2O_ForzaHor3 source, if you thought of that. Just some basic functions.)

I used the Han_stormtrooper.rax model as a startup which can be found here:
viewtopic.php?f=16&t=13867&p=126467&hilit=stormtrooper#p126467
(Thx to AceWell) :)

I've tested this one character model only so might not work for other characters
and will NOT work for static objects, I guess. Feel free to expand the code to do so.

The FVFsize is fixed; you might use the editbox to support different sizes (not implemented so far).

I'm a great fan of pattern (byte sequence) searching so here we go:

Find the counts (behind pattern 00 00 10 03, so search for that first)
find first "FACE " (46 41 43 45 00) after uvs
then find "pos ", get verts and uvs startaddress

and skip to the face indices table

--------------------------

I'm pretty sure we'll need some data alignment here (function DWORD dataAlignment(DWORD j) for example)
instead of simply adding 40 bytes here to skip to the face indices table:

pFBuf= pTmp + addrUV + 40 ; j= addrUV + 40 ; fprintf( stream, "# start of FIs: %x\n", j) ;
(where addrUV is the last uv's address +1)

https://www.pic-upload.de/view-32802746 ... r.jpg.html


here's the "main" function of the code, it's ugly and simple, but it works:
Code:
void SM_of_SW_BF3_loop(HWND hwnd, char szPathname[], DWORD dwStart)      // scanning the submeshes
{                                                                         // only one model tested
   char * pFBuf, *pTmp, szNo[4] ;
   BYTE cnt, fCnt=0, i, SMcnt, FVFsize= 24 ;
   int k, nValue[16] ;
   WORD FI[3] ;
   DWORD FIcnt, FIcnt_arr[999], vCnt, vCnt_arr[999] ;         //
   DWORD addrUV, addrV, j=0, jOld, lastJ, offs2 ;  //
   DWORD minFaceInd = 16777215, maxFaceInd = 0, lastFaceInd=0 ;

   pFBuf = (char *) lpFBuf ; pTmp= pFBuf ;
   pFBuf += dwStart ;
   SendMessage(GetDlgItem(hwnd, ID_LIST), LB_ADDSTRING, 0, (LPARAM) " creating obj:") ;
   cnt= 0 ; lastJ= 0 ; j= 0 ;
   // get counts of submeshes
   nValue[0]= 0; nValue[1]= 0; nValue[2]= 0x10; nValue[3]= 3;    // there's counts behind that pattern
   do {
        offs2 = FindBytes(lpFBuf, j, dwFileSize-j, nValue, 4) ;       // j is offset here
        if (offs2!=0) {
            pFBuf += offs2 + 4 ; j += offs2 + 4 ;
            GetDW(pFBuf, j, vCnt, true) ; GetDW(pFBuf, j, FIcnt, true) ; // j not used here
            vCnt_arr[cnt]= vCnt ; FIcnt_arr[cnt]= FIcnt ; fprintf( stream, "# %d %d\n", vCnt, FIcnt) ;
            if (cnt<999) cnt++ ; else chMB("Too many submeshes!") ;
            pFBuf += 8 ; j += 8 ;
        }
   } while ((offs2!=0)&&(j<dwFileSize)) ;
    // get counts of last submesh
   offs2 = FindBytes(lpFBuf, j, dwFileSize-j, nValue, 3) ;       // j is offset here
    if (offs2!=0) {
        pFBuf += offs2 + 4 ; j += offs2 + 4 ;
        GetDW(pFBuf, j, vCnt, true) ; GetDW(pFBuf, j, FIcnt, true) ; // j not used here
        vCnt_arr[cnt]= vCnt ; FIcnt_arr[cnt]= FIcnt ; fprintf( stream, "# %d %d\n", vCnt, FIcnt) ;
        pFBuf += 8 ; j += 8 ; cnt++ ;
    } else chMB("Error, last submesh not found!") ;
    // where's the stzartaddresses for vertices and uvs?
   nValue[0]= 0x46; nValue[1]= 0x41; nValue[2]= 0x43; nValue[3]= 0x45; nValue[4]= 0;    // 'FACE'
   offs2 = FindBytes(lpFBuf, j, dwFileSize-j, nValue, 5) ;       // j is offset here
    if (offs2!=0) {
        pFBuf += offs2 ; j += offs2 ;
        pFBuf += 8 ; j += 8 ;
    } else chMB("Error, 'FACE ' after last counts not found!") ;    // evtl. erste von 2 'FACE'
   nValue[0]= 0x50; nValue[1]= 0x4F; nValue[2]= 0x53; nValue[3]= 0;     // 'POS '
   offs2 = FindBytes(lpFBuf, j, dwFileSize-j, nValue, 4) ;       // j is offset here
    if (offs2!=0) {
        pFBuf += offs2 -32 ; j += offs2 - 32 ;                   //
        GetDW(pFBuf, j, addrV, true) ; pFBuf += 16 ; j += 16 ;
        GetDW(pFBuf, j, addrUV, true) ; fprintf( stream, "# %x %x\n", addrV, addrUV) ;
        pFBuf += 72 ; j += 72 ;
    } else chMB("Error, 'POS ' after 'FACE' not found!") ;
    if (j!=addrV) {chMB("address calculation went wrong! break...") ; return ;}
    SMcnt= cnt ;
    for (i=0;i < SMcnt;i++) {
        fprintf( stream, "# %x, %d\n", addrV, vCnt_arr[i]) ;
        log_short_Verts(addrV, vCnt_arr[i], FVFsize) ; addrV += FVFsize*vCnt_arr[i] ;
    }
    if (addrV!=addrUV) {chMB("UV address calculation went wrong! break...") ; return ;}
    // go for uvs, blocksize is 4
    for (i=0;i < SMcnt;i++) {
        log_short_UVs(addrUV, vCnt_arr[i], 4) ; addrUV += 4*vCnt_arr[i] ;
    }
    pFBuf= pTmp + addrUV + 40 ; j= addrUV + 40 ; fprintf( stream, "# start of FIs: %x\n", j) ;
    // go for face indices
    for (i=0;i < SMcnt;i++) {
        _itoa(i, szNo, 10) ; fprintf( stream, "g SM_%s\n", szNo) ;      // separating the submeshes (building groups)
        for (k=0;k<FIcnt_arr[i];k++) {       //
            //                       adding lastFaceInd in that function to get absolute FIs
            FI[fCnt]= (WORD) GetFaceIndexBigE(pFBuf, j, minFaceInd, maxFaceInd, lastFaceInd, false) ;   //
            fCnt++ ;
            if (fCnt==3) {
                fCnt=0 ;                        //
                fprintf( stream, "f %d/%d %d/%d %d/%d\n", FI[0],FI[0],FI[1],FI[1],FI[2],FI[2]) ;
            }
        }
        if (maxFaceInd<lastFaceInd) {
            fprintf( stream, "error! maxFI %d < lastFI %d\n\n", maxFaceInd, lastFaceInd) ;
        }
        else lastFaceInd = maxFaceInd ;         // needed to "convert" relative to absolute face indices
        fprintf( stream, "# next FI block: %x\n", j) ;
    }
}
(code doesn't care for normals)


Attachments:


You do not have the required permissions to view the files attached to this post. Register to gain access.


_________________
"you can't always get things served on a silver tray"
extracting simple models: http://forum.xentax.com/viewtopic.php?f=29&t=10894
Make_H2O-ForzaHor3-jm9.zip

"We are Microsoft. You will be assimilated. Resistance is Futile."


Last edited by shakotay2 on Sun Jul 02, 2017 9:14 pm, edited 1 time in total.

Top
 Profile  
 
 Post subject: Re: Make_obj (C source)
PostPosted: Mon Mar 27, 2017 3:40 pm 
Offline
M-M-M-Monster veteran
M-M-M-Monster veteran
User avatar

Joined: Fri Apr 20, 2012 9:24 am
Posts: 2250
Location: Nexus, searching for Jim Kirk
Has thanked: 501 times
Have thanks: 1163 times
I've updated the source code (see zip in previous post) for extracting an obj model from [CITY_PARAMEDIC-]dbd91b3e.36
which you need to extract from CITY_PARAMEDIC.PCPACK using chrrox quickbms script.

The code can handle this one model only atm. (Feel free to expand it for other .36 models.)

This is a short and uncomplete description how it was coded:
Code:
make a copy of the dummy function SM_of_DFF_loop()
rename it to SM_of_SpMan3_loop()

# we need a new entry for the select-game combobox:
in line 50: set model to 2, or active new model

In function DoFileOpenSave() in the SECOND
switch (model) selection for case 2: add SM_of_SpMan3_loop(hwnd, szFileName, 0) ;

In the callback function WndProc() in the case WM_CREATE: branch add
SendMessage(hwndCombo1, CB_ADDSTRING, 0, (LPARAM) "SpMan3") ;

as a new entry in the game-selection combo box.
Build and run to see how it works.

This was the simple part; now we have to fill our new function
SM_of_SpMan3_loop() with code to get all submeshes (SMs).

Best approach would be a full format analysis, but as you may know,
I really don't like such. But..

It's helpful to use magic tables, if any.
How to find them? Well, simply search for the counts.

hex2obj

If you're familiar with hex2obj you could try to extract the first submesh of the heli, [CA5_Heli-]63757bc7.36.
You can calculate the face indices count from (endaddr -startaddress) / 2, which is (0xecc52 - 0xe7974) / 2 = 10607 (0x296F)

Entering startaddr and count into the go1 editboxes and pressing that button
you'll get the assumed vertex count in the lower left list box: 6269 (0x187D)

getting the offsets of the magic tables:
----------------------------------------------
Those values (7D180000 and 6F290000) to be found at offsets 0x0DF4 and 0x0E04 in the CA5_Heli-63757bc7.36 file.

I'll use the patterns 04 00000000 and 02 000000 to find those tables.
(Understanding how to define patterns is a matter of experience; there's no general recipe.)

Code:
Fill the nValue[] array with the first pattern (04 00 00 00 00).

The FindBytes() function will return the offset addr (offs2) of a found pattern,
if any, otherwise offs2 will be zero!

Once a pattern is found we'd check for the second one (byte per byte) to avoid using
FindBytes() again (which doesn't make too much sense here).

Look at 0xDFF, the address of the first find, to understand why we check for
(*(pFBuf+9)==2) to assure that we didn't meet a false find
(the 3 following bytes need to be checked for zero, too).

These are the results for the counts of each submesh then (vertex, FIs) in the log/obj file:
# 0. 6269 10607
# 1. 199 341
# 2. 898 1631
# 3. 208 422
# 4. 34 65
# 5. 548 919
# 6. 153 249
# 7. 34 65
# 8. 555 933
# 9. 153 253
# 10. 3582 6275
# 11. 1562 2761
# 12. 986 1787
# 13. 83 164
# 14. 58 103
# 15. 990 1809
# 16. 83 165
# 17. 58 103
# 18. 2440 4221

using a simpler model
---------------------------------

19 submeshes for CA5_Heli-]63757bc7.36, yeah, a little bit too many for a first approach, so let's stick to
CITY_PARAMEDIC-dbd91b3e.36, which has 5 only and, much more important, I've extraced
a proper texture for it.

We go for the face indices now, using 0000 0100 0200 as a well-known search pattern
(which doesn't apply for all 3D formats, as you may know already).

Well, for .36 character models it seems to be some kind of signature, too, so an additional
check for a following xx 00 is required, else we've get 60 findings!

Code:
Create a copy of the search pattern loop in our SpMan3 function, it's important to
[b]reinitialize[/b] some variables: cnt= 0 ; pFBuf= pTmp; j= 0 ;
And setting the new search pattern, of course.
In FindBytes() the last parameter is 6 now, the number of bytes to be searched for.

Sadly we've too many finds for FIs' blocks, though, 7 instead of 5:
# 0. 2180 4169
# 1. 1132 2875
# 2. 86 207
# 3. 1290 2781
# 4. 798 1747

# 0xe78
# 0x1098
# 0x83740
# 0x92c14
# 0x95404
# 0xa4784
# 0xaf2e0

So we have to exclude the first two ones, with a simple check, that works for this
CITY_PARAMEDIC-dbd91b3e.36 file only.
The code change is expanding the if conditon if ((*(pFBuf+7)==0) by &&(j>0x40000),
where j is the actual address offset into the .36 file and pFBuf is just a char pointer to the file buffer.

(I'm sorry to say that this is my sloppy way to solve problems in a time saving manner.)

Now we have all we need: the counts, the FIs' starting addresses.
------------------------------------------------------------------------
We'll calculate the vertex address from FIstart - vCount * FVFsize,
where FVFsize is 48 for the PARAMEDIC character (52 for Fireman, for the Heli it was 36).

Sadly there's one different FVFsize to be found: 52 for submesh 3 (cnt==2).
(But we can replace this stupid solution by getting the FVFsize directly; it's 16 bytes before
the 0700000000000800 patterns.)

We need a loop controlled by SMcnt, the submesh count, to get all the submeshes.

I've placed some tristrips face index routine from my MakeH2O project in that loop.

We also need these vars:
Code:
BYTE fCnt=0 ;
int faceDir= 1 ;
DWORD minFaceInd = 16777215, maxFaceInd = 0, lastFaceInd=0 ;
DWORD f1,f2,f3 ;


Provided as is, don't blame me.
Works for CITY_PARAMEDIC-dbd91b3e.36 only atm.



And well, I didn't care for the textures; just used the "camera button" in TextureFinder. :D


Attachments:


You do not have the required permissions to view the files attached to this post. Register to gain access.


_________________
"you can't always get things served on a silver tray"
extracting simple models: http://forum.xentax.com/viewtopic.php?f=29&t=10894
Make_H2O-ForzaHor3-jm9.zip

"We are Microsoft. You will be assimilated. Resistance is Futile."


Top
 Profile  
 
 Post subject: Re: Make_obj (C source)
PostPosted: Sat Mar 31, 2018 10:26 pm 
Offline
M-M-M-Monster veteran
M-M-M-Monster veteran
User avatar

Joined: Fri Apr 20, 2012 9:24 am
Posts: 2250
Location: Nexus, searching for Jim Kirk
Has thanked: 501 times
Have thanks: 1163 times
again another year has come and gone, so here's a small update integrating Kamen Rider CW prefab models
(only two models tested so may badly fail on others)

Make_obj, C source included


Attachments:


You do not have the required permissions to view the files attached to this post. Register to gain access.


_________________
"you can't always get things served on a silver tray"
extracting simple models: http://forum.xentax.com/viewtopic.php?f=29&t=10894
Make_H2O-ForzaHor3-jm9.zip

"We are Microsoft. You will be assimilated. Resistance is Futile."


Top
 Profile  
 
 Post subject: Re: Make_obj (C source)
PostPosted: Sun Apr 01, 2018 1:35 am 
Offline
veteran
User avatar

Joined: Thu Sep 03, 2015 10:33 pm
Posts: 86
Has thanked: 12 times
Have thanks: 6 times
shakotay2 wrote:
again another year has come and gone, so here's a small update integrating Kamen Rider CW prefab models
(only two models tested so may badly fail on others)

Make_obj, C source included



Thank you very much.


Top
 Profile  
 
 Post subject: Re: Make_obj (C source)
PostPosted: Thu May 03, 2018 12:19 pm 
Offline
M-M-M-Monster veteran
M-M-M-Monster veteran
User avatar

Joined: Fri Apr 20, 2012 9:24 am
Posts: 2250
Location: Nexus, searching for Jim Kirk
Has thanked: 501 times
Have thanks: 1163 times
another month has come and gone :D so here's the next update (adding Skyforge mesh support)
(where the lod (or whatever) submeshes are spoiled)
First and maybe 2nd SM should be okay; feel free to fix it in the source.
(The problem is that there's more vertex blocks than face indices blocks i.e. up to 4 vertex blocks share the same FIs block.)
Seems each submesh has one "copy" at least, up to a count of 4. But I have no idea how to get the count via code.
Even more annoying: the copies may not follow in sequence.

The body sample (Base.Skin-Geometry.bin) to be found here (in slawdos' post):
viewtopic.php?f=16&t=13497&start=15

code for Skyforge mesh:
Code:
void SM_of_Skyforge_loop(HWND hwnd, char szPathname[], DWORD dwStart)
{
   char * pFBuf, *pTmp ;            // , szNo[4]
   BYTE cnt= 0, i, SMcnt ;
   int nValue[16] ;
   //WORD wFaceIndCnt, wVertsCnt, wOffs2VBlock ;         //
   DWORD FIaddr_arr[999], FIcnt=0, FIcnt_arr[999], uvAddr_arr[999], uvCnt, Vaddr_arr[999], vCnt, vCnt_arr[999] ;
   DWORD minFaceInd = 16777215, maxFaceInd = 0, lastFaceInd=0, startFI ;
   DWORD addrFI=0, addrUV, addrV, jOld, lastJ, offs2 ;  //
   DWORD j=0 ;
   float *pFloat ;
   float fData = 0.0f;      //
   bool bStop= false, bUV ;

   pFBuf = (char *) lpFBuf ; pTmp= pFBuf ;
   dwStart = 0 ; pFBuf += dwStart ;            //
   SendMessage(GetDlgItem(hwnd, ID_LIST), LB_ADDSTRING, 0, (LPARAM) " creating obj:") ;
   cnt = 0 ;
   nValue[0]= 0; nValue[1]= 0; nValue[2]= 1; nValue[3]= 0; nValue[4]= 2; //nValue[5]= 0;    // 0000 0100 02
   do {
        offs2 = FindBytes(lpFBuf, j, dwFileSize-j, nValue, 5) ;       // j is offset here
        if (offs2!=0) {
            pFBuf += offs2 ; j += offs2 ; fprintf( stream, "#") ;
            FIaddr_arr[cnt]= j ; if (cnt<511) cnt++ ; else chMB("Too many submeshes!") ;
            fprintf( stream, "%x\n", j) ;
            pFBuf += 6 ; j += 6 ;
        }
   } while ((offs2!=0)&&(j<dwFileSize)) ;
   FIaddr_arr[cnt] = dwFileSize ;               // but not sure if every Geometry.bin file has FIs 'til their ending!
   SMcnt = cnt ;
   for (i=0;i<SMcnt;i++) {
       FIcnt_arr[i] = (FIaddr_arr[i+1]-FIaddr_arr[i]) / 2 ;
            //fprintf( stream, "%d, ", FIcnt_arr[i]) ;
       FIcnt += FIcnt_arr[i] ;
   }
   pFBuf= pTmp ; pFBuf += 4 ;
   GetDW(pFBuf, j, vCnt, false) ;
   fprintf( stream, "\n# vCnt?: %d, FIs %d\n", vCnt/72, FIcnt) ;
   addrV = 8 ; startFI = 0;
   for (i=0;i<SMcnt;i++) {
        log_FIs(stream, FIaddr_arr[i], minFaceInd, maxFaceInd, lastFaceInd, FIcnt_arr, i) ;
        vCnt_arr[i] = lastFaceInd - startFI ; startFI = lastFaceInd ;
        fprintf( stream, "# vAddr: %x, %d\n", addrV, vCnt_arr[i]) ;
        log_short_Verts(addrV, vCnt_arr[i], 24) ;          // vertex stride: 24
       log_short_UVs(addrV+8, vCnt_arr[i], 24) ;
        addrV += vCnt_arr[i]*24 ;
   }
   fprintf( stream, "# v end: %x\n", addrV) ;
}

This line, calculating the address of the next vertex block, is the problematic one:
addrV += vCnt_arr[i]*24 ;

There are more vertex blocks than face indices blocks, so some (nearly identical) vertex blocks have to be skipped.

edit: fixed a small bug "precalculating" vCnt
added uvs support in above code (zip not updated!)

in void log_short_UVs(DWORD addrUV, DWORD Vcnt, BYTE vStride)
you need to uncomment the following line and comment out the "big endian" one:
lPosXYZ= nValue[0] + nValue[1]*256 ;
//lPosXYZ= nValue[0]*256 + nValue[1] ; // big endian for BF3

(or introduce a parameter bool bIsBigEndian for example)


Attachments:


You do not have the required permissions to view the files attached to this post. Register to gain access.


_________________
"you can't always get things served on a silver tray"
extracting simple models: http://forum.xentax.com/viewtopic.php?f=29&t=10894
Make_H2O-ForzaHor3-jm9.zip

"We are Microsoft. You will be assimilated. Resistance is Futile."


Top
 Profile  
 
 Post subject: Re: Make_obj (C source)
PostPosted: Thu May 10, 2018 5:47 pm 
Offline
M-M-M-Monster veteran
M-M-M-Monster veteran
User avatar

Joined: Fri Apr 20, 2012 9:24 am
Posts: 2250
Location: Nexus, searching for Jim Kirk
Has thanked: 501 times
Have thanks: 1163 times
here's an update to the code after akderebur gave some hint
('%" means: modulo, i.e. 5 % 3 is 2)
Replacement for the for (i=0;i<SMcnt;i++) {...} loop in the code of the previous post:
Code:
   for (ii=0;ii<SMcnt*3;ii++) {
        i = ii / 3 ;
        if ((ii % 3)==0) {
            log_FIs(stream, FIaddr_arr[i], minFaceInd, maxFaceInd, lastFaceInd, FIcnt_arr, i) ;
            vCnt_arr[i] = lastFaceInd - startFI ; startFI = lastFaceInd ;
        }
   }
   for (i=0;i<SMcnt;i++) vCntSum += vCnt_arr[i] ;
   vStride = (BYTE) (vCnt / vCntSum / 3) ;
   for (ii=0;ii<SMcnt*3;ii++) {
        i = ii / 3 ;
        if ((ii % 3)==0) {
            fprintf( stream, "# vAddr: %x, %d\n", addrV, vCnt_arr[i]) ;
            log_short_Verts(addrV, vCnt_arr[i], vStride) ;          // vertex stride: 24 or 28 or ...
            log_short_UVs(addrV+8, vCnt_arr[i], vStride) ;
        }
        addrV += vCnt_arr[i]*vStride ;
   }

You'll have to introduce two BYTE variables, ii and vStride and a DWORD var vCntSum which has to be set to 0 when initializing.

remark: the code may appear to be more complicated than it is. It's originally one loop only but it had to be split up into two loops because of the auto calculation of vStride which requires vCntSum.
The modulo thingie just chooses the first of the 3 mesh copies to be logged.

Pay attention to the line calculating the vStride:
vStride = (BYTE) (vCnt / vCntSum / 3) ;

The /3 doesn't apply for all Geometry.bin, this one for example has only one mesh "copy" (although it looks like two, hehehe):


Attachments:


You do not have the required permissions to view the files attached to this post. Register to gain access.


_________________
"you can't always get things served on a silver tray"
extracting simple models: http://forum.xentax.com/viewtopic.php?f=29&t=10894
Make_H2O-ForzaHor3-jm9.zip

"We are Microsoft. You will be assimilated. Resistance is Futile."


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 6 posts ] 

All times are UTC + 1 hour


Who is online

Users browsing this forum: mormor512 and 2 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group