Matrix Projection Poll

Published December 17, 2009
Advertisement
Currently in SlimDX our Matrix class exposes a large set of methods to create projection matrices. This closely mirrors the methods provided by D3DX, which was the original choice behind the design. While moving everything to C# and away from D3DX, I have a chance now to do some refactoring, so I thought I'd try it out and see what came of it.

This is the set of methods that we have currently:
public static void OrthoLH(float width, float height, float znear, float zfar, out Matrix result){}public static Matrix OrthoLH(float width, float height, float znear, float zfar){}public static void OrthoRH(float width, float height, float znear, float zfar, out Matrix result){}public static Matrix OrthoRH(float width, float height, float znear, float zfar){}public static void OrthoOffCenterLH(float left, float right, float bottom, float top, float znear, float zfar, out Matrix result){}public static Matrix OrthoOffCenterLH(float left, float right, float bottom, float top, float znear, float zfar){}public static void OrthoOffCenterRH(float left, float right, float bottom, float top, float znear, float zfar, out Matrix result){}public static Matrix OrthoOffCenterRH(float left, float right, float bottom, float top, float znear, float zfar){}public static void PerspectiveLH(float width, float height, float znear, float zfar, out Matrix result){}public static Matrix PerspectiveLH(float width, float height, float znear, float zfar){}public static void PerspectiveRH(float width, float height, float znear, float zfar, out Matrix result){}public static Matrix PerspectiveRH(float width, float height, float znear, float zfar){}public static void PerspectiveFovLH(float fov, float aspect, float znear, float zfar, out Matrix result){}public static Matrix PerspectiveFovLH(float fov, float aspect, float znear, float zfar){}public static void PerspectiveFovRH(float fov, float aspect, float znear, float zfar, out Matrix result){}public static Matrix PerspectiveFovRH(float fov, float aspect, float znear, float zfar){}public static void PerspectiveOffCenterLH(float left, float right, float bottom, float top, float znear, float zfar, out Matrix result){}public static Matrix PerspectiveOffCenterLH(float left, float right, float bottom, float top, float znear, float zfar){}public static void PerspectiveOffCenterRH(float left, float right, float bottom, float top, float znear, float zfar, out Matrix result){}public static Matrix PerspectiveOffCenterRH(float left, float right, float bottom, float top, float znear, float zfar){}


As you can see, there are quite a few. We double each method to provide a ref version for those who want very high speeds and a normal version for people who don't care as much.

My new proposed version looks like this:
public static Matrix PerspectiveFov(Handedness handedness, float fov, float aspect, float znear, float zfar){    float yScale = (float)(1.0 / Math.Tan(fov / 2.0f));    float xScale = yScale / aspect;    float width = 2 * znear / xScale;    float height = 2 * znear / yScale;    return Projection(ProjectionType.Perspective, handedness, -width / 2.0f, width / 2.0f, -height / 2.0f, height / 2.0f, znear, zfar);}public static Matrix Perspective(Handedness handedness, float width, float height, float znear, float zfar){    return Projection(ProjectionType.Perspective, handedness, -width / 2.0f, width / 2.0f, -height / 2.0f, height / 2.0f, znear, zfar);}public static Matrix Orthographic(Handedness handedness, float width, float height, float znear, float zfar){    return Projection(ProjectionType.Orthographic, handedness, -width / 2.0f, width / 2.0f, -height / 2.0f, height / 2.0f, znear, zfar);}public static Matrix Projection(ProjectionType type, Handedness handedness, float left, float right, float bottom, float top, float znear, float zfar){    Matrix result = new Matrix();    result.M11 = 2.0f / (right - left);    result.M22 = 2.0f / (top - bottom);    result.M33 = 1.0f / (zfar - znear);    result.M43 = znear / (znear - zfar);    if (type == ProjectionType.Orthographic)    {        result.M41 = (left + right) / (left - right);        result.M42 = (top + bottom) / (bottom - top);        result.M44 = left;    }    else    {        result.M11 *= znear;        result.M22 *= znear;        result.M33 *= -zfar;        result.M43 *= zfar;        result.M31 = (left + right) / (left - right);        result.M32 = (top + bottom) / (bottom - top);        result.M34 = 1.0f;    }    if (handedness == Handedness.Right)    {        result.M31 *= -1.0f;        result.M32 *= -1.0f;        result.M33 *= -1.0f;        result.M34 *= -1.0f;    }    return result;}


Note that I haven't listed the ref overloads here. With ref overloads, the new method would have 8 total functions, whereas the old method has 20. Now, ignoring the actual creation of the matrix in there, since I haven't quite tested it yet, which design do you think is better? Here is my thought process so far:

First, the pros. The new method has drastically less functions to maintain, and everything boils down to the one actual method for implementation. Additionally, it better follows the design guidelines of .NET by not encoding functional information into the name and instead allowing users to specify it via enum. I find it to be a slightly cleaner method over all.

Now the cons. The new method is slightly less optimal due to the extra branches occurring. This is less bad than it seems, however, since creating projection matrices is never done more than several times over the course of a frame, and usually much less than that, so it's unlikely to make any sort of impact on performance. If this were determined to be a huge issue, I have a slightly more hackish version of Projection that avoids the branches by making tricky use of the enum values [grin] Also, the new version breaks all existing code using SlimDX, since we'd be relying on these slightly new names. As said before, we have a new "2.0" version coming that will encapsulate all of these breaking changes, but it could still be considered an issue.

Ultimately the pros and cons nearly balance each other, and I think it comes down to a style issue, so I'm trying to take a poll here. Which do you prefer? I've talked to several people already, and the responses have been all over the board. Some people like the new methods and say the style is cleaner, some people want the old ones back, and a few people have said that the new method is better but they still prefer the old one due to stylistic reasons. How about you? What's your opinion?
Previous Entry I'm all growed up
Next Entry C# Quiz
0 likes 4 comments

Comments

Drilian
I know I've already voted on IRC, but to get the ball rolling:

I vote for the newer one. I think it's a cleaner interface, and allows more flexibility for the app in terms of how it creates its projection matrices.
December 17, 2009 02:33 PM
MaulingMonkey
I'm not terribly wild about the idea, but I could see how this could solve problems. Not mine, but other people's. Given that this is basically a bike shed, I'd be fine with it going through. I've already spent more time mulling over the issue than it would take for me to fix my code.

A point to raise though: A couple tens of one-liners to forward to the new equivalent versions doesn't sound like a huge maintenance burden -- new and old can coexist, even if it isn't 'slim' per se.

I also suspect there will be people who prefer the old aliases enough to do those one-liners themselves if SlimDX doesn't, even if it is for petty reasons like succinctness in a few lines of code. This would mean you're shifting the maintenance burden onto other people, not necessarily eliminating it per se -- not that this is necessarily a bad thing.
December 17, 2009 05:04 PM
superpig
Quote:Original post by MaulingMonkey
A point to raise though: A couple tens of one-liners to forward to the new equivalent versions doesn't sound like a huge maintenance burden -- new and old can coexist, even if it isn't 'slim' per se.


What he said. The other advantage to adding the oneliners yourself is that you can mark them with the [ObsoleteAttribute] to help prepare people for moving to 2.0.
December 18, 2009 03:03 AM
Ibasa
Just wanting to put my vote in for the new interface as well, it looks much nicer. Also agreement with the others about leaving the current ones as Obsolete, you may as well.
January 21, 2010 05:12 AM
You must log in to join the conversation.
Don't have a GameDev.net account? Sign up!
Advertisement

Latest Entries

New Blog

2022 views

Progress Update

1550 views

Start of Project

1513 views

New Job

2220 views

The Downward Spiral

2879 views

Job Interviews

1480 views
Advertisement