3
\$\begingroup\$

I have a texture with 256x256 pixels.

Consider this piece of code:

    protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.CornflowerBlue);

        spriteBatch.Begin();
        spriteBatch.Draw(texture, new Rectangle(0, 0, 100, 100), null, Color.White);
        spriteBatch.End();

        base.Draw(gameTime);
    }

If I run this, the texture appears (predictably) at the top-left corner of the window with the correct size (100x100).

If I do this instead (XNA 4.0-only):

spriteBatch.Draw(texture, new Rectangle(0, 0, 100, 100), null, Color.White, 0, new Vector2(0, 0), SpriteEffects.None, 0);

Everything is still as expected. However, as soon as I change the origin parameter, the position gets weird.

    protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.CornflowerBlue);

        spriteBatch.Begin();
        spriteBatch.Draw(texture, new Rectangle(0, 0, 100, 100), null, Color.White, 0, new Vector2(128, 128), SpriteEffects.None, 0);
        spriteBatch.End();

        base.Draw(gameTime);
    }

In this last example, only 1/4 of the texture is visible.

In other words, the origin parameter not only changes the origin of the rotation, but also the destination rectangle. In other words, the Top/Left coordinates of the rectangle are not the Top/Left coordinates of the drawn sprite, but rather the coordinates of the Origin.X/Origin.Y texture pixel.

If the target position was just a Vector2, I'd understand (and fully support) this behavior, but with a destination rectangle it just seems odd, especially since the documentation does not seem to mention this fact (although to be honest, the documentation barely says anything at all).

A StackOverflow answer even suggests that a destination rectangle with rotation doesn't make much sense, but XNA offers it anyway.

What is the reason for this behavior?

\$\endgroup\$
3
  • \$\begingroup\$ The reason may be beyond the scope of this site. If something needs to rotate a lot use a Vector2 for the second parameter. If it's static and needs to scale to your GUI use the rectangle. \$\endgroup\$
    – Dialock
    Mar 22, 2013 at 5:27
  • \$\begingroup\$ @Dialock I'm wondering if this is a bug or by design. And if it's by design, if there is some reason I'm missing to do it like this. I believe this is answerable, I'm wondering if the XNA team (or some other team with a similar library that made a similar decision) ever posted anything about this topic - and I think this is a practical problem since it took me a while to figure this out, so I believe this is within the scope of this site (but I'm open to edit suggestions to improve the question). \$\endgroup\$
    – luiscubal
    Mar 22, 2013 at 12:12
  • \$\begingroup\$ just know that that the origin does not physically change the x/y of the rectangle just as it doesn't affect the sprites position as a vector2. Create a small debug sprite and draw it on the rotating sprites position while changing the origin. I think you will understand what is going on then (make sure to move the rotating spite to the center of the screen.) \$\endgroup\$
    – Dialock
    Mar 22, 2013 at 17:03

2 Answers 2

5
\$\begingroup\$

Though your question about Draw arguments is understandable, I do think it is by design. Also,

the Top/Left coordinates of the rectangle are not the Top/Left coordinates of the drawn sprite, but rather the coordinates of the Origin.X/Origin.Y texture pixel.

This is an incorrect statement. Your origin has become the top-left visible pixel, which is governed by screen space, not by misdesign design of Spritebatch. The actual top-left pixel is simply off-screen.

For clarity, I put all your draws together into one screenshot, and I added some more with rotation. The black lines are gridlines at the destinationRectangle X and Y coordinates.

spriteBatch.Draw(texture, new Rectangle(100, 50, 100, 100), null, Color.White);
spriteBatch.Draw(texture, new Rectangle(100, 225, 100, 100), null, Color.White, 0, new Vector2(0, 0), SpriteEffects.None, 0);
spriteBatch.Draw(texture, new Rectangle(100, 400, 100, 100), null, Color.White, 0, new Vector2(128, 128), SpriteEffects.None, 0);

spriteBatch.Draw(texture, new Rectangle(400, 50, 100, 100), null, Color.White, MathHelper.PiOver4, Vector2.Zero, SpriteEffects.None, 0);
spriteBatch.Draw(texture, new Rectangle(400, 225, 100, 100), null, Color.White, MathHelper.PiOver4, new Vector2(0, 0), SpriteEffects.None, 0);
spriteBatch.Draw(texture, new Rectangle(400, 400, 100, 100), null, Color.White, MathHelper.PiOver4, new Vector2(128, 128), SpriteEffects.None, 0);

As you can see, your texture coordinate has not been changed. The destination rectangle has been moved (prior to rotating).

Does that make sense?

I think so.

To answer your question, yes, the origin affects both the rotation center and the draw destination. But that alteration is completely logical. A preemptive summary: a destination rectangle does not make sense for a rotated quad, and SpriteBatch.Draw()'s arguments make as much sense as is possible.

Consider the process of drawing this sprite. Using your rectangle with position and size, Spritebatch generates a quad that it will toss into its "batch". That quad needs to be rotated about some point (there is no draw overload that includes rotation but does not include origin) For that idea to make sense, some vertices of the quad need to be rotated from behind or above that origin. But you didn't specify a position, you only specified a destination rectangle. That means that the top-left corner vertex (based on the X and Y of your rectangle) must be moved away from the rotation point, and that move is -1 * origin. This happens internally, without any extra work by you. Yes, it changes the "idea" of a destination rectangle. But the height and width don't make any sense after rotation either (they've both changed).

Compare that to the alternative. For every rectangle that you want to draw, in order to determine its position, you have to manually rotate the top-leftmost corner relative to your origin, then transform your rectangle's X and Y, then pass that new rectangle into the actual draw call. You do all all that work simply to make the destinationRectangle's position match the screenspace position. In the meantime, the Rectangle's height and width are still wrong, because the rotation changed them, too. And some of your rectangle is farther left than your position value, so it was all for naught anyway.

Hopefully that makes sense. I am sure that most of your question came from the pixels that you thought had vanished (but were really just off-screen). If not, I'll be happy to argue about it more. :)

P.S. I am currently trying to roll some 2D physics, and I determined that SpriteBatch would cause more problems than it would solve exactly because of this kind of internal design decision. So I elected to generate quad vertices manually, to avoid my top-left point being transformed without my permission. But again, my trade-off for doing that is that I have to do much, much more work manually.

\$\endgroup\$
2
  • \$\begingroup\$ I think you misinterpreted my statement. In your own code (bottom left rectangle), (400, 400) is not the location of top-left pixel of the drawn rectangle but rather the location of the origin (128, 128). That's what I meant. The overload is underdocumented. That said, upvoted for "a destination rectangle does not make sense for a rotated quad". So I guess no possible design for this method would make sense, and this one is probably the least confusing. \$\endgroup\$
    – luiscubal
    Apr 21, 2013 at 18:57
  • \$\begingroup\$ Fair enough, I thought you were missing pixels when you drew them at 0,0. Regardless, you're right that this overload is not adequately described. Spritebatch is a little too magically easy to use; its many overloads obfuscate how it is working. \$\endgroup\$ Apr 22, 2013 at 0:52
0
\$\begingroup\$

Your question is "What is the reason for this behavior?", right?

My guess is that the reason for this behavior is to render a texture rotated within an axis-aligned rectangle.

An example; Let's say you have a multiplayer-lobby in your game, and you want to be able to have simple chat there. You could make an easter-egg (fits the season, eh?!) that makes your avatar rotate by writing "/loop-de-loop" in the chat. As your UI has some nice frame around the avatar, this looks kinda crappy, so you use this draw-method to make sure the avatar does not go out of the bounds of its frame when rotating.

This might not be the most used version of Draw, but I can see its uses, if it works the way I understood from your post.

Edit; It's worth noting that for drawing a texture rotated freely, you are better off using one of the overloads where you specify position as a Vector2.

http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.graphics.spritebatch.draw.aspx

Edit2; ReRead your question and noticed that you also wonder why changing the origin changes the position of the texture within the rectangle? I will have to take a look at how that works when I get home to be able to give a better answer. I would have thought changing the origin didn't "move" the texture within the destination rectangle, so I have to try it out to see how it works and decide how/if it is usefull :P

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .