/********************************************************************** *< FILE: ImportAnimation.cpp DESCRIPTION: Animation Import Routines CREATED BY: tazpn (Theo) HISTORY: *> Copyright (c) 2006, All Rights Reserved. **********************************************************************/ #include "stdafx.h" #include <IFrameTagManager.h> #include <notetrck.h> #include "MaxNifImport.h" #include "NIFImporter.h" #include "KFMImporter.h" #include "KFImporter.h" #include "AnimKey.h" #include "NifPlugins.h" #include <obj/NiInterpolator.h> #include <obj/NiTransformInterpolator.h> #include <obj/NiTransformData.h> #include <obj/NiTimeController.h> #include <obj/NiTransformController.h> #include <obj/NiTextKeyExtraData.h> #include <obj/NiKeyframeController.h> #include <obj/NiKeyframeData.h> #include <obj/NiStringPalette.h> #include <obj/NiBSplineCompTransformInterpolator.h> using namespace Niflib; const Class_ID IPOS_CONTROL_CLASS_ID = Class_ID(0x118f7e02,0xffee238a); enum { IPOS_X_REF = 0, IPOS_Y_REF = 1, IPOS_Z_REF = 2, IPOS_W_REF = 3, }; void* operator new(size_t size, NoteKey* stub ) { return MAX_new(size); } void operator delete(void* memblock, NoteKey* stub ) { return MAX_delete(memblock); } struct AnimationImport { AnimationImport(NifImporter& parent) : ni(parent) {} NifImporter ∋ vector<KeyTextValue> BuildKeyValues(NiObjectNETRef nref); bool AddValues(NiObjectNETRef nref); bool AddValues(vector<NiObjectNETRef>& nodes); bool AddValues(Control *c, NiKeyframeDataRef data, float time); bool AddBiped(Control *c, NiKeyframeDataRef data, float time); Control* MakePosition(Control *tmCont, Class_ID clsid); Control* MakePositionXYZ(Control *tmCont, Class_ID clsid); Control* MakeRotation(Control *tmCont, Class_ID rotClsid, Class_ID rollClsid); Control* MakeScale(Control *tmCont, Class_ID clsid); Control* GetTMController(const string& name); Matrix3 GetTM(const string& name, TimeValue t = 0); bool GetTransformData(ControllerLink& lnk, string name, NiKeyframeDataRef& outData, Point3& p, Quat& q, float& s); }; bool NifImporter::ImportAnimation() { if (!enableAnimations) return false; if (nodes.empty()) return false; NiNodeRef rootNode = root; AnimationImport ai(*this); return ai.AddValues(DynamicCast<NiObjectNET>(rootNode->GetChildren())); } static vector<ControllerLink>::iterator FindLink(string name, vector<ControllerLink>& links) { for (vector<ControllerLink>::iterator lnk=links.begin(); lnk != links.end(); ) { string target = (*lnk).targetName; if (target.empty()) { NiStringPaletteRef strings = lnk->stringPalette; target = strings->GetSubStr((*lnk).nodeNameOffset); } if (target == name) { return lnk; } } return links.end(); } static void ClearAnimation(Control *c) { if (c != NULL) { if (c->IsColorController()) return; if (IKeyControl *ikeys = GetKeyControlInterface(c)){ ikeys->SetNumKeys(0); } if (Control *sc = c->GetWController()) { if (sc != c) ClearAnimation(sc); } if (Control *sc = c->GetXController()) { if (sc != c) ClearAnimation(sc); } if (Control *sc = c->GetYController()) { if (sc != c) ClearAnimation(sc); } if (Control *sc = c->GetZController()) { if (sc != c) ClearAnimation(sc); } if (Control *sc = c->GetRotationController()) { if (sc != c) ClearAnimation(sc); } if (Control *sc = c->GetPositionController()) { if (sc != c) ClearAnimation(sc); } if (Control *sc = c->GetScaleController()) { if (sc != c) ClearAnimation(sc); } } } void NifImporter::ClearAnimation(INode *node) { if (node != NULL) { if (node->HasNoteTracks()){ for (int i = node->NumNoteTracks()-1; i>=0; --i ){ if (NoteTrack *nt = node->GetNoteTrack(i)) node->DeleteNoteTrack(nt, TRUE); } } node->DeleteKeys(TRACK_DOALL); ::ClearAnimation(node->GetTMController()); for (int i=0, n=node->NumberOfChildren(); i<n; ++i){ ClearAnimation(node->GetChildNode(i)); } // Clear animation priority node->SetUserPropFloat(NP_ANM_PRI, 0.0f); } } void NifImporter::ClearAnimation() { if (clearAnimation) { if (IFrameTagManager *tagMgr = (IFrameTagManager*)GetCOREInterface(FRAMETAGMANAGER_INTERFACE)) { int n = tagMgr->GetTagCount(); for (int i=n-1; i>=0; --i){ tagMgr->DeleteTag( tagMgr->GetTagID(i) ); } } ClearAnimation(gi->GetRootNode()); } } FPValue GetScriptedProperty( FPValue& thing, TCHAR* propName ) { init_thread_locals(); push_alloc_frame(); two_value_locals( thingValue, propNameValue ); save_current_frames(); trace_back_active = FALSE; FPValue retVal( TYPE_INT, 0 ); BOOL isUndefined = ((thing.type==TYPE_VALUE) && (thing.v==&undefined)); if( (thing.i!=0) && (!isUndefined) ) try { //Safe handling for NULL vl.thingValue = InterfaceFunction::FPValue_to_val( thing ); vl.propNameValue = Name::intern( propName ); vl.thingValue = vl.thingValue->get_property( &vl.propNameValue, 1 ); vl.thingValue->to_fpvalue( retVal ); } catch ( ... ) { clear_error_source_data(); restore_current_frames(); MAXScript_signals = 0; if (progress_bar_up) MAXScript_interface->ProgressEnd(), progress_bar_up = FALSE; } pop_value_locals(); pop_alloc_frame(); return retVal; } Value* GetFunction( Value* thing, TCHAR* funcName ) { init_thread_locals(); push_alloc_frame(); one_value_local( funcNameValue ); save_current_frames(); trace_back_active = FALSE; Value* retval = NULL; if( (thing!=0) ) try { //Safe handling for NULL vl.funcNameValue = Name::intern( funcName ); retval = thing->get_property( &vl.funcNameValue, 1 ); } catch ( ... ) { clear_error_source_data(); restore_current_frames(); MAXScript_signals = 0; if (progress_bar_up) MAXScript_interface->ProgressEnd(), progress_bar_up = FALSE; } pop_value_locals(); pop_alloc_frame(); return retval; } static FPValue myAddNewNoteKey(Value* noteTrack, int frame) { // Magic initialization stuff for maxscript. static bool script_initialized = false; if (!script_initialized) { init_MAXScript(); script_initialized = TRUE; } init_thread_locals(); push_alloc_frame(); five_value_locals(name, fn, track, frame, result); save_current_frames(); trace_back_active = FALSE; FPValue retVal( TYPE_INT, 0 ); try { // Create the name of the maxscript function we want. // and look it up in the global names vl.name = Name::intern(_T("addNewNoteKey")); vl.fn = globals->get(vl.name); // For some reason we get a global thunk back, so lets // check the cell which should point to the function. // Just in case if it points to another global thunk // try it again. while (vl.fn != NULL && is_globalthunk(vl.fn)) vl.fn = static_cast<GlobalThunk*>(vl.fn)->cell; while (vl.fn != NULL && is_constglobalthunk(vl.fn)) vl.fn = static_cast<ConstGlobalThunk*>(vl.fn)->cell; // Now we should have a MAXScriptFunction, which we can // call to do the actual conversion. If we didn't // get a MAXScriptFunction, we can't convert. if (vl.fn != NULL && vl.fn->_is_function()) { Value* args[4]; // Ok. convertToArchMat takes one parameter, the material // and an optional keyword paramter, replace, which tells // convertToArchMat whether to replace all reference to // the old material by the new one. args[0] = vl.track = noteTrack; // The original material args[1] = Integer::intern(frame); args[2] = &keyarg_marker; // Separates keyword params from mandatory // Call the funtion and save the result. vl.result = vl.fn->apply(args, 2); // If the result isn't NULL, try to convert it to a material. // If the convesion fails, an exception will be thrown. if (vl.result != NULL) vl.result->to_fpvalue(retVal); } } catch (...) { clear_error_source_data(); restore_current_frames(); MAXScript_signals = 0; if (progress_bar_up) MAXScript_interface->ProgressEnd(), progress_bar_up = FALSE; } // Magic Max Script stuff to clear the frame and locals. pop_value_locals(); pop_alloc_frame(); return retVal; } bool KFMImporter::ImportAnimation() { bool ok = false; int curFrame = 0; // Read Kf files AnimationImport ai(*this); float time = 0.0f; for(vector<NiControllerSequenceRef>::iterator itr = kf.begin(); itr != kf.end(); ++itr){ float minTime = 1e+35f; float maxTime = 0.0f; NiControllerSequenceRef cntr = (*itr); float start = cntr->GetStartTime(); float stop = cntr->GetStopTime(); float total = (stop - start); vector<ControllerLink> links = cntr->GetControllerData(); NiTextKeyExtraDataRef textKeyData = cntr->GetTextKeyExtraData(); vector<StringKey> textKeys = textKeyData->GetKeys(); if (!textKeys.empty()) { if (addNoteTracks) { string target = cntr->GetTargetName(); if ( INode *n = gi->GetINodeByName(target.c_str()) ) { #if 1 DefNoteTrack* nt = (DefNoteTrack*)NewDefaultNoteTrack(); n->AddNoteTrack(nt); for (vector<StringKey>::iterator itr=textKeys.begin(); itr != textKeys.end(); ++itr) { TimeValue t = TimeToFrame(time + (*itr).time); if (wildmatch("start*", (*itr).data)){ stringlist args = TokenizeCommandLine((*itr).data.c_str(), true); if (args.empty()) continue; bool hasName = false; bool hasLoop = false; CycleType ct = cntr->GetCycleType(); for (stringlist::iterator itr = args.begin(); itr != args.end(); ++itr) { if (strmatch("-name", *itr)) { if (++itr == args.end()) break; hasName = true; } else if (strmatch("-loop", *itr)) { hasLoop = true; } } if (!hasName) { string name = cntr->GetName(); if (name.empty()) name = FormatString("EMPTY_SEQUENCE_AT_%df", int(t * FramesPerSecond / TicksPerFrame) ); args.push_back("-name"); args.push_back(name); } if (!hasLoop && ct == CYCLE_LOOP) { args.push_back("-loop"); } string line = JoinCommandLine(args); NoteKey *key = new NoteKey(t, line.c_str(), 0); nt->keys.Append(1, &key); } else { NoteKey *key = new NoteKey(t, (*itr).data.c_str(), 0); nt->keys.Append(1, &key); } } #else TSTR script; script += "fn getActorManager obj = (\n" " local nt = undefined\n" " n = numNoteTracks obj\n" " for i = 1 to n do (\n" " local nt = getNoteTrack obj i\n" " if (nt.name == \"ActorManager\") then ( return nt )\n" " )\n" " nt = notetrack \"ActorManager\"\n" " addNoteTrack obj nt\n" " return nt\n" ")\n" "fn addNoteKey nt frame tag = (\n" " local Key = addNewNoteKey nt.keys frame\n" " Key.value = tag\n" ")\n"; script += FormatText("nt = getActorManager $'%s'\n", target.c_str()); for (vector<StringKey>::iterator itr=textKeys.begin(); itr != textKeys.end(); ++itr) { TimeValue t = TimeToFrame(time + (*itr).time); if (wildmatch("start*", (*itr).data)){ stringlist args = TokenizeCommandLine((*itr).data.c_str(), true); if (args.empty()) continue; bool hasName = false; bool hasLoop = false; CycleType ct = cntr->GetCycleType(); for (stringlist::iterator itr = args.begin(); itr != args.end(); ++itr) { if (strmatch("-name", *itr)) { if (++itr == args.end()) break; hasName = true; } else if (strmatch("-loop", *itr)) { hasLoop = true; } } if (!hasName) { string name = cntr->GetName(); if (name.empty()) name = FormatString("EMPTY_SEQUENCE_AT_%df", int(t * FramesPerSecond / TicksPerFrame) ); args.push_back("-name"); args.push_back(name); } if (!hasLoop && ct == CYCLE_LOOP) { args.push_back("-loop"); } string line = JoinCommandLine(args); script += FormatText("addNoteKey nt (%d/ticksPerFrame) \"%s\"\n", t, line.c_str()); } else { script += FormatText("addNoteKey nt (%d/ticksPerFrame) \"%s\"\n", t, (*itr).data.c_str()); } //NoteKey *key = new NoteKey(TimeToFrame(time + (*itr).time), (*itr).data.c_str(), 0); //nt->keys.Append(1, &key); } ExecuteMAXScriptScript(script, TRUE, NULL); #endif } } if (addTimeTags) { if (IFrameTagManager *tagMgr = (IFrameTagManager*)GetCOREInterface(FRAMETAGMANAGER_INTERFACE)) { for (vector<StringKey>::iterator itr=textKeys.begin(); itr != textKeys.end(); ++itr) { tagMgr->CreateNewTag(const_cast<TCHAR*>((*itr).data.c_str()), TimeToFrame(time + (*itr).time), 0, FALSE); } } } } for (vector<ControllerLink>::iterator lnk=links.begin(); lnk != links.end(); ++lnk) { string name = (*lnk).targetName; if (name.empty()) { NiStringPaletteRef strings = lnk->stringPalette; name = strings->GetSubStr((*lnk).nodeNameOffset); } if (name.empty()) continue; // I realize this is not the best way to do this but it works for some files if (mergeNonAccum && wildmatch("* NonAccum", name)) { name = name.substr(0, name.length() - 9); } Control *c = ai.GetTMController(name); if (NULL == c) continue; INode *n = gi->GetINodeByName(name.c_str()); if ((*lnk).priority_ != 0.0f) { npSetProp(n, NP_ANM_PRI, (*lnk).priority_); } NiKeyframeDataRef data; Point3 p; Quat q; float s; if (ai.GetTransformData(*lnk, name, data, p, q, s)) { PosRotScaleNode(n, p, q, s, prsDefault, 0); if (ai.AddValues(c, data, time)) { minTime = min(minTime, start); maxTime = max(maxTime, stop); ok = true; } } } if (maxTime > minTime && maxTime > 0.0f) time += (maxTime-minTime) + FramesIncrement; } if (time != 0.0f) { Interval range(0, TimeToFrame(time)); gi->SetAnimRange(range); } return ok; } bool AnimationImport::GetTransformData(ControllerLink& lnk, string name, NiKeyframeDataRef& outData, Point3& p, Quat& q, float& s) { Control *c = GetTMController(name); if (NULL == c) return false; if (lnk.interpolator){ if (NiTransformInterpolatorRef interp = lnk.interpolator) { if (NiTransformDataRef data = interp->GetData()){ outData = StaticCast<NiKeyframeData>(data); // Set initial conditions p = TOPOINT3(interp->GetTranslation()); q = TOQUAT(interp->GetRotation(), true); s = interp->GetScale(); return true; } } else if (NiBSplineCompTransformInterpolatorRef interp = lnk.interpolator) { int npoints = interp->GetNumControlPt(); if (npoints > 3) { NiKeyframeDataRef data = CreateBlock("NiKeyframeData"); data->SetRotateType(QUADRATIC_KEY); data->SetTranslateType(QUADRATIC_KEY); data->SetScaleType(QUADRATIC_KEY); data->SetTranslateKeys( interp->SampleTranslateKeys(npoints, 3) ); data->SetQuatRotateKeys( interp->SampleQuatRotateKeys(npoints, 3) ); data->SetScaleKeys( interp->SampleScaleKeys(npoints, 3) ); outData = data; p = TOPOINT3(interp->GetTranslation()); q = TOQUAT(interp->GetRotation(), true); s = interp->GetScale(); return true; } } } else if (lnk.controller) { if (NiTransformControllerRef tc = DynamicCast<NiTransformController>(lnk.controller)) { if (NiTransformInterpolatorRef interp = tc->GetInterpolator()) { if (NiTransformDataRef data = interp->GetData()){ outData = StaticCast<NiKeyframeData>(data); p = TOPOINT3(interp->GetTranslation()); q = TOQUAT(interp->GetRotation(), true); s = interp->GetScale(); return true; } } } else if (NiKeyframeControllerRef kfc = DynamicCast<NiKeyframeController>(lnk.controller)) { if (NiKeyframeDataRef data = kfc->GetData()) { outData = data; // values will not be used in transforms p = Point3(FloatNegINF, FloatNegINF, FloatNegINF); q = Quat(FloatNegINF, FloatNegINF, FloatNegINF, FloatNegINF); s = FloatNegINF; return true; } } } return false; } Control *AnimationImport::GetTMController(const string& name) { INode *n = ni.gi->GetINodeByName(name.c_str()); if (NULL == n) return NULL; Control *c = n->GetTMController(); if (NULL == c) return NULL; return c; } Matrix3 AnimationImport::GetTM(const string& name, TimeValue t) { INode *n = ni.gi->GetINodeByName(name.c_str()); if (NULL == n) return Matrix3(true); return n->GetObjTMAfterWSM(t); } vector<KeyTextValue> AnimationImport::BuildKeyValues(NiObjectNETRef nref) { if (NiTextKeyExtraDataRef keydata = SelectFirstObjectOfType<NiTextKeyExtraData>(nref->GetExtraData())) return keydata->GetKeys(); return vector<KeyTextValue>(); } bool AnimationImport::AddValues(vector<NiObjectNETRef>& nodes) { bool ok = false; for (vector<NiObjectNETRef>::iterator itr = nodes.begin(), end = nodes.end(); itr != end; ++itr) { ok |= AddValues(*itr); NiNodeRef ninode = (*itr); if (ninode) ok |= AddValues(DynamicCast<NiObjectNET>(ninode->GetChildren())); } return ok; } bool AnimationImport::AddValues(NiObjectNETRef nref) { Control *c = GetTMController(nref->GetName().c_str()); if (NULL == c) return false; float time = FramesIncrement; list< NiTimeControllerRef > clist = nref->GetControllers(); if (NiTransformControllerRef tc = SelectFirstObjectOfType<NiTransformController>(clist)) { if (NiTransformInterpolatorRef interp = tc->GetInterpolator()) { if (NiTransformDataRef data = interp->GetData()) { return AddValues(c, data, time); } } } else if (NiKeyframeControllerRef kf = SelectFirstObjectOfType<NiKeyframeController>(clist)) { if (NiKeyframeDataRef kfData = kf->GetData()) { return AddValues(c, kfData, time); } } return false; } bool AnimationImport::AddValues(Control *c, NiKeyframeDataRef data, float time) { #ifdef USE_BIPED // Bipeds are special. And will crash if you dont treat them with care if ( (c->ClassID() == BIPSLAVE_CONTROL_CLASS_ID) ||(c->ClassID() == BIPBODY_CONTROL_CLASS_ID) ||(c->ClassID() == FOOTPRINT_CLASS_ID)) { return AddBiped(c, data, time); } #endif vector<Vector3Key> posKeys = data->GetTranslateKeys(); vector<QuatKey> quatKeys = data->GetQuatRotateKeys(); vector<FloatKey> sclKeys = data->GetScaleKeys(); vector<FloatKey> xKeys = data->GetXRotateKeys(); vector<FloatKey> yKeys = data->GetYRotateKeys(); vector<FloatKey> zKeys = data->GetZRotateKeys(); // Require more than one key to import (to avoid zero frame positioning used in mw and daoc if (ni.requireMultipleKeys && !( posKeys.size() > 1 || quatKeys.size() > 1 || sclKeys.size() > 1 || xKeys.size() > 1 || yKeys.size() > 1 || zKeys.size() > 1 )) { return false; } // Handle Translation switch (data->GetTranslateType()) { case LINEAR_KEY: if (Control *subCtrl = MakePositionXYZ(c, Class_ID(LININTERP_FLOAT_CLASS_ID,0))) { vector<FloatKey> xkeys, ykeys, zkeys; SplitKeys(posKeys, xkeys, ykeys, zkeys); MergeKeys<ILinFloatKey, FloatKey>(subCtrl->GetXController(), xkeys, time); MergeKeys<ILinFloatKey, FloatKey>(subCtrl->GetYController(), ykeys, time); MergeKeys<ILinFloatKey, FloatKey>(subCtrl->GetZController(), zkeys, time); } break; case QUADRATIC_KEY: case XYZ_ROTATION_KEY: if (Control *subCtrl = MakePositionXYZ(c, Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID,0))) { vector<FloatKey> xkeys, ykeys, zkeys; SplitKeys(posKeys, xkeys, ykeys, zkeys); MergeKeys<IBezFloatKey, FloatKey>(subCtrl->GetXController(), xkeys, time); MergeKeys<IBezFloatKey, FloatKey>(subCtrl->GetYController(), ykeys, time); MergeKeys<IBezFloatKey, FloatKey>(subCtrl->GetZController(), zkeys, time); } break; case TBC_KEY: if (Control *subCtrl = MakePositionXYZ(c, Class_ID(TCBINTERP_FLOAT_CLASS_ID,0))) { vector<FloatKey> xkeys, ykeys, zkeys; SplitKeys(posKeys, xkeys, ykeys, zkeys); MergeKeys<ITCBFloatKey, FloatKey>(subCtrl->GetXController(), xkeys, time); MergeKeys<ITCBFloatKey, FloatKey>(subCtrl->GetYController(), ykeys, time); MergeKeys<ITCBFloatKey, FloatKey>(subCtrl->GetZController(), zkeys, time); } break; } // Handle Rotation switch (data->GetRotateType()) { case LINEAR_KEY: if (Control *subCtrl = MakeRotation(c, Class_ID(LININTERP_ROTATION_CLASS_ID,0), Class_ID(LININTERP_FLOAT_CLASS_ID,0))) { MergeKeys<ILinRotKey, QuatKey>(subCtrl, quatKeys, time); } break; case QUADRATIC_KEY: if (Control *subCtrl = MakeRotation(c, Class_ID(HYBRIDINTERP_ROTATION_CLASS_ID,0), Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID,0))) { MergeKeys<IBezQuatKey, QuatKey>(subCtrl, quatKeys, time); } break; case XYZ_ROTATION_KEY: if (Control *subCtrl = MakeRotation(c, Class_ID(EULER_CONTROL_CLASS_ID,0), Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID,0))) { MergeKeys<IBezFloatKey, FloatKey>(subCtrl->GetXController(), xKeys, time); MergeKeys<IBezFloatKey, FloatKey>(subCtrl->GetYController(), yKeys, time); MergeKeys<IBezFloatKey, FloatKey>(subCtrl->GetZController(), zKeys, time); } break; case TBC_KEY: if (ni.replaceTCBRotationWithBezier) { // TCB simply is not working for me. Better off with Bezier as a workaround if (Control *subCtrl = MakeRotation(c, Class_ID(HYBRIDINTERP_ROTATION_CLASS_ID,0), Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID,0))) { MergeKeys<IBezQuatKey, QuatKey>(subCtrl, quatKeys, time); } } else { if (Control *subCtrl = MakeRotation(c, Class_ID(TCBINTERP_ROTATION_CLASS_ID,0), Class_ID(TCBINTERP_FLOAT_CLASS_ID,0))) { MergeKeys<ITCBRotKey, QuatKey>(subCtrl, quatKeys, time); } } break; } // Handle Scale switch (data->GetScaleType()) { case LINEAR_KEY: if (Control *subCtrl = MakeScale(c, Class_ID(LININTERP_SCALE_CLASS_ID,0))) { MergeKeys<ILinScaleKey, FloatKey>(subCtrl, sclKeys, time); } break; case QUADRATIC_KEY: case XYZ_ROTATION_KEY: if (Control *subCtrl = MakeScale(c, Class_ID(HYBRIDINTERP_SCALE_CLASS_ID,0))) { MergeKeys<IBezScaleKey, FloatKey>(subCtrl, sclKeys, time); } break; case TBC_KEY: if (Control *subCtrl = MakeScale(c, Class_ID(TCBINTERP_SCALE_CLASS_ID,0))) { MergeKeys<ITCBScaleKey, FloatKey>(subCtrl, sclKeys, time); } break; } return true; } Control* AnimationImport::MakeRotation(Control *tmCont, Class_ID rotClsid, Class_ID rollClsid) { Interface *ip = ni.gi; if (Control *c = tmCont->GetRotationController()) { if (c->ClassID()!=rotClsid) { if (Control *tmpCtrl = (Control*)ip->CreateInstance(CTRL_ROTATION_CLASS_ID, rotClsid)) { if (!tmCont->SetRotationController(tmpCtrl)) tmpCtrl->DeleteThis(); else c = tmpCtrl; } } if (Control *r = tmCont->GetRollController()) { if (r->ClassID()!=rollClsid) { if (Control *r = (Control*)ip->CreateInstance(CTRL_FLOAT_CLASS_ID,rollClsid)) if (!tmCont->SetRollController(r)) r->DeleteThis(); } } return c; } return NULL; } Control* AnimationImport::MakeScale(Control *tmCont, Class_ID clsid) { Interface *ip = ni.gi; if (Control *c = tmCont->GetScaleController()) { if (c->ClassID()!=clsid) { if (Control *tmpCtrl = (Control*)ip->CreateInstance(CTRL_SCALE_CLASS_ID, clsid)){ if (!tmCont->SetScaleController(tmpCtrl)) tmpCtrl->DeleteThis(); else c = tmpCtrl; } } return c; } return NULL; } Control* AnimationImport::MakePosition(Control *tmCont, Class_ID clsid) { Interface *ip = ni.gi; if (Control *c = tmCont->GetPositionController()) { if (c->ClassID()!=clsid) { if (Control *tmpCtrl = (Control*)ip->CreateInstance(CTRL_POSITION_CLASS_ID, clsid)){ if (!tmCont->SetPositionController(tmpCtrl)) tmpCtrl->DeleteThis(); else c = tmpCtrl; } } return c; } return NULL; } Control* AnimationImport::MakePositionXYZ(Control *tmCont, Class_ID clsid) { Interface *ip = ni.gi; // First make the controller and XYZ Independent position controller, then fix individuals if (Control *c = MakePosition(tmCont, IPOS_CONTROL_CLASS_ID)){ if (Control *x = c->GetXController()){ if (x->ClassID()!= clsid) { if (Control *tmp = (Control*)ip->CreateInstance(CTRL_FLOAT_CLASS_ID, clsid)) c->SetReference(IPOS_X_REF, tmp); } } if (Control *y = c->GetYController()){ if (y->ClassID()!= clsid) { if (Control *tmp = (Control*)ip->CreateInstance(CTRL_FLOAT_CLASS_ID, clsid)) c->SetReference(IPOS_Y_REF, tmp); } } if (Control *z = c->GetZController()){ if (z->ClassID()!= clsid) { if (Control *tmp = (Control*)ip->CreateInstance(CTRL_FLOAT_CLASS_ID, clsid)) c->SetReference(IPOS_Z_REF, tmp); } } return c; } return NULL; } #ifdef USE_BIPED bool AnimationImport::AddBiped(Control *c, NiKeyframeDataRef data, float time) { return false; } #endif