Drawing 2D Line

You are an experienced programmer and have a problem with the engine, shaders, or advanced effects? Here you'll get answers.
No questions about C++ programming or topics which are answered in the tutorials!
Post Reply
AticAtac
Posts: 29
Joined: Mon Mar 22, 2004 2:46 pm
Location: Germany

Drawing 2D Line

Post by AticAtac »

I needed a 2D Line drawing function and extended irrlicht for this purpose
(only DirectX9 interface, feel free to do the same for the other ones).

First i changed the IVideoDriver::draw2DLine(...) so it can do clipping:

Code: Select all

virtual void draw2DLine(const core::position2d<s32>& start,
								const core::position2d<s32>& end, 
								SColor color=SColor(255,255,255,255),
								const core::rect<s32>* clip = 0) = 0;
Then change in CVideoSoftware.h

Code: Select all

//! Draws a 2d line. 
		virtual void draw2DLine(const core::position2d<s32>& start,
								const core::position2d<s32>& end, 
								SColor color=SColor(255,255,255,255),
								const core::rect<s32>* clip = 0);
Next change in CVideoNull.cpp/h :
New Function which does a 2d clipping line <-> rectangle:
This function belongs to class CVideoNull in CVideoNull.h as a protected function:

Code: Select all

//! Clip a 2D Line to 2D Rectangle : 0: outside, 1:inside, 2:clipped
		int clip2DLineRect(const core::rect<s32>* clip, core::position2d<s32>& p1, core::position2d<s32>& p2);
Implementation in CVideoNull.cpp: (using cohen-sutherland algo)

Code: Select all

//! Clip a 2D Line to 2D Rectangle : 0: outside, 1:inside, 2:clipped
int CVideoNull::clip2DLineRect(const core::rect<s32>* clip, core::position2d<s32>& p1, core::position2d<s32>& p2)
{
	enum { LEFT=1, RIGHT=2, BOTTOM=4, TOP=8};
	#define OUTCODE(x, y, rc, c) \
	c = 0; \
	if (y < rc->UpperLeftCorner.Y) c |= TOP; \
	else if (y > rc->LowerRightCorner.Y) c |= BOTTOM; \
	if (x < rc->UpperLeftCorner.X) c |= LEFT; \
	else if (x > rc->LowerRightCorner.X) c |= RIGHT;

	char outcode1=0;
	char outcode2=0;
	char outside;
	core::position2d<s32> tmp;	
	
	// do trivial rejects and outcodes
	if (p1.Y > clip->LowerRightCorner.Y)
		outcode1 = BOTTOM;
	else if (p1.Y < clip->UpperLeftCorner.Y)
		outcode1 = TOP;
	if (p2.Y > clip->LowerRightCorner.Y)
		outcode2 = BOTTOM;
	else if (p2.Y < clip->UpperLeftCorner.Y)
		outcode2 = TOP;
	// trivially outside
	if (outcode1 & outcode2)
		return 0;
	
	if (p1.X < clip->UpperLeftCorner.X)
		outcode1 |= LEFT;
	else if (p1.X > clip->LowerRightCorner.X)
		outcode1 |= RIGHT;
	if (p2.X < clip->UpperLeftCorner.X)
		outcode2 |= LEFT;
	else if (p2.X > clip->LowerRightCorner.X)
		outcode2 |= RIGHT;
	// trivially outside
	if (outcode1 & outcode2)
		return 0;

	// trivially accept
	if (outcode1 == 0 && outcode2 == 0)
		return 1;

	while (outcode1 | outcode2)
	{
		// may be partially inside box, find an outside point
		if (outcode1)
			outside = outcode1;
		else
			outside = outcode2;
		// clip each side
		if (outside & TOP) // y=y0+t(y1-y0)
		{
			tmp.Y = clip->UpperLeftCorner.Y;			
			tmp.X = p1.X + s32( f32(p2.X-p1.X) * f32(tmp.Y-p1.Y) / f32(p2.Y-p1.Y));
		} else if (outside & BOTTOM)
		{
			tmp.Y = clip->LowerRightCorner.Y;
			tmp.X = p1.X + s32( f32(p2.X-p1.X) * f32(tmp.Y-p1.Y) / f32(p2.Y-p1.Y));
		} else if (outside & RIGHT)
		{
			tmp.X = clip->LowerRightCorner.X;
			tmp.Y = p1.Y + s32( f32(p2.Y-p1.Y) * f32(tmp.X-p1.X) / f32(p2.X-p1.X));			
		} else if (outside & LEFT)
		{
			tmp.X = clip->UpperLeftCorner.X;
			tmp.Y = p1.Y + s32( f32(p2.Y-p1.Y) * f32(tmp.X-p1.X) / f32(p2.X-p1.X));
		}

		if (outside == outcode1)
		{
			p1 = tmp;
			OUTCODE(p1.X, p1.Y, clip, outcode1)
		} else
		{
			p2 = tmp;
			OUTCODE(p2.X, p2.Y, clip, outcode2)
		}

		if (outcode1 & outcode2)
			return false;
	}

	return 2;
}
Now the implementation of draw2DLine(...) in CVideoDirectX9.cpp :

Code: Select all

//! Draws a 2d line. 
void CVideoDirectX9::draw2DLine(const core::position2d<s32>& start,
								const core::position2d<s32>& end, 
								SColor color,
								const core::rect<s32>* clip)
{	
	core::position2d<s32> spos = start;
	core::position2d<s32> epos = end;
	if (clip)
	{
		if (clip2DLineRect(clip, spos, epos) == 0)
			return;
	}
	s32 xPlus = -(ScreenSize.Width>>1);
	f32 xFact = 1.0f / (ScreenSize.Width>>1);
	s32 yPlus = ScreenSize.Height-(ScreenSize.Height>>1);
	f32 yFact = 1.0f / (ScreenSize.Height>>1);
	S3DVertex vtx[2];
	vtx[0] = S3DVertex((f32)(spos.X+xPlus) * xFact, (f32)(yPlus-spos.Y) * yFact , 0.0f, 0.0f, 0.0f, 0.0f, color, 0.0f, 0.0f);
	vtx[1] = S3DVertex((f32)(epos.X+xPlus) * xFact, (f32)(yPlus- epos.Y) * yFact, 0.0f, 0.0f, 0.0f, 0.0f, color, 0.0f, 1.0f);
	s16 indices[2] = {0,1};
	setRenderStates2DMode(color.getAlpha() < 255, false, false);
	setTexture(0,0);
	setVertexShader(EVT_STANDARD);
	pID3DDevice->DrawIndexedPrimitiveUP(	D3DPT_LINELIST, 0, 2, 1, &indices[0],
											D3DFMT_INDEX16, &vtx[0], sizeof(S3DVertex));
}
Things to do:
- adjust the software draw2DLine(...) to do clipping (should be easy)
- implement OpenGL, DirectX8 versions of draw2DLine
AticAtac
Posts: 29
Joined: Mon Mar 22, 2004 2:46 pm
Location: Germany

Post by AticAtac »

I don't know if anyone could need this (at least i needed it), here is
another 2D Line version for a batch of lines:

Code: Select all

void CVideoDirectX9::draw2DLine(S3DVertex* v, s32 numv, const core::rect<s32>* clip)
{
	S3DVertex* v2 = new S3DVertex[numv];
	s16* indices;
	s32 numv2=0;

	s32 xPlus = -(ScreenSize.Width>>1);
	f32 xFact = 1.0f / (ScreenSize.Width>>1);
	s32 yPlus = ScreenSize.Height-(ScreenSize.Height>>1);
	f32 yFact = 1.0f / (ScreenSize.Height>>1);

	if (clip)
	{
		core::position2d<s32> p1, p2;
		for (s32 i=0; i<numv; i+=2)
		{			
			if (clip2DLineRect(clip, v[i], v[i+1], p1, p2) != 0)
			{
				v2[numv2] = S3DVertex(	f32(p1.X+xPlus)*xFact,
										f32(yPlus-p1.Y)*yFact, 
										0.0f, 0.0f, 0.0f, 0.0f, v[i].Color, 0.0f, 0.0f);
				v2[numv2+1] = S3DVertex(f32(p2.X+xPlus)*xFact,
										f32(yPlus-p2.Y)*yFact, 
										0.0f, 0.0f, 0.0f, 0.0f, v[i+1].Color, 0.0f, 0.0f);
				numv2 += 2;
			}
		}
	} else
	{
		numv2 = numv;
		for (s32 i=0; i<numv; i+=2)
		{			
		
			v2[i] =		S3DVertex(	f32(v[i].Pos.X+xPlus)*xFact,
									f32(yPlus-v[i].Pos.Y)*yFact, 
									0.0f, 0.0f, 0.0f, 0.0f, v[i].Color, 0.0f, 0.0f);
			
			v2[i+1] =	S3DVertex(	f32(v[i+1].Pos.X+xPlus)*xFact,
									f32(yPlus-v[i+1].Pos.Y)*yFact, 
									0.0f, 0.0f, 0.0f, 0.0f, v[i+1].Color, 0.0f, 0.0f);
		}		
	}

	if (numv2)
	{
		indices = new s16[numv2];
		for (s32 j=0; j<numv2; ++j)
			indices[j] = j;
		setRenderStates2DMode(v2[0].Color.getAlpha() < 255, false, false);
		setTexture(0,0);
		setVertexShader(EVT_STANDARD);
		pID3DDevice->DrawIndexedPrimitiveUP(D3DPT_LINELIST, 0, numv2, numv2>>1, &indices[0],
											D3DFMT_INDEX16, v2, sizeof(S3DVertex));
		delete[] indices;
	}
	delete[] v2;
}
In your app just set up the S3DVertex vertices and pass them to this function.
One note: the whole batch will be either with alpha (depending on 1st vertex) or non-alpha.
Post Reply