Notifications
Clear all

[Closed] [SDK] Add keys to controllers

Hey SDK-experts,

we try to copy the worldtransforms from one object to another for a given animation range. With AnimateOn() and the INode::getNodeTM() and INode::SetNodeTM() it’s already working.
But for performance reasons we tried another approach and tried to work as near on the object as possible.

So now we tried to use IKeyControl and then add a key to the fresh controllers of our helpobject. The next step would be to copy the transform of our object to the key of the helpobject. So we need to add a new key to the controller. Thats our problem. :rolleyes:

Of course there is the AddNewKey()-method, but we weren’t able to use it right. Another try now was to use the AppendKey() method. But there we need to have a fresh IKey for this specific type of key and add the transform.

Heres a code snippet of our test (newPosCtrl, newRotCtrl and newScaleCtrl are fresh controllers for helpObj):

// bake object transform to the helper
		IKeyControl *posKeys = GetKeyControlInterface(newPosCtrl);
		IKeyControl *rotKeys = GetKeyControlInterface(newRotCtrl);
		IKeyControl *scaleKeys = GetKeyControlInterface(newScaleCtrl);
		IKey *newPosKey = new IKey(); //wrong or right way to initialize the subtype at first?
		IKey *newRotKey = new IKey();
		IKey *newScaleKey = new IKey();

		for(int i = bakeBegin; i <= bakeEnd; i++) {

			objTM = &obj->GetNodeTM(t); // pointer to a Matrix3

			newPosKey->time = t;
			newRotKey->time = t;
			newScaleKey->time = t;

			HERE is our problem
			
			posKeys->AppendKey(newPosKey);
			rotKeys->AppendKey(newRotKey);
			scaleKeys->AppendKey(newScaleKey);

			t += ticksPerFrame;
		}

As you can see, in the for-loop is some empty space between assigning the timevalue and appending the keys to the controllinterfaces.
If we understand it right, we need to cast the three IKeys to different types of keys based on the controllers class ID and then set the values (Point3 for the location for TCP for example and so on).

But how to do that? (proper way to create a ITCBPoint3Key, for example)
Or do you think the way with get- and setNodeTM() with the AnimateOn()/AnimateOff()-value is already the fastest way?

Regards & Thanks, Florian

7 Replies

you should have a look at

maxsdk\howto\import_export\asciimp\import.cpp

it uses (in edited shorthand)

posCont->SetNumKeys(numofkeys);

then

for (int i=0; i<numofkeys; i++) 
{
			if (type == kBezier) 
			{
				IBezPoint3Key posKey;
				posKey.time = p->t;
				posKey.val = p->val;
				posKey.intan = p->inTan;
				posKey.outtan = p->outTan;
				posKey.flags = p->flags;
				posCont->SetKey(i, &posKey);
			}

etc...

}

also

objTM = &obj->GetNodeTM(t); // pointer to a Matrix3

I think that will cause you problems.

1 Reply
(@floric)
Joined: 10 months ago

Posts: 0

Oh, didn’t notice this at first. What do you mean?

Thanks for your fast answer.

I already checked that example at work. I knew the setKey() and getNodeTM()-methods. Both were easy to understand and helpful. I wasn’t sure how to create the keys for the the SetKey()-method.

But I completely missunderstood the SetNumKeys(). This a quite odd approach to create a new instance of a key but for our use case it should work perfect as we know the exact number of needed keys at runtime.

So thanks for your answer. I’ll try it tomorrow.

Regards, Florian

you can only reference an actual object (lvalue) in c++ the function return is a rvalue until it’s assigned to an object.

matrix3 tm = obj->GetNodeTM(t);
matrix3* tmptr = &tm;

Ok, I changed it but didn’t have any problems related because of that as far as I know. Anyway, thanks for the hint.

And I already stuck at another problem. :banghead:
Getting and adding position and scale from the matrix to TCB-keys works perfect, but I don’t get the right conversion for the rotation.

I can get the the right quat for the rotation from the matrix. But it seems like I need to do more then just making a AngAxis() from the Quat with the constructor. With this method the object is spinning if its really rotating, but to fast and not the right way.

AngAxisFromQ() and the AngAxis()-constructor are doing the same, if I’m right.

Tomorrow I’ll dive again into Quats and the maths behind them. I feel like I missed the most important point.

So many problems…

My current wrong try:

...
Matrix3 objMat = obj->GetNodeTM(t);
const Matrix3 *objMatPtr = &objMat;

Quat *rotTest = new Quat(*objMatPtr);
AngAxis *rotVal= new AngAxis(*rotTest); // AngAxis *rotVal= new AngAxis(*objMatPtr); has the same wrong behavior

rotKey.val = *rotVal;
...

I know it doesn’t solve the issues you have but why are you creating pointers ? A better way to approach it would be…

Matrix3 objMat = obj->GetNodeTM(t);
   Quat rotTest(objMat);
   AngAxis rotVal(rotTest);
   rotKey.val = rotVal;

then you’ll have no memory clean up to do because objMat, rotTest and rotVal are created on the stack (not on the heap with new) and are released automatically when the called function returns and the stack pointer is reset.

edit: you pass by reference to the constructors fpr quat and angaxis so no need for pointers or addresses at all.

you could probably for go the quat step altogether.

rotKey.val = AngAxis(obj->GetNodeTM(t));

Yeah, I already noticed that I’m missing some basics about C++. Until now this “project” was more a dirty hack to convert a working maxscript into a plugin because of performance reasons and to learn the concepts of the SDK.

My last question was more a mathematical one. Speed and a good coding style and architecture are my next steps.

I solved it with a quite simple thing… Your code worked exactly in the same wrong way like mine. The problem was, that adding a keyframe to the rotation from a existing matrix with a given rotation doesn’t work as intended. Instead of an absolute value the change of the rotation is added. Thats why rotating objects spinned like crazy. So we just need to calculate the difference or divide the newValue by the the previous one and the actual quat and then apply that to the key of the controller.

In case someone else will come to that problem:

AngAxis rotVal;
Quat newQuat, prevQuat;

for-loop

newQuat = Quat(*objMatPtr);

if(i != bakeBegin) {
        rotVal = AngAxis(newQuat - prevQuat);
        OR should be mathematical the same: rotVal = AngAxis(newQuat / prevQuat);
} else {
        rotVal = AngAxis(newQuat);
}
prevQuat = newQuat;

end-for

So, Klunk, again thanks for all your help.