What I want is to shatter a window. I have a point on the surface of the window and I have the normal vector of the window surface. What I need is random vectors perpendicular (if that’s the correct word) to that surface normal so I can cut the window along those vectors, but I’m honestly completely clueless as to how I would get these vectors. I’ve googled for hours and I’m sure a lot of the hits I’ve found are correct, but I just don’t understand how I would apply them to my scenario. One of the more infuriating hits I’ve found numerous times are people saying “there are an infinite amount of tangents to a normal vector”. Ok, but how do I find random ones?
I’m almost ashamed to ask this because I feel like it has to be absolutely trivial, but apparently I’m completely clueless about vectors and I’m hoping this can push me towards some slight understanding at least.
Here’s an illustration I found which sort of works. Let’s say we’re looking directly down on the window surface and I want to shatter the glass at point A. I need random vectors like B through F in this graphic so I can construct planes along them and slice the window.
For all I know getting the planes directly is easier as well, but I’m clueless about that as well and I already know how I can construct the planes if I get the vectors. I guess this graphic illustrates it well if we’re thinking of it like cutting planes:
The blue plane is my window surface and I want to cut it along the orange and green planes and x number of random planes like them.
There’s probably better maths to use, but you could generate a tangent to the normal of the window surface and then rotate it around the same window normal axis randomly or by some known amount… or alternatively use some offsets from the window normal and then project them onto the plane using Vector3.ProjectOnPlane and normalise them as directions.
// generate a tangent to the window normal
Vector3 tangent = Vector3.Cross(windowNormal, Vector3.up);
if (tangent.sqrMagnitude <= float.Epsilon) tangent = Vector3.Cross(windowNormal, Vector3.forward);
tangent.Normalize();
// rotate tangent around by 30degs
Quaternion rotation = Quaternion.AngleAxis(30, windowNormal);
Vector3 rotatedOnSurface = rotation * tangent;
That looks very similar to an example I found on Google, and funnily enough it’s actually harder than I thought it would be. That may be the reason why I was completely stumped by it. I was too intent on it only being 1 or 2 lines of code I guess. The if-statement really threw me off.
The only part I don’t understand is “float.Epsilon”. Using 0 doesn’t seem to produce different results for me.
For reference here’s the example I found on Google:
In that answer he says:
Which I guess makes sense. I’ll have to fiddle more with this and hopefully get some better understanding of 3d space.
It works perfectly by the way. Here’s the code I ended up with:
for (int i = 0; i < cuts; i++) {
// generate a tangent to the window normal
Vector3 tangent = Vector3.Cross(normal, Vector3.up);
if (tangent.sqrMagnitude == float.Epsilon) tangent = Vector3.Cross(normal, Vector3.forward);
tangent.Normalize();
// rotate tangent around by 30degs
Quaternion rotation = Quaternion.AngleAxis(Random.Range(0f, 360f), normal);
Vector3 rotatedOnSurface = rotation * tangent;
cuttingPlanes[i] = new Plane(rotatedOnSurface, point);
}
I realize the above code creates cuts 90 degrees rotated compared to my vectors because the vector you supply to the Plane constructor is the plane normal, but thanks to my now slightly improved understanding of vectors I know how I would rotate it 90 degrees so it’s correct. It doesn’t really matter though since I’m using random vectors anyway.
Edit: Hmm, I guess what I really mean is I don’t understand in what situation you would use float.Epsilon. Is it used to catch rounding errors or something? So every time you’re comparing a float to 0 you should really be comparing it to float.Epsilon?
The float.Epsilon check is probably more readable using Mathf.Approximately(tangent.sqrMagnitude, 0f) - and yep, it basically handles the case where the first arbitrary axis you’re crossing against to generate the tangent happens to be in the same or opposite direction to the window normal (which makes the cross product vector have a length of 0). Looking at your solution snippet, you will probably want to update your check for this slightly.
// generate a tangent to the window normal
Vector3 tangent = Vector3.Cross(windowNormal, Vector3.up);
if (Mathf.Approximately(tangent.sqrMagnitude, 0f)) tangent = Vector3.Cross(windowNormal, Vector3.forward);
tangent.Normalize();
Both tangent.magnitude and tangent.sqrMagnitude will both be small when the axis you’re crossing against is in the same or opposite direction to the window normal, so they’re fairly interchangeable in this case.
Remember to make some test cases to check that everything works as expected - floating point bugs come out of code that seems harmless and can be pretty annoying