DynamicLines - Mogre version

glateur

02-11-2009 17:19:34

Hi all,

I've been trying to make a class that can render lines in Ogre. There are 2 interesting tutorials about this on the wiki:
http://www.ogre3d.org/wiki/index.php/DynamicGrowingBuffers
http://www.ogre3d.org/wiki/index.php/DynamicLineDrawing
These appear to provide a solution to my problem, but they're in C++. I've been trying to 'translate' these into C#, but I can't seem to get it to work. Well, it compiles and it runs, but nothing gets rendered.

Two classes need to be definded: DynamicRenderable (extends SimpleRenderable) and DynamicLines (extends DynamicRenderable). This what I have so far:


Part 1/2: DynamicRenderable:

public partial class DynamicRenderable : SimpleRenderable
{
public RenderOperation rop;

protected uint mVertexBufferCapacity;
protected uint mIndexBufferCapacity;



public RenderOperation RenderOperation { get { return rop; } set { rop = value; } }

public virtual void createVertexDeclaration() { }

public unsafe DynamicRenderable(string name)
: base(null)
{
using (SceneManagerEnumerator.SceneManagerIterator smi = Root.Singleton.GetSceneManagerIterator())
{
smi.MoveNext();
ManualObject dummyObject = smi.Current.CreateManualObject(name);
typeof(SimpleRenderable).GetField("_native", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(this, (IntPtr)dummyObject.NativePtr);
}
}

public void initialize(RenderOperation.OperationTypes rtype, bool useIndices)
{
rop = new RenderOperation();
SetRenderOperation(rop);

rop.operationType = rtype;
rop.useIndexes = useIndices;
rop.vertexData = new VertexData();
if (useIndices) rop.indexData = new IndexData();

mVertexBufferCapacity = 0;
mIndexBufferCapacity = 0;

createVertexDeclaration();
}



public void prepareHardwareBuffers(uint vertexCount, uint indexCount)
{
// Prepare vertex buffer
uint newVertCapacity = mVertexBufferCapacity;
if ((vertexCount > mVertexBufferCapacity) ||
(mVertexBufferCapacity == 0))
{
// vertexCount exceeds current capacity!
// It is necessary to reallocate the buffer.

// Check if this is the first call
if (newVertCapacity == 0)
newVertCapacity = 1;

// Make capacity the next power of two
while (newVertCapacity < vertexCount)
newVertCapacity <<= 1;
}
else if (vertexCount < mVertexBufferCapacity >> 1)
{
// Make capacity the previous power of two
while (vertexCount < newVertCapacity >> 1)
newVertCapacity >>= 1;
}
if (newVertCapacity != mVertexBufferCapacity)
{
mVertexBufferCapacity = newVertCapacity;

// Create new vertex buffer
HardwareVertexBufferSharedPtr vbuf = HardwareBufferManager.Singleton.CreateVertexBuffer
(rop.vertexData.vertexDeclaration.GetVertexSize(0), mVertexBufferCapacity,
HardwareBuffer.Usage.HBU_STATIC_WRITE_ONLY); // TODO: Custom HBU_?

// Bind buffer
rop.vertexData.vertexBufferBinding.SetBinding(0, vbuf);
}
// Update vertex count in the render operation
rop.vertexData.vertexCount = vertexCount;

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

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

// Check if this is the first call
if (newIndexCapacity == 0)
newIndexCapacity = 1;

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

}
else if (indexCount < newIndexCapacity >> 1)
{
// Make capacity the previous power of two
while (indexCount < newIndexCapacity >> 1)
newIndexCapacity >>= 1;
}

if (newIndexCapacity != mIndexBufferCapacity)
{
mIndexBufferCapacity = newIndexCapacity;
// Create new index buffer
rop.indexData.indexBuffer = HardwareBufferManager.Singleton.CreateIndexBuffer(HardwareIndexBuffer.IndexType.IT_16BIT, mIndexBufferCapacity, HardwareBuffer.Usage.HBU_DYNAMIC_WRITE_ONLY);

}
// Update index count in the render operation
rop.indexData.indexCount = indexCount;
}
}

public float getBoundingRadius()
{
float sl = BoundingBox.Maximum.SquaredLength;
if (sl < BoundingBox.Minimum.SquaredLength) sl = BoundingBox.Minimum.SquaredLength;

return Mogre.Math.Sqrt(sl);
}

public float getSquaredViewDepth(Camera cam)
{
Vector3 vMin, vMax, vMid, vDist;
vMin = BoundingBox.Minimum;
vMax = BoundingBox.Maximum;
vMid = ((vMax - vMin) * 0.5f) + vMin;
vDist = cam.DerivedPosition - vMid;

return vDist.SquaredLength;
}
}


Questions/remarks about Part 1/2: DynamicRenderable:

1. About class DynamicRenderable : SimpleRenderable
It seems a bit strange that I have to keep my own RenderOperation property. I don't quite understand how SimpleRenderable.GetRenderOperation(RenderOperation op) is supposed to work. Or does it copy it's values to the RenderOperation argument?

2. About DynamicRenderable(string name) constructor
This is a hack I found in this post:
http://www.ogre3d.org/addonforums/viewtopic.php?f=8&t=11345
Unfortunately, I could not really translate this to the SimpleRenderable case. I've tried using smi.Current.CreateMovableObject(), but this requires a second type argument, which I couldn't get right (tried "SimpleRenderable" and such). This code is very likely to be wrong, so if anyone can comment on this, that would be great.



Part 2/2: DynamicLines:


public partial class DynamicLines : DynamicRenderable
{
public bool mDirty;
public ArrayList ALPoints;

public DynamicLines(RenderOperation.OperationTypes rtype, string matname, string name)
: base(name)
{
initialize(rtype, false);
//SetMaterial(matname);
mDirty = true;
ALPoints = new ArrayList();
}

public void addPoint(Vector3 p)
{
ALPoints.Add(p);
mDirty = true;
}

public void addPoint(float x, float y, float z)
{
ALPoints.Add(new Vector3(x, y, z));
mDirty = true;
}

public void Clear()
{
ALPoints = new ArrayList();
mDirty = true;
}

public void Update()
{
if (mDirty) fillHardwareBuffers();
}

public override void createVertexDeclaration()
{
rop.vertexData.vertexDeclaration.AddElement(0, 0, VertexElementType.VET_FLOAT3, VertexElementSemantic.VES_POSITION);
}

public float[] FloatArray
{
get
{
float[] rvfa = new float[3 * ALPoints.Count];

Vector3 cv3;
int i3;
for (int i = 0; i < ALPoints.Count; i++)
{
cv3 = (Vector3)ALPoints[i];
i3 = 3*i;
rvfa[i3] = cv3.x;
rvfa[i3+1] = cv3.y;
rvfa[i3+2] = cv3.z;
}

return rvfa;
}
}

public void fillHardwareBuffers()
{
uint size = (uint)ALPoints.Count;

prepareHardwareBuffers(size, 0);

if (size == 0)
{
BoundingBox.SetExtents(Vector3.ZERO, Vector3.ZERO);
mDirty = false;
return;
}

Vector3 vaabMin = (Vector3)ALPoints[0];
Vector3 vaabMax = (Vector3)ALPoints[0];

HardwareVertexBufferSharedPtr vbuf =
RenderOperation.vertexData.vertexBufferBinding.GetBuffer(0);
float[] vertices = FloatArray;
unsafe
{
void* voidPtr;
fixed (float* floatPtr = vertices)
{
voidPtr = floatPtr;
vbuf.WriteData(0, (uint)vertices.Length*4, voidPtr, true);
}
}

Vector3 cv3;
for (int i = 0; i < size; i++)
{
cv3 = (Vector3)ALPoints[i];

if (cv3.x < vaabMin.x) vaabMin.x = cv3.x;
if (cv3.y < vaabMin.y) vaabMin.y = cv3.y;
if (cv3.z < vaabMin.z) vaabMin.z = cv3.z;

if (cv3.x > vaabMax.x) vaabMax.x = cv3.x;
if (cv3.y > vaabMax.y) vaabMax.y = cv3.y;
if (cv3.z > vaabMax.z) vaabMax.z = cv3.z;
}


BoundingBox.SetExtents(vaabMin, vaabMax);

mDirty = false;
}
}


Questions/remarks about 2/2: DynamicLines:

1. About DynamicLines() constructor
I had to uncomment SetMaterial(matname), as it crashed the application (nothing in Ogre.log). I'm sure the material by the given name exists. Maybe my not being able to assign a material could explain a lot towards nothing being visible on the screen, I don't know (my axes are visible, though).

2. About fillHardwareBuffers()
The code to copy the vertex data to the buffer was kindly provided by this post:
http://www.ogre3d.org/addonforums/viewtopic.php?f=8&t=8354&p=48568&hilit=HardwareBufferManager#p48568
I've hardcoded the size of float to be 4 (I'm on a 32-bit machine).


The usage of this class can be summarized as follows:


DynamicLines lines = new DynamicLines(RenderOperation.OperationTypes.OT_LINE_LIST, matname, entname);
foreach (Vector3 v3 in someArrayList) lines.addPoint(v3);
scenenode.AttachObject(lines);
lines.Update();



Like I said, everything seems to be working, but nothing gets rendered on the screen.
If anybody has any experience with this, or any ideas what could be wrong/improved in this code, I'd be very happy to hear from you.

Cheers,
g

PantheR

02-11-2009 23:05:30

I draw lines in two ways. First one:


// ManualObject - cdef.dbgrident
// SceneNode - cdef.dbgridnode
//draw grid
public static void dbgDrawGrid(SceneManager mgr)
{
cdef.dbgrident = mgr.CreateManualObject("dbgrid");
cdef.dbgridnode = mgr.RootSceneNode.CreateChildSceneNode("dbgrid_node");

MaterialPtr myManualObjectMaterial = MaterialManager.Singleton.Create("dbgridMaterial", "debugger");
myManualObjectMaterial.ReceiveShadows = false;
myManualObjectMaterial.GetTechnique(0).SetLightingEnabled(true);
myManualObjectMaterial.GetTechnique(0).GetPass(0).SetDiffuse(0, 0, 1, 0);
myManualObjectMaterial.GetTechnique(0).GetPass(0).SetAmbient(0, 0, 1);
myManualObjectMaterial.GetTechnique(0).GetPass(0).SetSelfIllumination(0, 0, 1);

cdef.dbgrident.Begin("dbgridMaterial", Mogre.RenderOperation.OperationTypes.OT_LINE_LIST);
for (int i = 0; i < cdef.set.level.dimx + 1; i++)
{
cdef.dbgrident.Position(new Mogre.Vector3(i * cdef.set.dimh, cdef.DBGRID_VOFFSET, 0));
cdef.dbgrident.Position(new Mogre.Vector3(i * cdef.set.dimh, cdef.DBGRID_VOFFSET, cdef.set.level.dimz * cdef.set.dimw));
}
for (int j = 0; j < cdef.set.level.dimz + 1; j++)
{
cdef.dbgrident.Position(new Mogre.Vector3(0, cdef.DBGRID_VOFFSET, j * cdef.set.dimw));
cdef.dbgrident.Position(new Mogre.Vector3(cdef.set.level.dimx * cdef.set.dimh, cdef.DBGRID_VOFFSET, j * cdef.set.dimw));
}

// etc
cdef.dbgrident.End();
cdef.dbgrident.CastShadows = false;
cdef.dbgridnode.AttachObject(cdef.dbgrident);

}

This one simply creates an object consisting of lines.

Second - more simple - draw 1 line:

SceneNode myManualObjectNode = mSceneMgr.RootSceneNode.CreateChildSceneNode();
ManualObject myManualObject = mSceneMgr.CreateManualObject("manual1");
myManualObject.Begin("manual1Material", Mogre.RenderOperation.OperationTypes.OT_LINE_LIST);
myManualObject.Position(mouseRay.Origin); //line start
myManualObject.Position(mouseRay.Origin + (mouseRay.Direction*1000)); //line end
myManualObject.End();
myManualObjectNode.AttachObject(myManualObject);

glateur

03-11-2009 11:21:37

Thanks, PantheR, I had this implemented in no time, and it works like a charm!

However, I want more. More specifically, I want line thickness.
You can read all about that in this follow-up post:
http://www.ogre3d.org/addonforums/viewtopic.php?f=8&t=11506

Thanks again,
g