Sign in to follow this  
Sync Views

DirectX viewports

Recommended Posts

I want it so that the top left of the screen isn't always 0,0 but could be some other part of my game area (eg 100,75) where the entire game is like 4096,4096. I think I can do this with view ports so that the "view" direct x is drawing isn't alway from 0,0 and 800 wide by 600 high. However I have no idea how to use viewport or if thats even the correct way and I couldn't find anything useful online as everything seem to be for 3d but i'm useing 2d :( I'm noit sure if my D3D device is set up correctly for this so heres the code where I create the device.
//HRESULT d3d_int(int width, int height, D3DFORMAT format, bool fullscreen)
bool d3d_int (D3DFORMAT format)
{
    HRESULT hr;

    //Make Direct3D object
	d3d = Direct3DCreate9(D3D_SDK_VERSION);

    //Make sure NULL pointer was not returned
    if (!d3d) return false;

    //Get device capabilities
    ZeroMemory (&d3d_capabilities, sizeof(d3d_capabilities));
    if (FAILED(d3d->GetDeviceCaps (D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &d3d_capabilities))) return false;

    //Setup present parameters
	ZeroMemory(&d3d_parameters, sizeof(d3d_parameters));
    d3d_parameters.SwapEffect      = D3DSWAPEFFECT_DISCARD;
    d3d_parameters.hDeviceWindow   = cgm::hwnd;
    d3d_parameters.BackBufferCount = 1;
	d3d_parameters.EnableAutoDepthStencil = true;
	d3d_parameters.AutoDepthStencilFormat = D3DFMT_D16;

    if (fullscreen)
    {        
        d3d_parameters.Windowed = false;
        d3d_parameters.BackBufferWidth = window_width;
        d3d_parameters.BackBufferHeight = window_height;
        d3d_parameters.BackBufferFormat = format;
    }
	else
    {
        D3DDISPLAYMODE d3ddm;
        RECT rWindow;

        //Get display mode
        d3d->GetAdapterDisplayMode (D3DADAPTER_DEFAULT, &d3ddm);

        //Get window bounds
        GetClientRect (cgm::hwnd, &rWindow);

        //Setup screen dimensions
        window_width  = (unsigned short)(rWindow.right - rWindow.left);
        window_height = (unsigned short)(rWindow.bottom - rWindow.top);

        //Setup backbuffer
        d3d_parameters.Windowed = true;
        d3d_parameters.BackBufferFormat = d3ddm.Format;
        d3d_parameters.BackBufferWidth = rWindow.right - rWindow.left;
        d3d_parameters.BackBufferHeight = rWindow.bottom - rWindow.top;
    }

    //Check if hardware vertex processing is available
    if (d3d_capabilities.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)
    {    
        //Create device with hardware vertex processing
		hr = d3d->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL, hwnd,
            D3DCREATE_HARDWARE_VERTEXPROCESSING, &d3d_parameters, d3d_device);        
    }
    else
    {
        //Create device with software vertex processing
        hr = d3d->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL, hwnd,
            D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3d_parameters, &d3d_device);
    }

    //Make sure device was created
    if (FAILED(hr)) return false;

    //Set vertex shader
    d3d_device->SetVertexShader(NULL);
    d3d_device->SetFVF(cgm::d3d_vertexformat);

    //Create vertex buffer and set as stream source
	d3d_device->CreateVertexBuffer(sizeof(cgm::d3d_vertex) * 256, NULL, d3d_vertexformat, D3DPOOL_MANAGED,
                                  &d3d_vertex_buffer, NULL);
	d3d_device->SetStreamSource (0, d3d_vertex_buffer, 0, sizeof(cgm::d3d_vertex));

    //Setup rendering states
	d3d_device->SetRenderState(D3DRS_ZENABLE, FALSE);
	d3d_device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
	d3d_device->SetRenderState(D3DRS_LIGHTING, FALSE);
	cgm::d3d_device->SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);
	d3d_device->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
	d3d_device->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
        d3d_device->SetTextureStageState(0, D3DTSS_ALPHAOP, D3DTOP_MODULATE);
    return true;
}

Share this post


Link to post
Share on other sites
I did that. Then I came up with the idea of zooming in/out so relised I needed to add some scaling stuff as well.

Then I saw viewports and wondered if I was reinventing something that is done with a few simple function calls...

Share this post


Link to post
Share on other sites
First, let me make sure I understand. I could have sworn I just saw another post that made me question my suggestion, but now it's gone? Hmmm... Anyway, are you (1) trying to scroll the view your game world but still render to the full screen? Or, are are you (2) trying to restrict the area of your rendering to a smaller sub-rectangle of the screen?

Share this post


Link to post
Share on other sites
err... I'm trying to make it so my world is lets say 5000,5000 units and I want to make it so that when everything draws it's self (eg my obj_ball object calls "draw_sprite(x, y, spr_ball, 0xFF00FFFF);" so draw a sprite at it's location).

Now I want it so if my "viewport" is at 500,500 and the ball is at 600,700 the sprite gets drawn to 100,200 on the screen. But I also want this to work with zooming so...

If the screen is 800,400 and the viewport is at 500,500 and 1600 wide by 800 high then I the ball drawn to 50,100 on the screen. I also wouldn't mind being able to rotate the view but thats not on my prioity list.

Share this post


Link to post
Share on other sites
Alright, my apologies then if I led you astray, this is NOT a case you should use a viewport for. This is where the View Matrix comes into play. Your objects shouldn't have to add offsets to compensate for the current View or Camera, they should get rendered appropriately when you set the view matrix. Are you using the fixed function for your rendering? Or shaders? If fixed function, then are you using device->SetTransform( D3DTS_VIEW, &viewMatrix )? And, if so, how are you computing your view matrix?

Share this post


Link to post
Share on other sites
what?
this is the basic function I'm useing for drawing. All my other ones are following the same general lines.


bool draw_sprite(const int id, double x, double y){return draw_sprite(id, x, y, c_white, c_white, c_white, c_white);}
bool draw_sprite(const int id, double x, double y, unsigned long c1){return draw_sprite(id, x, y, c1, c1, c1, c1);}
bool draw_sprite(const int id, double x, double y, unsigned long c1, unsigned long c2, unsigned long c3, unsigned long c4)
{
if(!sprite_exists(id))return false;
if(d3d_lost)return true;
x -= 0.5 + cgm::sprite_list[id]->offset_x;
y -= 0.5 + cgm::sprite_list[id]->offset_y;
cgm::d3d_device->SetTexture (0, cgm::sprite_list[id]->image);

draw_quad(
x, y,
x + cgm::sprite_list[id]->w, y,
x + cgm::sprite_list[id]->w, y + cgm::sprite_list[id]->h,
x, y + cgm::sprite_list[id]->h,
c1, c2, c3, c4);
}

bool draw_quad(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4, unsigned long c1, unsigned long c2, unsigned long c3, unsigned long c4)
{
if (d3d_lost)return true;

cgm::d3d_vertex* vertices;
cgm::d3d_vertex_buffer->Lock(0, 0, (void**)&vertices, NULL);

vertices[0].colour = c1;
vertices[0].x = (float)x1;
vertices[0].y = (float)y1;
vertices[0].z = 0.0;
vertices[0].rhw = 1.0;
vertices[0].u = 0.0;
vertices[0].v = 0.0;

vertices[1].colour = c2;
vertices[1].x = (float)x2;
vertices[1].y = (float)y2;
vertices[1].z = 0.0;
vertices[1].rhw = 1.0;
vertices[1].u = 1.0;
vertices[1].v = 0.0;

vertices[2].colour = c3;
vertices[2].x = (float)x3;
vertices[2].y = (float)y3;
vertices[2].z = 0.0;
vertices[2].rhw = 1.0;
vertices[2].u = 1.0;
vertices[2].v = 1.0;

vertices[3].colour = c4;
vertices[3].x = (float)x4;
vertices[3].y = (float)y4;
vertices[3].z = 0.0;
vertices[3].rhw = 1.0;
vertices[3].u = 0.0;
vertices[3].v = 1.0;

cgm::d3d_vertex_buffer->Unlock();
cgm::d3d_device->DrawPrimitive (D3DPT_TRIANGLEFAN, 0, 2);
return true;
}


Share this post


Link to post
Share on other sites
If none of that made sense, then I think maybe you need to have a look at the "Transforms" section of your DirectX sdk help file. Type in "Transforms" in the "Index" tab and it should yield a pretty useful page.

Share this post


Link to post
Share on other sites
You probably want an orthogonal projection matrix, along with a view matrix looking down your Z axis (Assuming the tiles are on the XY plane). You'll want to make sure you avoid rendering tiles that are outside the visible area though (You'd want to do that with any method), for performance reasons.

Share this post


Link to post
Share on other sites
Quote:
Original post by Sync Views
And what do I do with that matrix so make it do what I want?
I knew there was something I forgot to mention [smile]

You set it as your projection matrix.

How are you doing 2D at the moment? Are you using transformed vertices (A FVF containing D3DFVF_XYZRHW)? And what is your projection matrix like at the moment?
The best way to do 2D is to use an orthogonal projection matrix, and real 3D vertices (Not RHW verts), since then you can get rotation or vertex shaders for free if you wish. A perspective projection matrix will make object further from the camera smaller (As normal perspective does in the real world), which probably isn't what you want for sprites.

EDIT: I may have mis-understood your question...
For the parameters to the matrix, you set the b and l parameters to the top left of your view, and the t and r to the bottom right corners (So it's flipped in Y). The zn and zf values should be a sensible Z range like 1.0f and 1000.0f, which gives you a reasonable range of Z values for if you want to use the Z-buffer.
So a view of 100x100 units, with the top left corner at (2000, 3000) would be:

D3DXMATRIX mat;
D3DXMatrixOrthoOffCenterLH(&mat, 2000.0f, 2100.0f, 3000.0f, 3100.0f, 1.0f, 1000.0f);
pDevice->SetTransform(D3DTS_PROJECTION, &mat);

Share this post


Link to post
Share on other sites
The vertext format i;m setting is:
const DWORD d3d_vertexformat = D3DFVF_XYZRHW | D3DFVF_DIFFUSE | D3DFVF_TEX1;


I assume your refering to the "D3DFVF_XYZRHW" bit? I really have no idea what RHW even is, i'm just setting it to 1.0 on all my vertices as the tutorial did (2d useing textured quads one on this site somewhere...)

Right now I'm just adding vertices with rhw as 1.0 and z as 0.0 (as seen in the code snipet above) and drawing a primitives with it. 0,0 is the top left of the screen with 1 unit on the vertices being 1 unit on the screen (so a vertex with 800,600 would be the bottom right of my window).

Share this post


Link to post
Share on other sites
Quote:
Original post by Sync Views
The vertext format i;m setting is:
*** Source Snippet Removed ***

I assume your refering to the "D3DFVF_XYZRHW" bit? I really have no idea what RHW even is, i'm just setting it to 1.0 on all my vertices as the tutorial did (2d useing textured quads one on this site somewhere...)

Right now I'm just adding vertices with rhw as 1.0 and z as 0.0 (as seen in the code snipet above) and drawing a primitives with it. 0,0 is the top left of the screen with 1 unit on the vertices being 1 unit on the screen (so a vertex with 800,600 would be the bottom right of my window).
RHW stands for "Reciprocal of Homogenous W" and effectively means "depth". When you use the D3DFVF_XYZRHW flag, you're telling D3D that you're drawing things in screen space, so it shouldn't do any transformation of the vertices (and shouldn't run the vertex shaders on them, if you have them), because you're giving the final position of the vertex how you want it. That means that the only way you can change their position (which is what you want) is to physically move the vertices, by locking the vertex buffer containing them, update the position, and then unlock the vertex buffer - something that the GPU should do wherever possible, since it does them for "free", and can handle e.g. rotation if you need it.

To get the GPU to do the transformation for you, you need to use untransformed vertices; that is you need D3D to pass the vertices through the world, view and projection matrices. For that, you'd use D3DFVF_XYZ to tell D3D you're passing in a XYZ coordinate (And remove the RHW member of your vertex struct), set up a projection matrix, and set an identity matrix for the view and world matrices (Which is the default, so you can leave the view and world matrices alone if you never change them).
If you do that, every vertex you pass in will be transformed by the three matrices, so you can just change the matrix to change the transform. For instance, you could rotate all the vertices (I know, I keep going on about rotation, rotation is cool [smile]), or you could move them all by a (X,Y) amount to move your world around (The same idea as changing the bounds of the projection matrix).

Google has loads of info on orthogonal projection matrices, this one looks interesting (Although I haven't read the whole thing).

Share this post


Link to post
Share on other sites
I couldn't find much useful on your search. Lots of stuff about the concept but nothing on how to actauly use it :(

I'll continue to experiment to see if I can get something to work for now...

EDIT: ok got something to work having played with the numbers and stuff (eg setting the z for my vertices to 1.0 instead of 0.0).


D3DXMatrixOrthoOffCenterLH(
&matrix,
view_x, view_x + view_w,
view_y, view_y + view_h,
1.0f, 1000.0f);



Only problem is it flips the y axis... How can I stop this?

also how can I make my text be effected by this as it seems to still be working in screen cordinates?


bool draw_text(int ft, int x, int y, std::string str, unsigned long colour)
{
if(d3d_lost)return true;
if(!font_exists(ft)) return false;

RECT rct;
rct.left=x;
rct.right=x+999;
rct.top=y;
rct.bottom=1;

rct.bottom = y + cgm::font_list[ft]->DrawTextA(NULL, str.c_str(), -1, &rct,DT_EXPANDTABS | DT_CALCRECT | DT_NOCLIP | align, colour);
cgm::font_list[ft]->DrawTextA(NULL, str.c_str(), -1, &rct,DT_EXPANDTABS | DT_NOCLIP | align, colour);
return true;
}



[Edited by - Sync Views on February 19, 2008 6:44:44 PM]

Share this post


Link to post
Share on other sites
Whoops, I'm an idiot. Swap your "view_y" and "view_y + view_h" parameters to prevent it flipping in Y.

ID3DXFont is designed to draw in screen space, so you'll have to just adjust the RECT you pass in according to your view area, or write your own font system (Which I wouldn't recommend).
You could try posting in the DirectX forum and asking if anyone knows if you can get it to draw in world space rather than screen space, I don't know of any way.

Share this post


Link to post
Share on other sites
Well I could could draw it to some kind of surface then draw the surface I assume? Still need to check out how this effects my render target system for drawing to surfaces though...

btw: You said this lets me do stuff like rotation etc for free but I'm still not seeing anyway to do it other than to transform the vertices of my quad eg:

bool draw_sprite_rot(const int id, double x, double y, double rot){return draw_sprite_rot(id, x, y, rot, c_white, c_white, c_white, c_white);}
bool draw_sprite_rot(const int id, double x, double y, double rot, unsigned long c1){return draw_sprite_rot(id, x, y, rot, c1, c1, c1, c1);}
bool draw_sprite_rot(const int id, double x, double y, double rot, unsigned long c1, unsigned long c2, unsigned long c3, unsigned long c4)
{
if (!sprite_exists(id))return false;
if (d3d_lost)return true;
if (rot == 0) return draw_sprite(id, x, y, c1, c2, c3, c4);

cgm::d3d_device->SetTexture (0, cgm::sprite_list[id]->image);
rot = degtorad(rot);

x -= cgm::sprite_list[id]->offset_x;
y -= cgm::sprite_list[id]->offset_y;

return draw_quad(
x+(cgm::sprite_list[id]->offset_x + (-cgm::sprite_list[id]->offset_x)*cos(rot) - (-cgm::sprite_list[id]->offset_y)*sin(rot)),
y+(cgm::sprite_list[id]->offset_y + (-cgm::sprite_list[id]->offset_x)*sin(rot) + (-cgm::sprite_list[id]->offset_y)*cos(rot)),

x+(cgm::sprite_list[id]->offset_x + ((cgm::sprite_list[id]->w) - cgm::sprite_list[id]->offset_x)*cos(rot) - (-cgm::sprite_list[id]->offset_y)*sin(rot)),
y+(cgm::sprite_list[id]->offset_y + ((cgm::sprite_list[id]->w) - cgm::sprite_list[id]->offset_x)*sin(rot) + (-cgm::sprite_list[id]->offset_y)*cos(rot)),

x+(cgm::sprite_list[id]->offset_x + ((cgm::sprite_list[id]->w) - cgm::sprite_list[id]->offset_x)*cos(rot) - ((cgm::sprite_list[id]->h) - cgm::sprite_list[id]->offset_y)*sin(rot)),
y+(cgm::sprite_list[id]->offset_y + ((cgm::sprite_list[id]->w) - cgm::sprite_list[id]->offset_x)*sin(rot) + ((cgm::sprite_list[id]->h) - cgm::sprite_list[id]->offset_y)*cos(rot)),

x+(cgm::sprite_list[id]->offset_x + (-cgm::sprite_list[id]->offset_x)*cos(rot) - ((cgm::sprite_list[id]->h) - cgm::sprite_list[id]->offset_y)*sin(rot)),
y+(cgm::sprite_list[id]->offset_y + (-cgm::sprite_list[id]->offset_x)*sin(rot) + ((cgm::sprite_list[id]->h) - cgm::sprite_list[id]->offset_y)*cos(rot)),

c1, c2, c3, c4);
}




EDIT: Also as I can use the matrix to scale and translate everything to screen space can I also get it to rotate the "view" as well as move it?

Share this post


Link to post
Share on other sites
Quote:
Original post by Sync Views
Well I could could draw it to some kind of surface then draw the surface I assume? Still need to check out how this effects my render target system for drawing to surfaces though...
Ah yes, that's a good way to do it.

Quote:
Original post by Sync Views
btw: You said this lets me do stuff like rotation etc for free but I'm still not seeing anyway to do it other than to transform the vertices of my quad eg:
*** Source Snippet Removed ***

EDIT: Also as I can use the matrix to scale and translate everything to screen space can I also get it to rotate the "view" as well as move it?
You'd change the world matrix to include rotation as well as translation as required by multiplying the matrices (I don't know if you've covered matrices, I covered them in Higher (A-level) maths).

Share this post


Link to post
Share on other sites
I suppose I'll cover them in the next two years then....not sure I really want to wait that long though so can you explain what I basicly need to do? eg looking at that matrix function I'm realy not seeing what I'm meant to change to do rotation as well :( (I'm asuming it's not as simple as how many degress/radians as it would need to take into acount where the "centre" of my object is?)

Share this post


Link to post
Share on other sites
Quote:
Original post by Sync Views
I suppose I'll cover them in the next two years then....not sure I really want to wait that long though so can you explain what I basicly need to do? eg looking at that matrix function I'm realy not seeing what I'm meant to change to do rotation as well :( (I'm asuming it's not as simple as how many degress/radians as it would need to take into acount where the "centre" of my object is?)
Again, googling for matrices should give loads of results.

If you have a matrix that translates an object by (10, 20), and a matrix that rotates an object by 45 degrees (Rotations are always about the origin), you can combine them into a matrix that will either rotate by 45 degrees, then translate by (10, 20), or you can combine them into a matrix that translates first, then rotates, depending on the order you multiply them in.

It's a bit difficult to go over it in detail, but you can think of it as m1 * m2 means "First do m1, then do m2", so:

D3DXMATRIX matRotate, matTranslate, matFinal;

// Create a matrix that rotates by 45 degrees about the Z axis:
D3DXMatrixRotationZ(&matRotate, D3DXToRadian(45.0f));

// Create a matrix that translates by (10, 20, 0):
D3DXMatrixTranslation(&matTranslate, 10.0f, 20.0f, 0.0f);

// Multiply the two matrices to give one that rotates, then translates:
D3DXMatrixMultiply(&matFinal, &matRotate, &matTranslate);

// matFinal is now a matrix that rotates things by 45 degrees, then translates
// them by (10, 20, 0).





EDIT:
If you have a tile or set of tiles which are from (0, 0) to (4000, 4000), and you want to rotate about the middle of them (2000, 2000), you need to:
Translate by (-2000, -2000)
Rotate
Translate by (2000, 2000)

Share this post


Link to post
Share on other sites
so roating is always aeround the origin of the world as in "0,0,0" not just the centre point between the vertices?

So to rotate an image around the point I want I need to:
-Translate so my origin point is at 0,0,0
-Rotate
-Translate it back to where I want it

Share this post


Link to post
Share on other sites
Quote:
Original post by Sync Views
so roating is always aeround the origin of the world as in "0,0,0" not just the centre point between the vertices?

So to rotate an image around the point I want I need to:
-Translate so my origin point is at 0,0,0
-Rotate
-Translate it back to where I want it
Yup, exactly. The rotation is done per vertex, and a vertex is just a point in space, so the GPU doesn't know where to rotate it "relative" to, so it has to use the origin.

Share this post


Link to post
Share on other sites
ok got that all working nicely now with zooming, roation and translation :)

1 last thing though. If I wanted to do a split screen type thing how do I make it so d3d transforms and draw it to just one area of the screen not the entire screen? (eg just the left half of the screen) so I can have 2 "views" one for each of the two players?

Share this post


Link to post
Share on other sites
Quote:
Original post by Sync Views
ok got that all working nicely now with zooming, roation and translation :)

1 last thing though. If I wanted to do a split screen type thing how do I make it so d3d transforms and draw it to just one area of the screen not the entire screen? (eg just the left half of the screen) so I can have 2 "views" one for each of the two players?
That's when you use multiple viewports. You'd call SetViewport() to set a viewport that covers the left side of the screen, render, then call SetViewport() to set the right side of the screen as the viewport, then render again.

Someone here may be able to suggest an alternative method too.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this