Serapth 6671 Report post Posted October 3, 2006 In an upcoming chapter of my learning XNA book, I am dealing with rotating sprites and using game tools. The tool creates sprite sheets and as part of its function, it will also create pixel perfect bounding boxes. ... This was a great idea, right up until I started rotating said sprites :( Suffice to say, there arent too many ellegant ways to rotate 2D bounding boxes... something I forgot before creating the tool ( otherwise, I would have used bounding spheres, and to hell with it! [smile] ). Anyways, the following code is what I used to create updated bounding boxes on an image rotated an arbitrary amount. Frankly, something about it is bugging me, although it seems to return the correct amounts every time. Can I please get a few eyes to look at it and tell me if I am making any glaring mistakes. Also, the code is aimed at newer developers, so unless it is brutal, I dont care too much about optimizations. Also, please keep an eye to constructs that might confuse (relatively) new developers. Thats also my goal here. In a nutshell, I take a bounding box, read from file, break it to its four cardinal points, translate those points about the origin, apply the rotate, then translate them back to real space. At this point, I traverse the point set ( as topLeft becomes relatively meaningless once rotated ), find the new boundaries, and update the bounding rect accordingly. Perhaps I am doing WAY too much work, it feels like I am to me. Anyways, here is the source ( C# and XNA, but should be readable to anyone with 3D experience ). Like I said earlier, the results seem correct at the least. public Rectangle currentBoundingBox { get { // Get the initial bounding box as saved to disk based on pixel data Rectangle rect = _sprites[_currentSprite].boundingBox; // Find the center point of the bounding box, this will be used to move the box centered to 0,0 for rotation float centerX = (float)((rect.Left + rect.Right) / 2); float centerY = (float)((rect.Top + rect.Bottom) / 2); // Now move each of the 4 bounding corners to the origin Vector2 topLeft = new Vector2(rect.Left - centerX, rect.Top - centerY); Vector2 topRight = new Vector2(rect.Right - centerX, rect.Top - centerY); Vector2 bottomRight = new Vector2(rect.Right - centerX, rect.Bottom - centerY); Vector2 bottomLeft = new Vector2(rect.Left - centerX, rect.Bottom - centerY); // Create a standard rotation around Z ( into the screen when dealing with 2d math ) Matrix rotMatrix = Matrix.CreateRotationZ(_currentSpriteRotation); // Now rotate each point topLeft = Vector2.Transform(topLeft, rotMatrix); topRight = Vector2.Transform(topRight, rotMatrix); bottomLeft = Vector2.Transform(bottomLeft, rotMatrix); bottomRight = Vector2.Transform(bottomRight, rotMatrix); // Position means nothing now, bounding box is based off the biggest and smallest possible X,Y coords // Find the tightest box that fits Vector2[] verts = new Vector2[4]; verts[0] = topLeft; verts[1] = topRight; verts[2] = bottomLeft; verts[3] = bottomRight; float smallestX, smallestY, biggestX, biggestY; // set the default values to insanely stupid defaults smallestX = smallestY = 10000; biggestX = biggestY = -10000; // loop throw all vertex info finding the outer limits ( oooohhwooowooo, do not attempt to adjust you're picture, we control t.....) foreach (Vector2 cur in verts) { if (cur.X > biggestX) biggestX = cur.X; if (cur.Y > biggestY) biggestY = cur.Y; if (cur.X < smallestX) smallestX = cur.X; if (cur.Y < smallestY) smallestY = cur.Y; } // now return the resulting bounding box translating X and Y back into realspace ( aka... not the origin ) return new Rectangle((int)(smallestX + centerX), (int)(smallestY + centerY), (int)((biggestX - smallestX)), (int)((biggestY - smallestY))); } } 0 Share this post Link to post Share on other sites
Zipster 2377 Report post Posted October 3, 2006 I'm not a huge fan of the loop for a known (small) number of vertices, and the arbitrary initial values tickle me the wrong way as well. You might try something like this:smallestX = Math.Min(topLeft.X, Math.Min(topRight.X, Math.Min(bottomLeft.X, bottomRight.X)));smallestY = Math.Min(topLeft.Y, Math.Min(topRight.Y, Math.Min(bottomLeft.Y, bottomRight.Y)));biggestX = Math.Max(topLeft.X, Math.Max(topRight.X, Math.Max(bottomLeft.X, bottomRight.X)));biggestY = Math.Max(topLeft.Y, Math.Max(topRight.Y, Math.Max(bottomLeft.Y, bottomRight.Y)));There might also be ways of determining the new boundaries without requiring any more math based on the rotation value. For instance, for rotations between 0° - 90°, topLeft.X will remain the minimum X, topRight.Y will remain the minimum Y, bottomRight.X will remain the maximum X, and bottomLeft.Y will remain the maximum Y. This relationship changes for the 90° - 180° rotation range and so on for each quadrant. However I don't think this is something you want to spend time explaining to your reader. 0 Share this post Link to post Share on other sites
darkelf2k5 286 Report post Posted October 3, 2006 If you need AABBs, use Zipster's solution. If you need OBBs, there has to be a rotation transformation, for eg. a matrix, for the sprites. I'd use that to transform the box's corners too. You can make this geneirc by storing a translation and a rotation matrix per sprite. If you need AABB you transform the box with the translation, and if you need an OBB you transform it with the translation than the rotation. Hope this helps. 0 Share this post Link to post Share on other sites
Serapth 6671 Report post Posted October 4, 2006 Quote:Original post by ZipsterI'm not a huge fan of the loop for a known (small) number of vertices, and the arbitrary initial values tickle me the wrong way as well. You might try something like this:smallestX = Math.Min(topLeft.X, Math.Min(topRight.X, Math.Min(bottomLeft.X, bottomRight.X)));smallestY = Math.Min(topLeft.Y, Math.Min(topRight.Y, Math.Min(bottomLeft.Y, bottomRight.Y)));biggestX = Math.Max(topLeft.X, Math.Max(topRight.X, Math.Max(bottomLeft.X, bottomRight.X)));biggestY = Math.Max(topLeft.Y, Math.Max(topRight.Y, Math.Max(bottomLeft.Y, bottomRight.Y)));There might also be ways of determining the new boundaries without requiring any more math based on the rotation value. For instance, for rotations between 0° - 90°, topLeft.X will remain the minimum X, topRight.Y will remain the minimum Y, bottomRight.X will remain the maximum X, and bottomLeft.Y will remain the maximum Y. This relationship changes for the 90° - 180° rotation range and so on for each quadrant. However I don't think this is something you want to spend time explaining to your reader.Perfect, thanks for the suggestion. I really felt kind of perverse for using a loop and your suggestion is much better and probrably easier to read.I considered going to 4 / 90 degree quadrant route, but when I started down that road it turned into a mess of almost unreadable code. I think im willing to sacrafice the performance hit for readability in this case.Again, thanks alot. 0 Share this post Link to post Share on other sites