lucky6969b 1330 Report post Posted June 11, 2009 Would handedness affect quaternions, vectors and matrices? I am now porting an openGL column major based application to directx 9? Having some difficulties with that...For example x = cross(z,up); y = cross(z,x); Vec cross ( const Vec& v1, const Vec& v2 ) { return Vec ( v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x ); } What I can interpret is the line x = cross(z,up) must be written as x = cross(up,z) for dx. Does it sound right? The whole bunch of source code is.... would you please help me correct the ones that are wrong? I gave up after looking at it for 2 nights. Thanks a lot # include <math.h> # include "math.h" const float srpi = 3.14159265f; const float srtiny = 1.0E-6f; # define SRNEXTZ(a,eps) ( (a)>-(eps) && (a)<(eps) ) float Vec::len () const { float f = x*x + y*y + z*z; if ( f>0 ) f = sqrtf ( f ); return f; } float Vec::len ( float n ) { float f = sqrtf (x*x + y*y + z*z); if ( f>0 ) { n/=f; x*=n; y*=n; z*=n; } return f; } Vec cross ( const Vec& v1, const Vec& v2 ) { return Vec ( v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x ); } void Quat::rot ( const Vec& axis, float radians ) { float f; // normalize axis: x=axis.x; y=axis.y; z=axis.z; f = x*x + y*y + z*z; if ( f>0 ) { f = sqrtf ( f ); x/=f; y/=f; z/=f; } // set the quaternion: radians/=2; f = sinf ( radians ); x*=f; y*=f; z*=f; w = cosf ( radians ); } // above ok jacky Quat operator * ( const Quat &q1, const Quat &q2 ) { Quat q; q.w = (q1.w*q2.w) - (q1.x*q2.x + q1.y*q2.y + q1.z*q2.z); // w1*w2-dot(v1,v2) q.x = q1.y*q2.z - q1.z*q2.y; // cross (q1.v,q2.v) q.y = q1.z*q2.x - q1.x*q2.z; q.z = q1.x*q2.y - q1.y*q2.x; q.x += (q1.x*q2.w) + (q2.x*q1.w); q.y += (q1.y*q2.w) + (q2.y*q1.w); q.z += (q1.z*q2.w) + (q2.z*q1.w); return q; } void quat_to_ts ( const Quat& q, float& tw, float& sx, float& sy ) { // First test if the swing is in the singularity: if ( SRNEXTZ(q.z,srtiny) && SRNEXTZ(q.w,srtiny) ) { sx=sy=srpi; tw=0; return; } // Decompose q into twist-swing // by solving the equation Qtwist(t*2) * Qswing(s*2) = q // note: (x,y) is the normalized swing axis (x*x+y*y=1) // ( Ct 0 0 St ) * ( Cs xSs ySs 0 ) = ( qw qx qy qz ) // ( CtCs xSsCt-yStSs xStSs+ySsCt StCs ) = ( qw qx qy qz ) (1) // From (1) CtCs / StCs = qw/qz => Ct/St = qw/qz => tan(t) = qz/qw (2) // The swing rotation/2 s comes from: // From (1) (CtCs)^2 + (StCs)^2 = qw^2 + qz^2 => Cs = sqrt ( qw^2 + qz^2 ) (3) // From (1) (xSsCt-yStSs)^2 + (xStSs+ySsCt)^2 = qx^2 + qy^2 => Ss = sqrt ( qx^2 + qy^2 ) (4) // From (1) : |SsCt -StSs| |x| = |qx| // |StSs +SsCt| |y| |qy| (5) double qw, qx, qy, qz; if ( q.w<0 ) { qw=-q.w; qx=-q.x; qy=-q.y; qz=-q.z; } else { qw=q.w; qx=q.x; qy=q.y; qz=q.z; } double t = atan2 ( qz, qw ); // from (2) double s = atan2( sqrt(qx*qx+qy*qy), sqrt(qz*qz+qw*qw) ); // from (3) and (4) double x=0, y=0, sins=sin(s); if ( !SRNEXTZ(sins,srtiny) ) { double sint = sin(t); double cost = cos(t); // by solving the linear system in (5): y = (-qx*sint + qy*cost)/sins; x = ( qx*cost + qy*sint)/sins; } tw = float(2.0*t); sx = float(x*2.0*s); sy = float(y*2.0*s); } void quat_to_st ( const Quat& q, float& sx, float& sy, float& tw ) { // Decompose q into swing-twist using a similar development as in // function quat_to_ts if ( SRNEXTZ(q.z,srtiny) && SRNEXTZ(q.w,srtiny) ) { sx=sy=srpi; tw=0; } double qw, qx, qy, qz; if ( q.w<0 ) { qw=-q.w; qx=-q.x; qy=-q.y; qz=-q.z; } else { qw=q.w; qx=q.x; qy=q.y; qz=q.z; } // Get the twist t: double t = 2.0 * atan2(qz,qw); double bet = atan2( sqrt(qx*qx+qy*qy), sqrt(qz*qz+qw*qw) ); double gam = t/2.0; double sinc = SRNEXTZ(bet,srtiny)? 1.0 : sin(bet)/bet; double singam = sin(gam); double cosgam = cos(gam); sx = float( (2.0/sinc) * (cosgam*qx - singam*qy) ); sy = float( (2.0/sinc) * (singam*qx + cosgam*qy) ); tw = float( t ); } Vec operator * ( const Vec &v, const Quat &q ) { Quat qv ( 0, v.x, v.y, v.z ); qv = q * qv * q.inverse(); return Vec ( qv.x, qv.y, qv.z ); } void Mat::set ( const Quat& q, const Vec& v ) { // convert quaternion to rotation matrix: float x2 = q.x+q.x; float x2x = x2*q.x; float x2y = x2*q.y; float x2z = x2*q.z; float x2w = x2*q.w; float y2 = q.y+q.y; float y2y = y2*q.y; float y2z = y2*q.z; float y2w = y2*q.w; float z2 = q.z+q.z; float z2z = z2*q.z; float z2w = z2*q.w; e[0] = 1.0f - y2y - z2z; e[1] = x2y + z2w; e[2] = x2z - y2w; e[3]=0; e[4] = x2y - z2w; e[5] = 1.0f - x2x - z2z; e[6] = y2z + x2w; e[7]=0; e[8] = x2z + y2w; e[9] = y2z - x2w; e[10] = 1.0f - x2x - y2y; e[11]=0; // now update translation: e[12] = v.x; e[13] = v.y; e[14] = v.z; e[15] = 1.0f; } void Mat::lookat ( const Vec& eye, const Vec& center, const Vec& up ) { Vec z = eye-center; z.normalize(); Vec x = cross ( up, z ); // Jacky for left-handed system, Vec x = cross(z,up); Vec y = cross ( z, x ); // Jacky this is correct for both systems x.normalize(); y.normalize(); e[0]=x.x; e[1]=y.x; e[2]=z.x; e[3]=0; // jacky need transpose? it's a column vector where dx is row based e[4]=x.y; e[5]=y.y; e[6]=z.y; e[7]=0; e[8]=x.z; e[9]=y.z; e[10]=z.z; e[11]=0; e[15]=1.0f; Vec v = eye*-1.0f; // left combine translation: e[12] = v.x*e[0] + v.y*e[4] + v.z*e[8]; e[13] = v.x*e[1] + v.y*e[5] + v.z*e[9]; e[14] = v.x*e[2] + v.y*e[6] + v.z*e[10]; } # define E11 e[0] # define E12 e[1] # define E13 e[2] # define E14 e[3] # define E21 e[4] # define E22 e[5] # define E23 e[6] # define E24 e[7] # define E31 e[8] # define E32 e[9] # define E33 e[10] # define E34 e[11] # define E41 e[12] # define E42 e[13] # define E43 e[14] # define E44 e[15] void Mat::inverse ( Mat &inv ) const { float m12 = E21*E32 - E22*E31; float m13 = E21*E33 - E23*E31; float m14 = E21*E34 - E24*E31; float m23 = E22*E33 - E23*E32; float m24 = E22*E34 - E24*E32; float m34 = E23*E34 - E24*E33; float d1 = E12*m34 - E13*m24 + E14*m23; float d2 = E11*m34 - E13*m14 + E14*m13; float d3 = E11*m24 - E12*m14 + E14*m12; float d4 = E11*m23 - E12*m13 + E13*m12; float d = -E41*d1 + E42*d2 - E43*d3 + E44*d4; if (d==0.0) return; d = 1.0f/d; inv.E11 = (E42*m34 - E43*m24 + E44*m23) * d; inv.E21 = (E43*m14 - E41*m34 - E44*m13) * d; inv.E31 = (E41*m24 - E42*m14 + E44*m12) * d; inv.E41 = (E42*m13 - E41*m23 - E43*m12) * d; inv.E14 = (E13*m24 - E12*m34 - E14*m23) * d; inv.E24 = (E11*m34 - E13*m14 + E14*m13) * d; inv.E34 = (E12*m14 - E11*m24 - E14*m12) * d; inv.E44 = (E11*m23 - E12*m13 + E13*m12) * d; m12 = E11*E42 - E12*E41; m13 = E11*E43 - E13*E41; m14 = E11*E44 - E14*E41; m23 = E12*E43 - E13*E42; m24 = E12*E44 - E14*E42; m34 = E13*E44 - E14*E43; inv.E12 = (E32*m34 - E33*m24 + E34*m23) * d; inv.E22 = (E33*m14 - E31*m34 - E34*m13) * d; inv.E32 = (E31*m24 - E32*m14 + E34*m12) * d; inv.E42 = (E32*m13 - E31*m23 - E33*m12) * d; inv.E13 = (E23*m24 - E22*m34 - E24*m23) * d; inv.E23 = (E21*m34 - E23*m14 + E24*m13) * d; inv.E33 = (E22*m14 - E21*m24 - E24*m12) * d; inv.E43 = (E21*m23 - E22*m13 + E23*m12) * d; } Vec operator * ( const Vec &v, const Mat &m ) { Vec r ( m.E11*v.x + m.E21*v.y + m.E31*v.z + m.E41, m.E12*v.x + m.E22*v.y + m.E32*v.z + m.E42, m.E13*v.x + m.E23*v.y + m.E33*v.z + m.E43 ); float w = m.E14*v.x + m.E24*v.y + m.E34*v.z + m.E44; if ( w!=0.0 && w!=1.0 ) { r.x/=w; r.y/=w; r.z/=w; } return r; } Vec operator * ( const Mat &m, const Vec &v ) { Vec r ( m.E11*v.x + m.E12*v.y + m.E13*v.z + m.E14, m.E21*v.x + m.E22*v.y + m.E23*v.z + m.E24, m.E31*v.x + m.E32*v.y + m.E33*v.z + m.E34 ); float w = m.E41*v.x + m.E42*v.y + m.E43*v.z + m.E44; if ( w!=0.0 && w!=1.0 ) { r.x/=w; r.y/=w; r.z/=w; } return r; } Thanks Jack [Edited by - lucky6969b on June 11, 2009 3:37:58 AM] 0 Share this post Link to post Share on other sites
lucky6969b 1330 Report post Posted June 11, 2009 I am having difficulties understanding the inverse function done by the author.Can I assume that he was using pivoting for a 4x4 matrix?I am cross-checking it with a reference, but not too sure how he did that.ThanksJack 0 Share this post Link to post Share on other sites
RandomBystander 122 Report post Posted June 11, 2009 Quote:Original post by lucky6969bI am having difficulties understanding the inverse function done by the author.Can I assume that he was using pivoting for a 4x4 matrix?I am cross-checking it with a reference, but not too sure how he did that.ThanksJackTry to look up Cramer's_rule, that's what he seems to be doing. There is probably no pivoting going on, only computation of several determinants. 0 Share this post Link to post Share on other sites
lucky6969b 1330 Report post Posted June 11, 2009 Quote:Original post by RandomBystanderQuote:Original post by lucky6969bI am having difficulties understanding the inverse function done by the author.Can I assume that he was using pivoting for a 4x4 matrix?I am cross-checking it with a reference, but not too sure how he did that.ThanksJackTry to look up Cramer's_rule, that's what he seems to be doing. There is probably no pivoting going on, only computation of several determinants.Thanks for your reply.I found this on the webhttp://en.wikipedia.org/wiki/Cramer%27s_rule#Finding_inverse_matrixHowever, still not understand it... can you explain it with more details?Like this very first section, what does he want to achieve?float m12 = E21*E32 - E22*E31; float m13 = E21*E33 - E23*E31; float m14 = E21*E34 - E24*E31; float m23 = E22*E33 - E23*E32; float m24 = E22*E34 - E24*E32; float m34 = E23*E34 - E24*E33;ThanksJack 0 Share this post Link to post Share on other sites
jyk 2094 Report post Posted June 11, 2009 Just a quick comment on the 'handedness' issue. Generally speaking, the (geometric) handedness of the coordinate system used should not affect your math library code (by which I mean code that constructs or manipulates vectors, matrices, quaternions, and so on). In other words (for example) a function that builds a rotation matrix or quaternion or computes the cross product of two vectors should be exactly the same regardless of the handedness of the coordinate system being used.Projection transforms and 'view' transforms (e.g. a 'look at' transform) are exceptions in that they're generally constructed differently for left- and right-handed systems. The differences actually have to do with which direction is considered 'forward' in view space, but are also an indirect result of the coordinate system handedness and the convention that the positive x axis should point to the right in view space.As for computing cross products, with a typical orthonormal basis the relationships between the vectors should be:x = yXzy = zXxz = xXyIf you're seeing the arguments flipped around depending on handedness, you might be looking at a 'look at' function (where the flipping is related to which direction is to be considered forward in view space).Even though most functions should be unaffected by handedness, you may find that the results of applying said functions change when switching handedness. For example, triangle windings may flip, models may be mirrored, and rotations may appear to go the 'wrong' way. In these cases though it's the input data that should be changed, not the functions that work with the data.Also, keep in mind that the DirectX/D3D and OpenGL APIs differ in a few other ways as well, most notably matrix layout (row major vs. column major), vector notation (row vs. column vectors) and the near plane distance for the canonical view volume (zero vs. negative one). The issues of matrix layout, vector notation, and coordinate system handedness are often confused with each other, but in fact they are three separate and unrelated issues (although they can interact with each other in ways that can be quite confusing). 1 Share this post Link to post Share on other sites
lucky6969b 1330 Report post Posted June 11, 2009 So the conclusion is I don't need to change anything..Is that right? :)ThanksJack 0 Share this post Link to post Share on other sites
jyk 2094 Report post Posted June 11, 2009 Quote:So the conclusion is I don't need to change anything..Is that right? :)No, not necessarily. As I mentioned earlier, you may have to make adjustments for the differing view space and projection transform conventions, and you may also have to make adjustments to your data so that the visual results are consistent between the two APIs.There also may be cases where you'll need to take matrix layout and/or vector notation conventions into account (note however that despite the different conventions used, D3D and OpenGL matrices are actually arranged in memory the same way in that the elements of the basis vectors are stored contiguously). 1 Share this post Link to post Share on other sites
lucky6969b 1330 Report post Posted June 12, 2009 Quote:Original post by jykQuote:So the conclusion is I don't need to change anything..Is that right? :)No, not necessarily. As I mentioned earlier, you may have to make adjustments for the differing view space and projection transform conventions, and you may also have to make adjustments to your data so that the visual results are consistent between the two APIs.There also may be cases where you'll need to take matrix layout and/or vector notation conventions into account (note however that despite the different conventions used, D3D and OpenGL matrices are actually arranged in memory the same way in that the elements of the basis vectors are stored contiguously).Thanks jyk,Actually, at first blush, I am most concerned about the code that I post would work or not... :) sorry for making you repeat again.You really gave me a lot of good info.... but would you mind checking the code for me? I just really need to know if the source code would work on Direct3D or not....ThanksJack 0 Share this post Link to post Share on other sites
jyk 2094 Report post Posted June 12, 2009 Quote:You really gave me a lot of good info.... but would you mind checking the code for me? I just really need to know if the source code would work on Direct3D or not....I'd certainly like to be able to help, but it would take me a long time to proof the code that you posted with any real accuracy. (I did spot a couple of things though - for example, it looks to me like you might indeed need to transpose the upper-left 3x3 portion of your 'look at' matrix, as the comment indicates.)IMO, the best way to confirm the correctness of this sort of code is to gain a solid understanding of the underlying algorithms so that you can proof the implementations yourself. (You can also use reliable references, such as books, articles, or source code that you know works correctly, as a sanity check.)Here are a couple of tips:1. Make your code as clean and well-formated as possible. Avoid unclear and unnecessarily terse variable names (for example, there's little reason to name a variable bet instead of beta), and don't cram lots of code onto one line (for example, don't put an 'if' statement and the block of code associated with it all on the same line). The code you posted isn't bad, by the way (I've seen a lot worse), but it could still use some cleaning up.2. Use 2-d indexing for matrix element access rather than 1-d indexing. Storage order is an implementation detail that should be kept out of the actual 'math' code. (That is, when you're writing a function to build a rotation matrix, you shouldn't be thinking about whether the matrix is row- or column-major, since that has nothing to do with the math.) Using 1-d indexing also makes the code harder to proofread (for yourself and for others) since you have to convert the indices in your head as you go, and because you have to know what the storage order is in order to know which elements are being accessed. (Actually, you're already doing this with your E** macros, but only in some places. Also, I'd recommend using an inline function rather than a macro.)(The above is all IMO, by the way.)If there are particular functions you're unsure of, perhaps you could clean them up and post them individually; that might make the task of proofing them seem a little less daunting :) 0 Share this post Link to post Share on other sites
Steve_Segreto 2080 Report post Posted June 27, 2009 Whoa! That's some ugly code.If you are porting that to DirectX 9.0 here is my recommendation:*DO NOT* port the math functions directly, instead use D3DX.*DO* port the data types from your system to DirectX's D3DX system.So don't use the Vec, Quat and Mat classes methods and operators. Whenever you encounter data of these types convert them to these types:D3DXVECTOR3, D3DXQUATERNION and D3DXMATRIXthen use the built-in D3DX math library functions (see the DirectX 9.0 SDK)D3DXVec3Cross(), D3DXVec3Dot(), D3DXMatrixMultiply(), D3DXMatrixInverse(), D3DXMatrixTranspose(), D3DXQuaternionMultiply(), D3DXMatrixLookAtLH(), etc, etc..Now to convert the original Vec, Quat and Mat data to DirectX formats, you will probably need the following (I'm assuming the native types were OpenGL right-handed):D3DXVECTOR3 VecToD3DXVECTOR3( Vec& v ){ return (D3DXVECTOR3( v.x, v.z, v.y ) );}D3DXQUATERNION QuatToD3DXQUATERNION( Quat &q ){ return (D3DXQUATERNION( q.x, q.z, q.y, q.w ) );}D3DXMATRIX MatToD3DXMATRIX( Mat &m ){ D3DXMATRIX outM; // // Right Vector // outM( 0, 0 ) = m.e[ 0 ]; outM( 0, 1 ) = m.e[ 2 ]; outM( 0, 2 ) = m.e[ 1 ]; outM( 0, 3 ) = m.e[ 3 ]; // // Up Vector // outM( 1, 0 ) = m.e[ 8 ]; outM( 1, 1 ) = m.e[ 10 ]; outM( 1, 2 ) = m.e[ 9 ]; outM( 1, 3 ) = m.e[ 11 ]; // // Look Vector // outM( 2, 0 ) = m.e[ 4 ]; outM( 2, 1 ) = m.e[ 6 ]; outM( 2, 2 ) = m.e[ 5 ]; outM( 2, 3 ) = m.e[ 7 ]; // // Position Vector // outM( 3, 0 ) = m.e[ 12 ]; outM( 3, 1 ) = m.e[ 14 ]; outM( 3, 2 ) = m.e[ 13 ]; outM( 3, 3 ) = m.e[ 15 ]; return (outM);}Here's some explanation:Let's say a left-handed DirectX 4x4 looks like this: { rx, ry, rz, 0 }{ ux, uy, uz, 0 }{ lx, ly, lz, 0 }{ px, py, pz, 1 } where the vector u is the up vector { 0, 1, 0 } and the vector l is the look vector that points into the plane of the screen and the vector r is the cross product of u x l, and of course p is the position vector in the world. Assuming openGL 4x4's use this same naming system, here's what I did with them to get them to display correctly under DirectX: { rx, rz, ry, 0 }{ lx, lz, ly, 0 }{ ux, uz, uy, 0 }{ px, pz, py, 1 } In openGL the y and z coordinates are transposed (i.e. for them z is up/down and y is into the monitor), which also means the up and look vectors are transposed in the 4x4. For quaternions I had to do the following: DirectX: { x, y, z, w } or { ._41, ._42, ._43, -w } if taken from a 4x4 converted from openGL to directX and then converted to a quaternion using D3DXQuaternionRotationMatrix(). openGL: { x, z, y, w } !! IMPORTANT !!For triangles, I had to change the winding order from {v1, v2, v3} to{v3, v2, v1}.For (u, v) coordinates I left them the same as I had them before.Hope this helps! 1 Share this post Link to post Share on other sites
jyk 2094 Report post Posted June 27, 2009 Quote:In openGL the y and z coordinates are transposed (i.e. for them z is up/down and y is into the monitor), which also means the up and look vectors are transposed in the 4x4.This isn't right - in OpenGL, y is up and -z is forward in view space, and in world and model space, the basis vectors can be oriented however you want (in terms of which is forward, which is up, and which points to the side).I may have misunderstood your post though... 0 Share this post Link to post Share on other sites
Steve_Segreto 2080 Report post Posted June 27, 2009 No misunderstanding, the original poster will likely have to just hack around with the matrix conversion until they get something that looks right. :)I had some 3-D resources that I assumed came from OpenGL's right-handed coordinate system and for those resources the z-axis was indeed up/down and y was into the monitor, so my somewhat hackish technique worked. Figured it might work for OP also, if not maybe he can try some other permutations of the basis vectors in the MatToD3DXMATRIX function. 0 Share this post Link to post Share on other sites
legends2k 473 Report post Posted March 11, 2013 Just a quick comment on the 'handedness' issue. Generally speaking, the (geometric) handedness of the coordinate system used should not affect your math library code (by which I mean code that constructs or manipulates vectors, matrices, quaternions, and so on). In other words (for example) a function that builds a rotation matrix or quaternion or computes the cross product of two vectors should be exactly the same regardless of the handedness of the coordinate system being used. Projection transforms and 'view' transforms (e.g. a 'look at' transform) are exceptions in that they're generally constructed differently for left- and right-handed systems. The differences actually have to do with which direction is considered 'forward' in view space, but are also an indirect result of the coordinate system handedness and the convention that the positive x axis should point to the right in view space. As for computing cross products, with a typical orthonormal basis the relationships between the vectors should be: x = yXz y = zXx z = xXy If you're seeing the arguments flipped around depending on handedness, you might be looking at a 'look at' function (where the flipping is related to which direction is to be considered forward in view space). Even though most functions should be unaffected by handedness, you may find that the results of applying said functions change when switching handedness. For example, triangle windings may flip, models may be mirrored, and rotations may appear to go the 'wrong' way. In these cases though it's the input data that should be changed, not the functions that work with the data. Also, keep in mind that the DirectX/D3D and OpenGL APIs differ in a few other ways as well, most notably matrix layout (row major vs. column major), vector notation (row vs. column vectors) and the near plane distance for the canonical view volume (zero vs. negative one). The issues of matrix layout, vector notation, and coordinate system handedness are often confused with each other, but in fact they are three separate and unrelated issues (although they can interact with each other in ways that can be quite confusing). This led me to thinking about coordinate system handedness and I see the point that numbers are numbers and the formula for cross or dot product are the same irrespective of the human who perceives it and hence we'd get the same results be it we consider data in any handedness. Then why do we see handedness conversions being talked about? i.e. converting some asset from one handed system to another doesn't seem to make sense; say a triangle data of v0 = (0, 0, 0), v1 = (0, 0, 1), v2 = (1, 0, 1) could be interpreted as a triangle irrespective of the system used and in both cases the base of the triangle would be away from the origin, hence why would this (or any asset/mesh) data be called LHS or RHS. Also in another post I noticed this you can test the "handedness" of a coordinate system given the basis {v1, v2, v3}. if (v1 x v2) . v3 > 0, then it''s right-handed. if (v1 x v2) . v3 < 0, then it''s left-handed but what beats me is, even here, I'm unable to differentiate the handedness, since in both systems, i x j = k and there by k.k will always be > 0 (k^2, to be exact), then how does the above hold true? It'd be great if you can explain this to me. Thanks for you time. 0 Share this post Link to post Share on other sites
Bacterius 13165 Report post Posted March 12, 2013 So I can't reply to thread that are 60 days old but somehow this 4 year old thread got bumped? OK, I guess... In any case, legends2k, this is an old thread. I suggest you create a new thread with your question (perhaps referencing this thread as a link). There's nothing wrong with your question, which is interesting, but thread resurrection is usually not looked upon favorably, and you will likely get more answers if you start a new topic rather than have people read through everything that's already here. 0 Share this post Link to post Share on other sites