Sign in to follow this  
DragonGeo2

Y/Z Axis Swizzling

Recommended Posts

I need help coming up with a function or a matrix that I can use to apply to my mesh hierarchies that will convert them from the Z-up coordinate system that they are stored in on the disk into the Y-up coordinate system that my graphics and physics engine uses.

Part of the problem is that the meshes are pre-written and modification of them is not an option (so I can't simply go through all of the meshes in 3DS Max and flip them onto their side and then re-export and whatnot). The way the meshes work is that they are stored as a hierarchy of nodes, each node has its own 4x4 transformation (translate/rotate/scale) matrix.

This is a pretty standard way of doing things, but my problem is that those 4x4 matrices are stored in a Z-up coordinate system and I need some way to convert them to a Y-up coordinate system. I've tried two different methods but neither has succeeded in correcting the various artifacts that arise from the methods. What would happen is that most (90%) of the models in the world would draw correctly, but then the other 10% would have their submesh matrices horribly messed up so that different parts of the models would be hanging out at odd angles and be in the wrong place and whatnot.

The first "old" method was sort of more brute-force. I modified the recursive GetLocalTransform() in the mesh loader code to swizzle the rotation each time a matrix was retrieved from one of the submesh nodes. However, I couldn't do this same trick with translations (or perhaps I could but just didn't realize how) because what would happen is that nodes which had an odd number of parent nodes would work correctly but nodes with an even number of parent nodes would work incorrectly (because they would swizzle and then un-swizzle and then swizzle and then un-swizzle, etc.) So with this old method I also had to switch the Y and Z components of each vertex as I copied it into the vertex buffer.

I decided that this old method had too many artifacts (though as it turns out it has as many different artifacts as the new method does). So I undid my original swizzling modification inside of GetLocalTransform() and removed my manual swizzling as I put verts into the VB and added a new root node to every model that had a swizzle matrix:
1, 0, 0, 0,
0, 0, 1, 0,
0, 1, 0, 0,
0, 0, 0, 1


In any case, I desperately need help fixing the glitches that arise from either of these methods (I don't really care which gets fixed, so long as the models look right when I'm done!) Also, if you really want to, you can check out the entire code-base (requires VS2010) from the SVN here:
https://sourceforge.net/scm/?type=svn&group_id=325208

Pictures of the old method's results (the wireframe represents the collision mesh which is stored with a separate root transform):
Picture 1
Picture 2
Picture 3
Picture 4
Picture 5
Picture 6

Code from the Old Method:
Quote:
// Swizzle the components of the vertices before stuffing them in the VB:
const Niflib::Vector3& thisVec = verts[x];
thisVert.position.xPos = thisVec.x;
thisVert.position.zPos = thisVec.y;
thisVert.position.yPos = thisVec.z;

const Niflib::Vector3& thisNorm = normals[x];
thisVert.normal.xPos = thisNorm.x;
thisVert.normal.zPos = thisNorm.y;
thisVert.normal.yPos = thisNorm.z;
Quote:
// Copy the submesh matrix so we can modify it for swizzling (this code is the same between versions):
const Niflib::Matrix44& worldmat = currtri->GetWorldTransform();
memcpy( (void* const)&(newmesh.submatrix), (const void* const)&(worldmat), sizeof(worldmat) );

// Swizzle the submesh matrix translations:
const float temp = newmesh.submatrix.m[1][3];
newmesh.submatrix.m[1][3] = newmesh.submatrix.m[2][3];
newmesh.submatrix.m[2][3] = temp;
Quote:
Matrix44 NiAVObject::GetLocalTransform() const
{
Quaternion qrot = rotation.AsQuaternion();
const float temp = qrot.y;
qrot.y = qrot.z;
qrot.z = temp;
Vector3 trans_copy = translation;

return Matrix44(translation, /*rotation*/qrot.AsMatrix(), scale );
}
Pictures of the new method's results:
Picture 1
Picture 2
Picture 3
Picture 4
Picture 5
Picture 6

New Method:
Quote:
// DON'T swizzle the components of the vertices, just put them straight into the VB:
thisVert.position.xPos = thisVec.x;
thisVert.position.zPos = thisVec.z;
thisVert.position.yPos = thisVec.y;

const Niflib::Vector3& thisNorm = normals[x];
thisVert.normal.xPos = thisNorm.x;
thisVert.normal.zPos = thisNorm.z;
thisVert.normal.yPos = thisNorm.y;
Quote:
// Sets the swizzle matrix as the top-level matrix of a mesh so that all submatrices get transformed and then swizzled before being drawn:
static Niflib::NiNode newRootc;
newRootc.SetLocalTransform(Niflib::Matrix44(1, 0, 0, 0,
0, 0, 1, 0,
0, 1, 0, 0,
0, 0, 0, 1) );
newRootc.AddChild(rootNode);
newRootc.AddRef();
Quote:
// Copy the submesh matrix so we can modify it for swizzling (this code is the same between versions):
const Niflib::Matrix44& worldmat = currtri->GetWorldTransform();
memcpy( (void* const)&(newmesh.submatrix), (const void* const)&(worldmat), sizeof(worldmat) );

// Swizzle the submesh matrix translations (but in this new method, we're swizzling what looks like transposed matrix coordinates):
const float temp = newmesh.submatrix.m[3][1];
newmesh.submatrix.m[3][1] = newmesh.submatrix.m[3][2];
newmesh.submatrix.m[3][2] = temp;
Quote:
Matrix44 NiAVObject::GetLocalTransform() const
{
return Matrix44(translation, rotation, scale);
}

Share this post


Link to post
Share on other sites
Try this:

Matrix44 NiAVObject::GetLocalTransform() const
{
Matrix44 conv_matrix(
1, 0, 0, 0,
0, 0, 1, 0,
0, 1, 0, 0,
0, 0, 0, 1);
Matrix44 local_trans(translation, rotation, scale);

return conv_matrix * local_trans * conv_matrix
}

Share this post


Link to post
Share on other sites
Not 100% sure on how it works, while working on an FBX exporter I spend forever trying to figure it out and found a couple of examples online that used that to convert transforms and it ended up working.

Share this post


Link to post
Share on other sites
I had a similar issue when converting EA Mythic's Dark Age of Camelot models/keyframes. The root cause was different handedness between my rendering system and the stored keyframe quaternions.

Here for example is what I did with my Dark Age of Camelot models (this may or may not help you):


D3DXMATRIX NIF_To_D3DXMATRIX (Niflib::Matrix44 mn, float scale)
{
D3DXMATRIX m;
D3DXMatrixIdentity(&m);
//
// Transpose the NIF 4x4 to DirectX 4x4
// Protect against QNANs
//
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
if (_isnan(mn[i][j]))
{
return m;
}
}
}
m._11 = mn[0][0];
m._12 = mn[0][2];
m._13 = mn[0][1];
m._14 = mn[0][3];

m._21 = mn[2][0];
m._22 = mn[2][2];
m._23 = mn[2][1];
m._24 = mn[2][3];

m._31 = mn[1][0];
m._32 = mn[1][2];
m._33 = mn[1][1];
m._34 = mn[1][3];

m._41 = mn[3][0] * scale;
m._42 = mn[3][2] * scale;
m._43 = mn[3][1] * scale;
m._44 = mn[3][3];

return (m);
}



For quaternion rotations what I did was this (basically flipped z and y):

D3DXQUATERNION q(x, z, y, w);

So altogether what I ended up with was:

If an incoming transformation matrix was made out of:

[ dx, dy, dz, dw ]
[ ux, uy, uz, uw ]
[ rx, ry, rz, rw ]
[ px, py, pz, pw ]

Where { d, u, r } are the three basis vectors for the coordinate system of the model and { p } is the vector representing the object space translation,

then I changed it to:

[ dx, dz, dy, dw ]
[ rx, rz, ry, rw ]
[ ux, uz, uy, uw ]
[ px, pz, py, pw ]

which amounted to flipping the right and up vectors and swapping the z and y-axis.

[Edited by - Steve_Segreto on November 18, 2010 5:56:10 PM]

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this