0

I am in the middle of making a rope physics simulation in c++ (spring physics). I successfully implemented basic rope movement, which is made up from several "bones" (which are just some particles [masses] with position, weight, etc). When each particle's position is calculated, I assign them each to its corresponding bone of a skinned mesh armature. What I want is to calculate a particle's rotation so that it is rotated into its child particle (Circle is the head of the bone [the position of the particle], the black dot is the tail, which should connect to its child's head, and so on...): the problem visualized I looked up some threads and came to this: Finding quaternion representing the rotation from one vector to another and tried to implement the accepted answer to my project, so I've got this:

XMVECTOR q;
XMVECTOR a = XMVector3Cross(head,tail);
XMVECTOR lh=XMVector3Length(head),lt=XMVector3Length(tail),dot=XMVector3Dot(head,tail);

q.m128_f32[0] = a.m128_f32[0]; //assigning the x coordinate
q.m128_f32[1] = a.m128_f32[1]; //assigning the y coordinate
q.m128_f32[2] = a.m128_f32[2]; //assigning the z coordinate
q.m128_f32[3] = sqrt(pow(lh.m128_f32[0],2)*pow(lt.m128_f32[0],2)) + dot.m128_f32[0]; //assigning the w coordinate
return XMQuaternionNormalize(q);

Unfortunately, it has not worked for me for some reason, so tried another which also failed:

XMVECTOR Head = XMVector3Normalize( head );
XMVECTOR Tail = XMVector3Normalize( tail );

float angle = acos(XMVector3Dot(Head,Tail).m128_f32[0]); //acos(dot(Head,Tail))
XMVECTOR axis = XMVector3Normalize(XMVector3Cross(Head,Tail));

XMVECTOR q = XMQuaternionRotationAxis(axis,angle);

I would be very grateful if someone would post me an alternate solution, and sorry for the poor paint skills.

Community
  • 1
  • 1

1 Answers1

1

Don't use the members of XMVECTOR directly... the XMStore/XMLoad functions are there for that reason. The bottom solution should be as follows:

XMVECTOR Head = XMVectorNormalize(head);
XMVECTOR Tail = XMVectorNormalize(tail);

float angle = 0.0f;
XMStoreFloat(&angle,XMVector3AngleBetweenVectors(Head,Tail));
XMVECTOR axis = XMVectorCross(Head,Tail);

return XMQuaternionAxisAngle(axis,angle);

In this way you avoid the direct access to XMVECTOR, which may help to clear up the problems.

The first solution you had also should work, but again, should use the XMStore/XMLoad functions as opposed to direct access.

XMFLOAT3 a;
XMStoreFloat3(&a,XMVector3Cross(head,tail));
float lh2, lt2, dot;
XMStoreFloat(&lh2,XMVector3Dot(head,head)); //this calculates the length squared.
XMStoreFloat(&lt2,XMVector3Dot(tail,tail)); //same as above
XMStoreFloat(&dot,XMVector3Dot(head,tail));

XMFLOAT4 q;
q.x = a.x; //assigning the x coordinate
q.y = a.y; //assigning the y coordinate
q.z = a.z; //assigning the z coordinate
q.w = sqrt(lh2*lt2) + dot; //assigning the w coordinate
return XMQuaternionNormalize(XMLoadFloat4(&q));

UPDATE:

The above solutions will work if they are used carefully, but more often than not, they will produce results that end up centered around the origin. This is because of change of reference. To produce the desired behavior, you need to do the following (assuming points are represented by a struct/class Point):

XMMATRIX CalculateTransform(Point p)
{
    XMVECTOR pos=XMLoadFloat3(&p.GetPosition());
    //Get your new rotation quaternion using the methods above
    XMMATRIX r=XMMatrixRotationQuaternion(CalculateRotation(p.GetHead(),p.GetTail()));
    XMMATRIX t=XMMatrixTranslationFromVector(pos);
    if(p.HasParent())
        return CalculateTransform(p.GetParent())*r*t;
    else
        return r*t;
}

This will compose the movements of every prior bone in the hierarchy together to form the final transformation matrix of the point passed to the function.

The above solution will work for small models (<10-20 bones or so), but for much larger models, you will want a memoization algorithm in there as well to speed up the calculation, as the recursion as it stands now will calculate the transformation matrix for the root bone many times over.

Alex
  • 1,794
  • 9
  • 20
  • Thank you for your answer, I tried it out but the output is still not correct (the same as was, actually), the bones only rotate around randomly as it seems, but good to know, that the problem lies elsewhere, then. I also do not understand what problem could occur if I access XMVECTOR directly. :) – János Turánszki Aug 13 '13 at 16:14
  • 1
    The contents of the XMVECTOR members aren't arranged as one would expect, knowing the structure of the XMFLOAT4 structure. Also, the details of the XMVECTOR implementation can change from version to version and from platform to platform. Finally, direct access most often times incurs a performance hit. See [this link](http://msdn.microsoft.com/en-us/library/windows/desktop/ee420742(v=vs.85).aspx) for more details on that. – Alex Aug 13 '13 at 16:38
  • I just need to set rotation between two points, without attention to its parent, because after the simulation is complete, I have absolute world coordinates for every bone/particle/mass. And not to mention, even the first bone gets uncorrect rotation (somewhat rotated around the X axis while it is idle, and strangely, that is the case with every other bone). I guess the problem lies still elsewhere. – János Turánszki Aug 14 '13 at 09:43
  • 1
    So, if I am understanding you correctly, you want a rotation that maps from the current forward vector of the particle's direction to the new vector given by the difference in positions between the particle and its child next in line? – Alex Aug 14 '13 at 17:56
  • Yes, that is right. I do not fully understand those two rotation formulas mentioned earlier, I think it should go like this: If( head.x=tail.x and head.z=tail.z and head.y>=tail.y) then {identity quaternion for the rotation}, else{ there is rotation }. – János Turánszki Aug 14 '13 at 20:47
  • That check is only necessary if you use the function with the call to `XMQuaternionRotationAxis`, which requires that the cross product be nonzero, and even then, it simplifies to `if(!XMVectorIsZero(a)){ return XMQuaternionIdentity();} else {return q;}`. The more likely issue is the vectors themselves. In your inputs to the rotation calculation, you want head to be the difference between your current position and the child's new position (or between the parent's new position and the your current), and tail to be your old direction. If your rotations end up backwards, flip head and tail. – Alex Aug 15 '13 at 19:28
  • Sorry I left your answer unaccepted, It is correct, I just moved on and forgot about it. – János Turánszki Sep 02 '13 at 19:06