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 > Painfully Loading MD2 Models

#86250 - dak - Mon Jun 05, 2006 10:00 pm

bah, I hate asking for help but I'm kind of stumped here. I'm aware that there are MD2 loaders availible to use, but I'm the kind of jackass that needs to create things to get a grip on whats going on. With the assistance of some OGL literature, I created what seemed to be a pretty straight forward loader while making appropriate changes to fit the handheld. I used the console to output the general numTri info etc. to make sure things aren't going nuts (everything seems to be in order), but the only thing weird is that the model when displayed on screen looks like a jagged cube.

This is my first time working with Nintendo handheld tech as well as with loading a model (tho I do have a good amount of OGL experience). First thing I tried was playing around with casting things between floats, f32, and v16 (particularly when setting the point lists). Then I made my draw function render only 5 polygons. It seems that the polys rendered are just staying in one place, bunching up like pantyhose. Now I love nothing more than bunched up pantyhose, but this is driving me insane.

Well, heres the code; I'll be refreshing like a madman all day to see if anyone can help me out. btw, perhaps this has something to do with it: I am awaiting my flash/pass equiptment to come in and am debugging on the latest version of Dualis.

:D thanks in advanced to those who help and at the least stop by...

Code:

// Map Game Cartridge memory to ARM9
   WAIT_CR &= ~0x80;
   
   GBFS_FILE const * filePtr; //FILE *filePtr;
   unsigned int fileLen = 0;
   uint8* buffer;
   
   modelData_t *model;
   modelHeader_t *modHeader;
   texture_t *md2Tex;
   
   stIdx_t *stPtr;
   frame_t *frm;
   vector_t *pointListPtr;
   mesh_t *triIndex, *bufIndexPtr;
   int i, j;
   
   //Open the model file
   filePtr = find_first_gbfs_file((void*)0x08000000);
   
    buffer = (uint8*)gbfs_get_obj(filePtr, filename, &fileLen);
   
   //Extract model file header
   modHeader = (modelHeader_t*)buffer;
   
   //Allocate memory for model data
   model = (modelData_t*)malloc(sizeof(modelData_t));
   
   //Allocate memory for all vertices used in model, inc animations
   model->pointList = (vector_t*)malloc(sizeof(vector_t)*modHeader->numXYZ*modHeader->numFrames);
   
   //Store "vital" model data
   model->numPoints = modHeader->numXYZ;
   model->numFrames = modHeader->numFrames;
   model->frameSize = modHeader->frameSize;
   
   //loop thru number of frames in model file
   for (j=0; j<modHeader->numFrames; j++) {
      //offset to the points in this frame
      frm = (frame_t*)&buffer[modHeader->offsetFrames + modHeader->frameSize * j];
      
      //calculate the point positions based on frame details
      pointListPtr = (vector_t*)&model->pointList[modHeader->numXYZ * j];
      for (i=0; i<modHeader->numXYZ; i++) {
      pointListPtr[i].point[0] = floattov16(frm->scale[0] * frm->fp[i].v[0] + frm->translate[0]);
      pointListPtr[i].point[1] = floattov16(frm->scale[1] * frm->fp[i].v[1] + frm->translate[1]);
      pointListPtr[i].point[2] = floattov16(frm->scale[2] * frm->fp[i].v[2] + frm->translate[2]);
      
      //   pointListPtr[i].point[0] = floattov16((mulf32(floattof32(frm->scale[0]), inttof32(frm->fp[i].v[0])) + floattof32(frm->translate[0]))>>3);
      //   pointListPtr[i].point[1] = floattov16((mulf32(inttof32(-1),(mulf32(floattof32(frm->scale[1]), inttof32(frm->fp[i].v[1])) + floattof32(frm->translate[1]))))>>3);
      //   pointListPtr[i].point[2] = floattov16((mulf32(floattof32(frm->scale[2]), inttof32(frm->fp[i].v[2])) + floattof32(frm->translate[2]))>>3);
      }
   }
   
   //load the model texture
   //md2Tex =
   // ---------------------
   
   //Allocate the list of tri indices
   triIndex = (mesh_t*)malloc(sizeof(mesh_t) * modHeader->numTris);
   
   //Set total number of tri
   model->numTri = modHeader->numTris;
   model->triIdx = triIndex;
   
   //point to tri indices in buffer
   bufIndexPtr = (mesh_t*)&buffer[modHeader->offsetTris];
   
   //for all tri in each frame
   for (i=0; i<modHeader->numTris; i++) {   // [opt] memcopy
      //Store mesh and texture indices
      triIndex[i].meshIdx[0] = bufIndexPtr[i].meshIdx[0];
      triIndex[i].meshIdx[1] = bufIndexPtr[i].meshIdx[1];
      triIndex[i].meshIdx[2] = bufIndexPtr[i].meshIdx[2];
      //Then TEX shit
      //Then TEX shit
      //Then TEX shit
   }
   
   //initialize animation variables
   model->curFrame = 0;
   model->nextFrame = 1;
   model->interpol = 0.0;
   
   return model;



-dak[/i]

#86326 - dak - Tue Jun 06, 2006 11:06 am

So I rewrote all of it ... different approach (probably better now XD), same problem. Boxxy looking 3D thinggy : /

I took my MD2 code and slapped it onto an old OGL project I had on my desktop and attempted to load the exact same file. Success!! Apparently the code itself is totally fine, I seem to be missing some detail on either the NDS system or my implementation simply will not work on Dualis.

If anything, perhaps it has something to do with the whacky v16 type. Can anyone tell me if there are any tricks to be "aware" of when handling that type, and also, is casting something like the following even realistic with this type?

Code:

v[0] = floattov16((pframe->scale[0] * pvert->v[0]) + pframe->translate[0]);
v[1] = floattov16((pframe->scale[1] * pvert->v[1]) + pframe->translate[1]);
v[2] = floattov16((pframe->scale[2] * pvert->v[2]) + pframe->translate[2]);


v -> v16[3]
scale and translate -> float[3]
pvert->v -> unsigned char[3]

thanks,
dak

#86328 - dak - Tue Jun 06, 2006 11:35 am

I figured it out...
:D I have been talking to myself, but its been therapeutic!!

-dak

#86329 - acox - Tue Jun 06, 2006 11:44 am

dak wrote:
I figured it out...
:D I have been talking to myself, but its been therapeutic!!

-dak


And the answer was?
(for the benefit of future searchers)
_________________
3D on GBA

#86341 - dak - Tue Jun 06, 2006 2:49 pm

I'm new to this whole DS dev mumbo jumbo, so forgive my n00b sauce in advance...

I was wracking my brain over what was causing my model to be so funky looking. I tried rewriting the code and even got it working on another platform which meant something sneaky was happening on the DS. The problem seemed to have been an issue with getting the MD2's floats and turning them into v16 used by the libnds. Normally, when decompressing a vertex you'd simply do

Code:

v[0] = pframe->scale[0] * pvert->v[0] + pframe->translate[0];
v[1] = pframe->scale[1] * pvert->v[1] + pframe->translate[1];
v[2] = pframe->scale[2] * pvert->v[2] + pframe->translate[2];


Pretty standard stuff, I suppose, but this yeilds a float which works quite nicely with the PC's glVertex3fv( ... ). Unfortunately (fortunately?) we have glVertex3v16 to work with, so we need to cast things. I also decided to go with mulf32( ... , ... ) as I figured it may be more efficient (whatev). It still did not quite work, so I dug deeper into the forums and stumbled onto Webez's code. There was the answer; After casting each number into f32 and then decompressing, just before converting to a v16 type, you have to shift 3 ( >> 3 ).

Code:

v[0] = (mulf32(floattof32(pframe->scale[0]), inttof32(pvert->v[0])) + floattof32(pframe->translate[0]))>>3;
v[1] = (mulf32(floattof32(pframe->scale[1]), inttof32(pvert->v[1])) + floattof32(pframe->translate[1]))>>3;
v[2] = (mulf32(floattof32(pframe->scale[2]), inttof32(pvert->v[2])) + floattof32(pframe->translate[2]))>>3;


I can guess why, but I'm sure someone else will wander in and give a much more technically thorough explaination. I'm much more of a story teller.

Anyway, thanks goes out to Webez for tackling an MD2 loader before me; I'll be in your shadow for a while :)

-dak

#86354 - mike260 - Tue Jun 06, 2006 4:24 pm

One way to work around the 16bitness of DS vertices is:

1) Get the bounding-box of your model
2) Scale all the model's verts so this bounding-box just about fits the available precision
3) When rendering your model, use glScale to reverse this scale and get your verts back into the correct units.

For extra points, you can also translate the verts to make even better use of the available precision.

For extra-combo-multiplier-bonus points, use displaylists and bake the glScale/glTranslate into the displaylist itself; that way it happens completely automatically and your rendering code doesn't even need to know about it.
_________________
"Ever tried? Ever failed? No matter. Try Again. Fail again. Fail better."
-- Samuel Beckett

#86682 - Payk - Thu Jun 08, 2006 5:49 pm

Yes i also so did that like it.

Models[num].preframes[N].pretriangles[I].vertex[j].v[0] =floattov16(v[0]/128);
Models[num].preframes[N].pretriangles[I].vertex[j].v[1] =floattov16(v[1]/128);
Models[num].preframes[N].pretriangles[I].vertex[j].v[2] =floattov16(v[2]/128);

But i changed much in the modelstructures. Now all triangles are stored each frame instead of using indices. Thats about 3,5x bigger (depending how much frames u got) But renderscene is much faster.

So on that way renderscene just goes like:
void RenderMD2ModelMirrowed (int n,int num){
int i, j;

if ((n < 0) || (n > Models[num].header.num_frames - 1))
return;
glBegin (GL_TRIANGLES);
//glColor3f(EnvR,EnvG,EnvB);
for (i = 0; i < Models[num].header.num_tris; ++i)
{

for (j = 0; j < 3; ++j)
{

glNormal3f(anorms_table[Models[num].preframes[n].pretriangles[i].vertex[j].normalIndex][0],
anorms_table[Models[num].preframes[n].pretriangles[i].vertex[j].normalIndex][1],
anorms_table[Models[num].preframes[n].pretriangles[i].vertex[j].normalIndex][2]);

glTexCoord2f32 (Models[num].pretexcoords[Models[num].textindex[i].st[j]].t, Models[num].pretexcoords[Models[num].textindex[i].st[j]].s);
glVertex3v16 (Models[num].preframes[n].pretriangles[i].vertex[j].v[0],
Models[num].preframes[n].pretriangles[i].vertex[j].v[1],
-Models[num].preframes[n].pretriangles[i].vertex[j].v[2]);

}
}
glEnd ();
}
So just give normalvec. and vertices for each triangle. No scale no translate no searching right triangles for right frame...just do it...

#86822 - dak - Fri Jun 09, 2006 4:39 pm

hmm, I also precalculate my point list, but I did it in a way where the point data itself is not a large as you have stated. What I did was keep all of the uncompressed data on the buffer (which still points to the filesystem) and used it to fill a vector_t * pointList on memory.

The vectors are defined as v16 v[3] -- it used to be f32, but since the smaller size works, I'll be using it :)

If the calculations were done on the fly then the as far as space used (assuming we have a model with 200 triangles), my count is:

..mem compressed..
200tri * 3vert * 3vec * (sizeof(u8)*4) = 7200bytes
(plus an additional 40bytes in the frame_t structure)

..mem decompressed..
200tri * 3vert * 3vec * (sizeof(v16)*3) = 10800bytes

I'd say thats a pretty reasonable size for a boost of speed. The only major things I'm storing are (precalculated) pointList, Vert/ST indices, and stList.

#86852 - Payk - Fri Jun 09, 2006 10:53 pm

v16 is definitly enough.
it offers 65536 possibility vertexes per axis (not vertextes but their position)
Since ds has 256x192 i see no reason to use more bits for that (just if u want to zoom in or use a md2 as map which is very stupid because it would c?lculate triangles for not seeable things)
For my opinion even 9bit would be fine. A fixed point value (0.9) u just have to convert the vertex positions to that would work good i think.
But not sure. But hey that huge they arent in ram. i use trees with 15 triangles and chars with about 50 triangles and 20 frames. they are small in ram. Even my method eats more ram its still under 50kb. (i think the char has 36kb but not sure). i displayed size out some time ago it was 3.5x bigger then as file. but it was faster i did a little benchmark.
Tomorrow i gonna try to rape the t10 for vertex pos then i will know if 16bit is neccesary. but i dont think.

#86895 - crossraleigh - Sat Jun 10, 2006 8:55 am

You don't have to worry about the DS using 16-bit coordinates when the model format uses 8-bit coordinates! Use a matrix to scale and translate; it will be quicker and you won't have overflow errors.

Furthermore, I don't think it's necessary to create an intermediate buffer each frame; you're better off using the GL commands. The DS doesn't support fans, but it's easy to convert them to strips.

#87516 - Payk - Wed Jun 14, 2006 7:04 am

perhaps its not neccecary for you, but make a benchmark. i did. it doesnt makes that huge difference but when u are going to make own lights u need all optimazions. Also u will have then each exact pos. of vertex in each frame. Very usefull for own lights. I have thought much how to simulate dot lights. This was one of first step to do so. For me its neccecary.

#87545 - Webez - Wed Jun 14, 2006 12:34 pm

The easiest way to deal with vertex as you have been said is to scale them and then rescale. I always divide them by 100 and then rescale by the same factor with glScale. As for display lists i wouldn't try to use them with a md2 unless you want a file as big as your whole RAM.

#87905 - Payk - Fri Jun 16, 2006 12:17 pm

Ok my way of md2-structure was good idea...like i said it allows own lights...hmhm watch that:
[Images not permitted - Click here to view it]
There are much more then 4 lights possible. They arent dirrectional.
That are dotlights, so they have a position and a radius. They just iluminate whats in radius of course. I plan to make some dirrectional lights,too. Same thing: more then 4 of course.
Interessted in HowThatStuffWorks?
Send me a pm i will explain...a source cant help since it is made for my engine and uses lookuptables and values which fit to it...
But i can help u to have them too.