XeNTaX Forum Index
Forum MultiEx Commander Tools Tools Home
It is currently Wed Aug 23, 2017 6:50 pm

All times are UTC + 1 hour




Post new topic Reply to topic  [ 2 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: 2096
Has thanked: 405 times
Have thanks: 1058 times














You can make the ads go away by registering

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.


_________________
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: 2096
Has thanked: 405 times
Have thanks: 1058 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.


_________________
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  [ 2 posts ] 

All times are UTC + 1 hour


Who is online

Users browsing this forum: wanglata and 1 guest


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