DynamicLineDrawing         A way to draw lines dynamically

Like the Line 3D example, this implements a way to draw lines dynamically. I mostly followed the same interface as the Line 3D example, however this version is based on the DynamicRenderable code and hence can dynamically grow or shrink the hardware buffer according to the number of points. This version also has a public interface for switching between lines strips and line segments.

DynamicLines.h

#ifndef _DYNAMIC_LINES_H_
#define _DYNAMIC_LINES_H_

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

class DynamicLines : public DynamicRenderable
{
  typedef Ogre::Vector3 Vector3;
  typedef Ogre::Quaternion Quaternion;
  typedef Ogre::Camera Camera;
  typedef Ogre::Real Real;
  typedef Ogre::RenderOperation::OperationType OperationType;

public:
  /// Constructor - see setOperationType() for description of argument.
  DynamicLines(OperationType opType=Ogre::RenderOperation::OT_LINE_STRIP);
  virtual ~DynamicLines();

  /// Add a point to the point list
  void addPoint(const Ogre::Vector3 &p);
  /// Add a point to the point list
  void addPoint(Real x, Real y, Real z);

  /// Change the location of an existing point in the point list
  void setPoint(unsigned short index, const Vector3 &value);

  /// Return the location of an existing point in the point list
  const Vector3& getPoint(unsigned short index) const;

  /// Return the total number of points in the point list
  unsigned short getNumPoints(void) const;

  /// Remove all points from the point list
  void clear();

  /// Call this to update the hardware buffer after making changes.  
  void update();

  /** Set the type of operation to draw with.
   * @param opType Can be one of 
   *    - RenderOperation::OT_LINE_STRIP
   *    - RenderOperation::OT_LINE_LIST
   *    - RenderOperation::OT_POINT_LIST
   *    - RenderOperation::OT_TRIANGLE_LIST
   *    - RenderOperation::OT_TRIANGLE_STRIP
   *    - RenderOperation::OT_TRIANGLE_FAN
   *    The default is OT_LINE_STRIP.
   */
  void setOperationType(OperationType opType);
  OperationType getOperationType() const;

protected:
  /// Implementation DynamicRenderable, creates a simple vertex-only decl
  virtual void createVertexDeclaration();
  /// Implementation DynamicRenderable, pushes point list out to hardware memory
  virtual void fillHardwareBuffers();

private:
  std::vector<Vector3> mPoints;
  bool mDirty;
};

#endif

DynamicLines.cpp

#include "DynamicLines.h"
#include <Ogre.h>
#include <cassert>
#include <cmath>

using namespace Ogre;

enum {
  POSITION_BINDING,
  TEXCOORD_BINDING
};

DynamicLines::DynamicLines(OperationType opType)
{
  initialize(opType,false);
  setMaterial("BaseWhiteNoLighting");
  mDirty = true;
}

DynamicLines::~DynamicLines()
{
}

void DynamicLines::setOperationType(OperationType opType)
{
  mRenderOp.operationType = opType;
}

RenderOperation::OperationType DynamicLines::getOperationType() const
{
  return mRenderOp.operationType;
}

void DynamicLines::addPoint(const Vector3 &p)
{
   mPoints.push_back(p);
   mDirty = true;
}
void DynamicLines::addPoint(Real x, Real y, Real z)
{
   mPoints.push_back(Vector3(x,y,z));
   mDirty = true;
}
const Vector3& DynamicLines::getPoint(unsigned short index) const
{
   assert(index < mPoints.size() && "Point index is out of bounds!!");
   return mPoints[index];
}
unsigned short DynamicLines::getNumPoints(void) const
{
  return (unsigned short)mPoints.size();
}
void DynamicLines::setPoint(unsigned short index, const Vector3 &value)
{
  assert(index < mPoints.size() && "Point index is out of bounds!!");

  mPoints[index] = value;
  mDirty = true;
}
void DynamicLines::clear()
{
  mPoints.clear();
  mDirty = true;
}

void DynamicLines::update()
{
  if (mDirty) fillHardwareBuffers();
}

void DynamicLines::createVertexDeclaration()
{
  VertexDeclaration *decl = mRenderOp.vertexData->vertexDeclaration;
  decl->addElement(POSITION_BINDING, 0, VET_FLOAT3, VES_POSITION);
}

void DynamicLines::fillHardwareBuffers()
{
  int size = mPoints.size();

  prepareHardwareBuffers(size,0);

  if (!size) { 
    mBox.setExtents(Vector3::ZERO,Vector3::ZERO);
    mDirty=false;
    return;
  }
  
  Vector3 vaabMin = mPoints[0];
  Vector3 vaabMax = mPoints[0];

  HardwareVertexBufferSharedPtr vbuf =
    mRenderOp.vertexData->vertexBufferBinding->getBuffer(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);

  mDirty = false;
}

/*
void DynamicLines::getWorldTransforms(Matrix4 *xform) const
{
   // return identity matrix to prevent parent transforms
   *xform = Matrix4::IDENTITY;
}
*/
/*
const Quaternion &DynamicLines::getWorldOrientation(void) const
{
   return Quaternion::IDENTITY;
}

const Vector3 &DynamicLines::getWorldPosition(void) const
{
   return Vector3::ZERO;
}
*/

Example

Here is an example of how to use it:

std::deque<Ogre::Vector3> somePoints;
// add points
somePoints.push_back(Ogre::Vector3(0.0f, 0.0f, 0.0f ));
somePoints.push_back(Ogre::Vector3(452.0f, 2345.0f, 453.0f));

In the initialization somewhere, create the initial lines object:
DynamicLines *lines = new DynamicLines(RenderOperation::OT_LINE_LIST);
for (int i=0; i<somePoints.size(); i++) {
  lines->addPoint(somePoints[i]);
}
lines->update();
SceneNode *linesNode = mScene->getRootSceneNode()->createChildSceneNode("lines");
linesNode->attachObject(lines);


Then later on when you want to update the lines:

SceneNode *lnode = dynamic_cast<SceneNode*>(mScene->getRootSceneNode()->getChild("lines"));
DynamicLines *lines = dynamic_cast<DynamicLines*>(lnode->getAttachedObject(0));

// add points if you wish (maybe elsewhere, this is just an example)
//somePoints.push_back(Ogre::Vector3(4543.0f, 45.0f, 134.0f));

if (lines->getNumPoints()!= somePoints.size()) {
  // Oh no!  Size changed, just recreate the list from scratch
  lines->clear();
  for (int i=0; i<somePoints.size(); ++i) {
    lines->addPoint(somePoints[i]);
  }
}
else {
  // Just values have changed, use 'setPoint' instead of 'addPoint'
  for (int i=0; i<somePoints.size(); ++i) {
    lines->setPoint(i,somePoints[i]);
  }
}
lines->update();

A word about the bounding box


The inherited mBox member is used to decide whether or not the object is in view and therefore should be drawn. It is not supposed to be dynamic, so you must take an extra step to get OGRE to notice whatever changes were made to it.

You will want to keep a pointer to the scene node you attached the DynamicLines instance to and call the following whenever mBox changes:

linesNode->needUpdate();