tiles/textures and sprites

This is a discussion on tiles/textures and sprites within the Game Programming forums, part of the General Programming Boards category; Ive just started using DirectX 9 with C/C++ and im about to start my first game. Its going to be ...

  1. #1
    Registered User
    Join Date
    Aug 2003
    Posts
    288

    Question tiles/textures and sprites

    Ive just started using DirectX 9 with C/C++ and im about to start my first game.

    Its going to be a Tic-Tac-Toe clone (how original), and i was wondering, for the 9 squares, should i use 9 different squares (ie 18 triangles drawn as 9 primitives) and texture them with the X and O images, or do i use 9 sprites?

    Im not sure which 1 is more efficient so i was wondering if someone could give me a few pointers regarding the subject.

    I was also wondering, i tried rendering a textured plane, and my FPS was around 50-80, is this because im not rendering it efficiently? or is it normal?, i usually get 1400+ fps when rendering a blank screen, and around 800+ when rendering non-textured objects.

    Any help would be appreciated, thanx

    PaYnE

  2. #2
    Registered User heat511's Avatar
    Join Date
    Dec 2001
    Posts
    169
    try disabling or enabling vsync in your graphics card options (that limits the fps to whatever your refresh monitor rate is... at least in opengl)
    "uh uh uh, you didn't say the magic word"
    -Jurassic Park

  3. #3
    Registered User
    Join Date
    Aug 2003
    Posts
    288
    VSync was already disabled, enabling it only made it worse, by keeping the FPS at ~75 (which is my refresh rate)

  4. #4
    Super Moderator VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,596
    TexturedPlane.h
    Code:
    #include <d3dx9.h>
    #include <string>
    
    
    class TexturedPlane
    {
       private:
         IDirect3DDevice9            *_Device;
         IDirect3DTexture9          *_Texture;
         IDirect3DVertexBuffer9   *_VBuf;
         IDirect3DIndexBuffer9     *_IBuf;
    
         std::string                 TextureFile;
         float                           _height;
         float                           _scale;
       public:
         TexturedPlane();
         ~TexturedPlane();
         Init(IDirect3DDevice9 *device,std::string File,float height,float scale);
         Render(D3DXVECTOR3 &cameraVec);
    };
    TexturedPlane.cpp
    Code:
    #include "TexturedPlane.h"
    
    
    struct PlaneVertex
    {
       PlaneVertex(float x,float y,float z,float u,float v):x(x),y(y),z(z),u(u),v(v) {};
       float x,y,z,u,v;
       static const DWORD FVF;
    };
    
    const DWORD FVF=D3DFVF_XYZ | D3DFVF_TEX1;
    
    TexturedPlane::TexturedPlane()
    {
      _Device=0;
      _Texture=0;
      _height=0.0f;
      _scale=0.0f;
      _VBuf=0;
      _IBuf=0;
    }
    
    void TexturedPlane::Init(IDirect3DDevice9 *device,std::string File,float height,float scale)
    {
       _Device=device;
       _height=height;
       _scale=scale;
       TextureFile=File;
    
       D3DXCreateTextureFromFile(Device,File.c_str(),&Texture);
      
       _Device->CreateVertexBuffer(4*sizeof(SkyPlaneVertex),
    			  D3DUSAGE_WRITEONLY,
    			  PlaneVertex::FVF,
    			  D3DPOOL_MANAGED,
                                                      &_VBuf,
                                                      0);
    
       PlaneVertex* v=0;
    
       _VBuf->Lock(0,0,(void **)&v,0);
    
       v[0]=SkyPlaneVertex(-1.0f*scale,-1.0f*height,-1.0f*scale,0.0f,1.0f);
       v[1]=SkyPlaneVertex(-1.0f*scale,-1.0f*height, 1.0f*scale,0.0f,0.0f);
       v[2]=SkyPlaneVertex( 1.0f*scale,-1.0f*height, 1.0f*scale,1.0f,0.0f);
       v[3]=SkyPlaneVertex( 1.0f*scale,-1.0f*height,-1.0f*scale,1.0f,1.0f);
    
       _VBuf->Unlock;
    
      _Device->CreateIndexBuffer(6*sizeof(WORD),
                                                    D3DUSAGE_WRITEONLY,
    			D3DFMT_INDEX16,
    			D3DPOOL_MANAGED,
    			&_IBuf,
    			0);
    	
      WORD *indices=0;
      
      _IBuf->Lock(0,0,(void **)&indices,0);
    
      //Counter clockwise winding order - can see when below
      //0,1,2  3,5,4 -> clockwise winding order - can see when above
      indices[0]=0;indices[1]=2;indices[2]=1;
      indices[3]=0;indices[4]=3;indices[5]=2;
    
      _IBuf->Unlock();
    }
    
    void TexturedPlane::Render(D3DXVECTOR3  &cameraVec)
    {
      D3DXMATRIX W;
      D3DXMatrixTranslation(&W,
                                           cameraVec.x,
                                           cameraVec.y,
                                           cameraVec.z);
    
      _Device->SetTransform(D3DTS_WORLD,&W);
    
      _Device->SetStreamSource(0,VB,0,sizeof(SkyPlaneVertex));
      _Device->SetFVF(SkyPlaneVertex::FVF);
      _Device->SetIndices(IB);
      _Device->SetTexture(0,_Texture);
      _Device->DrawIndexedPrimitive(D3DPT_TRIANGLELIST,
    			      0,0,4,0,2);
    }

    That should work. Create the plane like this:

    TexturePlane MyTexPlane;

    Init the plane like this, where device is a valid IDirect3DDevice9 interface


    MyTexPlane.Init(Device,"somefile.bmp",-200.0f,400.0f);

    Draw the plane like this, where camera is a valid D3DXVECTOR3.

    MyTexPlane.Render(camera);


    You will have to alter the plane vertexes to create a flat plane that faces the viewer - this creates a plane that 'slides' on the y axis - thus the plane can be used as a sky or water. For sky the vertexes must be in the order specified - for water they must be reversed - this is because the backface cull will remove the plane.
    A simple test is to test the y of the plane against the y of the camera and set the winding order via SetRenderState() in Direct3D. Don't forget to reset the winding order to the default prior to leaving the function or your other geometry will be culled.

    Here are the coords for a plane that faces the viewer and has a clockwise winding order (clockwise cull).

    -1.0f,1.0f,1.0f,0.0f,0.0f
    -1.0f,-1.0f,1.0f,0.0f,1.0f
    1.0f,-1.0f,1.0f,1.0f,0.0f
    1.0f,1.0f,1.0f,1.0f,1.0f


    Or you could simply rotate the plane I have to the desired angle - but this would be a waste since you could just re-specify the vertexes.
    Last edited by VirtualAce; 04-05-2004 at 04:23 PM.

  5. #5
    Registered User
    Join Date
    Aug 2003
    Posts
    288
    Thanx Bubba, thats excellent, but i just have 2 questions:

    1) Why do u need both a vertex and an index buffer in the Init function
    2) And to use it in the tic-tac-toe game, do i need to create 9 different planes?

    Thanx again

  6. #6
    Super Moderator VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,596
    A vertex buffer holds all the vertexes. Take this quad for instance.


    Vertex:
    0: 0,1,1
    1: 0,-1,1
    2: 1,-1,1
    3: 1,1,1

    Now we must decide how to 'connect the dots' if you will.

    So the first triangle is (with counterclockwise winding)

    0,2,1

    The second is:

    0,3,2


    Index buffers and their benfits are not clearly evident from this small sample. Basically in any given mesh several vertexes will be shared by all triangles. Instead of specifying this vertex for every triangle by re-defining it, we simply create an index buffer into our vertex list that tells Direct3D how to connect the vertexes to form triangles. This allows us to use one vertex multiple times simply by indexing into the vertex list.

    Otherwise our vertex list would be 6 vertexes long, but 2 of those (0 and 2) are shared. It's a waste to put them in the master vertex list twice so we use an index buffer.

    The index buffer says connect vertexes 0,2 and 1, and 0,3 and 2 to make 2 triangles.

    For the texture plane I did not have to use an index buffer or but I chose to.

    For the tic-tac-toe board you do not have to create 9 or even 1 planes. I just thought you wanted a textured flat plane and that would give it to you.


    Code:
    TexturedPlane TicTacToeBoard;
    TexturedPlane XPiece;
    TexturedPlane OPiece;
    
    
    
    TicTacToeBoard.Init(Device,"tictactoeboard.jpg",100.0f,0.0f);
    XPiece.Init(Device,"xpiece.jpg",30.0f,0.0f);
    OPiece.Init(Device,"opiece.jpg",30.0f,0,0f);
    
    
    
    void Render(void)
    {
       D3DXVECTOR3 dir(0.0f,0.0f,0.0f);
       TicTacToeBoard.Render(dir);
       
       ...loop through board
       ...find location and see if it is X or O
       switch (piece)
      {
           case XPIECE: XPiece.Render(XPieceVector);
           case OPIECE: OPiece.Render(OPiece.Vector);
      }
    }

    So yes this gives you 9 planes but they are really just bitmaps when using them this way. Since TicTacToe does not require 3D, you could just as easily do this in DirectX using the hardware blitter.

  7. #7
    Registered User
    Join Date
    Aug 2003
    Posts
    288
    thanx again, but i didnt really get the last line
    you could just as easily do this in DirectX using the hardware blitter.
    the hardware blitter being? the only reason im using Direct3D is because im trying to make a 3d version of tic-tac-toe, im not going to make another 2d tic-tac-toe, there are too many lying around, so i thought id make mine special.

    But thanx alot for the help

  8. #8
    Super Moderator VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,596
    Well then use my textured plane class for the 3D version. Yes you will have 10 planes. 1 for the board, 9 for the X's and O's. An alternative would be to make the X's and O's complete 3D by creating them in a 3D program and loading them in your code as X File meshes. The way I've described will cause your pieces to look flat when rotated.

    Also the board should be a mesh - no need for a textured plane here. So if you don't need it that's ok. I would use meshes instead.

  9. #9
    Registered User
    Join Date
    Aug 2003
    Posts
    288
    perfect, thanx again, for the input (when its 3d) how exactly do i know which piece hes clicking? if the board is rotated and translated, i cant use my screen values, i read something about raycasting but i have no idea what it meant and if i needed to use it.

    if your not to busy can u please explain, or give any links that u might have

    thanx

  10. #10
    Super Moderator VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,596
    You need to cast a ray out into 3 space and then do a ray/sphere intersection test. I have source for this and several of my books explain this - it is called picking - simply because you are picking a 3D object with the mouse cursor.

    PM me and Ill help more.

    The theory and code are too long to post here.

    EDIT: I will post it anyways. It's for DirectX 9.0, specifically Direct3D.

    This is almost verbatim from the book Introduction to 3D Game Programming with Directx 9.0 by Frank D. Luna.

    Gotta give credit where credit is due. I will re-wrap this in my own class....but here is the basic idea.
    Code:
    struct Ray
    {
      D3DXVECTOR3 origin;
      D3DXVECTOR3 direction;
    };
    
    struct BoundingSphere
    {
      BoundingSphere();
      D3DXVector3      center;
      float                    radius;
    };
    
    Ray CalculatePickingRay(IDirect3DDevice9 *Device,int x,int y)
    {
      float px=0.0f;
      float py=0.0f;
    
      D3DVIEWPORT vp;
      Device->GetViewPort(&vp);
    
      D3DXMATRIX proj;
      Device->GetTransform(D3DTS_PROJECTION,&proj);
      
      px=(((2.0f*x)/vp.Width-1.0f)/proj(0,0);
      py=(((-2.0f*x)/vp.Width-1.0f)/proj(0,0);
    
      Ray ray;
     
      ray.origin=D3DXVECTOR3(0.0f,0.0f,0.0f);
      ray.direction=D3DXVECTOR3(px,py,1.0f);
    
      return ray;
    }
    
    void TransformRay(Ray *ray, D3DXMATRIX *T)
    {
      //transform origin, w=1
      D3DXVec3TransformCoord(&ray->origin,
                                                 &ray->origin,
                                                 T);
    
      //transform direction, w=0
      D3DXVec3TransformNormal(&ray->direction,
                                                  &ray->direction,
                                                  T);
    
      //normalize direction
      D3DXVecNormalize(&ray->direction,&ray->direction);
    
    }
    
    
    bool raySphereIntersect(Ray *ray,Boundingsphere *sphere)
    {
      D3DXVector3  v=ray->origin-sphere->center;
    
      float b=2.0f*D3DXVec3Dot(&ray->direction,&v);
      float c=D3DXVec3Dot(&v,&v)-(sphere->radius*sphere->radius);
    
      //Find discriminant
      float discriminant=(b*b)-(4.0f*c);
    
      if (discriminant<0.0f) return false;
    
      float s0=(-b+discriminant)/2.0f;
      float s1=(-b-discriminant)/2.0f;
    
      //if solution is >=0 we intersected a sphere
    
      if (s0>=0.0f || s1>=0.0f) return true;
    
      return false;
    }
    Now in your game....where CMouse is a class containing a valid pointer to a IDirectInputDevice8 interface (_pDID8) and Device is a valid pointer to an IDirect3DDevice9 interface.


    Code:
    
    bool CMouse::Pick(BoundingSphere *SphereToTest)
    {
      //Update mouse
      _pDID8->GetDeviceState(sizeof(DIMOUSESTATE),&mousestate);
    
      curmousex+=mousestate.lX;
      curmousey+=mousestate.lY;
    
      Ray ray=CalcPickingRay((int)curmousex,(int)curmousey);
    
      D3DXMATRIX View;
      Device->GetTransform(D3DTS_VIEW,&View);
    
      D3DXMATRIX viewInverse;
      D3DXMatrixInverse(&viewInverse,0,&View);
    
      TransformRay(&ray,&viewInverse);
    
      if (RaySphereIntersect(&ray,&SphereToTest))
      {
         //we hit the object
         return true;
      }
      return false;  
    }

    There are a lot of D3DX helper functions used here simply because I didn't want to re-invent the wheel. Anytime you see a D3DX prefix on a function call it is a helper function.

    You should be able to create the helper functions that I used on your own - they can be found in any 3D math book, game programming book, etc. I think www.gamedev.net even has a tutorial on matrices that will help you as well.

    This should work given you create a bounding sphere for each of your objects. If you want a more exact test:

    Code:
    1.  Test for ray/bounding sphere intersection.
    2.  If hit
         a.  Iterate through triangle list
         b.  Perform ray/triangle intersect on all tri's
         c.  If hit return true else return false.
    3.  If not hit in step 2 - return false;
    Obvious optimizations are using several bounding spheres for each object. First test the main sphere, then test the smaller spheres, then test only the triangles that fall within the smaller sphere that was hit. Much faster.

    I could write a 3D object class here for you but I leave that as an exerices for you. You can hold all of the bounding spheres by creating an array (BoundingSphere **Spheres) or any other structure that might work for you.
    Last edited by VirtualAce; 04-20-2004 at 08:57 PM.

  11. #11
    Registered User
    Join Date
    Aug 2003
    Posts
    288
    Thanks yet again Bubba,

    I just read through your code, and i had a few questions:

    1) For the sphere intersection test, how can i make it test, for example, if the ray is intersecting with a plane, you already know about the tic tac toe im working on, and i thought id give it a try on that first.

    I made 9 squares (planes), each made up of 2 triangles. How can i use your wrapper to test is the ray is intersecting with a specific triangle, or square.

    2) I know a ray has to be generated from a mouse point, so i was wondering, can i use the traditional GetCursorPos to determine the source of the ray? instead of using DirectInput, since i have yet to implement that.

    Thanks in advance, PaYnE

    [EDIT]

    Is the code you posted correct? I just read through it thoroughly and:

    Code:
    dxRay CalculatePickingRay(IDirect3DDevice9 *Device, int X, int Y)
    {
    	float px = 0.0f;
    	float py = 0.0f;
    
    	D3DVIEWPORT9 vp;
    	Device->GetViewport(&vp);
    
    	D3DXMATRIX proj;
    	Device->GetTransform(D3DTS_PROJECTION, &proj);
    
    	px = (((2.0f * X) / vp.Width - 1.0f) / proj(0, 0);
    	py = (((-2.0f * X) / vp.Width - 1.0f) / proj(0, 0);
    
    	dxRay Ray;
    
    	Ray.origin = D3DXVECTOR3(0.0f, 0.0f, 0.0f);
    	Ray.direction = D3DXVECTOR3(px, py, 1.0f);
    
    	return Ray;
    }
    Is Y even used? and:

    Code:
    py = (((-2.0f * X) / vp.Width - 1.0f) / proj(0, 0);
    
    shouldnt it be
    
    py = (((-2.0f * Y) / vp.Width - 1.0f) / proj(0, 0);
    Note: dxRay is just me renaming ur Ray struct
    Last edited by X PaYnE X; 04-22-2004 at 04:14 AM.

  12. #12
    Super Moderator VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,596
    Yes it should.


    And yes you can use any method you desire to get the mouse coords.

    To test for intersect with a plane or a triangle - which is really one in the same since any 3 points in 3D space will always create a plane - use Ax+By+Cz+D=0 and solve for what you need. I know this is vague but if you go to www.gamedev.net they have some code and tutorials there for testing interstections with a plane.

    My 3D math book is on it's way so I will be able to help much more then. I could attempt to derive it here but it would only serve to confuse you and that is not what I want to do.

    But for your purposes a sphere will do just fine.

    Another way to do this test...which will only work if you do not have any objects in front of the object you are picking...is to get it's screen coordinates from Direct3D. Then find the bounding box created by the screen coords by testing:

    Code:
    ...
    WORD maxy=0,maxx=0;
    WORD minx=65535,miny=65535;
    
    if (screen.x<minx) minx=screen.x;
    if (screen.x>maxx) maxx=screen.x;
    if (screen.y<miny) miny=screen.y;
    if (screen.y>maxy) maxy=screen.y;
    ...
    Now your bounding box is defined by these

    minx,miny,maxx,maxy

    (minx,miny)-------------------------------------(maxx,miny)
    .
    .
    .
    .
    .
    .
    .
    .
    .
    (minx,maxy)-------------------------------------(maxx,maxy)

    I didn't put the right side of the box in because the board does not do spaces right - what you see is not what you get.

    Then to test if the mouse cursor is in the box is a simple point in box test.

    The <> denotes a function to retrieve the variable specified

    Code:
    ...
    mx=<mouse.x>;
    my=<mouse.y>;
    Rect BoundingBox(minx,miny,maxx,maxy);
    
    if (mx>BoundingBox.left && mx<BoundingBox.right)
    {
       if (my>BoundingBox.top && my<BoundingBox.bottom)
       {
          //mouse cursor is in the specified rectangle 
       }
    }
    ...

    Notice I said this will only work if there are no objects overlapping the object being picked or selected. If there is it is still no problem except that this code will only pick the object being tested...it is not based on z depth. And since the z buffer does not have any way of knowing which object the z coord in the buffer belongs to...we can't use the z buffer to select objects.

    However, I find the picking algorithm to be extremely complex and you would do better to come up with one yourself. I do not feel it is necessary to fire a ray into 3 space just to pick an object. Since we already know it's screen coords due to the transformation pipeline...we should be able to extract a 2D bounding box from those coords. Now getting Direct3D to allow you to do this may not be the simplest thing and might require locking and unlocking a vertex buffer. Look in the DirectX 9.0 SDK for functions relating to getting access to the already transformed vertex's members - like final screen coordinates or screen space.

    The z problem can then be solved quite easily. Create a bounding box for all screen objects that lie near the mouse cursor - near is a relative term and the exact distance to test is purely up to you. Then when you test...find out which of those objects is closest by looking at it's view coords. You can get an average z distance by adding the vertexes z component and dividing by the number of vertexes or you can simply use the center of the object for z. So if object1 and object2 both lie within the prescribed distance of the mouse...test object2's depth against object1's. You know what to do from there. This might introduce new problems...but to me it is much better than firing a ray into 3 space...doing an inverse projection transform, etc., etc.

    Try it and see what you get. I'd be interested to see what you come up with.














    if (screen.x)

  13. #13
    Super Moderator VirtualAce's Avatar
    Join Date
    Aug 2001
    Posts
    9,596
    Did some more research and it seems that Microsoft is actually your friend in this instance.


    Their are loads of D3DX helper functions that will help you do this exact thing with much less code.

    Check out the SDK.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. making sprites
    By DavidP in forum Game Programming
    Replies: 9
    Last Post: 02-20-2010, 06:00 AM
  2. DirectX and Sprites
    By andyhunter in forum Game Programming
    Replies: 6
    Last Post: 12-24-2004, 05:40 PM
  3. I Found some nice sprites but need more...
    By BigCheese in forum C++ Programming
    Replies: 1
    Last Post: 03-23-2004, 05:03 PM
  4. drawing sprites with the windows gdi
    By lambs4 in forum Game Programming
    Replies: 10
    Last Post: 08-14-2003, 09:06 AM
  5. Animating Multiple Sprites
    By Tommaso in forum Game Programming
    Replies: 2
    Last Post: 10-11-2002, 12:06 AM

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21