Reputation: 41
So I've been writing my own physics engine off and on for a while now and I recently started setting up DirectX rendering instead of hardcoding in my rendering with OpenGL. Now, at the very end, I've come across what seems to be some very bizarre behavior that I cannot figure out.
In order to pass Matrices to a shader to draw things, you call a function that takes a const float*
and the typical way to do this is to pass in a D3DXMATRIX
cast to float*
. D3DXMATRIX
and the cast operators are declared as:
typedef struct D3DXMATRIX : public D3DMATRIX
{
...
operator FLOAT* ();
operator CONST FLOAT* () const;
....
} D3DXMATRIX, *LPD3DMATRIX;
and D3DMATRIX
is declared as:
typedef struct _D3DMATRIX {
union {
struct {
float __11, _12, _13, _14;
float __21, _22, _23, _24;
float __31, _32, _33, _34;
float __41, _42, _43, _44;
};
float m[4][4];
};
} D3DMATRIX;
The two casting operators are defined in a different file (D3DX10math.inl) as:
D3DX10INLINE
D3DXMATRIX::operator FLOAT* ()
{
return (FLOAT *) &_11;
}
D3DX10INLINE
D3DXMATRIX::operator CONST FLOAT* () const
{
return (CONST FLOAT *) &_11;
}
The CONST
and FLOAT
keywords are defined in minwindef.h
and D3DX10INLINE
is defined in D3DX10.h as:
#ifndef CONST
#define CONST const
#endif
....
typedef float FLOAT;
....
#define D3DX10INLINE __forceline
The call to the function I described is simply:
D3DXMATRIX m;
foo->SetMatrix((float*)&m);
Throughout my engine I have been using my own Matrix
class, and when I attempt to pass it into the above function instead of a D3DXMATRIX
, it sends incorrect data. My Matrix
class is (seemingly) defined in the same way as D3DXMATRIX
, but refuses to function the same way. Here is my matrix class (or the relevant parts):
class Matrix {
public:
....
operator float* ();
operator const float* () const;
....
private:
union {
struct {
float _00, _01, _02, _03;
float _10, _11, _12, _13;
float _20, _21, _22, _23;
float _30, _31, _32, _33;
};
float m[4][4];
}
};
and the functions are define as:
__forceinline
Matrix::operator float* ()
{
return (float *) &_00;
}
__forceinline
Matrix::operator const float* () const
{
return (const float *) &_00;
}
I've tried defining the operators in the CPP file, in the header file, in the class definition, all with and without inline
and __forceinline
, but nothing will ever make it work. I suppose I could copy all the data in each of my matrices to a D3DXMATRIX
, but that wastes a bunch of time/operations and should be irrelevant. The biggest issue is that I cannot for the life of my figure out why everything behaves this way. I cannot possibly see why.
To be sure of the data being passed into the DirectX function, I set up a test function so I can debug it and see what the exact values being passed in:
void Test(const float *a, const float *b)
{
bool result[16] = {false};
float aa[16], bb[16];
for(unsigned int i = 0; i < 16; ++i)
{
result[i] = a[i] == b[i];
aa[i] = a[i];
bb[i] = b[i];
}
}
For example, if I pass in a D3DXMATRIX
with the values 1-16
for each of the 16 floats as the first parameter one of my matrices with the same values as b
then aa
will be populated with the values 1-16
just fine, but bb
will = {0.0, 1.875, 0.0, 2.0, 0.0, 2.125, 0.0, 2.5, 0.0, 2.315, 0.0, 2.375, 0.0, 2.4375, 0.0, 2.5}
So...anyone have any ideas why this code behaves this way?
Upvotes: 2
Views: 670
Reputation: 41321
You are actually using operator float*
incorrectly.
D3DXMATRIX m;
foo->SetMatrix((float*)&m);
Here you convert the address of m
to float*
. This isn't invoking operator float
, it's a mere reinterpret_cast
(and compiler should give a warning about that). But fortunately for you D3DMATRIX
doesn't contain any other members and no padding at the beginning so everything works.
If Matrix
contains other data members before its _00
or is not standard-layout such conversion may produce indeterminate results.
So the bottomline is, you should do
foo->SetMatrix((float*)m);
or just
foo->SetMatrix(m);
Upvotes: 1