Sprite.Draw() draws my textures too small - c#

I declared a device + sprite in a Windows.Form like this
PresentParameters presentParameters = new PresentParameters();
presentParameters.Windowed = true;
presentParameters.SwapEffect = SwapEffect.Copy;
var device = new Device(Manager.Adapters.Default.Adapter, DeviceType.Hardware, this, CreateFlags.HardwareVertexProcessing, presentParameters);
var sprite = new Sprite(device);
I loaded a texture via TextureLoader.FromFile(device, "image.png");
In my Draw method i startet the device scene, then the sprite scene, then i wrote
sprite.Draw2D(texture, PointF.Empty, 0, PointF.Empty, Color.White);
the drawing itself works, but it draws only a big portion of the image scaled up to the screen (like 90%)
i tried it with a source rectangle with the given texture size too, but the same bug occurred
any suggestions?

I am experienced in C++ DirectX, but not C# DirectX, so take this with a grain of salt.
In my experiences with the Sprite interface, you need to scale, rotate, and translate just like you need to with 3D objects. You may be forgetting to scale. Here is the code of my Update function.
void Button::Update()
{
Sprite->Begin(D3DXSPRITE_ALPHABLEND);
D3DXMATRIX trans;
D3DXMATRIX scale;
D3DXMATRIX world;
D3DXMatrixIdentity(&world);
D3DXMatrixTranslation(&trans, pos.x, pos.y, 0.0f);
D3DXMatrixScaling(&scale, scaleFactor, scaleFactor, 1.0f);
world = scale * trans;
Sprite->SetTransform(&world);
Sprite->Draw(buttonTexture, NULL, NULL, &D3DXVECTOR3(-width2, -height2, 0.0), whitecol);
Sprite->End();
}
Admittedly, this isn't a very object-oriented way of doing things, but it suits my needs.

Caveat: I am not an DirectX expert, but I had the same problem.
When you load the sprite it expands the sprite to fit a size where each dimension is a power of 2. For example, If you sprite was 200 x 65, the sprite will have a width of 256 (and the image will be expanded to a width of 256, increasing it slightly) by 128 (almost doubling the height).
When you draw the image, it will be almost twice the height you expected.
My solution was to modify my image file to have a height and width of a factor of 2 and then only draw the portion that was the original size.

Related

How do I scale the graphics of a game?

I'm making a game in C# and XNA 4.0. It's pretty much finished, but now I want to add settings so that players can change the window size if they want to. The current setup goes like this:
void Initialize()
{
//The window size is initally 800x480
graphics.PreferredBackBufferWidth = 800;
graphics.PreferredBackBufferHeight = 480;
graphics.ApplyChanges();
}
void Update()
{
//If the player completes an action, the window size is changed
if (windowSizeChanged)
{
graphics.PreferredBackBufferWidth = 1024;
graphics.PreferredBackBufferHeight = 720;
graphics.ApplyChanges();
}
}
Using this code, this is what the game looks like at specific resolutions:
800x480
1024x720
As you can hopefully see, when the window size is changed it does not affect the actual graphics of the game. The sprites and hitboxes of all of the objects stay the same size, so they instead fill up a small box in the corner of the screen rather than the entire window. Can anyone tell me how I can scale the sprites so that they fill up the window? I assume I would need to use a matrix of some sort, but can anyone point me in the right direction?
Edit:
Here's the draw code.
void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
base.Draw(gameTime);
spriteBatch.Begin();
//Background texture drawn at default window size
spriteBatch.Draw(background, new Rectangle(0, 0, 800, 480), Color.White);
//Each object in the level (player, block, etc.) is drawn with a specific texture and a rectangle variable specifying the size and coordinates
//E.g. Each block is a size of 64x64 pixels and in this level they are placed at X-coordinates 0, 64, 128 and so on
//Each Y-coordinate for the blocks in this level is '480 - 64' (Window height minus block height)
foreach (/*Object in level*/)
{
spriteBatch.Draw(object.Texture, object.TextureSize, Color.White);
}
spriteBatch.End();
}
By default, SpriteBatch assumes that your world space is the same as client space, which is the size of the window. You can read about SpriteBatch and different spaces in a post by Andrew Russell.
When you are resizing the backbuffer, the window size will also change changing the world space together with it (which you don't want). In order not to allow that, you should stick a transformation matrix in between the transformation pipeline to make that correction.
SpriteBatch.Begin allows exactly that in one of its overloads.
There are numerous ways to approach the scaling, but I assume that you want to scale uniformly, meaning that sprites don't get stretched out when the aspect ratio changes compared to the initial aspect ratio. The following code will adjust the scaling based on initial screen height.
...
const float initialScreenHeight = 480f;
Matrix transform = Matrix.CreateScale(GraphicsDevice.Viewport.Height / viewportHeightInWorldSpace);
spriteBatch.Begin(SpriteSortMode.Deferred, null, null, null, null, null, transform);
...
Note that when changing the resolution such that the aspect ratio changes compared to the initial aspect ratio, you will run into issues such as drawing out of screen (to the right) or not drawing at the right edge of the screen (getting a similar blue background as currently).
Also, you don't want to calculate that scaling matrix every frame in the Draw method, but only when the resolution is changed.

How to scale down a sprite based on the screen size

I have a 2D tower defense game I'm making and I want to scale down the towers to match the size of a tile. So I do:
public static tile = graphics.PreferredBackBufferWidth / 24;
To get the size of a tile and then
float scale = tile / 80;
To get the scale however when I run the game only a sliver of the sprite is drawn. What am I doing wrong?
Code where i draw the sprite:
spriteBatch.Draw(texture, center, null, Color.White, rotation,
origin, scale, SpriteEffects.None, 0);
Article on Scaling Sprites Based On Screen Size.
http://msdn.microsoft.com/en-us/library/bb447674.aspx
Also read more about the preferredbackbufferwidth:
http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.graphicsdevicemanager.preferredbackbufferwidth.aspx
If u want to know all overloads, read this:
http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.graphics.spritebatch.draw.aspx

Fitting a rectangle into screen with XNA

I am drawing a rectangle with primitives in XNA. The width is:
width = GraphicsDevice.Viewport.Width
and the height is
height = GraphicsDevice.Viewport.Height
I am trying to fit this rectangle in the screen (using different screens and devices) but I am not sure where to put the camera on the Z-axis. Sometimes the camera is too close and sometimes to far.
This is what I am using to get the camera distance:
//Height of piramid
float alpha = 0;
float beta = 0;
float gamma = 0;
alpha = (float)Math.Sqrt((width / 2 * width/2) + (height / 2 * height / 2));
beta = height / ((float)Math.Cos(MathHelper.ToRadians(67.5f)) * 2);
gamma = (float)Math.Sqrt(beta*beta - alpha*alpha);
position = new Vector3(0, 0, gamma);
Any idea where to put the camera on the Z-axis?
The trick to doing this is to draw the rectangle directly in raster space. This is the space that the GPU works in when actually rendering stuff.
Normally your model starts in its own model space, you apply a world transform to get it into world space. You then apply a view transform to move the points in the world around the camera (to get the effect of the camera moving around the world). Then you apply a projection matrix to get all those points into the space that the GPU uses for drawing.
It just so happens that this space is always - no matter the screen size - from (-1,-1) in the bottom left corner of the viewport, to (1,1) in the top right (and from 0 to 1 on the Z axis for depth).
So the trick is to set all your matrices (world, view, project) to Matrix.Identity, and then draw a rectangle from (-1,-1,0) to (1,1,0). You probably also want to set DepthStencilState.None so that it doesn't affect the depth buffer.
An alternative method is to just use SpriteBatch and draw to a rectangle that is the same size as the Viewport.

Trying to draw textured cube primitive in XNA with quads

Right now I'm using XNA 4.0 with Windows Phone Developer Tools to create a textured cube using a predefined quad class on MSDN.
The front/back/left/right faces of the cube will draw fine (for every cube that I make), however the top and bottom faces won't render. The rasterizer state's cull mode is set to none and the quad that represents the top face exists, and it seems as if it would draw, but for some reason it won't.
Is there a problem with my code, or is this not happening for some other reason?
Here's the code:
Game1.cs: http://pastebin.com/RHU7jNXA
Quad.cs & Cube.cs: http://pastebin.com/P9gz5q4C
It's because your top and bottom faces have a height to them. They should have 0 height.
Here you are passing in a value as height:
Faces[4] = new Quad(topFaceOrigin, Vector3.Normalize(Vector3.Down), Up, Size, Size);
And then here in Quad constructor it's being used to give incorrect LowerLeft & LowerRight values:
LowerLeft = UpperLeft - (Up * height);
LowerRight = UpperRight - (Up * height);
I would recommend changing how you create all your quads; each face really should have different parameters. Right now all your faces are passing in practically the same stuff.

"Wrap-around" effect with a Direct3D.Texture

Given a destination rectangle and an x/y offset value, I need an image to be drawn within the confines of that destination rectangle. If the offset would push the image off the edge of the rectangle, then the part that "pushes out" should appear on the opposite side of the destination rectangle. In simplest terms, I need a scrolling background.
In GDI, I can accomplish this with an "ImageAttributes" object that uses a tile wrap mode:
ImageAttributes attributes = new ImageAttributes();
attributes.SetWrapMode(System.Drawing.Drawing2D.WrapMode.Tile);
Rectangle rectangle = new Rectangle(0, 0, (int)width, (int)height);
g.DrawImage(bmp, rectangle, -x, -y, width, height, GraphicsUnit.Pixel, attributes);
Now, I need a way to do this in DirectX. Assume that this is the method I have right now:
public void RenderTexture(PrismDXObject obj, D3D.Texture texture, int xOffset, int yOffset)
{
if (obj != null && texture != null)
{
_renderSprite.Begin(D3D.SpriteFlags.AlphaBlend);
_renderSprite.Draw(texture,
new Rectangle(0, 0, (int)obj.Width, (int)obj.Height),
new Vector3(0.0f, 0.0f, 0.0f),
new Vector3((int)obj.Left, (int)obj.Top, 0.0f),
obj.RenderColor);
_renderSprite.End();
}
}
}
...where "_renderSprite" is a D3D.Sprite, and PrismDXObject is a simple class that stores x/y/width/height/color. How can I update this method so that xOffset and yOffset can be used to make the texture wrap? Remember, my end-goal is a scrolling background that loops as the player walks forward.
Incidentally, that RenderTexture() method is meant to be a "library method" which can be called from anywhere in my program... so if I'm doing something really inefficient or ill-advised, I'd welcome a friendly warning! My main concern is getting the wrapping background to work, though.
I'm not sure that the sprite mechanism allows for what I'm about to explain, but 2 triangles certainly do. If this does not work with sprites, use triangles directly:
What you're asking for is directly supported by the texturing subsystem, it is called texture wrapping.
When you specify the texture coordinates that your quad will use, instead of using the 0,0-1,1 range, you can use 0+xoffset/tex_x_size, 0+yoffset/tex_y_size, 1+xoffset/tex_x_size, 1+yoffset/tex_y_size for your texture coordinates.
Then, the only thing left to do is to specify that the texture sampler you will use to map your background does texture wrapping. To do this, you need to set to D3DTADDRESS_WRAP the D3DSAMP_ADDRESSU and D3DSAMP_ADDRESSV sampler states. Note, this is the default for the sampler state.
that's it. Now, getting back to D3D.Sprite specifically, the Draw method takes a rectangle that tells which part of the texture to use. have you tried drawing xoffset, yoffset, xOffset+obj,Width, yoffset+obj.height ? This will only work if the sprite subsystem uses a sampler that has wrapping on, and I don't know how sprite is implemented internally.

Categories