Math lib changes
While working on the new graphics and scene subsystems I eventually came to the point where some math code was needed (managing the view, world and projections transforms and their combinations, and flattening matrix hierarchies into world space). My original plan was to create a low level functional math library which looks much like HLSL and uses SSE intrinsics for performance. I started this stuff and soon it became clear that it would be quite an undertaking to correctly implement and test all this. And then there would only be an SSE implementation, when there's still SSE2..4, 3DNow around, and completely different intrinsics on the Xbox360 and other platforms. Sure, the problem could be solved by throwing manpower at it. But that's never a good idea for solving programming problems. So I looked around for a more pragmatic solution and found it in the form of the D3DX math library. The D3DX math functions are very complete, specialized for games, and support all current (and presumably future) vector instructions sets under the hood, and the 360 math library basically offers the same feature set (although it's much more down to the metal).
There are 2 disadvantages:
There a few other aspects to consider:
Another basic change I wanted to do for some time was to differentiate between points and vectors. There is now a Math::point and a Math::vector class which both derive from the generic Math::float4 class. A point describes a position in 3d space and a vector describes a direction and length in 3d space, generalized to homogeneous 4d space (the w component of a point is always 1.0, the w component of a vector is always 0.0). By creating 2 different classes with the right operator overloading one can encode the computation rules for points and vectors right into the C++ code, so that the compiler throws an error when the rules are violated:
So the new math lib basically looks like this:
The following low level classes directly call D3DX functions:
* matrix44 (D3DXMatrix functions)
* float4 (D3DXVec4 functions)
* quaternion (D3DXQuaternion functions)
* plane (D3DXPlane functions)
All other classes (like bbox, sphere, line, etc...) are generic and use functionality provided by the above low level classes. There is also a new scalar type (which is just a typedef'ed float), which helps in porting to some platforms (for instance, on NintendoDS, all math code is fixed point, so a scalar would be typedef'ed from one of the fixed point types). I still have to write a complete set of test and benchmark classes for the math library, but for now I'm quite happy that a very big chunk of work has been reduced to about 2 days of implementation time.
There are 2 disadvantages:
- additional calling overhead since D3DX functions are not inlined
- not portable to other platforms except DirectX and Xbox360
There a few other aspects to consider:
- With C++ math code, performance shouldn't get into the way of convenience. For instance, a proper operator+() always costs some performance because a temporary object must be constructed (the return value). But it's much more convenient and readable to use C++ operator overloading in generic game code instead of using (for instance) intrinsics. The point is to pay special attention to inner loops and use lower level code there when it actually makes sense.
- There should only be very few places in Nebula where heavy math code is actually executed on the CPU (in Nebula2 these are: particle systems, animation code, computing shadow caster geometry for skinned characters). In Nebula3 these tasks are either offloaded to the GPU, or will be obsolete). In general, the CPU should NEVER touch geometry per-frame and per-vertex.
Another basic change I wanted to do for some time was to differentiate between points and vectors. There is now a Math::point and a Math::vector class which both derive from the generic Math::float4 class. A point describes a position in 3d space and a vector describes a direction and length in 3d space, generalized to homogeneous 4d space (the w component of a point is always 1.0, the w component of a vector is always 0.0). By creating 2 different classes with the right operator overloading one can encode the computation rules for points and vectors right into the C++ code, so that the compiler throws an error when the rules are violated:
- (point + point) is illegal
- (point * scalar) is illegal
- point = point + vector
- vector = point - point
- vector = vector + vector
- vector = vector - vector
- vector = vector * scalar
- etc...
So the new math lib basically looks like this:
The following low level classes directly call D3DX functions:
* matrix44 (D3DXMatrix functions)
* float4 (D3DXVec4 functions)
* quaternion (D3DXQuaternion functions)
* plane (D3DXPlane functions)
All other classes (like bbox, sphere, line, etc...) are generic and use functionality provided by the above low level classes. There is also a new scalar type (which is just a typedef'ed float), which helps in porting to some platforms (for instance, on NintendoDS, all math code is fixed point, so a scalar would be typedef'ed from one of the fixed point types). I still have to write a complete set of test and benchmark classes for the math library, but for now I'm quite happy that a very big chunk of work has been reduced to about 2 days of implementation time.