//------------------------------ Control* AnimationImporter::createAndFillMaxTransformationController( COLLADAFW::AnimationCurve* animationCurve ) { if ( animationCurve->getInterpolationType() != COLLADAFW::AnimationCurve::INTERPOLATION_LINEAR ) { // we support only linear transformations return 0; } if ( animationCurve->getOutDimension() != 16 ) { // we can only handle animations with 16 dimension as matrix controller return 0; } Control* maxController = createMaxTransformationController( animationCurve ); if ( !maxController ) { return 0; } IKeyControl* maxKeyController = GetKeyControlInterface( maxController ); fillMaxTransformationController( maxController, animationCurve); return maxController; }
// REAPPLYANIMATION // Now that we've reparented a node within the hierarchy, re-apply all its animation. void ReapplyAnimation(INode *node, plSampleVec *samples) { Control *controller = node->GetTMController(); Control *rotControl = NewDefaultRotationController(); // we set the default rotation controller type above in RemoveBiped() Control *posControl = NewDefaultPositionController(); // '' '' Control *scaleControl = NewDefaultScaleController(); // '' '' controller->SetRotationController(rotControl); controller->SetPositionController(posControl); controller->SetScaleController(scaleControl); for(int i = 0; i < samples->size(); i++) { nodeTMInfo *info = (*samples)[i]; Matrix3 m = info->fMat3; TimeValue t = info->fTime; #if 1 node->SetNodeTM(t, m); #else AffineParts parts; INode *parent = node->GetParentNode(); Matrix3 parentTM = parent->GetNodeTM(t); Matrix3 invParentTM = Inverse(parentTM); m *= invParentTM; decomp_affine(m, &parts); Quat q(parts.q.x, parts.q.y, parts.q.z, parts.q.w); Point3 p(parts.t.x, parts.t.y, parts.t.z); rotControl->SetValue(t, q); posControl->SetValue(t, p); #endif } IKeyControl *posKeyCont = GetKeyControlInterface(posControl); IKeyControl *scaleKeyCont = GetKeyControlInterface(scaleControl); ReduceKeys<ILinPoint3Key>(node, posKeyCont); EliminateScaleKeys(node, scaleKeyCont); // grrrr ReduceKeys<ILinScaleKey>(node, scaleKeyCont); }
//------------------------------ Control* AnimationImporter::createAndFillMaxFloatController( COLLADAFW::AnimationCurve* animationCurve, size_t dimension ) { COLLADAFW::AnimationCurve::InterpolationType interpolationType = animationCurve->getInterpolationType(); bool isLinear = (interpolationType == COLLADAFW::AnimationCurve::INTERPOLATION_LINEAR); Control* maxController = createMaxFloatController( animationCurve, isLinear ); if ( !maxController ) return 0; IKeyControl* maxKeyController = GetKeyControlInterface( maxController ); fillMaxFloatController( maxKeyController, animationCurve, dimension, isLinear); return maxController; }
// ADJUSTROTKEYS void AdjustRotKeys(INode *node) { Control *controller = node->GetTMController(); Control *rotControl = controller->GetRotationController(); IKeyControl *rotKeyCont = GetKeyControlInterface(rotControl); int numKeys = rotKeyCont->GetNumKeys(); for(int i = 0; i < numKeys; i++) { ITCBKey key; rotKeyCont->GetKey(i, &key); key.cont = 0; rotKeyCont->SetKey(i, &key); } }
/** * This method will add all the position keys found in the controller to * the animation path. */ void OSGExp::exportPosKeys(osg::AnimationPath* animationPath, Control* cont){ if (!cont) return; int i; IKeyControl *ikc = GetKeyControlInterface(cont); // TCB position if (ikc && cont->ClassID() == Class_ID(TCBINTERP_POSITION_CLASS_ID, 0)) { int numKeys; if (numKeys = ikc->GetNumKeys()) { for (i=0; i<numKeys; i++) { ITCBPoint3Key key; ikc->GetKey(i, &key); ; // NOT SUPPORTED YET } } } // Bezier position else if (ikc && cont->ClassID() == Class_ID(HYBRIDINTERP_POSITION_CLASS_ID, 0)){ int numKeys; if(numKeys = ikc->GetNumKeys()){ for (i=0; i<numKeys; i++) { IBezPoint3Key key; ikc->GetKey(i, &key); ; // NOT SUPPORTED YET } } } // Linear position else if (ikc && cont->ClassID() == Class_ID(LININTERP_POSITION_CLASS_ID, 0)) { int numKeys; if(numKeys = ikc->GetNumKeys()){ for (i=0; i<numKeys; i++) { ILinPoint3Key key; ikc->GetKey(i, &key); addControlPos(animationPath, (key.time/(float)TIME_TICKSPERSEC),key.val); } } } }
void AsciiExp::DumpScaleKeys(Control* cont, int indentLevel) { if (!cont) return; int i; TSTR indent = GetIndent(indentLevel); IKeyControl *ikc = GetKeyControlInterface(cont); if (ikc && cont->ClassID() == Class_ID(TCBINTERP_SCALE_CLASS_ID, 0)) { int numKeys = ikc->GetNumKeys(); if (numKeys != 0) { _ftprintf(pStream, _T("%s\t\t%s {\n"), indent.data(), ID_CONTROL_SCALE_TCB); for (i=0; i<numKeys; i++) { ITCBScaleKey key; ikc->GetKey(i, &key); _ftprintf(pStream, _T("%s\t\t\t%s %d\t%s"), indent.data(), ID_TCB_SCALE_KEY, key.time, Format(key.val)); _ftprintf(pStream, _T("\t%s\t%s\t%s\t%s\t%s\n"), Format(key.tens), Format(key.cont), Format(key.bias), Format(key.easeIn), Format(key.easeOut)); } _ftprintf(pStream, _T("%s\t\t}\n"), indent.data()); } } else if (ikc && cont->ClassID() == Class_ID(HYBRIDINTERP_SCALE_CLASS_ID, 0)) { int numKeys = ikc->GetNumKeys(); if (numKeys != 0) { _ftprintf(pStream, _T("%s\t\t%s {\n"), indent.data(), ID_CONTROL_SCALE_BEZIER); for (i=0; i<numKeys; i++) { IBezScaleKey key; ikc->GetKey(i, &key); _ftprintf(pStream, _T("%s\t\t\t%s %d\t%s"), indent.data(), ID_BEZIER_SCALE_KEY, key.time, Format(key.val)); _ftprintf(pStream, _T("\t%s\t%s\t%d\n"), Format(key.intan), Format(key.outtan), key.flags); } _ftprintf(pStream, _T("%s\t\t}\n"), indent.data()); } } else if (ikc && cont->ClassID() == Class_ID(LININTERP_SCALE_CLASS_ID, 0)) { int numKeys = ikc->GetNumKeys(); if (numKeys != 0) { _ftprintf(pStream, _T("%s\t\t%s {\n"), indent.data(), ID_CONTROL_SCALE_LINEAR); for (i=0; i<numKeys; i++) { ILinScaleKey key; ikc->GetKey(i, &key); _ftprintf(pStream, _T("%s\t\t\t%s %d\t%s\n"), indent.data(), ID_SCALE_KEY, key.time, Format(key.val)); } _ftprintf(pStream, _T("%s\t\t}\n"), indent.data()); } } }
void AsciiExp::DumpRotKeys(Control* cont, int indentLevel) { if (!cont) return; int i; TSTR indent = GetIndent(indentLevel); IKeyControl *ikc = GetKeyControlInterface(cont); if (ikc && cont->ClassID() == Class_ID(TCBINTERP_ROTATION_CLASS_ID, 0)) { int numKeys = ikc->GetNumKeys(); if (numKeys != 0) { _ftprintf(pStream, _T("%s\t\t%s {\n"), indent.data(), ID_CONTROL_ROT_TCB); for (i=0; i<numKeys; i++) { ITCBRotKey key; ikc->GetKey(i, &key); _ftprintf(pStream, _T("%s\t\t\t%s %d\t%s"), indent.data(), ID_TCB_ROT_KEY, key.time, Format(key.val)); _ftprintf(pStream, _T("\t%s\t%s\t%s\t%s\t%s\n"), Format(key.tens), Format(key.cont), Format(key.bias), Format(key.easeIn), Format(key.easeOut)); } _ftprintf(pStream, _T("%s\t\t}\n"), indent.data()); } } else if (ikc && cont->ClassID() == Class_ID(HYBRIDINTERP_ROTATION_CLASS_ID, 0)) { int numKeys = ikc->GetNumKeys(); if (numKeys != 0) { _ftprintf(pStream, _T("%s\t\t%s {\n"), indent.data(), ID_CONTROL_ROT_BEZIER); for (i=0; i<numKeys; i++) { IBezQuatKey key; ikc->GetKey(i, &key); _ftprintf(pStream, _T("%s\t\t\t%s %d\t%s\n"), indent.data(), // Quaternions are converted to AngAxis when written to file ID_ROT_KEY, key.time, Format(key.val)); // There is no intan/outtan for Quat Rotations } _ftprintf(pStream, _T("%s\t\t}\n"), indent.data()); } } else if (ikc && cont->ClassID() == Class_ID(LININTERP_ROTATION_CLASS_ID, 0)) { int numKeys = ikc->GetNumKeys(); if (numKeys != 0) { _ftprintf(pStream, _T("%s\t\t%s {\n"), indent.data(), ID_CONTROL_ROT_LINEAR); for (i=0; i<numKeys; i++) { ILinRotKey key; ikc->GetKey(i, &key); _ftprintf(pStream, _T("%s\t\t\t%s %d\t%s\n"), indent.data(), // Quaternions are converted to AngAxis when written to file ID_ROT_KEY, key.time, Format(key.val)); } _ftprintf(pStream, _T("%s\t\t}\n"), indent.data()); } } }
// Output float keys if this is a known float controller that // supports key operations. Otherwise we will sample the controller // once for each frame to get the value. void AsciiExp::DumpFloatKeys(Control* cont, int indentLevel) { if (!cont) return; int i; TSTR indent = GetIndent(indentLevel); IKeyControl *ikc = NULL; // If the user wants us to always sample, we will ignore the KeyControlInterface if (!GetAlwaysSample()) ikc = GetKeyControlInterface(cont); // TCB float if (ikc && cont->ClassID() == Class_ID(TCBINTERP_FLOAT_CLASS_ID, 0)) { _ftprintf(pStream, _T("%s\t\t%s {\n"), indent.data(), ID_CONTROL_FLOAT_TCB); for (i=0; i<ikc->GetNumKeys(); i++) { ITCBFloatKey key; ikc->GetKey(i, &key); _ftprintf(pStream, _T("%s\t\t\t%s %d\t%s"), indent.data(), ID_TCB_FLOAT_KEY, key.time, Format(key.val)); _ftprintf(pStream, _T("\t%s\t%s\t%s\t%s\t%s\n"), Format(key.tens), Format(key.cont), Format(key.bias), Format(key.easeIn), Format(key.easeOut)); } _ftprintf(pStream, _T("%s\t\t}\n"), indent.data()); } // Bezier float else if (ikc && cont->ClassID() == Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID, 0)) { _ftprintf(pStream, _T("%s\t\t%s {\n"), indent.data(), ID_CONTROL_FLOAT_BEZIER); for (i=0; i<ikc->GetNumKeys(); i++) { IBezFloatKey key; ikc->GetKey(i, &key); _ftprintf(pStream, _T("%s\t\t\t%s %d\t%s"), indent.data(), ID_BEZIER_FLOAT_KEY, key.time, Format(key.val)); _ftprintf(pStream, _T("\t%s\t%s\t%d\n"), Format(key.intan), Format(key.outtan), key.flags); } _ftprintf(pStream, _T("%s\t\t}\n"), indent.data()); } else if (ikc && cont->ClassID() == Class_ID(LININTERP_FLOAT_CLASS_ID, 0)) { _ftprintf(pStream, _T("%s\t\t%s {\n"), indent.data(), ID_CONTROL_FLOAT_LINEAR); for (i=0; i<ikc->GetNumKeys(); i++) { ILinFloatKey key; ikc->GetKey(i, &key); _ftprintf(pStream, _T("%s\t\t\t%s %d\t%s\n"), indent.data(), ID_FLOAT_KEY, key.time, Format(key.val)); } _ftprintf(pStream, _T("%s\t\t}\n"), indent.data()); } else { // Unknown controller, no key interface or sample on demand - // This might be a procedural controller or something else we // don't know about. The last resort is to get the value from the // controller at every n frames. TSTR name; cont->GetClassName(name); TSTR className = FixupName(name); Interface14 *iface = GetCOREInterface14(); UINT codepage = iface-> DefaultTextSaveCodePage(true); const char* className_locale = className.ToCP(codepage); _ftprintf(pStream, _T("%s\t\t%s \"%hs\" {\n"), indent.data(), ID_CONTROL_FLOAT_SAMPLE, className_locale); // If it is animated at all... if (cont->IsAnimated()) { // Get the range of the controller animation Interval range; // Get range of full animation Interval animRange = ip->GetAnimRange(); TimeValue t = cont->GetTimeRange(TIMERANGE_ALL).Start(); float value; // While we are inside the animation... while (animRange.InInterval(t)) { // Sample the controller range = FOREVER; cont->GetValue(t, &value, range); // Set time to start of controller validity interval t = range.Start(); // Output the sample _ftprintf(pStream, _T("%s\t\t\t%s %d\t%s\n"), indent.data(), ID_FLOAT_KEY, t, Format(value)); // If the end of the controller validity is beyond the // range of the animation if (range.End() > cont->GetTimeRange(TIMERANGE_ALL).End()) { break; } else { t = (range.End()/GetTicksPerFrame()+GetKeyFrameStep()) * GetTicksPerFrame(); } } } _ftprintf(pStream, _T("%s\t\t}\n"), indent.data()); } }
void XsiExp::DumpScaleKeys( INode * node, int indentLevel) { Control * cont = node->GetTMController()->GetScaleController(); IKeyControl * ikc = GetKeyControlInterface(cont); INode * parent = node->GetParentNode(); if (!cont || !parent || (parent && parent->IsRootNode()) || !ikc) { // no controller or root node return; } int numKeys = ikc->GetNumKeys(); if (numKeys <= 1) { return; } Object * obj = node->EvalWorldState(0).obj; BOOL isBone = obj && obj->ClassID() == Class_ID(BONE_CLASS_ID, 0) ? TRUE : FALSE; // anim keys header TSTR indent = GetIndent(indentLevel); fprintf(pStream,"%s\tSI_AnimationKey {\n", indent.data()); fprintf(pStream,"%s\t\t1;\n", indent.data()); // 1 means scale keys fprintf(pStream,"%s\t\t%d;\n", indent.data(), numKeys); int t, delta = GetTicksPerFrame(); Matrix3 matrix; AffineParts ap; for (int i = 0; i < numKeys; i++) { // get the key's time if (cont->ClassID() == Class_ID(TCBINTERP_SCALE_CLASS_ID, 0)) { ITCBRotKey key; ikc->GetKey(i, &key); t = key.time; } else if (cont->ClassID() == Class_ID(HYBRIDINTERP_SCALE_CLASS_ID, 0)) { IBezQuatKey key; ikc->GetKey(i, &key); t = key.time; } else if (cont->ClassID() == Class_ID(LININTERP_SCALE_CLASS_ID, 0)) { ILinRotKey key; ikc->GetKey(i, &key); t = key.time; } // sample the node's matrix matrix = node->GetNodeTM(t) * Inverse(node->GetParentTM(t)); if (!isBone) { matrix = matrix * topMatrix; } decomp_affine(matrix, &ap); fprintf(pStream, "%s\t\t%d; 3; %.6f, %.6f, %.6f;;%s\n", indent.data(), t / delta, ap.k.x, ap.k.z, ap.k.y, i == numKeys - 1 ? ";\n" : ","); } // anim keys close fprintf(pStream,"%s\t}\n\n", indent.data()); }
//BOOL TbsAnimObj::IsKnownController(Control* cont) //{ // ulong partA, partB; // // if (!cont) // return FALSE; // ////Listed below are the first ULONG of the 8 byte ID. ////The second ULONG is 0 for all built-in classes (unless noted otherwise). ////For example a Class_ID for a TriObject would read: ////Class_ID(TRIOBJ_CLASS_ID, 0); ////Note that only built-in classes should have the second ULONG equal to 0. ////All plug-in developers should use both ULONGs. // // // // ClassID는 두개의 ULONG변수로 구성되며 내장된 클래스들은 모두 두번째 // // 변수는 0의 값을 갖고 있다. // // 단, 개발자의 플로그인은 두번째 값을 갖게 된다. // partA = cont->ClassID().PartA(); // partB = cont->ClassID().PartB(); // // if (partB != 0x00) // return FALSE; // // switch (partA) { // case TCBINTERP_POSITION_CLASS_ID: // case TCBINTERP_ROTATION_CLASS_ID: // case TCBINTERP_SCALE_CLASS_ID: // case HYBRIDINTERP_POSITION_CLASS_ID: // case HYBRIDINTERP_ROTATION_CLASS_ID: // case HYBRIDINTERP_SCALE_CLASS_ID: // case LININTERP_POSITION_CLASS_ID: // case LININTERP_ROTATION_CLASS_ID: // case LININTERP_SCALE_CLASS_ID: // return TRUE; // } // // return FALSE; //} void TbsAnimObj::DumpFloatKeys(Control* cont, TMesh* pMesh) { if (!cont) return; int i; IKeyControl *ikc = NULL; ikc = GetKeyControlInterface(cont); TAnimTrack Anim; // TCB float if (ikc && cont->ClassID() == Class_ID(TCBINTERP_FLOAT_CLASS_ID, 0)) { for (i=0; i<ikc->GetNumKeys(); i++) { ITCBFloatKey key; ikc->GetKey(i, &key); Anim.iTick = key.time; Anim.vValue.x = key.val; pMesh->m_VisTrack.push_back( Anim ); } } // Bezier float else if (ikc && cont->ClassID() == Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID, 0)) { for (i=0; i<ikc->GetNumKeys(); i++) { IBezFloatKey key; ikc->GetKey(i, &key); Anim.iTick = key.time; Anim.vValue.x = key.val; pMesh->m_VisTrack.push_back( Anim ); } } else if (ikc && cont->ClassID() == Class_ID(LININTERP_FLOAT_CLASS_ID, 0)) { for (i=0; i<ikc->GetNumKeys(); i++) { ILinFloatKey key; ikc->GetKey(i, &key); Anim.iTick = key.time; Anim.vValue.x = key.val; pMesh->m_VisTrack.push_back( Anim ); } } else { TSTR name; cont->GetClassName(name); // If it is animated at all... if (cont->IsAnimated()) { // Get the range of the controller animation Interval range; // Get range of full animation Interval animRange = m_p3dsMax->GetAnimRange(); TimeValue t = cont->GetTimeRange(TIMERANGE_ALL).Start(); float value; // While we are inside the animation... while (animRange.InInterval(t)) { // Sample the controller range = FOREVER; cont->GetValue(t, &value, range); // Set time to start of controller validity interval t = range.Start(); Anim.iTick = t; Anim.vValue.x = value; pMesh->m_VisTrack.push_back( Anim ); if (range.End() > cont->GetTimeRange(TIMERANGE_ALL).End()) { break; } else { //t = (range.End()/GetTicksPerFrame()+GetKeyFrameStep()) * GetTicksPerFrame(); t = (range.End()/GetTicksPerFrame()) * GetTicksPerFrame(); } } } } }
void bgAnimMax::DumpFloatKeys(Control* pControl, bgMesh* pMesh) { if (!pControl) return; int i; bgAnimTrack Anim; IKeyControl *ikc = NULL; ikc = GetKeyControlInterface(pControl); if (ikc && pControl->ClassID() == Class_ID(TCBINTERP_FLOAT_CLASS_ID, 0)) { for (i = 0; i < ikc->GetNumKeys(); i++) { ITCBFloatKey key; ikc->GetKey(i, &key); Anim.iTick = key.time; Anim.vValue.x = key.val; pMesh->AlpTrack.push_back(Anim); } } else if (ikc && pControl->ClassID() == Class_ID(HYBRIDINTERP_FLOAT_CLASS_ID, 0)) { for (i = 0; i < ikc->GetNumKeys(); i++) { IBezFloatKey key; ikc->GetKey(i, &key); Anim.iTick = key.time; Anim.vValue.x = key.val; pMesh->AlpTrack.push_back(Anim); } } else if (ikc && pControl->ClassID() == Class_ID(LININTERP_FLOAT_CLASS_ID, 0)) { for (i = 0; i < ikc->GetNumKeys(); i++) { ILinFloatKey key; ikc->GetKey(i, &key); Anim.iTick = key.time; Anim.vValue.x = key.val; pMesh->AlpTrack.push_back(Anim); } } else { TSTR name; pControl->GetClassName(name); if (pControl->IsAnimated()) { float value; Interval range; Interval animRange = m_p3DMax->GetAnimRange(); TimeValue t = pControl->GetTimeRange(TIMERANGE_ALL).Start(); while (animRange.InInterval(t)) { range = FOREVER; pControl->GetValue(t, &value, range); t = range.Start(); Anim.iTick = t; Anim.vValue.x = value; pMesh->AlpTrack.push_back(Anim); if (range.End() > pControl->GetTimeRange(TIMERANGE_ALL).End()) { break; } else { t = (range.End() / GetTicksPerFrame()) * GetTicksPerFrame(); } } } } }
void RandKeysUtil::Apply() { BOOL timeMode = iu->GetMajorMode()==TVMODE_EDITTIME; BOOL fcurveMode = iu->GetMajorMode()==TVMODE_EDITFCURVE; Interval iv = iu->GetTimeSelection(); if (!doTime && !doVal) return; theHold.Begin(); // Turn animation on SuspendAnimate(); AnimateOn(); for (int i=0; i<iu->GetNumTracks(); i++) { if ((timeMode||fcurveMode) && !iu->IsSelected(i)) continue; // Get Interfaces Animatable *anim = iu->GetAnim(i); Animatable *client = iu->GetClient(i); int subNum = iu->GetSubNum(i); Control *cont = GetControlInterface(anim); IKeyControl *ikc = GetKeyControlInterface(anim); IKey *key = GetKeyPointer(anim->SuperClassID(),anim->ClassID()); if (!ikc || !cont || !key) continue; if (fcurveMode && !anim->IsCurveSelected()) continue; // Get the param dim float min = negVal, max = posVal; ParamDimension *dim = client->GetParamDimension(subNum); if (dim) { min = dim->UnConvert(min); max = dim->UnConvert(max); } for (int j=0; j<ikc->GetNumKeys(); j++) { // Get the key data ikc->GetKey(j,key); // Check if it's selected if (timeMode && !iv.InInterval(key->time)) continue; if (!timeMode && !(key->flags&IKEY_SELECTED)) continue; // Randomize time if (doTime) { key->time = (int)CompRand( float(key->time-negTime), float(key->time+posTime)); ikc->SetKey(j,key); } } if (doTime) ikc->SortKeys(); for (j=0; j<ikc->GetNumKeys(); j++) { // Get the key data ikc->GetKey(j,key); // Check if it's selected if (timeMode && !iv.InInterval(key->time)) continue; if (!timeMode && !(key->flags&IKEY_SELECTED)) continue; // Randomize value if (doVal) { Point3 pt, ang; Point4 p4; float f; Quat q; ScaleValue s; BOOL doX, doY, doZ, doW; doX = doY = doZ = doW = TRUE; if (!fcurveMode) { if (!(key->flags&IKEY_XSEL)) doX = FALSE; if (!(key->flags&IKEY_YSEL)) doY = FALSE; if (!(key->flags&IKEY_ZSEL)) doZ = FALSE; if (!(key->flags&IKEY_WSEL)) doW = FALSE; } switch (anim->SuperClassID()) { case CTRL_FLOAT_CLASS_ID: cont->GetValue(key->time,&f,FOREVER); f = CompRand(f-min,f+max); cont->SetValue(key->time,&f); break; case CTRL_POSITION_CLASS_ID: case CTRL_POINT3_CLASS_ID: cont->GetValue(key->time,&pt,FOREVER); if (doX) pt.x = CompRand(pt.x-min,pt.x+max); if (doY) pt.y = CompRand(pt.y-min,pt.y+max); if (doZ) pt.z = CompRand(pt.z-min,pt.z+max); cont->SetValue(key->time,&pt); break; case CTRL_POINT4_CLASS_ID: cont->GetValue(key->time,&p4,FOREVER); if (doX) p4.x = CompRand(p4.x-min,p4.x+max); if (doY) p4.y = CompRand(p4.y-min,p4.y+max); if (doZ) p4.z = CompRand(p4.z-min,p4.z+max); if (doW) p4.w = CompRand(p4.w-min,p4.w+max); cont->SetValue(key->time,&p4); break; case CTRL_ROTATION_CLASS_ID: cont->GetValue(key->time,&q,FOREVER); QuatToEuler(q, ang); ang.x = CompRand(ang.x-min,ang.x+max); ang.y = CompRand(ang.y-min,ang.y+max); ang.z = CompRand(ang.z-min,ang.z+max); EulerToQuat(ang,q); cont->SetValue(key->time,&q); break; case CTRL_SCALE_CLASS_ID: cont->GetValue(key->time,&s,FOREVER); if (doX) s.s.x = CompRand(s.s.x-min,s.s.x+max); if (doY) s.s.y = CompRand(s.s.y-min,s.s.y+max); if (doZ) s.s.z = CompRand(s.s.z-min,s.s.z+max); cont->SetValue(key->time,&s); break; } } } } ResumeAnimate(); theHold.Accept(GetString(IDS_RB_RANDOMIZEKEYS)); ip->RedrawViews(ip->GetTime()); }