Sign in to follow this  
GotenRulezU

Question about drawing triangles.

Recommended Posts

GotenRulezU    100
I have been experimenting with dx a lil bit here lately trying to learn it slowly and thouroughly as i forget crap all the time(i've tried to learn dx before) So I start by drawing triangles to the screen but I have a problem when I draw triangles to the screen they are using the last vertices in the previous triangle as the first vert of the current one. how can I draw the triangles seperate? Leme show you what my code was and you can tell me what I need to do in order to make it so I can draw triangles seperate from each other. BTW I store all my triangles in a vector so I can add and remove them dynamically.
/////////////THIS IS MY PROGRAM CLASS////////////////////
class Program{
private:
	IDirect3D9 *pD3D;
	IDirect3DDevice9 *pDevice;
	IDirect3DVertexBuffer9 *pVB;
	D3DCOLOR bgColor;
	vector<tri> trilist;
public:
	HRESULT InitD3D(bool bWindowed, HWND hwnd){
		pD3D = Direct3DCreate9(D3D_SDK_VERSION);
		if(!pD3D){
			CatchErrors("Error initializing D3D");
			return E_FAIL;
		}
		D3DPRESENT_PARAMETERS d3dpp;
		D3DDISPLAYMODE d3ddm;
		ZeroMemory(&d3ddm,sizeof(D3DDISPLAYMODE));
		pD3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm);
		ZeroMemory(&d3dpp,sizeof(D3DPRESENT_PARAMETERS));
		d3dpp.BackBufferCount = 1;
		d3dpp.MultiSampleType = D3DMULTISAMPLE_NONE;
		d3dpp.MultiSampleQuality = 0;
		d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
		d3dpp.hDeviceWindow = hwnd;
		d3dpp.Flags = 0;
		d3dpp.FullScreen_RefreshRateInHz = D3DPRESENT_RATE_DEFAULT;
		d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT;
		d3dpp.BackBufferFormat = d3ddm.Format;
		d3dpp.EnableAutoDepthStencil = FALSE;
		if(bWindowed){
			d3dpp.Windowed = TRUE;
		}
		else{
			RECT rect;
			GetWindowRect(hwnd,&rect);
			int bbHeight = rect.bottom - rect.top;
			int bbWidth = rect.right - rect.left;
			d3dpp.BackBufferHeight = bbHeight;
			d3dpp.BackBufferWidth = bbWidth;
			d3dpp.Windowed = FALSE;
		}
		if(FAILED(pD3D->CreateDevice(D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,hwnd,
				D3DCREATE_SOFTWARE_VERTEXPROCESSING,&d3dpp,&pDevice))){
			CatchErrors("Error creating device!");
			return E_FAIL;
		}
		bgColor = D3DCOLOR_COLORVALUE(1,0,0,0);
		pDevice->SetFVF(point_fvf);
		pDevice->SetRenderState(D3DRS_FILLMODE,D3DFILL_SOLID);
		return S_OK;
	}
	void move(){
	}
	HRESULT ShutdownD3D(){
		if(pD3D != NULL){
			if(FAILED(pD3D->Release())){
				CatchErrors("Error shutting down D3D");
				return E_FAIL;
			}
		}
		if(pDevice != NULL){
			if(FAILED(pDevice->Release())){
				CatchErrors("Error shutting down D3D Device");
				return E_FAIL;
			}
		}
		if(pVB != NULL){
			if(FAILED(pVB->Release())){
				CatchErrors("Error shutting down D3D VB");
				return E_FAIL;
			}
		}
		return S_OK;
	}
	HRESULT RenderD3D(){
		bgColor = current;
		if(FAILED(pDevice->Clear(0,NULL,D3DCLEAR_TARGET,bgColor,1.0f,0))){
			CatchErrors("Failed to clear!");
			return E_FAIL;
		}
		pDevice->BeginScene();
		for(int i = 0; i < trilist.size(); i++){

			if(FAILED(pDevice->DrawPrimitiveUP(D3DPT_TRIANGLELIST,1,&trilist.at(i),sizeof(tri)))){
				CatchErrors("failed to draw");
				return E_FAIL;
			}

		}
		pDevice->EndScene();
		pDevice->Present(NULL,NULL,NULL,NULL);
		return S_OK;
	}
	void CatchErrors(string error){
		MessageBox(NULL,error.c_str(),"Error",MB_OK);
		ShutdownD3D();
	}
	void SetBGColor(D3DCOLOR iColor){
		bgColor = iColor;
	}
		
	void AddFirstTriangle(int x1, int y1, int x2, int y2, int x3, int y3, D3DCOLOR color){
		tri temp[3];
		temp[0].color = color;
		temp[0].rhw = 1;
		temp[0].x = x1;
		temp[0].y = y1;
		temp[0].z = 1;
		trilist.push_back(temp[0]);
		temp[1].color = color;
		temp[1].rhw = 1;
		temp[1].x = x2;
		temp[1].y = y2;
		temp[1].z = 1;
		trilist.push_back(temp[1]);
		temp[2].color = color;
		temp[2].rhw = 1;
		temp[2].x = x3;
		temp[2].y = y3;
		temp[2].z = 1;
		trilist.push_back(temp[2]);
	}
	void AddTriangle(int x1, int y1, int x2, int y2, D3DCOLOR color){
		tri temp[2];
		temp[0].color = color;
		temp[0].rhw = 1;
		temp[0].x = x1;
		temp[0].y = y1;
		temp[0].z = 1;
		trilist.push_back(temp[0]);
		temp[1].color = color;
		temp[1].rhw = 1;
		temp[1].x = x2;
		temp[1].y = y2;
		temp[1].z = 1;
		trilist.push_back(temp[1]);
	}


}program;

in the main function I create two triangles like so
	program.AddFirstTriangle(0,0,150,0,0,150,D3DCOLOR_COLORVALUE(0,1,0,0));
	program.AddTriangle(150,300,0,300,D3DCOLOR_COLORVALUE(1,0,0,0));

Thx for any input you can give me good/ bad about my program. In my finished project I hope to have a basic wrapper to make d3d more easier to use for me. thx, -Cory

Share this post


Link to post
Share on other sites
Mushu    1396
If you want each triangle to be separate, you need 3 vertices for each triangle. In your setup to render 2 triangles, you only supply 5 vertices. You don't need the "addFirstTriangle"/"addTriangle" setup - just make addTriangle add 3 vertices (like addFirstTriangle currently does) and use it exclusively. You'd do otherwise if you wantede to reuse vertices, like for a triangle strip.

Additionally, your render loop is a little bork bork:

for(int i = 0; i < trilist.size(); i++){

if(FAILED(pDevice->DrawPrimitiveUP(D3DPT_TRIANGLELIST,1,&trilist.at(i),sizeof(tri)))){
CatchErrors("failed to draw");
return E_FAIL;
}

}


What you have there is a request to draw a triangle for every vertex in the vector. I'm not sure why you did it that way, but this may prove more successful:
pDevice->DrawPrimitiveUP(D3DPT_TRIANGLELIST,trilist.size()/3,&trilist.at(i),sizeof(tri))

Note that this removes the loop entirely, and just gives the whole array to D3D to process. Also note the trilist.size()/3. Because we're using <ttD3D_TRIANGLE_LIST we're telling D3D that each 3 vertices defines a triangle. Thus, the number of triangles we wish to draw is equal to the number of vertices (trilist.size()) divided by 3.

I'd also rename "trilist" to something else - its not a list of triangles (AFAIK), but rather a list of vertices.

Hope that helps some.

Share this post


Link to post
Share on other sites
GotenRulezU    100
thx a lot for the help i originally ahd only an add triangle and not the one with 2 verts i changed it cuz i thought d3d was like dat ;) btw my render loop was "bork bork" on accident. this is better correct?
for(int i = 0; i < trilist.size(); i+=3)
edit: also i forgot to say i named it trilist because i may want to use dots instead of triangles and to draw the dots would be only 1 vertex per draw so i have to keep dots and triangles separate right?
thx again,
-Cory

Share this post


Link to post
Share on other sites
Zahlman    1682
Answer to question:

That's how drawing a D3DPT_TRIANGLELIST interprets the data. The idea is to create a "mesh" of triangles that are connected to each other and approximate a surface. If you want to draw separate triangles, you could try making separate vectors of points (with just the three points specified) and drawing each one separately; but it's probably a much better idea to look for other flags to pass to DrawPrimitiveUP() rather than D3DPT_TRIANGLELIST (and set up corresponding data). You do know about MSDN, right?

Answers to problems:

- Functions with names that include "init" and "shutdown" are smelly. What you want to do is "init" stuff in the object's constructor, and "shutdown" stuff in the destructor. That way, the relevant functions are called automatically at appropriate times (the beginning end of the object's lifetime), so you can't forget.


class Program {
Program(bool bWindowed, HWND hwnd) { // instead of InitD3D
// ...
}
~Program() { // instead of ShutdownD3D
// ...
}
};


- Don't return an HRESULT and have this common CatchErrors() function setup. I know that's how Windows does lots of its stuff internally, but it's not very maintainable. These 'errors' are exceptional circumstances, right? (i.e. things that "shouldn't" happen, and are beyond your control?) Besides... CatchErrors() already told the user that something failed. What is the calling code going to do when it sees E_FAIL? Report again? Abort the program? Now we're handling the problem in two separate places.

So, in C++, we handle exceptional conditions with... exceptions.

e.g.


class Program {
class D3DException : public std::runtime_error {};

Program(bool bWindowed, HWND hwnd){
pD3D = Direct3DCreate9(D3D_SDK_VERSION);
if (!pD3D) {
throw D3DException("Error initializing D3D");
}
// other stuff
// no return statement at end
}

// etc.
};

// In the main loop

int main() {
try {
Program p;
p.doLotsOfStuff();
// The destructor of p will be called whether or not there
// was an exception...
} catch (Program::D3DException& ex) {
// If an exception is thrown, then all destructors are called
// for everything in-progress except main(), then this part
// is done, and finally destructors are called for the local
// variables of the try {} scope.
MessageBox(NULL, ex.what(), "Everything died horribly", MB_OK);
}
}


- However, throwing exceptions from the destructor (directly or indirectly) is a big no-no, so we need to use the "normal" handling there (except we don't return anything, because there isn't anything we can do if shutdown fails). But we can still make things easier for ourselves with a utility function:


class Program {
// ...

private:
template <typename T>
void cleanup(T* D3DThing, const char* name) {
if (D3DThing) {
if (FAILED(D3DThing->Release())) {
MessageBox(NULL, name, "I can't even shut down"
" properly, but it's too late to do anything about that anyway", MB_OK);
}
}
}

public:
~Program() {
cleanup(pD3D, "Error shutting down D3D");
cleanup(pDevice, "Error shutting down D3D Device");
cleanup(pVB, "Error shutting down D3D VB");
}

// ...
};


- Similarly, you can make a constructor for your point structure (why is it called 'tri'?), to make the loading code shorter. Also, there's no point loading those things into a temporary array when you're just going to push them back on the vector.


struct point {
int x;
int y;
int z;
int rhw;
D3DCOLOR color;

point(int x, int y, int z, int rhw, D3DCOLOR color) : x(x), y(y), z(z),
rhw(rhw), color(color) {}
};


// ...
void AddFirstTriangle(int x1, int y1, int x2, int y2, int x3, int y3, D3DCOLOR color){
trilist.push_back(point(x1, y1, 1, 1, color));
trilist.push_back(point(x2, y2, 1, 1, color));
trilist.push_back(point(x3, y3, 1, 1, color));
}
void AddTriangle(int x1, int y1, int x2, int y2, D3DCOLOR color){
trilist.push_back(point(x1, y1, 1, 1, color));
trilist.push_back(point(x2, y2, 1, 1, color));
}


- The interface kinda sucks because the calling code has to think about whether or not the triangle it's inserting is first. One way to get around that is to always use the 3-point version, and if there is already some stuff in the vector, make sure that point 0 matches the last point in the vector, and then just insert the other two.

Share this post


Link to post
Share on other sites
Mushu    1396
Quote:
Original post by GotenRulezU
thx a lot for the help i originally ahd only an add triangle and not the one with 2 verts i changed it cuz i thought d3d was like dat ;) btw my render loop was "bork bork" on accident. this is better correct?
for(int i = 0; i < trilist.size(); i+=3)
edit: also i forgot to say i named it trilist because i may want to use dots instead of triangles and to draw the dots would be only 1 vertex per draw so i have to keep dots and triangles separate right?
thx again,
-Cory

No, there's no reason to use a loop like that at all. Let D3D handle the stuff for you - this will reduce the number of draw calls you make per pass and increase the performance of your application:

HRESULT RenderD3D(){
bgColor = current;
if(FAILED(pDevice->Clear(0,NULL,D3DCLEAR_TARGET,bgColor,1.0f,0))){
CatchErrors("Failed to clear!");
return E_FAIL;
}
pDevice->BeginScene();

if(FAILED(pDevice->DrawPrimitiveUP(D3DPT_TRIANGLELIST,trilist.size()/3,&trilist.at(i),sizeof(tri)))){
CatchErrors("failed to draw");
return E_FAIL;
}

}
pDevice->EndScene();
pDevice->Present(NULL,NULL,NULL,NULL);
return S_OK;
}




Notice the for loop you had is now replaced by a single draw call per pass, as opposed to a draw per triangle.

Quote:
That's how drawing a D3DPT_TRIANGLELIST interprets the data. The idea is to create a "mesh" of triangles that are connected to each other and approximate a surface. If you want to draw separate triangles, you could try making separate vectors of points (with just the three points specified) and drawing each one separately

Actually, no. D3DPT_TRIANGLELIST is the one for isolated triangles. I think his problem was he wasn't feeding it enough vertices (in addition to orignally having a really weird loop).

If, rather, you want to draw 'dots' you should consider using D3DPT_POINTLIST in place of D3DPT_TRIANGLELIST.

Share this post


Link to post
Share on other sites
GotenRulezU    100
i havent compiled this but wouldnt it fail?
[quote]

HRESULT RenderD3D(){
bgColor = current;
if(FAILED(pDevice->Clear(0,NULL,D3DCLEAR_TARGET,bgColor,1.0f,0))){
CatchErrors("Failed to clear!");
return E_FAIL;
}
pDevice->BeginScene();

if(FAILED(pDevice->DrawPrimitiveUP(D3DPT_TRIANGLELIST,trilist.size()/3,&trilist.at(i),sizeof(tri)))){
CatchErrors("failed to draw");
return E_FAIL;
}

}
pDevice->EndScene();
pDevice->Present(NULL,NULL,NULL,NULL);
return S_OK;
}


[/quote]
i think it would say i undeclared identifier.
thx for both of your input it is VERY EXTREMELY SUPER HELPFULL.
-CORY

Share this post


Link to post
Share on other sites
Mushu    1396
Oops, my bad. You want to give it the index of the first element, so use this line for the draw call -

pDevice->DrawPrimitiveUP(D3DPT_TRIANGLELIST,trilist.size()/3,&trilist.at(0),sizeof(tri))

That should, I think, do the trick :)

Share this post


Link to post
Share on other sites
Mushu    1396
It will continue reading from that pointer (to the index of the first element) until it has the amount of information specified by the number of primitives you request. Since we're asking it to render trilist.size()/3 and each primitive is 3 vertices, it will read

trilist.size()/3 * 3 == trilist.size()

vertices, so, yes. It will traverse the entire vector.

Note this only works because a vector is guarenteed to be sequential memory. Using something like std::list or std::map will have undefined results at best.

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