3DS Max 2009 Exporter - Morph Targets

3d4dd29a9990ebb547e4f881d9c5380e
0
swifty666 101 Mar 13, 2009 at 14:12

Hi All, This is my first post so i just thought i’d introduce myself :)

My name is Leigh and I just started working for MySapient based in Newcastle, currently a junior tools programmer :D.

I’m modifying an old max exporter plugin to work with max 2009 and all has been going well, but now I am having some troubles with exporting data from a Morpher Modifier on the model.

I manage to identify that the model has a morpher modifier and get a hold of it, i then cast it into a MorphR3*, but if i try and access the chanBank vector, it’s empty, i thought it was supposed to get initialised with 100 channels, even if you hadn’t actually applied any channels on the model yet and they would just be inactive channels. I can’t find any functions for setting up the chanBank vector?

There looks to be a lovely MorphControl Class in the SDK reference, which would be perfect for exporting as it appears you can get morph targets using it from simple functions, but i can’t find how to get a hold of the class from my INode?

Sorry for the length of this post! It’s doing my nutt! lol

Here is some code that i have at the moment:-

int _PSXExport::WriteMorphModifierTargets(void *data, FILE *stream)
{
    ObjectEntry *oe = (ObjectEntry *)data;

    /* Grab the first morph-modifier only */
    MorphR3 *mpMorphMod;
    if((mpMorphMod=_GetMorphMod(oe->entry->node)))
    {
        /* We fond one! */

        /* MorphR3 (chanBank is a 100-length array of morphChannel classes.) */
        int nI;

        /* First, count the active channels */
        unsigned long nMChannels= 0;
        for(nI=0;nI<100;nI++)
        {
            /* Only write out active channels */
            if(mpMorphMod->chanBank[nI].mActive)
            { //This is what dies as chanBank has no elements!
                nMChannels++;
            }
        }

        WRTERR(&nMChannels,4);

        /* Now, write out the morph-target for each active channel */
        int nNumPoints = -1;    // Used to check to see if number of points the same.
        for(nI=0;nI<100;nI++)
        {
            /* Only write out active channels */
            if(mpMorphMod->chanBank[nI].mActive)
            {
                TCHAR szBuf[1024];


                PsxOeWithMc owmc(oe, &(mpMorphMod->chanBank[nI]), nI);

                if(nNumPoints==-1)
                {

                    if(oe->tpTri)
                    {
                        if(owmc.m_mcpMorphChannel->mNumPoints != oe->tpTri->mesh.getNumVerts())
                        {
                            _stprintf(szBuf, "The Node's mesh \"%s\" Has %d points, but the first morph target #%d has %d points.",
                                oe->entry->name, oe->tpTri->mesh.getNumVerts(), nI, owmc.m_mcpMorphChannel->mNumPoints);
                            LOGALERT(_T("Error!"),szBuf);
                            if(!G_bSuppressPrompts)    {MessageBox(GetActiveWindow(), szBuf, _T("Error!"), MB_OK | MB_ICONSTOP);}                        
                            return 0;
                        }
                    }

                    // TODO: Patches and splines(?)
                }
                else
                {
                    if(owmc.m_mcpMorphChannel->mNumPoints != nNumPoints)
                    {
                        _stprintf(szBuf, "The Node \"%s\" Has at least one morph-target (Morph-Channel #%d) with a different number of vertices (%d where the others have %d).\nThis one could have the wrong number of vertices, or the others could have the wrong number of vertices.",
                            oe->entry->name, nI, owmc.m_mcpMorphChannel->mNumPoints, nNumPoints);
                        LOGALERT(_T("Error!"),szBuf);
                        if(!G_bSuppressPrompts)    {MessageBox(GetActiveWindow(), szBuf, _T("Error!"), MB_OK | MB_ICONSTOP);}                        
                        return 0;
                    }

                }


                nNumPoints = owmc.m_mcpMorphChannel->mNumPoints;


                /* Write out the morph-target for this morph-channel */
                if(WriteChunk(MORPHTARGET, stream, &owmc, NULL /* data2 */) == 0)
                    return 0;
            }
        }
    }


    return(1);
}

And the _GetMorphMod function is :-

static MorphR3 * _GetMorphMod(INode *npNode)
{
    Object *pObj = npNode->GetObjectRef();
    IDerivedObject *pDerObj = NULL;
    Modifier *pMod = NULL;

    if( pObj->SuperClassID() == GEN_DERIVOB_CLASS_ID) 
    {
        pDerObj = (IDerivedObject *) pObj;

        /* Find the first morph-modifier in the object's modifier-stack */
        int nModIndex;
        int nNumModifiers = pDerObj->NumModifiers();

        for(nModIndex = 0 ; nModIndex < nNumModifiers ; nModIndex++ )
        {
            pMod = pDerObj->GetModifier(nModIndex);

            if(pMod->ClassID() == MR3_CLASS_ID)
            {
                /* We've found one! */
                MorphR3* pMorphR3 = (MorphR3*)pMod;
                return pMorphR3;    /* Just one modifier */

            }
        }
    }

    return NULL;
}

9 Replies

Please log in or register to post a reply.

3d4dd29a9990ebb547e4f881d9c5380e
0
swifty666 101 Mar 16, 2009 at 10:28

After much messing around, I have actually found the answer to this, and the answer is quite shocking!

The Morpher.dle that is shipped with the 3DS Max 2009 installation is broken! Recompiling the Morpher.dle yourself from the SDK samples and replacing it has fixed my problem. chanBank is now populated with 100 entries as it should be. I didn’t believe this was true at first, so I replaced my Morpher.dle with one from a colleague’s PC, and re-loaded max, only to find that yes in fact it broke my exporter. Replacing my Morpher.dle again with the self-compiled version fixed it!

I have made no functional change whatsoever to the morpher sample code, other than add a dummy test function to check it was running ok, which simply took in an integer value, and returned it back.

Weird!
Leigh

3d4dd29a9990ebb547e4f881d9c5380e
0
swifty666 101 Mar 16, 2009 at 16:26

Woop woop! It’s all fixed! Cue lengthy explanation:-

I am not quite sure exactly why this problem occurs, but we know why it breaks code and how to get around it so that you can export morph data from your exporter plugin.

The culprit is the morphChannel class. Again, I don’t know why this is happening, because i am compiling the morpher dle myself, and referring to the wm3.h from my exporter in order to use MorphR3 classes and morphChannel classes, so it’s not like i have two copies of the code that differ. Anyway, essentially, from within morpher.dle, the apparent size of a morpherChannel is 140 bytes. But from within my exporter dle, the apparent size is 172 bytes. This makes it obvious as to why data is appearing corrupted, when looking at the data from my exporter, we are looking through mis-aligned memory glasses you might say!

As i said, i can’t think why the dles would think of morphChannel differently as there’s only one copy of the source for the class, which is creating the lib file, which is used as input to the linker in my exporter. But simply extending the morpher.dle source to have getter functions for any data you want to export, will be a good enough work around. Also declaring the chanBank variable at the end of the MorphR3 class will help to ensure that’s the only variable that is currupt in the eyes of the exporter, assuming data order in the memory isnt changed by any optimisation.

I hope that this post helps out some people out there, it’s been doing our nutt’s in and morph targets are essential for us! And i havn’t found any other forum threads which actually tell of a solution! I’ll be sure to post this wherever this problem is documented.

If anyone has any insight, to help further understand the cause of this class size difference, it would be appreciated :o).

Thanks,
Leigh

A8433b04cb41dd57113740b779f61acb
0
Reedbeta 167 Mar 16, 2009 at 17:46

Hmm, I wonder if you are compiling the header with some different settings than the version shipped with Max. Maybe there are some #define flags affecting alignment/packing you need to set before you include the max headers?

3d4dd29a9990ebb547e4f881d9c5380e
0
swifty666 101 Mar 16, 2009 at 18:07

We did think that and had a look through configuration setting to see if there was anything involving alignment etc, but have now narrowed down the problem to the fact that using a std::vector in a class is where the corruption starts, so any class in the morpher dle that has a std:vector in it, will need to have these variables declared at the end, and functions written for access to any information inside them.

even std:vector<double> gets broken when accessed directly from a pointer to the vector which is not from within morpher.dle (even though a message window displaying the memory value the pointer points to from my exporter dle, and the memory value the vector is stored at from within the morpher dle have the exact same result) ?!

Leigh

A8433b04cb41dd57113740b779f61acb
0
Reedbeta 167 Mar 16, 2009 at 20:18

Hmmm…maybe the Max people use a different STL implementation (that happens to have a different internal layout for std::vector) than you do. When you say std::vector<double> gets broken even though the pointers are the same, do you mean the pointers to the std::vector object, or the pointers to the array it holds (i.e. &vector[0])? Because if the latter is what’s getting broken, that is REALLY weird.

3d4dd29a9990ebb547e4f881d9c5380e
0
swifty666 101 Mar 17, 2009 at 09:35

If i write a function inside of the morpher.dle to return a pointer to the data i want inside the vector i can use this fine within my exporter dle, but i can’t index or get any data from a pointer to a vector object from within the exporter, or i get assertion errors in debug or crashes in release. if i get a pointer to the vector object, i literally can’t use it for anything lol, even running functions like size() return erroneous values, or get()/[] breaks. The pointer to the vector object from within the exporter has the same address value as a pointer from within the morpher, it’s just unuseable! Not to worry though, i don’t really have the time to go any deeper just need to get it working and adding getter functions is working nicely. It would be nice if autodesk let us know that their STD templates were different to that shipped with visual studio but there you go! :)

Thanks for your help!

3d4dd29a9990ebb547e4f881d9c5380e
0
swifty666 101 Mar 25, 2009 at 09:51

http://www.programmersheaven.com/mb/CandCPP/310449/310449/needs-to-have-dll-interface-warning/?S=B20000

Explaination of why STL isnt working properly.

The STL classes cannot be accessed from dlls when not initialised by the same dll.

Essentially.. it’s impossible to export morph targets from 3ds max without editing the morph target plugin that max comes with!

Good to know..

A8433b04cb41dd57113740b779f61acb
0
Reedbeta 167 Mar 25, 2009 at 17:20

I don’t think the link you posted actually describes the problem. In that case, the guy was getting a harmless-but-annoying compiler warning, but his DLL and client app were still using the same STL implementation.

Based on what you described earlier I think it’s pretty conclusive that MAX was compiled using a different STL implementation than MSVC, which happen not to have binary-compatible internal layouts for the std::vector object, leading to things breaking whenever your app tries to access its members. That is a good thing for people working with it to know, though.

737f596d6c8bf61e9903381836841153
0
berkbig 101 Aug 27, 2012 at 09:09

Hi all, so I’ve run across this exact problem over the last week and came up with a cleaner solution to the problem which I thought I would share.

The problem is that the size of the std::vector (and presumably any other standard template library structures) differs between the two DLL’s - specifically:

The way the projects are set up plugins for max get a std::vector of 16 bytes - 3 pointers and 4 bytes from the base class
Using default compiler settings for visual studio (v2008 here - YMMV) std::vectors are 24 bytes

So the question is, where do those extra bytes come from?

The answer turns out to be that there are 2 defines which can affect vector size. _HAS_ITERATOR_DEBUGGING=1 and _SECURE_SCL=1 . In this case, the projects that come with the 3dsmax SDK all have _SECURE_SCL=0 defined in the preprocessor directives, so if you want to communicate with them you need to define that too. Form that point onwards your structure sizes should match and you are golden.

Hope that helps

Cheers

Sean