gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

DS development > stuck with 3ds model import function

#108491 - chrissieboy - Thu Nov 09, 2006 3:48 pm

Hi guys im busy with a function to load a 3ds model.
I renamed the model.3ds to hovercraft.bin and include it in my main.cpp
I read some tutorials about how the 3ds model is build.
I viewed the model with a hex viewer, everything went ok.
But when my code reach the VERTICES LIST wich starts with 0x4110 it stops reading the 3ds file(renamed to .bin).
Everything after the VERTICES LIST is also not be found in the hovercraft.bin

I really dont understand why this is happening???
Because the MAIN CHUNK, 3D EDITOR CHUNK, OBJECT BLOCK and TRIANGULAR MESH are all found??

When i printout on the ds the part of memory where VERTICES LIST is it displays the hex values:
10 41 00 0B 00 00 EA 00 D7 85 84 3F

so it really exists but the swith/case doesn't see that its there or something???

Does anyone no why ? because im really stuck now...


Code:

for(int i=0;i<2800;i++)
{


    switch (((u16*)hovercraft_bin)[i])
    {
      // MAIN CHUNK
        case 0x4d4d:
      iprintf("%04x  ", ((u16*)hovercraft_bin)[i]);
      iprintf("MAIN CHUNK Found! %d\n", i);
        break;
      
      // 3D EDITOR CHUNK
        case 0x3D3D:
      iprintf("%04x  ", ((u16*)hovercraft_bin)[i]);
      iprintf("3D EDITOR CHUNK Found! %d\n",i);
        break;

      // OBJECT BLOCK
        case 0x4000:
      iprintf("%04x  ", ((u16*)hovercraft_bin)[i]);
      iprintf("OBJECT BLOCK Found! %d\n", i);
      
        break;

      // TRIANGULAR MESH
        case 0x4100:
      iprintf("%04x  ", ((u16*)hovercraft_bin)[i]);
      iprintf("TRIANGULAR MESH Found! %d\n", i);
        break;

      // VERTICES LIST
        case 0x4110:
      iprintf("%04x  ", ((u16*)hovercraft_bin)[i]);
      iprintf("VERTICES LIST Found! %d\n", i);
        break;    

      // FACES DESCRIPTION
        case 0x4120:
      iprintf("%04x  ", ((u16*)hovercraft_bin)[i]);
      iprintf("FACES DESCRIPTION Found! %d\n",i);
        break;    

      // MAPPING COORDINATES LIST
        case 0x4140:
      iprintf("%04x  ", ((u16*)hovercraft_bin)[i]);
      iprintf("MAPPING COORDINATES LIST Found! %d\n",i );
        break;
   }
   
}
[/b]

#108520 - thegamefreak0134 - Thu Nov 09, 2006 9:12 pm

The only thing I can figure is that you are limiting your initial for loop to 2800. Perhaps set it using the sizeof() function to make sure it scans the entire array? I see no obvious problems with the code, and it looks like you copied and pasted and changed the values, so the code after the problem point should be fine. The behavior you describe sounds very much like the loop is stopping before it ever gets to the verticies list.
_________________
What if the hokey-pokey really is what it's all about?

[url=http:/www.darknovagames.com/index.php?action=recruit&clanid=1]Support Zeta on DarkNova![/url]

#108530 - chrissieboy - Thu Nov 09, 2006 10:45 pm

yeah that was also the first thing i was thinking, but if i make it 1000000 it still doesn't works.

But i figured out that its 16 bytes after the TRIANGULAIR MESH, so it MUST work but i dont know why it doesn't works???

So still stuck, anyway thanx for your time and help!

#108548 - Cearn - Fri Nov 10, 2006 12:28 am

Looking at the 3Ds file specs (www.wotsit.org, FTW) it doesn't seem like the file doesn't care much about data alignment. For example, a chunk header is a short followed directly by a long, even though that means that the long isn't 4byte aligned like an ARM processor requires. It's possible somewhere there's an odd-sized chunk, which might throw all the later chunks off their proper alignment. Since you've managed to find the vertex chunk yourself, check whether it's on an even byte boundary.

#108581 - toa - Fri Nov 10, 2006 6:48 am

http://sourceforge.net/projects/lib3ds/

Very nice C-interface 3ds decoding etc. library.

#108605 - chrissieboy - Fri Nov 10, 2006 11:52 am

Cearn!! thanx i had take a look at it, when i rename my .3ds file in 3dstudio max.
So the model was Object02 and i renamed it in 3dmax to bject02 then it follows a good 4 byte segment, and i see the rest of the chunks found!!
Because i use ((u16*)hovercraft_bin)[i] it expects 4 byte segment i think?

So if i make it ((u8*)hovercraft_bin)[i] it expects 2 byte segment i think?

If i use this code :

Code:

if(((u8*)hovercraft_bin)[i] == 0x40 && ((u8*)hovercraft_bin)[i-1] == 0x10)

It will find the chunk 0x4010, but then there comes another problem, then i must read the float vectors x,y,z wich are each 4 bytes :

EA 00 D7 85 84 3F 66 1F EF 3E FE C5


So i thought to make something like this :
Code:

vector.x[0] = ((u8*)hovercraft_bin)[i+5] + ((u8*)hovercraft_bin)[i+6] + ((u8*)hovercraft_bin)[i+7] + ((u8*)hovercraft_bin)[i+8]

vector.y[0] = ((u8*)hovercraft_bin)[i+9] + ((u8*)hovercraft_bin)[i+10] + ((u8*)hovercraft_bin)[i+11] + ((u8*)hovercraft_bin)[i+12]

vector.z[0] = ((u8*)hovercraft_bin)[i+13] + ((u8*)hovercraft_bin)[i+14] + ((u8*)hovercraft_bin)[i+15] + ((u8*)hovercraft_bin)[i+16]


But i can't figure out whats the code to merge 4 time ((u8*)hovercraft_bin)[i] together? because above doesn't work :(

I thought maybe merge it like this together?? :
Code:

vector.z[0] = ((u8*)hovercraft_bin)[i+13] & ((u8*)hovercraft_bin)[i+14] & ((u8*)hovercraft_bin)[i+15] & ((u8*)hovercraft_bin)[i+16]


Also no good results, sorry for my bad programming but im very new to c++, so high level coding with pointers im still learning, but i think i need a pointer to search trough the hex data.
So when i read 4bytes i move the pointer 4bytes, and when i read 2bytes i move the pointer 2bytes, and then search further for the chunks?

Also found this tutorial about loading 3ds model :
http://www.spacesimulator.net/tut4_3dsloader.html

But this is loading an 3ds model from a fat system, and my efa cardridge is not able to use chrishm his fat filesystem :(

Hope you guys can help me ;)
Anyway thanx for all your help and time!!!

#108615 - Cearn - Fri Nov 10, 2006 2:53 pm

chrissieboy wrote:
Cearn!! thanx i had take a look at it, when i rename my .3ds file in 3dstudio max.
So the model was Object02 and i renamed it in 3dmax to bject02 then it follows a good 4 byte segment, and i see the rest of the chunks found!!
Because i use ((u16*)hovercraft_bin)[i] it expects 4 byte segment i think?

So if i make it ((u8*)hovercraft_bin)[i] it expects 2 byte segment i think?

? These sentences make little sense. Please clarify.

Anyway, as I said before:
Code:
 C type  | ARM name | typedef | size
char     | byte     | s8/u8   | 1 byte
short    | halfword | s16/u16 | 2 bytes
int/long | word     | s32/u32 | 4 bytes


So if you interpret the file as a halfword array you'll always work with 2-byte boundaries. The problem is that .3ds files aren't halfword arrays or use any kind of alignment requirements so that reading them as anything else than byte for byte could be problematic.

chrissieboy wrote:
If i use this code :

Code:

if(((u8*)hovercraft_bin)[i] == 0x40 && ((u8*)hovercraft_bin)[i-1] == 0x10)

It will find the chunk 0x4010, but then there comes another problem, then i must read the float vectors x,y,z wich are each 4 bytes :

EA 00 D7 85 84 3F 66 1F EF 3E FE C5

So i thought to make something like this :
Code:

vector.x[0] = ((u8*)hovercraft_bin)[i+5] + ((u8*)hovercraft_bin)[i+6] + ((u8*)hovercraft_bin)[i+7] + ((u8*)hovercraft_bin)[i+8]

Reading individual bytes and piecing them together is part of what you should be doing, yeah, but there are several points to be aware of here.
First, simply adding the individual bytes does just that: add the bytes. This is what you're doing here: 0xEA + 0x00 + 0xD7 + 0x85. Naturally, this isn't how you string together bytes to a word. Borrowing from this thread:

tepples wrote:
unsigned int peeki32(const unsigned char *src) {
return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
}

unsigned int peeki16(const unsigned char *src) {
return src[0] | (src[1] << 8);
}

Reading floats is eve more problematic because the 4-byte chunk you'll have assembled will be an integer and assigning it to a float will convert the integer to a float, not reinterpret the bits. For example 0x85D700EA = -2049507094, which I'm sure you don't have in your file somewhere. Floats have a different bit format so simply casting won't work. What might work is this (note: untested)
Code:
float peekfloat(const unsigned char *src) {
  u32 tmp[1];
  *tmp= src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24);
  return *(float*)tmp;
}

There are better ways, especially as the floats will have to be converted back to fixed points later on, but this'll have to do for now.

As you're still getting to grips with C++ and pointers, I must again strongly urge you to work it out on a PC first. The ability to set breakpoints and examining memory and variables in an IDE will help you a lot. Forget about reading 3d files for a moment and learn proper pointer/memory use first. Create a small, well-defined set of test-data and read that with differently typed pointers and at different byte offsets and see what happens. This will allow you to be prepared for problems you might encounter in Real Life.

Also, use pointer variables instead of casting hovercraft_bin directly all the time. It'll make your code more readable and maintainable. A 3ds file is composed of chunks, so parsing them as such (rather than just one big array) is more proper. The chunks also indicate how big each of them is, so you don't have to search the whole file for them. For example:

Code:
// Pseudo code for 3ds parsing
void parse_3ds(const u8 *src)
{
    const u8 *chunk= src;
    while( not EOF )
    {
        // Get chunk type
        u16 chunk_type = peeki16(chunk);
        switch(chunk_type)
        {
        case CHUNK_TYPE0:
            chunk += load_chunk_type0(chunk);
            break;
        case CHUNK_TYPE1:
            chunk += load_chunk_type1(chunk);
            break;

        ...  other cases

        default:
            chunk += peeki32(chunk+2);
        }
    }
}

u32 load_chunk_type0(const u8* chunk)
{
    chunk_len= peeki32(chunk+2);
    chunk += 6;  // skip chunk header

    // code to load chunk

    return chunk_len;  // return chunk length so the caller knows how much to skip
}


It should be something like that, I'm pretty sure there is a more optimal way of reading them, but this would be a start.

#108617 - chrissieboy - Fri Nov 10, 2006 3:30 pm

thank you very very very much Cearn!

It start to make sense to me now, but i think i first need to learn more about pointers, because when i read it, its very difficult for me to understand.

I think its a good idea to learn with the debugger because then i really understand how and what c is doing in the memory, i used it already a couple of times.

But when reading the spacesim tutorial i thought this is a good tutorial to start with and trying to use it on the ds.

But now i know its not that easy ;)

I'm going to expiriment and read a lot this weekend, so hope to come back next week with a finished 3ds model loader ;)

Anyway thank you all very very much for your time, and helping me to get further.

When im stuck next week im going to post it here again!!

-Chris