Line3D

A place for users of OGRE to discuss ideas and experiences of utilitising OGRE in their games / demos / applications.
Trep
Gnoblar
Posts: 1
Joined: Sun Mar 07, 2004 1:46 am

Line3D

Post by Trep »

Hey all,

I was poking around the forums the other day and saw some old code for doing 3D lines. Just thought I'd share my version.

Line3D.h

Code: Select all

#ifndef __LINE3D_H__
#define __LINE3D_H__

#include "Ogre.h"
#include <vector>

using namespace Ogre;
using namespace std;

#define POSITION_BINDING 0
#define TEXCOORD_BINDING 1

class Line3D:public SimpleRenderable
{
public:
	Line3D(void);
	~Line3D(void);

	void addPoint(const Vector3 &p);
	const Vector3 &getPoint(unsigned short index) const;
	unsigned short getNumPoints(void) const;
	void updatePoint(unsigned short index, const Vector3 &value);
	void drawLine(Vector3 &start, Vector3 &end);
	void drawLines(void);

	Real getSquaredViewDepth(const Camera *cam) const;
	Real getBoundingRadius(void) const;
protected:
	//void getWorldTransforms(Matrix4 *xform) const;
	const Quaternion &getWorldOrientation(void) const;
	const Vector3 &getWorldPosition(void) const;

	vector<Vector3> mPoints;
	bool mDrawn;
};

#endif /* __LINE3D_H__ */
Line3D.cpp

Code: Select all

#include "Line3D.h"

Line3D::Line3D(void)
{
	mRenderOp.vertexData = new VertexData();
	mDrawn = false;

	this->setMaterial("BaseWhiteNoLighting");
}

Line3D::~Line3D(void)
{
	delete mRenderOp.vertexData;
}

void Line3D::addPoint(const Vector3 &p)
{
	mPoints.push_back(p);
}

const Vector3 &Line3D::getPoint(unsigned short index) const
{
	assert(index < mPoints.size() && "Point index is out of bounds!!");

	return mPoints[index];
}

unsigned short Line3D::getNumPoints(void) const
{
	return (unsigned short)mPoints.size();
}

void Line3D::updatePoint(unsigned short index, const Vector3 &value)
{
	assert(index < mPoints.size() && "Point index is out of bounds!!");

	mPoints[index] = value;
}

void Line3D::drawLine(Vector3 &start, Vector3 &end)
{
	if(mPoints.size())
		mPoints.clear();

	mPoints.push_back(start);
	mPoints.push_back(end);

	drawLines();
}

void Line3D::drawLines(void)
{
	if(mDrawn)
		return;
	else
		mDrawn = true;

	// Initialization stuff
	mRenderOp.indexData = 0;
	mRenderOp.vertexData->vertexCount = mPoints.size();
	mRenderOp.vertexData->vertexStart = 0;
	mRenderOp.operationType = RenderOperation::OT_LINE_STRIP; // OT_LINE_LIST, OT_LINE_STRIP
	mRenderOp.useIndexes = false;

	VertexDeclaration *decl = mRenderOp.vertexData->vertexDeclaration;
	VertexBufferBinding *bind = mRenderOp.vertexData->vertexBufferBinding;

	decl->addElement(POSITION_BINDING, 0, VET_FLOAT3, VES_POSITION);

	HardwareVertexBufferSharedPtr vbuf =
		HardwareBufferManager::getSingleton().createVertexBuffer(
			decl->getVertexSize(POSITION_BINDING),
			mRenderOp.vertexData->vertexCount,
			HardwareBuffer::HBU_STATIC_WRITE_ONLY);

	bind->setBinding(POSITION_BINDING, vbuf);

	// Drawing stuff
	int size = mPoints.size();
	Vector3 vaabMin = mPoints[0];
	Vector3 vaabMax = mPoints[0];

	Real *prPos = static_cast<Real*>(vbuf->lock(HardwareBuffer::HBL_DISCARD));

	for(int i = 0; i < size; i++)
	{
		*prPos++ = mPoints[i].x;
		*prPos++ = mPoints[i].y;
		*prPos++ = mPoints[i].z;

		if(mPoints[i].x < vaabMin.x)
			vaabMin.x = mPoints[i].x;
		if(mPoints[i].y < vaabMin.y)
			vaabMin.y = mPoints[i].y;
		if(mPoints[i].z < vaabMin.z)
			vaabMin.z = mPoints[i].z;

		if(mPoints[i].x > vaabMax.x)
			vaabMax.x = mPoints[i].x;
		if(mPoints[i].y > vaabMax.y)
			vaabMax.y = mPoints[i].y;
		if(mPoints[i].z > vaabMax.z)
			vaabMax.z = mPoints[i].z;
	}

	vbuf->unlock();

	mBox.setExtents(vaabMin, vaabMax);
}

Real Line3D::getSquaredViewDepth(const Camera *cam) const
{
	Vector3 vMin, vMax, vMid, vDist;
	vMin = mBox.getMinimum();
	vMax = mBox.getMaximum();
	vMid = ((vMin - vMax) * 0.5) + vMin;
	vDist = cam->getDerivedPosition() - vMid;

	return vDist.squaredLength();
}

Real Line3D::getBoundingRadius(void) const
{
	return Math::Sqrt(max(mBox.getMaximum().squaredLength(), mBox.getMinimum().squaredLength()));
	//return mRadius;
}
/*
void Line3D::getWorldTransforms(Matrix4 *xform) const
{
	// return identity matrix to prevent parent transforms
	*xform = Matrix4::IDENTITY;
}
*/
const Quaternion &Line3D::getWorldOrientation(void) const
{
	return Quaternion::IDENTITY;
}

const Vector3 &Line3D::getWorldPosition(void) const
{
	return Vector3::ZERO;
}
Somewhere inside a scene

Code: Select all

	Line3D *myLine = new Line3D();
	myLine->addPoint(Vector3(0.0, 9.6, 0.0));
	myLine->addPoint(Vector3(160.0, 9.6, 0.0));
	myLine->addPoint(Vector3(160.0, 9.6, 160.0));
	myLine->addPoint(Vector3(0.0, 9.6, 160.0));
	myLine->addPoint(Vector3(0.0, 9.6, 0.0));
	myLine->drawLines();

	SceneNode *myNode = mSceneMgr->getRootSceneNode()->createChildSceneNode();
	myNode->attachObject(myLine);
Notes:
1. Based off of a combination of SimpleSpline, and WireBoundingBox.
2. No ability to batch add points yet (a simple loop through a vector should do the trick).
3. I haven't figured out how to make changes to the points and then rebuild the buffer (hence the bool to prevent attempting to redraw). I'm extremely green when it comes to hardware buffers (as you can probably tell by looking at my code here).
4. Simply uncomment the lines for getWorldTransforms to disable movement and rotation.
5. Let me know what you think and how it can be improved.
User avatar
Antiarc
Greenskin
Posts: 120
Joined: Thu Jan 23, 2003 8:40 am

Post by Antiarc »

Thank you! I was looking for that code the other day, and I couldn't find it! My life has meaning again! :D
User avatar
aloknarula
Gnoblar
Posts: 8
Joined: Mon Dec 20, 2004 8:00 am

Thanks Man

Post by aloknarula »

hi,
Thank you. you saved me hrs of work.

Alok
groups.msn.com/IndianGameProgrammers
User avatar
:wumpus:
OGRE Retired Team Member
OGRE Retired Team Member
Posts: 3067
Joined: Tue Feb 10, 2004 12:53 pm
Location: The Netherlands
x 1

Post by :wumpus: »

This really needs to be wiki'ed
User avatar
Kencho
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 4011
Joined: Fri Sep 19, 2003 6:28 pm
Location: Burgos, Spain
x 2

Post by Kencho »

As I said in other thread, I'll put my hands on this as soon as I get the time for that (about two weeks). Specially Line3D and Point3D
Image
User avatar
Kojack
OGRE Moderator
OGRE Moderator
Posts: 7157
Joined: Sun Jan 25, 2004 7:35 am
Location: Brisbane, Australia
x 535

Post by Kojack »

Line drawing is actually one of the handiest things for debugging. For example, getting ai to leave behind trails so you can examine the path they took, drawing lines to objects which might have spawned below ground level, etc.

Cool work Trep. The only real suggestion I'd have is to add vertex colours to each point of the lines.

What's really handy is when you use coloured lines to represent data, like making a bounding box where lines fade from black to red along positive X, black to green along positive Y and black to blue along positive Z. Just a glance at the bounding box shows it's true orientation (of course these are physics bounding boxes which can be any orientation, not axis aligned boxes which aren't going to be sideways or upsidedown, etc).


In my line renderable, I got around the update of vertices issue in a simple brute force way. Probably crappy, but it worked. :)
Basically, the renderable is created with a max vertex count. After that, you can add lines to it (with a colour per end point). Each update you can clear the line count so all lines added next will overwrite the previous ones. It's all done directly into the vertex buffer. If you don't call clear(), the lines stay there. If you call clear(), you can redraw them all. This was mainly so I can have a variable number of lines (up to a defined max) per frame, for example rendering contact point normals from a collision detection system (where the number of contacts in a scene changes every update, remembering which is which to turn them off again is a pain so I just redo them each time). Not efficient maybe, but it's only really for debugging anyway. :)

I need to do a triangle one next, for the Novodex debug rendering callbacks.
User avatar
DWORD
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 1365
Joined: Tue Sep 07, 2004 12:43 pm
Location: Aalborg, Denmark

Post by DWORD »

I don't know if this can help you, but I once wrote a DynamicRenderable class derived from SimpleRenderable. It provides mechanisms to allow for dynamically growing hardware buffers. Simply put, you tell on update how many vertices/indices you want, and it reallocates the buffers (only) if necessary. It's quite flexible, as you can create your own vertex declaration etc. But it's not complete, e.g. you can't decrease the capacity of the buffers, but that shouldn't be hard to add. Here we go:

DynamicRenderable.h

Code: Select all

#ifndef DYNAMIC_RENDERABLE_H
#define DYNAMIC_RENDERABLE_H

/// Abstract base class providing mechanisms for dynamically growing hardware buffers.
class DynamicRenderable : public SimpleRenderable
{
public:
	/// Constructor
	DynamicRenderable();
	/// Virtual destructor
	virtual ~DynamicRenderable();

	/** Initializes the dynamic renderable.
		@remarks
			This function should only be called once. It initializes the render operation, and calls
			the abstract function createVertexDeclaration().
		@param operationType The type of render operation to perform.
		@param useIndices Specifies whether to use indices to determine the vertices to use as input. */
	void initialize(RenderOperation::OperationType operationType, bool useIndices);
	/// Implementation of Ogre::SimpleRenderable
	virtual Real getBoundingRadius(void) const;
	/// Implementation of Ogre::SimpleRenderable
	virtual Real getSquaredViewDepth(const Camera* cam) const;

protected:
	/// Maximum capacity of the currently allocated vertex buffer.
	size_t mVertexBufferCapacity;
	/// Maximum capacity of the currently allocated index buffer.
	size_t mIndexBufferCapacity;

	/// Creates the vertex declaration.
	virtual void createVertexDeclaration() = 0;
	/** Prepares the hardware buffers for the requested vertex and index counts.
		@remarks
			This function must be called before locking the buffers in fillHardwareBuffers(). It
			guarantees that the hardware buffers are large enough to hold at least the requested number of
			vertices and indices (if using indices). The buffers are possibly reallocated to achieve this.
		@par
			The vertex and index count in the render operation are set to the values of vertexCount
			and indexCount respectively.
		@param vertexCount The number of vertices the buffer must hold.
		@param indexCount The number of indices the buffer must hold. This parameter is ignored if
			not using indices. */
	void prepareHardwareBuffers(size_t vertexCount, size_t indexCount);
	/** Fills the hardware vertex and index buffers with data.
		@remarks
			This function must call prepareHardwareBuffers() before locking the buffers to ensure the they
			are large enough for the data to be written. Afterwards the vertex and index buffers (if using
			indices) can be locked, and data can be written to them. */
	virtual void fillHardwareBuffers() = 0;
};

#endif // DYNAMIC_RENDERABLE_H
DynamicRenderable.cpp

Code: Select all

#include "DynamicRenderable.h"

DynamicRenderable::DynamicRenderable()
{
}

DynamicRenderable::~DynamicRenderable()
{
	delete mRenderOp.vertexData;
	delete mRenderOp.indexData;
}

void DynamicRenderable::initialize(RenderOperation::OperationType operationType, bool useIndices)
{
	// Initialize render operation
	mRenderOp.operationType = operationType;
	mRenderOp.useIndexes = useIndices;
	mRenderOp.vertexData = new VertexData;
	if (mRenderOp.useIndexes)
		mRenderOp.indexData = new IndexData;

	// Reset buffer capacities
	mVertexBufferCapacity = 0;
	mIndexBufferCapacity = 0;

	// Create vertex declaration
	createVertexDeclaration();
}

void DynamicRenderable::prepareHardwareBuffers(size_t vertexCount, size_t indexCount)
{
	// Prepare vertex buffer
	if ((vertexCount > mVertexBufferCapacity) ||
		(!mVertexBufferCapacity))
	{
		// vertexCount exceeds current capacity!
		// It is necessary to reallocate the buffer.

		// Check if this is the first call
		if (!mVertexBufferCapacity)
			mVertexBufferCapacity = 1;

		// Make capacity the next power of two
		while (mVertexBufferCapacity < vertexCount)
			mVertexBufferCapacity <<= 1;

		// Create new vertex buffer
		HardwareVertexBufferSharedPtr vbuf =
			HardwareBufferManager::getSingleton().createVertexBuffer(
				mRenderOp.vertexData->vertexDeclaration->getVertexSize(0),
				mVertexBufferCapacity,
				HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY); // TODO: Custom HBU_?

		// Bind buffer
		mRenderOp.vertexData->vertexBufferBinding->setBinding(0, vbuf);
	}

	// Update vertex count in the render operation
	mRenderOp.vertexData->vertexCount = vertexCount;

	if (mRenderOp.useIndexes)
	{
		OgreAssert(indexCount <= std::numeric_limits<unsigned short>::max(), "indexCount exceeds 16 bit");

		// Prepare index buffer
		if ((indexCount > mIndexBufferCapacity) ||
			(!mIndexBufferCapacity))
		{
			// indexCount exceeds current capacity!
			// It is necessary to reallocate the buffer.

			// Check if this is the first call
			if (!mIndexBufferCapacity)
				mIndexBufferCapacity = 1;

			// Make capacity the next power of two
			while (mIndexBufferCapacity < indexCount)
				mIndexBufferCapacity <<= 1;

			// Create new index buffer
			mRenderOp.indexData->indexBuffer =
				HardwareBufferManager::getSingleton().createIndexBuffer(
					HardwareIndexBuffer::IT_16BIT,
					mIndexBufferCapacity,
					HardwareBuffer::HBU_DYNAMIC_WRITE_ONLY); // TODO: Custom HBU_?
		}

		// Update index count in the render operation
		mRenderOp.indexData->indexCount = indexCount;
	}
}

Real DynamicRenderable::getBoundingRadius(void) const
{
	// TODO: Implementation
	return 0.0;
}

Real DynamicRenderable::getSquaredViewDepth(const Camera* cam) const
{
	// TODO: Implementation
	return 0.0;
}
User avatar
:wumpus:
OGRE Retired Team Member
OGRE Retired Team Member
Posts: 3067
Joined: Tue Feb 10, 2004 12:53 pm
Location: The Netherlands
x 1

Post by :wumpus: »

Looks very useful! But again, could everyone please post such code snippets on the Wiki and not here?
User avatar
sinbad
OGRE Retired Team Member
OGRE Retired Team Member
Posts: 19269
Joined: Sun Oct 06, 2002 11:19 pm
Location: Guernsey, Channel Islands
x 66

Post by sinbad »

User avatar
DWORD
OGRE Retired Moderator
OGRE Retired Moderator
Posts: 1365
Joined: Tue Sep 07, 2004 12:43 pm
Location: Aalborg, Denmark

Post by DWORD »

SORRY! Was right about to wiki it myself, but looks like sinbad did it already...
RoundSparrow
Greenskin
Posts: 145
Joined: Wed Jan 19, 2005 4:36 am
Location: Arica, Chile

Re: Line3D

Post by RoundSparrow »

Trep wrote: Notes:
1. Based off of a combination of SimpleSpline, and WireBoundingBox.
2. No ability to batch add points yet (a simple loop through a vector should do the trick).
3. I haven't figured out how to make changes to the points and then rebuild the buffer (hence the bool to prevent attempting to redraw). I'm extremely green when it comes to hardware buffers (as you can probably tell by looking at my code here).
4. Simply uncomment the lines for getWorldTransforms to disable movement and rotation.
5. Let me know what you think and how it can be improved.
Trep - I am trying to implement a Bezier curve in the same fashion as your line and I am a complete novice too... So I wanted to review your notes...

For as many points as a curve is going to need, I think your #2 makes sense (batch addition). How do we go about this?

Also doing Kojack's suggestion to "add vertex colours to each point of the lines".

Can anyone with more internal Ogre experience comment about the design and efficency of this Line3D code in general? I don't want this to be strictly a case of blind leading the blind :)

Anyone want to work together on this? Post here please.
User avatar
baxissimo
Greenskin
Posts: 135
Joined: Wed Feb 23, 2005 1:28 pm
Location: Tokyo, JAPAN

New line drawing class based on DWORD's DynamicRenderable

Post by baxissimo »

I made a new line drawing class, DynamicLines, which is sort of a love child of DWORD's DynamicRenderable, and Trep's Line3D.

I also modified DynamicRenderable some so that it will also reduce the buffer size when needed.

All of these changes are


***** WIKIED ******

and

***** WIKIED ******


of course!

Enjoy!