One of the first things that I had to do when I started working with 3D was fix the XSkinExp.dle plugin for 3DStudio Max so that our artists could export objects from MAX in the X file format, so that I could load it into my fledgling 3D engine. Luckily, a few searches on Google helped me to fix the actual bugs.
This is a link from the google groups that details how to fix the bugs in XSkinExp.dle, which I only found recently but it summarizes the bits and pieces that I found, and references one of the better sites:
http://groups.google.com/groups?q=author:araslanov%40yahoo.com&hl=en&lr=&ie=UTF-8&oe=UTF-8&selm=94d701c2072f%2455633ea0%243aef2ecf%40TKMSFTNGXA09&rnum=1
However, none of the posts that I found posted a solution on how to convert from Max's right handed camera to DirectX's left handed camera. Discreet posted a solution to change the max camera, but I didn't want to actually change the max camera. The following link, which I also found only recently, gives a max script so that the artists can do the rotation.
http://groups.google.com/groups?q=xskinexp+rotate&hl=en&lr=&ie=UTF-8&oe=UTF-8&selm=OW5g0PDqBHA.2224%40tkmsftngp03&rnum=1
As tempting as it was to just give the maxscript to the artists, I felt that this wasn't the best solution. It's easier for the artists to work with the object without rotating it, and there might be other areas where having code to do the conversion would be convenient.
So I gave up searching for articles with xskinexp as one of the keywords, and searched for an algorithm to rotate around an axis. I was able to very quickly find a solution here:
http://astronomy.swin.edu.au/~pbourke/geometry/rotate/
I adapted the code to use D3DX, so that I could rotate the mesh as soon as it was loaded. I may try to incorporate it into the exporter next, but it was quicker for me to rotate it as a mesh in D3D than to figure out how to do it in the max code. Here is my code:
D3DXVECTOR3 RotatePointAboutLine(D3DXVECTOR3 vPointToRotate, FLOAT fTheta,D3DXVECTOR3 vAxisStartPoint,D3DXVECTOR3 vAxisEndPoint)
{
D3DXVECTOR3 u,q1,q2;
float d;
/* Step 1 */
q1.x = vPointToRotate.x - vAxisStartPoint.x;
q1.y = vPointToRotate.y - vAxisStartPoint.y;
q1.z = vPointToRotate.z - vAxisStartPoint.z;
u.x = vAxisEndPoint.x - vAxisStartPoint.x;
u.y = vAxisEndPoint.y - vAxisStartPoint.y;
u.z = vAxisEndPoint.z - vAxisStartPoint.z;
D3DXVec3Normalize(&u, &u);
d = sqrt(u.y*u.y + u.z*u.z);
/* Step 2 */
if (abs(d) > 0.000001) {
q2.x = q1.x;
q2.y = q1.y * u.z / d - q1.z * u.y / d;
q2.z = q1.y * u.y / d + q1.z * u.z / d;
} else {
q2 = q1;
}
/* Step 3 */
q1.x = q2.x * d - q2.z * u.x;
q1.y = q2.y;
q1.z = q2.x * u.x + q2.z * d;
/* Step 4 */
q2.x = q1.x * cos(fTheta) - q1.y * sin(fTheta);
q2.y = q1.x * sin(fTheta) + q1.y * cos(fTheta);
q2.z = q1.z;
/* Inverse of step 3 */
q1.x = q2.x * d + q2.z * u.x;
q1.y = q2.y;
q1.z = - q2.x * u.x + q2.z * d;
/* Inverse of step 2 */
if (abs(d) > 0.000001) {
q2.x = q1.x;
q2.y = q1.y * u.z / d + q1.z * u.y / d;
q2.z = - q1.y * u.y / d + q1.z * u.z / d;
} else {
q2 = q1;
}
/* Inverse of step 1 */
q1.x = q2.x + vAxisStartPoint.x;
q1.y = q2.y + vAxisStartPoint.y;
q1.z = q2.z + vAxisStartPoint.z;
return(q1);
}
HRESULT RotateMesh(ID3DXMesh *pMesh, FLOAT fAngle, D3DXVECTOR3 vAxis)
{
BYTE *ptr=NULL;
HRESULT hr;
VOID* pVB;
D3DXVECTOR3 *vPtr;
D3DXVECTOR3 vStart;
D3DXVECTOR3 vEnd;
FLOAT fRadius;
D3DXVECTOR3 v3Center;
DWORD numVerts;
DWORD fvf;
DWORD vertSize;
if (FAILED(hr=pMesh->LockVertexBuffer(0,&pVB)))
{
// return on failure
return hr;
}
numVerts = pMesh->GetNumVertices();
// get the FVF flags
fvf=pMesh->GetFVF();
// calculate vertex size
vertSize=D3DXGetFVFVertexSize(fvf);
D3DXComputeBoundingSphere((D3DXVECTOR3*)pVB,
numVerts,
pMesh->GetNumBytesPerVertex(),
&v3Center,
&fRadius);
// the Axis vector should be a direction vector that represents the axis
// to be rotated about. So to rotate around the x axis, vAxis = {1, 0, 0}
// multiply the axis by the radius.
vAxis *= fRadius;
// add the axis vector to the center for the start point of the line
vStart = v3Center + vAxis;
// subtract the axis vector to the center for the end point of the line
vEnd = v3Center - vAxis;
// now vStart and vEnd have the beginning and end points of the object's origin
ptr = (PBYTE) pVB;
// loop through the vertices
for (DWORD i=0;i
// get pointer to location
vPtr=(D3DXVECTOR3 *) ptr;
// now rotate each vertex in the mesh around the line
*vPtr = RotatePointAboutLine(*vPtr, fAngle, vStart, vEnd);
// increment pointer to next vertex
ptr += vertSize;
}
// unlock the vertex buffer
if (FAILED(hr=pMesh->UnlockVertexBuffer()))
{
// return on failure
return hr;
}
return S_OK;
}
So now we can rotate the mesh as soon as it's loaded into memory, which means that we don't have to do it every time we go to render the object.
Frankly, I'm surprised that no one came up with this sooner. Perhaps they did, but they certainly weren't helpful enough to post it. So I also posted my solution in google groups.