Strange texture shifting bug when using PointClamp or PointWrap - c#

I am making a small RPG game and I'm currently on window rendering (things like inventory/quest windows).
I draw the actual window with 9 quads textured from a "skin", 4 corners, 4 borders and the middle.
The problem is, at arbitrary coordinates a triangle or two shifts slightly downwards.
It happens consistently, and only if PointClamp or PointWrap is used. Other opions work fine but they give a blurry look.
The same bug actually happens with the green bar above, however I "fixed" it by using Linear instead of Point.
I use this function to convert from pixel coordinates to screen coordinates:
Note: This function works as I originally expected in MonoGame.
public static Vector3 PixelToScreen(GraphicsDevice device, float X, float Y)
{
float xscale = (float)device.Viewport.Width / 2;
float yscale = (float)device.Viewport.Height / 2;
return new Vector3(((int)X / xscale) - 1f, 1f - ((int)Y / yscale), 0);
}
I suspect this function might be the source of my problem. Is there a "right way" to do it?
A picture is worth a thousand words, so here's a screenshot with the problem.
http://i.stack.imgur.com/rNsnH.png
I am quite sure the solution is trivial, but I just can't catch it.
UPDATE
After some more digging and researching, I finally found a solution. It turns out that
it has something to do with how texture pixels are mapped to the screen pixels.
UPDATE #2
After porting this code to MonoGame, I've noticed a different bug where everything looks "blurry". Curiously enough, removing the offset (reverting to the original function) fixes the problem!
The Fix
public static Vector3 PixelToScreen(GraphicsDevice device, float X, float Y)
{
X -= 0.5f; // Offset the "pixel value" by half a pixel
Y -= 0.5f; // To provide "expected results" use negative value
float xscale = (float)device.Viewport.Width / 2;
float yscale = (float)device.Viewport.Height / 2;
return new Vector3((X / xscale) - 1f, 1f - (Y / yscale), 0);
}
More on the topic:
Directly Mapping Texels to Pixels (Direct3D 9)
Understanding Half-Pixel and Half-Texel Offsets
This question is obviously resolved now, but I hope my findings will help someone stuck in the same situation.

The Fix
public static Vector3 PixelToScreen(GraphicsDevice device, float X, float Y)
{
X -= 0.5f; // Offset the "pixel value" by half a pixel
Y -= 0.5f; // To provide "expected results" use negative value
float xscale = (float)device.Viewport.Width / 2;
float yscale = (float)device.Viewport.Height / 2;
return new Vector3((X / xscale) - 1f, 1f - (Y / yscale), 0);
}
Do not use these offsets in MonoGame as it somehow takes care of the matters internally.

Related

Problems with buoyancy and multiple Gertsner-waves. Waves created using Shadergraph and equations recreated in code to try and simulate floatation

Quick summation:
I am attempting to create an ocean comprised of planes that can be easily loaded and unloaded based on distance. On this ocean I want a boat to sail with a player onboard in the first person, where I want them to experience the buoyancy of their boat relative to the surrounding waves.
I am new to shadergraph and have been following several tutorials to try and create the desired effect.
These tutorials include
Catlikecoding's Wave shader
https://catlikecoding.com/unity/tutorials/flow/waves/
Zicore's Gertsner wave
https://www.youtube.com/watch?v=Awd1hRpLSoI&ab_channel=Zicore
Tom Weiland's dynamic water physics
https://www.youtube.com/watch?v=eL_zHQEju8s&ab_channel=TomWeiland
These resources have gotten me a good chunk of the way there, but I've run into some issues regarding the boat physics specifically.
I understand the math behind simulating Gertsner waves, and have tried to set up a WaveManager that calculates the y-value of a "floater" at position (x,z).
Floater.cs
public Rigidbody rigidBody;
public float depthBeforeSubmerged = 1f;
public float displacementAmount = 3f;
public int floaterCount = 1;
public float waterDrag = 0.99f;
public float waterAngularDrag = 0.5f;
private void FixedUpdate()
{
rigidBody.AddForceAtPosition(Physics.gravity / floaterCount, transform.position, ForceMode.Acceleration);
float waveHeight = WaveManager.instance.GetWaveHeight(transform.position.x,transform.position.z);
if(transform.position.y < waveHeight)
{
float displacementMultiplier = Mathf.Clamp01((waveHeight-transform.position.y) / depthBeforeSubmerged) * displacementAmount;
rigidBody.AddForceAtPosition(new Vector3(0f, Mathf.Abs(Physics.gravity.y) * displacementMultiplier, 0f),transform.position, ForceMode.Acceleration);
rigidBody.AddForce(displacementMultiplier * -rigidBody.velocity * waterDrag * Time.fixedDeltaTime, ForceMode.VelocityChange);
rigidBody.AddTorque(displacementMultiplier * -rigidBody.angularVelocity * waterAngularDrag * Time.fixedDeltaTime, ForceMode.VelocityChange);
}
}
This is pretty much lifted directly from Tom Weiland's video. Basically, when my floatpoint dips below the calculated wave, it applies force to make it travel upwards. Following his instructions carefully yielded decent results, but the problem arose when I started using Shadergraph to create my ocean.
The main issue is I wanted the waves to be tileable across multiple planes, so I used the object position and transformed it to world position to do calculations, and then added it back to the object position before manipulating the vertices of the ocean plane.
I've tried to show it below here:
This makes the ocean plane tileable and looks great, but also enlarges it in the scene quite a bit. I've put a regular plane on top to show the difference. Both are 1x1 units in the inspector.
So this is the first problem. The calculations I do in my WaveManager aren't lining up properly with the actual visual representation of the waves.
The second problem is that I can't seem to make the calculations done in WaveManager give me the correct y-coordinates.
In the shader, the waves are animated using the Time-component.
I've found the documentation to be a bit sparse on Shadergraph components, probably because I'm self taught and have a hard time wrapping my head around some of these concepts.
I've had a hard time working out how to calculate the change in y-coordinates over time in the wavemanager-script. The different solutions I've tried have just made the y-coordinate slowly grow larger into the negative range. I just have no idea how to make my calculations match up with the ones done on the GPU.
It's no important that it be super accurate, just good enough to sell the effect with small waves.
The Wavemanager code, finally.
private void Start()
{
waveLengthA = waves.GetFloat("_WaveLengthA");
waveLengthB = waves.GetFloat("_WaveLengthB");
waveLengthC = waves.GetFloat("_WaveLengthC");
waveLengthD = waves.GetFloat("_WaveLengthD");
steepnessA = waves.GetFloat("_SteepnessA");
steepnessB = waves.GetFloat("_SteepnessB");
steepnessC = waves.GetFloat("_SteepnessC");
steepnessD = waves.GetFloat("_SteepnessD");
directionA = waves.GetVector("_DirectionA");
directionB = waves.GetVector("_DirectionB");
directionC = waves.GetVector("_DirectionC");
directionD = waves.GetVector("_DirectionD");
kA = (2 * Mathf.PI) / waveLengthA;
kB = (2 * Mathf.PI) / waveLengthB;
kC = (2 * Mathf.PI) / waveLengthC;
kD = (2 * Mathf.PI) / waveLengthD;
cA = Mathf.Sqrt(Mathf.Abs(Physics.gravity.y)/ kA);
cB = Mathf.Sqrt(Mathf.Abs(Physics.gravity.y) / kB);
cC = Mathf.Sqrt(Mathf.Abs(Physics.gravity.y) / kC);
cD = Mathf.Sqrt(Mathf.Abs(Physics.gravity.y) / kD);
}
private void Update()
{
offset += Time.deltaTime;
}
public float GetWaveHeight(float x,float z)
{
fA = kA*(directionA.x * x + directionA.y * z - cA * offset);
fB = kB * (directionB.x * x + directionB.y * z - cB * offset);
fC = kC * (directionC.x * x + directionC.y * z - cC * offset);
fD = kD * (directionD.x * x + directionD.y * z - cD * offset);
position += new Vector3(x + directionA.x * steepnessA / kA * Mathf.Cos(fA),steepnessA/kA*Mathf.Sin(fA),z+directionA.y*steepnessA/kA*Mathf.Cos(fA));
position += new Vector3(x + directionB.x * steepnessB / kB * Mathf.Cos(fB),steepnessB/kB*Mathf.Sin(fB),z+directionB.y*steepnessB/kB*Mathf.Cos(fB));
position += new Vector3(x + directionC.x * steepnessC / kC * Mathf.Cos(fC),steepnessC/kC*Mathf.Sin(fC),z+directionC.y*steepnessC/kC*Mathf.Cos(fC));
position += new Vector3(x + directionD.x * steepnessD / kD * Mathf.Cos(fD),steepnessC/kD*Mathf.Sin(fD),z+directionD.y*steepnessD/kD*Mathf.Cos(fD));
return position.y;
}
The code above is quite ugly with a lot of repetition, but my plan is to make a constructor at some point to make it easier to read.
I grab all the values used in my shader, to make sure they match even if I change the look of the waves. Then I do the calculations from Catlikecoding and plot in the x- and z-coordinates of my floating object.
As far as I can understand, it should work if I just combine alle the calculated vectors, but obviously I'm missing something.
From what I've seen others do, they often opt to create custom planes with more vertices, that can cover their entire gameworld and avoid the problem, but I'm making a larger world and am worried about performance. (Though I don't know if I should be even.) I really like the fact that my ocean planes are tileable.
Does anyone here know of any solutions or help me solve the issue of worldspace vs objectspace, or how to accurately recreate the time-progression as seen in the shader?
Any help would be much appreciated.
So, for anyone struggling with this, I found the answer.
When combining multiple waves together, the manipulated plane grows in size for every wave added.
In my above question, I had somehow messed up the formulas for calculating the waves. I redid them and got the correct result.
Now, the trick is to simply divide the resultant wave, by the number of waves that you are combining. This will make sure that the actual size of the plane won't change.
You of course need to do this in your waveManager code as well. It's important to keep in mind that you only need the y-coordinate, so you only have to calculate that. For each wave, calculate the y-coordinate and then divide the combined height by the number of waves. This will make the floatation code work as it should!
Hope this helps someone out there who struggled like me.

How to prevent floating point error in point-polygon sliding collision detection

I'm trying to write a function to handle movement within a game I'm programming. What I have nearly works, but there are a couple situations where it breaks down.
I've coded up a minimal demonstrative example, presented below. In this example, I'm trying to calculate the travel of an object, represented by a point, and movement vector. This object's movement path is checked against a collection of polygons, which are broken down into line segments for testing. When this object collides with a line segment, I want it to slide along that segment (rather than stop or bounce away).
To do this, I check along my intended path for collisions, and if I find an intersection, I do a new test from that intersection point along the path of the line segment I've collided with, with the magnitude of the remainder of movement.
The problem arises when we slide along a line segment into a "pocket". Often times, the collision check will pass on both of the line segments that form the pocket, and the object will slip through. Because I'm travelling parallel to one of the line segments, and I'm intersecting with both line segments at an end points, I believe this issue is caused by floating point error. Whether or not it slips through, is caught, or is caught once and then slips through on the second check seems to be totally random.
I'm calculating intersection using a simple algorithm I found here: https://stackoverflow.com/a/20679579/4208739, but I've tried many other algorithms as well. All exhibit the same problems.
(Vector2 is class provided by the Unity library, it just holds x and y coordinates as floats. The Vector2.Dot function just calculates the dot product).
//returns the final destination of the intended movement, given the starting position, intended direction of movement, and provided collection of line segments
//slideMax provides a hard cap on number of slides allowed before we give up
Vector2 Move(Vector2 pos, Vector2[] lineStarts, Vector2[] lineEnds, Vector2 moveDir, int slideMax)
{
int slideCount = 0;
while (moveDir != Vector2.zero && slideCount < slideMax)
{
pos = DynamicMove(pos, lineStarts, lineEnds, moveDir, out moveDir);
slideCount++;
}
return pos;
}
//returns what portion of the intended movement can be performed before collision, and the vector of "slide" that the object should follow, if there is a collision
Vector2 DynamicMove(Vector2 pos, Vector2[] lineStarts, Vector2[] lineEnds, Vector2 moveDir, out Vector2 slideDir)
{
slideDir = Vector2.zero;
float moveRemainder = 1f;
for (int i = 0; i < lineStarts.Length; i++)
{
Vector2 tSlide;
float rem = LineProj(pos, moveDir, lineStarts[i], lineEnds[i], out tSlide);
if (rem < moveRemainder)
{
moveRemainder = rem;
slideDir = tSlide;
}
}
return pos + moveDir * moveRemainder;
}
//Calculates point of collision between the intended movement and the passed in line segment, also calculate vector of slide, if applicable
float LineProj(Vector2 pos, Vector2 moveDir, Vector2 lineStart, Vector2 lineEnd, out Vector2 slideDir)
{
slideDir = new Vector2(0, 0);
float start = (lineStart.x - pos.x) * moveDir.y - (lineStart.y - pos.y) * moveDir.x;
float end = (lineEnd.x - pos.x) * moveDir.y - (lineEnd.y - pos.y) * moveDir.x;
if (start < 0 || end > 0)
return 1;
//https://stackoverflow.com/a/20679579/4208739
//Uses Cramer's Rule
float L1A = -moveDir.y;
float L1B = moveDir.x;
float L1C = -(pos.x *(moveDir.y + pos.y) - (moveDir.x + pos.x)*pos.y);
float L2A = lineStart.y - lineEnd.y;
float L2B = lineEnd.x - lineStart.x;
float L2C = -(lineStart.x * lineEnd.y - lineEnd.x * lineStart.y);
float D = L1A * L2B - L1B * L2A;
float Dx = L1C * L2B - L1B * L2C;
float Dy = L1A * L2C - L1C * L2A;
if (D == 0)
return 1;
Vector2 inter = new Vector2(Dx / D, Dy / D);
if (Vector2.Dot(inter - pos, moveDir) < 0)
return 1;
float t = (inter - pos).magnitude / moveDir.magnitude;
if (t > 1)
return 1;
slideDir = (1 - t) * Vector2.Dot((lineEnd - lineStart).normalized, moveDir.normalized) * (lineEnd - lineStart).normalized;
return t;
}
Is there some way to calculate collision that isn't susceptible to this sort of problem? I imagine I can't totally eradicate floating point error, but is there a way to check that will at least guarantee I collide with ONE of the two line segments at the pocket? Or is there something more fundamentally wrong with going about things in this way?
If anything is unclear I can draw diagrams or write up examples.
EDIT: Having reflected on this issue more, and in response to Eric's answer, I'm wondering if converting my math from floating point to fixed point could solve the issue? In practice I'd really just be converting my values (which can fit comfortably in the range of -100 to 100) to ints, and then performing the math under those constraints? I haven't pieced all the issues together quite yet, but I might give that a try. If anyone has any information about anything like that, I'd be appreciative.
You have a line that, ideally, is aimed exactly at a point, the endpoint of a segment. That means any error in calculation, no matter how small, could say the line misses the point. I see three potential solutions:
Analyze the arithmetic and design it to ensure it is done with no error, perhaps by using extended-precision techniques.
Analyze the arithmetic and design it to ensure it is done with a slight error in favor of collision, perhaps by adding a slight bias toward collision.
Extend the line segment slightly.
It seems like the third would be easiest—the two line segments forming a pocket could just be extended by a bit, so they cross. Then the sliding path would not be aimed at a point; it would be aimed at the interior of a segment, and there would be margin for error.

Attempting to find pitch and yaw to turn to an object in 3d space

I have two 3d vectors containing position data for two objects within my game. One of the objects needs to turn its camera directly to the other based on the current positions (the setting of the pitch/yaw are absolute so realistically I can ignore those two variables). What I need help with is the math, honestly I just can't find anything about doing this properly.
//An example in pseudocode
//(x, y, z)
Vector3 mychar = new Vector3(100, 100, 100);
Vector3 explosion = new Vector3(555, 1000, 300);
//(pitch, yaw)
Vector2 myRotation = new Vector2(0, 0); //Can assume this is always the case because the setting of rotation is absolute rather than relative
Vector2 expRotation = new Vector2(35, 14);
//The math I cant seem to figure out would be in this function
Vector2 newRotationj = GetRotationBetween(myRotation, mychar, explosion);
Here's what I have so far but I'm getting problems with the Y rotations (its the "up" direction and the horizon is at 0 degrees) Randomly my character will look in the mirrored direction in y (IE if the object is -20 below horizon it does +20) however it's only sometimes and I dont see where my math is wrong.
float dx = posIt.x - posMe.x;
float dy = posIt.y - posMe.y;
float dz = posIt.z - posMe.z;
yaw = Convert.ToSingle(Math.Atan(dx / dz) * 180 / Math.PI);
pitch = Convert.ToSingle(Math.Atan(dy / dz) * 180 / Math.PI);

(Monogame/HLSL) Problems with ShadowMapping - Shadow dependent on Camera position

I'm banging my head at this problem for quite a while now and finally realized that i need serious help...
So basically i wanted to implement proper shadows into my project im writing in Monogame. For this I wrote a deferred dhader in HLSL using multiple tutorials, mainly for old XNA.
The Problem is, that although my lighting and shadow work for a spotlight, the light on the floor of my scene is very dependent on my camera, as you can see in the images: https://imgur.com/a/TU7y0bs
I tried many different things to solve this problem:
A bigger DepthBias will widen the radius that is "shadow free" with massive peter panning and the described issue is not fixed at all.
One paper suggested using an exponential shadow map, but i didn't like the results at all, as the light bleeding was unbearable and smaller shadows (like the one behind the torch at the wall) would not get rendered.
I switched my GBuffer DepthMap to 1-z/w to get more precision, but that did not fix the problem either.
I am using a
new RenderTarget2D(device,
Width, Height, false, SurfaceFormat.Vector2, DepthFormat.Depth24Stencil8)
to store the depth from the lights perspective.
I Calculate the Shadow using this PixelShader Function:
Note, that i want to adapt this shader into a point light in the future - thats why im simply using length(LightPos - PixelPos).
SpotLight.fx - PixelShader
float4 PS(VSO input) : SV_TARGET0
{
// Fancy Lighting equations
input.ScreenPosition.xy /= input.ScreenPosition.w;
float2 UV = 0.5f * (float2(input.ScreenPosition.x, -input.ScreenPosition.y) + 1) - float2(1.0f / GBufferTextureSize.xy);
// Sample Depth from DepthMap
float Depth = DepthMap.Sample(SampleTypeClamp, UV).x;
// Getting the PixelPosition in WorldSpace
float4 Position = 1.0f;
Position.xy = input.ScreenPosition.xy;
Position.z = Depth;
// Transform Position to WorldSpace
Position = mul(Position, InverseViewProjection);
Position /= Position.w;
float4 LightScreenPos = mul(Position, LightViewProjection);
LightScreenPos /= LightScreenPos.w;
// Calculate Projected UV from Light POV -> ScreenPos is in [-1;1] Space
float2 LightUV = 0.5f * (float2(LightScreenPos.x, -LightScreenPos.y) + 1.0f);
float lightDepth = ShadowMap.Sample(SampleDot, LightUV).r;
// Linear depth model
float closestDepth = lightDepth * LightFarplane; // Depth is stored in [0, 1]; bring it to [0, farplane]
float currentDepth = length(LightPosition.xyz - Position.xyz) - DepthBias;
ShadowFactor = step(currentDepth, closestDepth); // closestDepth > currentDepth -> Occluded, Shadow.
float4 phong = Phong(...);
return ShadowFactor * phong;
}
LightViewProjection is simply light.View * light.Projection
InverseViewProjection is Matrix.Invert(camera.View * Camera.Projection)
Phong() is a function i call to finalize the lighting
The lightDepthMap simply stores length(lightPos - Position)
I'd like to have that artifact shown in the pictures gone to be able to adapt the code to point lights, as well.
Could this be a problem with the way i retrieve the world position from screen space and my depth got a to low resolution?
Help is much appreciated!
--- Update ---
I changed my Lighting shader to display the difference between the distance stored in the shadowMap and the distance calculated on the spot in the Pixelshader:
float4 PixelShaderFct(...) : SV_TARGET0
{
// Get Depth from Texture
float4 Position = 1.0f;
Position.xy = input.ScreenPosition.xy;
Position.z = Depth;
Position = mul(Position, InverseViewProjection);
Position /= Position.w;
float4 LightScreenPos = mul(Position, LightViewProjection);
LightScreenPos /= LightScreenPos.w;
// Calculate Projected UV from Light POV -> ScreenPos is in [-1;1] Space
float2 LUV = 0.5f * (float2(LightScreenPos.x, -LightScreenPos.y) + 1.0f);
float lightZ = ShadowMap.Sample(SampleDot, LUV).r;
float Attenuation = AttenuationMap.Sample(SampleType, LUV).r;
float ShadowFactor = 1;
// Linear depth model; lightZ stores (LightPos - Pos)/LightFarPlane
float closestDepth = lightZ * LightFarPlane;
float currentDepth = length(LightPosition.xyz - Position.xyz) -DepthBias;
return (closestDepth - currentDepth);
}
As I am basically outputting Length - (Length - Bias) one would expect to have an Image with "DepthBias" as its color. But that is not the result I'm getting here:
https://imgur.com/a/4PXLH7s
Based on this result, I'm assuming that either i've got precision issues (which i find weird, given that im working with near- and farplanes of [0.1, 50]), or something is wrong with the way im recovering the worldPosition of a given pixel from my DepthMap.
I finally found the solution and I'm posting it here if someone stumbles across the same issue:
The Tutorial I used was for XNA / DX9. But as im targetting DX10+ a tiny change needs to be done:
In XNA / DX9 the UV coordinates are not align with the actual pixels and need to be aligned. That is what - float2(1.0f / GBufferTextureSize.xy); in float2 UV = 0.5f * (float2(input.ScreenPosition.x, -input.ScreenPosition.y) + 1) - float2(1.0f / GBufferTextureSize.xy); was for. This is NOT needed in DX10 and above and will result in the issue i had.
Solution:
UV Coordinates for a Fullscreen Quad:
For XNA / DX9:
input.ScreenPosition.xy /= input.ScreenPosition.w;
float2 UV = 0.5f * (float2(input.ScreenPosition.x, -input.ScreenPosition.y) + 1) - float2(1.0f / GBufferTextureSize.xy);
For Monogame / DX10+
input.ScreenPosition.xy /= input.ScreenPosition.w;
float2 UV = 0.5f * (float2(input.ScreenPosition.x, -input.ScreenPosition.y) + 1)

What's wrong with this XNA RotateVector2 function?

I know this is probably a very simple question, but I can't seem to figure it out. First of all, I want to specify that I did look over Google and SO for half an hour or so without finding the answer to my question(yes, I am serious).
Basically, I want to rotate a Vector2 around a point(which, in my case, is always the (0, 0) vector). So, I tried to make a function to do it with the parameters being the point to rotate and the angle(in degrees) to rotate by.
Here's a quick drawing showing what I'm trying to achieve:
I want to take V1(red vector), rotate it by an angle A(blue), to obtain a new vector (V2, green). In this example I used one of the simplest case: V1 on the axis, and a 90 degree angle, but I want the function to handle more "complicated" cases too.
So here's my function:
public static Vector2 RotateVector2(Vector2 point, float degrees)
{
return Vector2.Transform(point,
Matrix.CreateRotationZ(MathHelper.ToRadians(degrees)));
}
So, what am I doing wrong? When I run the code and call this function with the (0, -1) vector and a 90 degrees angle, I get the vector (1, 4.371139E-08) ...
Also, what if I want to accept a point to rotate around as a parameter too? So that the rotation doesn't always happen around (0, 0)...
Chris Schmich's answer regarding floating point precision and using radians is correct. I suggest an alternate implementation for RotateVector2 and answer the second part of your question.
Building a 4x4 rotation matrix to rotate a vector will cause a lot of unnecessary operations. The matrix transform is actually doing the following but using a lot of redundant math:
public static Vector2 RotateVector2(Vector2 point, float radians)
{
float cosRadians = (float)Math.Cos(radians);
float sinRadians = (float)Math.Sin(radians);
return new Vector2(
point.X * cosRadians - point.Y * sinRadians,
point.X * sinRadians + point.Y * cosRadians);
}
If you want to rotate around an arbitrary point, you first need to translate your space so that the point to be rotated around is the origin, do the rotation and then reverse the translation.
public static Vector2 RotateVector2(Vector2 point, float radians, Vector2 pivot)
{
float cosRadians = (float)Math.Cos(radians);
float sinRadians = (float)Math.Sin(radians);
Vector2 translatedPoint = new Vector2();
translatedPoint.X = point.X - pivot.X;
translatedPoint.Y = point.Y - pivot.Y;
Vector2 rotatedPoint = new Vector2();
rotatedPoint.X = translatedPoint.X * cosRadians - translatedPoint.Y * sinRadians + pivot.X;
rotatedPoint.Y = translatedPoint.X * sinRadians + translatedPoint.Y * cosRadians + pivot.Y;
return rotatedPoint;
}
Note that the vector arithmetic has been inlined for maximum speed.
So, what am I doing wrong? When I run the code and call this function with the (0, -1) vector and a 90 degrees angle, I get the vector (1, 4.371139E-08) ...
Your code is correct, this is just a floating point representation issue. 4.371139E-08 is essentially zero (it's 0.0000000431139), but the transformation did not produce a value that was exactly zero. This is a common problem with floating point that you should be aware of. This SO answer has some additional good points about floating point.
Also, if possible, you should stick with radians instead of using degrees. This is likely introducing more error into your computations.

Categories