/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include "HeadSpin.h"
#include "Max.h"
#include "notetrck.h"
#include "hsTypes.h"
#include "hsTemplates.h"
#include "plMaxAnimUtils.h"
#include "MaxExport/plErrorMsg.h"
float TimeValueToGameTime(TimeValue t)
{
int FR = ::GetFrameRate();
int TPF = ::GetTicksPerFrame();
int TPS = TPF*FR;
return float(t)/float(TPS);
}
bool GetSegMapAnimTime(const char *animName, SegmentMap *segMap, SegmentSpec::SegType type, float& begin, float& end)
{
if (segMap)
{
if (animName && segMap->find(animName) != segMap->end())
{
SegmentSpec *spec = (*segMap)[animName];
if (spec->fType == type)
{
if (spec->fStart != -1)
begin = spec->fStart;
if (spec->fEnd != -1)
end = spec->fEnd;
return true;
}
}
}
return false;
}
SegmentMap *GetSharedAnimSegmentMap(std::vector& anims, plErrorMsg *pErrorMsg)
{
if (anims.empty())
return nil;
SegmentMap *segMap = GetAnimSegmentMap(anims[0], pErrorMsg);
if (!segMap)
return nil;
int i;
for (i = 1; i < anims.size(); i++)
{
SegmentMap *curSegMap = GetAnimSegmentMap(anims[i], pErrorMsg);
// This node doesn't have a segmap, so we can't have any anims shared among all the nodes.
if (!curSegMap)
{
DeleteSegmentMap(segMap);
return nil;
}
if (segMap->begin() == segMap->end())
{
DeleteSegmentMap(segMap);
return nil;
}
SegmentMap::iterator it = segMap->begin();
while (it != segMap->end())
{
if (curSegMap->find(it->second->fName) == curSegMap->end())
{
SegmentMap::iterator del = it;
it++;
segMap->erase(del->second->fName);
}
else
it++;
}
DeleteSegmentMap(curSegMap);
}
return segMap;
}
SegmentSpec::SegmentSpec(float start, float end, char * name, SegType type) :
fStart(start), fEnd(end), fName(name), fType(type), fInitial(-1)
{
}
SegmentSpec::~SegmentSpec()
{
delete [] fName;
}
// constants used for parsing the note tracks
enum NoteType
{
kNoteStartAnim,
kNoteEndAnim,
kNoteStartLoop,
kNoteEndLoop,
kNoteMarker,
kNoteStopPoint,
kNoteInitial,
kNoteUnknown,
kNoteSuppress,
};
SegmentSpec::SegmentSpec()
{
fStart = -1;
fEnd = -1;
fInitial = -1;
fName = nil;
fType = kAnim;
}
bool SegmentSpec::Contains(SegmentSpec *spec)
{
if (!spec)
return false;
if (spec->fType == kMarker || spec->fType == kStopPoint)
return (spec->fStart >= fStart && spec->fStart <= fEnd);
if (fStart == -1 || fEnd == -1)
return false;
if (spec->fStart == -1)
return (fStart < spec->fEnd);
if (spec->fEnd == -1)
return (fEnd > spec->fStart);
if (fStart <= spec->fStart && fEnd >= spec->fEnd)
return true;
return false;
}
bool DoesHaveStopPoints(Animatable *anim)
{
if (!anim || !anim->HasNoteTracks())
return false;
int numTracks = anim->NumNoteTracks();
for (int i = 0; i < numTracks; i++)
{
DefNoteTrack *track = (DefNoteTrack *)anim->GetNoteTrack(i);
int numKeys = track->keys.Count();
for (int j = 0; j < numKeys; j++)
{
char buf[256];
strcpy(buf, track->keys[j]->note);
strlwr(buf);
if (strstr(buf, "@stoppoint"))
return true;
}
}
return false;
}
void GetSegment(const char *note, float time, SegmentMap *segMap, plErrorMsg *pErrMsg)
{
char segName[256];
char segSuffix[256];
int matchedFields = sscanf(note, " %[^@] @ %s ", segName, segSuffix);
if (matchedFields == 2)
{
NoteType type = kNoteUnknown;
if (!stricmp(segSuffix, "start") ||
!stricmp(segSuffix, "begin"))
type = kNoteStartAnim;
else if (!stricmp(segSuffix, "end"))
type = kNoteEndAnim;
else if (!stricmp(segSuffix, "startloop") ||
!stricmp(segSuffix, "loopstart") ||
!stricmp(segSuffix, "beginloop") ||
!stricmp(segSuffix, "loopbegin"))
type = kNoteStartLoop;
else if (!stricmp(segSuffix, "endloop") ||
!stricmp(segSuffix, "loopend"))
type = kNoteEndLoop;
else if (!stricmp(segSuffix, "marker"))
type = kNoteMarker;
else if (!stricmp(segSuffix, "stoppoint"))
type = kNoteStopPoint;
else if (!stricmp(segSuffix, "initial"))
type = kNoteInitial;
else if (!stricmp(segSuffix, "suppress"))
type = kNoteSuppress;
if (type == kNoteUnknown)
{
if (pErrMsg)
{
pErrMsg->Set(true, "NoteTrack Anim Error", "Malformed segment note: %s", segName);
pErrMsg->Show();
pErrMsg->Set();
}
}
else
{
SegmentMap::iterator existing = segMap->find(segName);
SegmentSpec *existingSpec = (existing != segMap->end()) ? (*existing).second : nil;
const char *kErrorTitle = "NoteTrack Anim Error";
if (existingSpec)
{
// an existing spec, but we're processing a start note?
if (type == kNoteStartAnim && pErrMsg)
{
pErrMsg->Set(true, kErrorTitle, "Got out of order start note. No Start given for %s", segName).Show();
pErrMsg->Set();
}
// existing spec, has an end, we're also processing an end?
else if (type == kNoteEndAnim && existingSpec->fEnd != -1 && pErrMsg)
{
pErrMsg->Set(true, kErrorTitle, "Got two ends for the same segment %s", segName).Show();
pErrMsg->Set();
}
else if (type == kNoteStartLoop && existingSpec->fStart != -1 && pErrMsg)
{
pErrMsg->Set(true, kErrorTitle, "Got two loop starts for the same segment, %s", segName).Show();
pErrMsg->Set();
}
else if (type == kNoteEndLoop && existingSpec->fEnd != -1 && pErrMsg)
{
pErrMsg->Set(true, kErrorTitle, "Got two loop ends for the same segment, %s", segName).Show();
pErrMsg->Set();
}
else if (type == kNoteMarker && pErrMsg)
{
pErrMsg->Set(true, kErrorTitle, "Marker has the same name (%s) as another spec in its notetrack", segName).Show();
pErrMsg->Set();
}
else if (type == kNoteStopPoint && pErrMsg)
{
pErrMsg->Set(true, kErrorTitle, "Stop point has the same name (%s) as another spec in its notetrack", segName).Show();
pErrMsg->Set();
}
if (type == kNoteEndAnim || type == kNoteEndLoop)
existingSpec->fEnd = time;
else if (type == kNoteStartLoop)
existingSpec->fStart = time;
else if (type == kNoteInitial)
existingSpec->fInitial = time;
}
else
{
if (type == kNoteEndAnim && pErrMsg)
{
pErrMsg->Set(true, kErrorTitle, "Got an end note without a corresponding start. Ignoring %s", segName).Show();
pErrMsg->Set();
}
else
{
char *nameCopy = TRACKED_NEW char[strlen(segName)+1];
strcpy(nameCopy, segName);
switch (type)
{
case kNoteStartAnim:
(*segMap)[nameCopy] = TRACKED_NEW SegmentSpec(time, -1, nameCopy, SegmentSpec::kAnim);
break;
case kNoteStartLoop:
(*segMap)[nameCopy] = TRACKED_NEW SegmentSpec(time, -1, nameCopy, SegmentSpec::kLoop);
break;
case kNoteEndLoop:
(*segMap)[nameCopy] = TRACKED_NEW SegmentSpec(-1, time, nameCopy, SegmentSpec::kLoop);
break;
case kNoteMarker:
(*segMap)[nameCopy] = TRACKED_NEW SegmentSpec(time, -1, nameCopy, SegmentSpec::kMarker);
break;
case kNoteStopPoint:
(*segMap)[nameCopy] = TRACKED_NEW SegmentSpec(time, -1, nameCopy, SegmentSpec::kStopPoint);
break;
case kNoteSuppress:
(*segMap)[nameCopy] = TRACKED_NEW SegmentSpec(-1, -1, nameCopy, SegmentSpec::kSuppress);
break;
default:
delete [] nameCopy;
break;
}
}
}
}
}
}
// Read through all the notes in all the note tracks on the given node
// Check the contents of each node for a name like "walk@start", i.e. @[start | end]
// For each match, open a segment specification and p
SegmentMap * GetAnimSegmentMap(Animatable *anim, plErrorMsg *pErrMsg)
{
if (!anim->HasNoteTracks())
return nil;
SegmentMap *segMap = TRACKED_NEW SegmentMap();
int numTracks = anim->NumNoteTracks();
for (int i = 0; i < numTracks; i++)
{
DefNoteTrack * track = (DefNoteTrack *)anim->GetNoteTrack(i);
int numKeys = track->keys.Count();
for (int j = 0; j < numKeys; j++)
{
char *note = track->keys[j]->note;
float time = TimeValueToGameTime(track->keys[j]->time);
GetSegment(note, time, segMap, pErrMsg);
}
}
return segMap;
}
void DeleteSegmentMap(SegmentMap *segMap)
{
// If we have a segment map, delete the memory associated with it
if (segMap)
{
for (SegmentMap::iterator i = segMap->begin(); i != segMap->end(); i++)
delete (*i).second;
delete segMap;
}
}