4
\$\begingroup\$

I have finished working through the book "Game Physics Engine Development 2nd Ed" by Millington, and have got it working, but I want to adapt it to work with DirectX.

I understand that D3D9+ has the option to use either left handed, or right handed convention, but I am unsure about how to return my matrices to be usable by D3D. The source code gives returning OpenGL column major matrices (the transpose of the working transform matrix shown below), but DirectX is row major.

For those unfamiliar for the organization of the matrices used in the book:

[r11 r12 r13 t1]
[r21 r22 r23 t2]
[r31 r32 r33 t3]
[ 0   0   0   1]

r## meaning the value of that element in the rotation matrix, and t# meaning the translation value.

So the question in short is: How do I convert the matrix above to be easily usable by D3D? All of the documentation that I have found simply states that D3D is row major, but not where to put what elements so that it is usable by D3D in terms of rotation, and translation elements.

\$\endgroup\$
1
  • \$\begingroup\$ Its much easier than it sounds, you can simply memcpy them (or if you need it faster, even cast them, once you made sure the alignment restrictions are the same for both) \$\endgroup\$ Mar 31, 2012 at 9:31

2 Answers 2

9
\$\begingroup\$

First, a point of clarification: row-major means something different than row vector. OpenGL uses column vectors, which abstractly means that you consider a vector as a 4x1 matrix, and transform a vector v by a matrix M with v' = M · v. DirectX uses row vectors, which means that you consider a vector as a 1x4 matrix, and transform a vector w by a matrix N with w' = w · N. Notice that the two vectors are simply the transpose of each other: if you have an OpenGL vector v, then the DirectX vector is vT. And thanks to the linear algebra rule (A · B)T = BT · AT, you can see that the DirectX transformed vector (v')T = (M · v)T = vT · MT. That is, the DirectX matrix is simply the transpose of the OpenGL matrix.

And unrelated: OpenGL expects column-major storage, which means that the in-memory representation of a matrix keeps columns contiguous. So your example matrix would be stored as {r11, r21, r31, 0, r12, r22, r32, 0, r13, r23, r33, 0, t1, t2, t3, 1}. DirectX expects row-major storage, which means that the in-memory representation of a matrix keeps rows contiguous. So your example matrix would be stored as {r11, r12, r13, t1, r21, r22, r23, t2, r31, r32, r33, t3, 0, 0, 0, 1}. (Incidentally, in C and C++ two-dimensional arrays use row-major storage.)

And the fun part: it's easy to conflate these because they have "similar" effects. That is, if you take a matrix that's intended to be used with column vectors, and store it in column-major format, you get exactly the same thing as the transpose of that matrix (i.e. a matrix that represents the same transformation, but on row vectors) stored in row-major format. In a sense, the two operations cancel each other out. So OpenGL matrices and DirectX matrices look the same in raw memory. (And, of course, both of these are different than right-handedness vs left-handedness).

To summarize: If you're working in DirectX and looking at references that present OpenGL matrices, just transpose them to get matrices that you can use with DirectX.

\$\endgroup\$
10
  • \$\begingroup\$ so then if I am storing the given matrix as a linear array then I can just give it as is to D3D, and it should work without any modification? with the understanding that the elements might need to be placed into the D3d array manually \$\endgroup\$
    – gardian06
    Mar 30, 2012 at 21:01
  • \$\begingroup\$ Yes — if you had that matrix stored column-major, which is not most people's first instinct. For example, D3DXMATRIX's constructor takes elements in row-major order. In general, do a mental transpose to take an OpenGL-style matrix and think of it as a DirectX-style matrix (this makes the D3D documentation work for you as well), and then put it in a matrix type like D3DXMATRIX that prevents you from having to think about row-major vs. column-major. \$\endgroup\$ Mar 30, 2012 at 21:04
  • 2
    \$\begingroup\$ @John Calcbeek, So OpenGL matrices and DirectX matrices look the same in raw memory. this is the most important sentence in your answer, and the reason why the last paragraph is wrong. You dont need to transpose them, just memcpy them, since as you already said, in memory they look the same. (or if you need it faster, even cast them, once you made sure the alignment restrictions are the same for both) \$\endgroup\$ Mar 31, 2012 at 9:26
  • 2
    \$\begingroup\$ @John they don't have to coexist in one application, they can however, it doesn't really matter. The question says the source returns an OpenGL matrix. If they are not in one app, just write the memory to a file as it is, without any transpose and read it back into the other app. The same as a memcpy, just via a file. No need to transpose whatsoever. In fact if you would transpose, the outcome would be wrong. \$\endgroup\$ Mar 31, 2012 at 18:09
  • 1
    \$\begingroup\$ I agree with @MaikSemder that the fact they're laid out exactly the same in memory is important to understanding how this all works. With this knowledge it's easier to see that column/row major are just different documentation/API styles and the end result is the same (plus the right/left handed difference but it's a different topic). \$\endgroup\$
    – Gilead
    Apr 1, 2012 at 14:16
2
\$\begingroup\$

D3D isn't row-major; you can quite happily use column-major matrixes with D3D code and they will work. So the answer to your question is: "do nothing, because you don't need to do anything".

D3D itself uses column-major matrixes internally. The D3DX utility library matrix functions will generate row-major matrixes for you, and work on the assumption that you use row-major matrixes, but you don't have to use this library with D3D. You can just as easily use a different library, or roll your own.

Likewise it's worth noting that you can also use row-major matrixes with OpenGL in the same way if you wish. (You can even use the D3DX library functions with OpenGL, which amply illustrates that this is really not that big a deal.)

The whole D3D vs OpenGL row-major vs column-major thing is completely blown out of proportion. All that a matrix is is an array of floats. Feed them to your pipeline, do the multiplications in the correct order, and nothing else matters.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ The most important thing is internal consistency, and knowing what format you're getting from an API and what format it expects. As long as you understand what's going on and aren't just cargo-culting (i.e. confusing row-major storage with row vectors), you'll do fine. \$\endgroup\$ Mar 31, 2012 at 2:43
  • \$\begingroup\$ Just because the representation in memory looks the same, does not mean they are not row-major and column-major. In fact they are, this is a case of 2 "errors" (or differences) canceling out each other. You have this effect often when using matrices, 2 "errors" negate each other, giving you the right result at the end. The last sentence of your first paragraph is the right answer though, do nothing, they are memory-wise the same, just memcpy them. \$\endgroup\$ Mar 31, 2012 at 9:35

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .