Skip to main content
GameDev.net gamedev.net
🔒 Locked

drawing pixels ok this way?

Started by Schildpad Jun 18, 2005 at 6:22 AM 12 replies 2.7k views
Original Post
Schildpad
Schildpad
I didn't like using the Direct3D sprite device because I didn't really know what is going on. I like to know what I'm doing. So I thought, I'll write my own class. So I went through the articles and found an interesting one on writing directly into video memory with direct draw. This would give me a few extra advantages too. What I do is the following: 1. Create Direct3D Device 2. Get the Device's backbuffer (GetBackBuffer()) 3. Start loop 3.1 LockRect() 3.2 write into video memory 3.3 UnlockRect() 3.4 Present() Now my questions: 1. Is this a good way to do this? if not, how IS a good way? 2. Does pitch / 4 work on all video cards? because I saw somewhere saying that the pitch can be different on some video cards. Something about a few more bytes per line. Maybe this new pitch thing is to solve that problem. ow yeah, I'm using DirectX 9 SDK.
DevLiquidKnight
DevLiquidKnight
LockRect and UnlockRect tends to be bad (so ive heard) you can do that though I guess. Im not sure of a good way to do it but this will at least give you an idea. Perhaps surface offscreen rendering?
Stevieboy
Stevieboy
You can draw pixels directly to the backbuffer surface (or use surfaces directly), as you suggest, but you won't be doing much more than directdraw, (not as good either), there probably are uses for it (i'm trying to use surfaces myself at the moment to "concatenate" letters to save as words for my own text/font thing).

Try using quads instead, a quad is just two triangles togather, also use the rhw setting, this means the lighting etc, for the quad is taken care of.


struct Vertex
{
float x, y, z;
float rhw;
D3DCOLOR color; // The color
float tu, tv; //use to adjust the textures position on the quad/triangle
};

#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1)

//Globals
//You need most of the ordinary stuff including a texture;
LPDIRECT3DTEXTURE9 m_pTexture;
char *filename;


Vertex *vertices;


TuStart = 0.0f;//this gets whole texture to show properly
TuEnd = 1.0f;
TvStart = 0.0f;
TvEnd = 1.0f;

float x, y, cx, cy;//how ever big you want the quad

void InitVertices(TuStart,TuEnd,TvStart,TvEnd,x,y,cx,cy)
{
Vertices = new Vertex[4];
//top left
Vertices[2].x = x; //x start size/position onscreen
Vertices[2].y = y; //y start size/position onscreen
Vertices[2].z = 0.5f;
Vertices[2].rhw = 1.0f;
Vertices[2].color = D3DCOLOR_XRGB(250,250,250);
Vertices[2].tu = TuStart;
Vertices[2].tv = TvStart;

//bottom left
Vertices[0].x = x;
Vertices[0].y = y+cy; //y start size + endsize -how big its going to be.
Vertices[0].z = 0.5f;
Vertices[0].rhw = 1.0f;
Vertices[0].color = D3DCOLOR_XRGB(250,250,250);
Vertices[0].tu = TuStart;
Vertices[0].tv = TvEnd;

//bottom right
Vertices[1].x = x+cx;
Vertices[1].y = y+cy;
Vertices[1].z = 0.5f;
Vertices[1].rhw = 1.0f;
Vertices[1].color = D3DCOLOR_XRGB(250,250,250);
Vertices[1].tu = TuEnd;
Vertices[1].tv = TvEnd;

//top right
Vertices[3].x = x+cx;
Vertices[3].y = y;
Vertices[3].z = 0.5f;
Vertices[3].rhw = 1.0f;
Vertices[3].color = D3DCOLOR_XRGB(250,250,250);
Vertices[3].tu = TuEnd;
Vertices[3].tv = TvStart;

}


void LoadTexture(LPDIRECT3DDEVICE9 mp_d3dDevice, char *texturename)
{
filename = texturename;
p_d3dDevice = mp_d3dDevice;
D3DXCreateTextureFromFile( p_d3dDevice, filename, &m_pTexture );
}


void FillInTehVB()
{
p_d3dDevice->CreateVertexBuffer( 4*sizeof(Vertex),
0, D3DFVF_CUSTOMVERTEX,
D3DPOOL_DEFAULT, &p_VertexBuffer, NULL );

VOID* pVertices;
p_VertexBuffer->Lock( 0, 4*sizeof(Vertex), (void**)&pVertices, 0 );

memcpy( pVertices, Vertices, 4*sizeof(Vertex));
p_VertexBuffer->Unlock();

}

void SetSomeTextureStages()
{
p_d3dDevice->SetTextureStageState(0, D3DTSS_CONSTANT, D3DTEXF_LINEAR );
p_d3dDevice->SetTextureStageState(0, D3DTSS_CONSTANT, D3DTEXF_LINEAR );

)

void DrawVB
{

p_d3dDevice->SetTexture( 0, m_pTexture );

// Render the vertex buffer contents
p_d3dDevice->SetStreamSource( 0, p_VertexBuffer, 0, sizeof(Vertex) );
p_d3dDevice->SetFVF( D3DFVF_CUSTOMVERTEX );
p_d3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 2);

}

Most of this won't copy & paste, you have to set it up your self properly, but this is the idea, most of the functions would go before the game loop, (after InitD3D), then the DrawVB can be called during the Render loop.
(p_d3ddevice is the same as g_pd3dDevice, mine has just come from my own texture class, thats why its different.)
circlesoft
circlesoft
Quote:
Original post by DevLiquidKnight
LockRect and UnlockRect tends to be bad (so ive heard) you can do that though I guess. Im not sure of a good way to do it but this will at least give you an idea. Perhaps surface offscreen rendering?


Right - locking textures at runtime is very, very bad. It is incredibly slow, because all of the data must be transferred over the AGP bus (and back again, if you are updating). If at all, you should only be doing it at loadtime.

If you want a slight idea of how ID3DXSprite works, check out Stevie's post. It *does not* lock the backbuffer and plot the pixels right onto it. Instead, it creates 2 triangles, forming a quad, and renders it. Actually, it's a bit more complicated than that, since ID3DXSprite batches all of the drawing calls to improve performance.

It is undoubetdly a great thing to understand how the underlying interfaces work. Not only does it increase your knowledge of graphics programming, but it also allows you to utilize the interfaces in questions at a higher level. However, this does not mean that you should be recreating the wheel. ID3DXSprite is a very efficient sprite renderer and does a lot more than simply push textured quads to the GPU. If you have the means, you should be using it.
Schildpad
Schildpad
Quote:
It is undoubetdly a great thing to understand how the underlying interfaces work. Not only does it increase your knowledge of graphics programming, but it also allows you to utilize the interfaces in questions at a higher level.

I'll check it out.

Quote:
Most of this won't copy & paste

I never copy, paste things I don't understand. ;)
Waaay to much trouble getting something to work that way, and definitly anoying not knowing what you're actually doing.

// Edit: checked it
I allready did this once. I thought it was nice for textures, but I also want to draw things like ovals and lines. For lines and circles I would need to draw 1 pixel, that's why I started the whole draw 1 pixel thing. Is creating a vertex that is 1 pixel high and 1 pixel wide still faster than locking and unlocking every time? Or is there a better way to draw circles and lines?

thx for hanging on with me here :)
Spoonbender
Spoonbender
It might be useful if you can say exactly what it is you need to do.
Sounds to me like you'd be better served with GDI than DirectX. Sure, it's slow, but not as slow as trying to plot each individual pixel.

-indirectX-
-indirectX-
Hi,
As long as you only lock and unlock the buffer once every frame, there shouldn't be any problems - where you "write into video memory" make sure you do everything you need, there should be no problems.

As for the vertical pitch of the screen, it all gets very complicated with bytes per pixel and, as you say, extra bytes on each row but if you're using Direct3D, it should take care of everything for you. If it doesn't, I think the information about the 'memory pitch' is in the Surface Description (SurfaceDesc), although I can't remember specifically how to get at it in Direct3D.

Have Fun!

-indirectX-
--------------------------------www.hughosborne.co.uk[email=hugh.osborne@googlemail.com]hugh.osborne@googlemail.com[/email]
mikeschuld
mikeschuld
Instead of just writing your own class with a new method I would have just done research to figure out what directX is doing. Maybe you wouldn't have a problem with it once you knew what the internals were up to. You never know what you might have learned along the way either. :)
--------------------------------------------------------------------------Michael Schuld
Schildpad
Schildpad
Quote:
Original post by mikeschuld
Instead of just writing your own class with a new method I would have just done research to figure out what directX is doing. Maybe you wouldn't have a problem with it once you knew what the internals were up to. You never know what you might have learned along the way either. :)

Yeah I know, but DirectX doesn't have a way of drawing ovals or lines.

// edit: ok, it has lines, now I feel ashamed of myself :D, but still, how do I do ovals? Ovals aren't really necesary, but still ...
jesteve
jesteve
Maybe you could try getting the surface's graphic context (in managed languages) or hwnd (in native ones) and use GDI functions to draw ellipses. Horribly slow but is a way :D
Schildpad
Schildpad
I'll just forget about elipses then, rather have none than slow ones.
thx for the help everyone, I'll manage now.
Toraque
Toraque
For an oval, or any reqular geometric shape, you could easily use a sprite with an alpha texture. If the oval shape has to be computed at runtime, you can try some transforms on the sprite to stretch or skew it.
DvDmanDT
DvDmanDT
Another thing.. Unless I'm misstaken, you can make a VB of pixels you wanna draw, then use like D3DPT_something to draw pixels.. It's not impossible that I misunderstood the purpose of that D3DPT though.. Maybe it was called D3DPT_POINT? Or is that only for point sprites?
deadimp
deadimp
Yeah, I've been wondering about this too. I'm not sure whether it would be more efficient to do it DvDmanDT's way with a vertex buffer using D3DPT_POINTLIST (you were close) in IDirect3DDevice8::DrawPrimitive (or whichever version you are using), or simply unlocking the destination surface. Also, does anyone know of any other way to retrieve pixel values?
As for the elliptical stuff, here is the basic function I made for it. It's almost as basic as you can get, and the ratio of points to size is kinda off (it is less detailed the smaller it is...). I'm not sure it is the most efficient, but it works for me.
BYTE DGID_ELLIPSE_DIV=1; //Used to lower the 'resolution' of ellipses (including circles)bool draw_ellipse(long x1, long y1, long x2, long y2,D3DCOLOR *colors,DGIC_FLAG flags) { view_align(x1,y1,x2,y2); if (x2<0 || x1>DGI_WIDTH || y2<0 || y1>DGI_HEIGHT) return false; //Not to be drawn foo! if (colors==NULL) { colors=&brush_color; flags=DGIC_COL1; } //Fun shtuff right heeyah float cx, cy, rx, ry; rx=(x2-x1)/2; ry=(y2-y1)/2; //Skip the uber complicated stuff cx=x1+rx; cy=y1+ry; //I don't think Ramanujan'll slap me for this (http://www.csgnetwork.com/mthcircumellipse.html) ULONG points=ceil((pi*2*sqrt(fabs(rx*rx+ry*ry)/2))/DGID_ELLIPSE_DIV); if (points<3) return false; //No point in simply drawing a line (ha ha, pun) //Eep... floating point fun, here I come. Yippee. float astep=(pi*2)/points, angle=0; vector<D3DCUSTOMVERTEX> vPoints; D3DCUSTOMVERTEX vPoint={cx,cy,0.0f,1.0f,*colors}; vPoints.push_back(vPoint); //Add center point for (ULONG i=0;i<=points;i++) {  vPoint.x=cx+(rx*cos(angle)); vPoint.y=cy-(ry*sin(angle));  if (flags & DGIC_COL2) vPoint.color=colors[1];  else vPoint.color=*colors;  vPoints.push_back(vPoint);  angle+=astep; } D3DMakeVertexBuffer(vPoints); return D3DDrawVertexPrimitive(D3DDVP_ELLIPSE,points+1); //Remember center point}

NOTE: D3DMakeVertexBuffer, D3DDrawVertexPrimitive are functions I made to interact with a global vertex buffer (because I'm lazy). For circles, 'rx' and 'ry' could be replaced simply by 'r' (which should be an argument). Also, my D3DDVP_ELLIPSE flag makes sure the vertex drawing utilizes D3DPT_TRIANGLEFAN.
ANOTHER NOTE: Ignore the coloring flags and view alignment. You get the basic idea.
Projects:> Thacmus - CMS (PHP 5, MySQL)Paused:> dgi> MegaMan X Crossfire

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.