Add Vector3.TransformCoord that does the same as XMVector3TransformCoord by mapping the result back to w = 1. Documentation to be clear about its behaviour.
Working on this, I think I have found how to do it.
You are doing it wrong. To do a perspective division (going back to W = 1) you should do:
// TODO: Check special case when w == 0.0
var w = (position.X*matrix.M14) + (position.Y*matrix.M24) + (position.Z*matrix.M34) + matrix.M44;
var invW = 1f / w;
destinationArray[i] = new Vector3(
invW * ((position.X*matrix.M11) + (position.Y*matrix.M21) + (position.Z*matrix.M31) + matrix.M41),
invW * ((position.X*matrix.M12) + (position.Y*matrix.M22) + (position.Z*matrix.M32) + matrix.M42),
invW * ((position.X*matrix.M13) + (position.Y*matrix.M23) + (position.Z*matrix.M33) + matrix.M43));
P.S. Looks like MonoGame needs a function to perspective divide Vector4 to Vector3.
@AntonPetrov83 Thank you for the help.
Will take care of this shortly!
Unfortunetly I've been quite busy. Taking care of it now. Already implemented the solution you suggested, thank you for your help by the way!
It makes way more sense what you said! I've been reading about the subject, and I'm still in doubt about what happens when w==0.0. In that case I should just omit the multiplication by invW? Or is it another way, @AntonPetrov83, @KonajuGames ?
When w is 0 you should omit the division by w and should not add the translation of the matrix.
@Jjagg From what you said, I presume destinationArray would look like this then:
destinationArray[i] = sourceArray[i];
Oops, I was thinking about the w component of the vector that is passed in, but that's set to 1 implicitly for TransformCoord (and 0 for TransformNormal). If the computed w component after the matrix multiplication is 0, I'm not sure what you should do or what it would mean geometrically. I think the user would have passed some weird matrix and you can just ignore the w component when it happens.
@Jjagg That's my thought exactly. From what I read I assume that w=0 means clipping... So I guess I should ignore it and just do
destinationArray[i] = sourceArray[i];
@KonajuGames and @AntonPetrov83 , is this assumption correct?
if the resulting w = 0 then there is no way to project it back to 3D space. A value of 0 means it's no longer a point but it became a direction/normal. IMO, we don't need to do any checks (which introduce unnecessary compares and branching). Just divide the resulting vector by w (or as @AntonPetrov83 said, use the faster invW) and let the runtime throw exceptions or spit out NANs.
@nkast Thank you for your answer!
From learning I knew that "A value of 0 means it's no longer a point but it became a direction/normal". That's why this little detail was confusing me.
Thanks everyone for the help!
@KonajuGames, I've just commited an update to this. Hope it is right now.
@nkast You suggested the methods I should update too, on the pull request.
None of them are the Transform methods that receive a Quaternion. Is it supposed to work with Quaternions too?
@nkast I did this: Matrix matrix = Matrix.CreateFromQuaternion(rotation);
But while checking Matrix class source, I see that the elements calculated on the matrix, will make w be equal to 1 always.
So, with this I assume that it shouldn't work with Quaternions...
I commited the changes.
However, the PerspectiveDivision method seems to be useless now. For performance reason, wouldn't it make more sense to just delete that methos and go with what Anton suggested and use invW in every method?
Indeed, it is strange to have a method performing simple division.
The idea behind precalculating invW is to replace multiple divisions with multiple multiplications which is a common optimization technique.
Introducing MathHelper.PerspectiveDivision() is not optimal at all, you can use common division operator / instead. :)
@AntonPetrov83 Yeah, when I did it, a year ago, I didn't know much about optimization and was just starting programming more seriously. Now that I have more experience I look at it and it doesn't make sense. I will fix it then. Is there anything else on the pull request that you think needs improvement?
Most helpful comment
if the resulting w = 0 then there is no way to project it back to 3D space. A value of 0 means it's no longer a point but it became a direction/normal. IMO, we don't need to do any checks (which introduce unnecessary compares and branching). Just divide the resulting vector by w (or as @AntonPetrov83 said, use the faster invW) and let the runtime throw exceptions or spit out NANs.