[Closed] [SDK]: Exporter class method for NoteTrack Collection…
never worked with NoteTracks in max sdk, but there is a sample code in SDK:
from …\maxsdk\samples\import_export\vrmlexp\vrmlexp.cpp
void
VRBLExport::StartNode(INode* node, Object* obj, int level, BOOL outputName)
{
if (node->IsRootNode()) {
mStream.Printf(_T("Separator {
"));
return;
}
Indent(level);
if (obj->SuperClassID() == CAMERA_CLASS_ID ||
obj->SuperClassID() == LIGHT_CLASS_ID || !outputName) {
// Lights and cameras need different top-level names for triggers
mStream.Printf(_T("DEF %s_TopLevel Separator {
"), mNodes.GetNodeName(node));
} else {
mStream.Printf(_T("DEF %s Separator {
"), mNodes.GetNodeName(node));
if (IsMirror(node)) {
Indent(level+1);
mStream.Printf(_T("ShapeHints {
"));
Indent(level+2);
mStream.Printf(_T("vertexOrdering CLOCKWISE
"));
Indent(level+1);
mStream.Printf(_T("}
"));
}
}
if (!obj)
return;
// Put note tracks as info nodes
int numNotes = node->NumNoteTracks();
for(int i=0; i < numNotes; i++) {
DefNoteTrack *nt = (DefNoteTrack*) node->GetNoteTrack(i);
for (int j = 0; j < nt->keys.Count(); j++) {
NoteKey* nk = nt->keys[j];
TSTR note = nk->note;
if (note.Length() > 0) {
Indent(level+1);
mStream.Printf(_T("Info { string \"frame %d: %s\" }
"),
nk->time/GetTicksPerFrame(), note.data());
}
}
}
}
also look at …\maxsdk\samples\import_export\vrmlexp\vrml2.cpp
can be helpful too.
In the body of the “DoExport” method m_noteCatalog is never initialized to anything… just passed into the CollectNotes method as:
so you are indexing into an uninitialized vector
with
if ( l_index == 0 )
{
catalog[k] = m_noNotes;
}
else
{
catalog[k].x = startNum;
catalog[k].y = l_index;
startNum += l_index; // increment starting note number for next frame
}
then ?
:argh: well I will fix that in a jiffy… I overlooked doing that inside the CollectNotes method.
OK, so that fixed it. How embarrassing… @Klunk-1 and @denisT – Thank you both.
So here is the final code for my exporter’s CollectNotes method:
void ROFFexport::CollectNotes(INode* node, std::vector<IPoint2>& catalog, NoteKeyTab& noteList)
{
int l_start = (m_pExportOptions->nSegFrom) * GetTicksPerFrame();
int l_end = (m_pExportOptions->nSegTo) * GetTicksPerFrame();
int k;
TimeValue t; // Value in ticks
int numFrames = (l_end - l_start) + 1;
int numNoteTracks = node->NumNoteTracks();
int startNum = 0;
catalog.resize(numFrames);
for(k = 0, t = l_start; t <= l_end; t += GetTicksPerFrame(), k++)
{
int l_index = 0;
for(int i = 0; i < numNoteTracks; i++)
{
DefNoteTrack* nt = (DefNoteTrack*)node->GetNoteTrack(i);
//Cycle thru note keys to see if a key exists at TimeValue t
int numKeys = nt->NumKeys();
for (int j = 0; j < numKeys; j++)
{
NoteKey* nk = nt->keys[j];
TimeValue keyTime = ( nk->time );
if (keyTime == t)
{
// We have a key at TimeValue t... so append to noteList
noteList.Append(1,&nk);
l_index++; // increment local index (total notes on frame k)
}
}
}
if ( l_index == 0 )
{
catalog[k] = m_noNotes;
}
else
{
catalog[k] = IPoint2(startNum, l_index);
startNum += l_index; // increment starting note number for next frame
}
}
}
I just wanted to post an update here in case anyone ever attempted to recycle this code snippet… wrapping up my exporter (just need to implement that progress bar class)… I kept having a 3ds Max Application Error crash following a File > Reset (or File > Exit with no Reset) after exporting a test scene. I isolated this problem to my CollectNotes method… but couldn’t find my error.
...finally, with Klunk's help-- he pointed out that something was going wrong with my NoteKeyTab& noteList parameter that was causing it to trash memory. So I abandoned trying to use NoteKeyTab like a general container and replaced it with: Tab< NoteKey* >& noteList
...and my File > Reset (or File > Exit with no Reset) crashes disappeared. I also resized the notes catalog, and filled it with a default value of (-1,0) [meaning no notes on that frame], before passing it into the method-- so here is the updated code:
void ROFF2Export::CollectNotes(INode* node, std::vector<IPoint2>& catalog, Tab<NoteKey*>& noteList, int& totalNotes)
{
int l_start = (m_pExportOptions->nSegFrom) * GetTicksPerFrame(); // Convert to Ticks
int l_end = (m_pExportOptions->nSegTo) * GetTicksPerFrame(); // Convert to Ticks
int k;
TimeValue t; // Value in ticks
int numNoteTracks = node->NumNoteTracks();
int startNum = 0;
for(k = 0, t = l_start; t <= l_end; t += GetTicksPerFrame(), k++)
{
int l_count = 0;
for(int i = 0; i < numNoteTracks; i++)
{
DefNoteTrack* nt = (DefNoteTrack*)node->GetNoteTrack(i);
//Cycle thru note keys to see if key exists at TimeValue t
int numKeys = nt->keys.Count();
if ( numKeys > 0 )
{
for (int j = 0; j < numKeys; j++)
{
NoteKey* nk = nt->keys[j];
TimeValue keyTime = ( nk->time );
if (keyTime == t)
{
// We have a key at TimeValue t... so append to noteList
noteList.Append(1, &nk);
l_count++; // increment local count (total notes per frame k)
}
}
}
}
if ( l_count > 0 )
{
catalog[k].x = startNum;
catalog[k].y = l_count;
startNum += l_count; // increment starting note number for next frame
}
}
totalNotes = noteList.Count();
}
Edit: I realize that subAnims can have their own NoteTracks… but for my purposes– the method above only looks for NoteTracks on the node itself.