Creating meshes and index buffers

Auphim

12-11-2006 19:23:19

Hello there,

I been trying to create a simple mesh. It seems that OgreDotNet has a class named MeshBuilderHelper, but I can't find something similar on Mogre.

Does anyone have any C# code or tutorial for building a mesh on the fly?

In fact, I have a problem when building an index buffer. I got the error "Error 1 Property or indexer 'Mogre.IndexData.indexBuffer' cannot be assigned to -- it is read only ".

My code is more or less this one:

.....

MeshPtr msh = MeshManager.Singleton.CreateManual(name, "ATest");
// Create a submesh
SubMesh sub = msh.CreateSubMesh();

....

// Allocate the index buffer
sub.indexData.indexCount = SomeNumber;
sub.indexData.indexBuffer = HardwareBufferManager.Singleton.CreateIndexBuffer(HardwareIndexBuffer.IndexType.IT_16BIT, sub.indexData.indexCount, HardwareBuffer.Usage.HBU_STATIC_WRITE_ONLY, false);


I think this code is similar to some Ogre examples (C++).
What is wrong with it?


Thanks for the help

Bekas

12-11-2006 20:04:28

In fact, I have a problem when building an index buffer. I got the error "Error 1 Property or indexer 'Mogre.IndexData.indexBuffer' cannot be assigned to -- it is read only ".

This is a bug, thanks for reporting it.

Soon I'll release a new SDK (upgraded to Ogre 1.2.4) and it will be fixed.

Auphim

12-11-2006 20:49:26

thanks Bekas,
I have another question once this point is resolved. Can I avoid unsafe code when filling in the vertex or index buffer?

Folloging C++ samples, I have code like :

unsafe
{
float* pVertex = (float*)vBuf.Lock(HardwareBuffer.LockOptions.HBL_DISCARD);
ushort* pIndices = (ushort*)iBuf.Lock(HardwareBuffer.LockOptions.HBL_DISCARD);

.....

for (ushort j = 0; j < segments; j++)
{
*pIndices++ = (ushort)(segments * i );
...
}

....

// Close the vertex buffer
vBuf.Unlock();
// Close the index buffer
iBuf.Unlock();
}


Thanks again.

Bekas

12-11-2006 21:01:34

Can I avoid unsafe code when filling in the vertex or index buffer?
Nope, you have to use unsafe.
Even pure C# Axiom uses unsafe code to fill the buffers, for efficiency :wink:

Bekas

12-11-2006 21:04:21

You can use Marshal.Copy to copy a managed array, but unsafe is necessary to get the void* pointer.

Auphim

12-11-2006 21:24:09

Well, something like MeshBuilderHelper would be nice. See an example at:

http://www.ogre3d.org/phpBB2addons/view ... =setbounds

The MeshBuilderHelper class seems to be very simple, but I'dont know how to integrate it into your code.

I could try to code it in C# and hide the unsafe code inside this class.... I have to think about it.

Bekas

12-11-2006 22:09:43

Try to convert the C++ MeshBuilderHelper class to C# code. And feel free to add it to the wiki if you like :wink:

Auphim

13-11-2006 16:51:34

here you have my first version of MeshBuilderHelper:

/// <summary>
/// A helper class for manual mesh
/// This code is based on MeshBuilderHelper.h
/// develloped by rastaman 11/16/2005
/// </summary>
class MeshBuilderHelper
{

public MeshBuilderHelper(String name, String resourcegroup,
bool usesharedvertices, uint vertexstart, uint vertexcount)
{
mName = name;
mResourceGroup = resourcegroup;
mVertextStart = vertexstart;
mVertexCount = vertexcount;

// Return now if already exists
if (MeshManager.Singleton.ResourceExists(name))
return;

mMeshPtr = MeshManager.Singleton.CreateManual(mName, mResourceGroup);
mSubMesh = mMeshPtr.CreateSubMesh();
mSubMesh.useSharedVertices = usesharedvertices;
mSubMesh.vertexData = new VertexData();
mSubMesh.vertexData.vertexStart = mVertextStart;
mSubMesh.vertexData.vertexCount = mVertexCount;
offset = 0;
mIndexType = HardwareIndexBuffer.IndexType.IT_16BIT;
}


public virtual VertexElement AddElement(VertexElementType theType, VertexElementSemantic semantic)
{
// I think there is a bug in VertexElement
VertexElement ve = mSubMesh.vertexData.vertexDeclaration.AddElement(0, offset, theType, semantic);
offset += VertexElement.GetTypeSize(theType);
return ve;
}

public virtual void CreateVertexBuffer(uint numVerts, HardwareBuffer.Usage usage)
{
CreateVertexBuffer(numVerts, usage, false);
}
public virtual void CreateVertexBuffer(uint numVerts, HardwareBuffer.Usage usage, bool useShadowBuffer)
{

mVertexSize = offset;
mNumVerts = numVerts;
mvbuf = HardwareBufferManager.Singleton.CreateVertexBuffer(
mVertexSize, mNumVerts, usage, useShadowBuffer);
unsafe
{
pVBuffStart = (void*)mvbuf.Lock(HardwareBuffer.LockOptions.HBL_DISCARD);
}
}

public virtual void SetVertFloat(uint vertexindex, uint byteoffset, float val1)
{
unsafe
{
if (vertexindex > mNumVerts)
return;
char* pp = (char*)pVBuffStart;
pp += (mVertexSize * vertexindex) + byteoffset;
float* p = (float*)pp;
*p++ = val1;
}
}

public virtual void SetVertFloat(uint vertexindex, uint byteoffset, float val1, float val2)
{
unsafe
{
if (vertexindex > mNumVerts)
return;
char* pp = (char*)pVBuffStart;
pp += (mVertexSize * vertexindex) + byteoffset;
float* p = (float*)pp;
*p++ = val1;
*p++ = val2;
}
}

public virtual void SetVertFloat(uint vertexindex, uint byteoffset, float val1, float val2, float val3)
{
unsafe
{

if (vertexindex > mNumVerts)
return;
char* pp = (char*)pVBuffStart;
pp += (mVertexSize * vertexindex) + byteoffset;
float* p = (float*)pp;
*p++ = val1;
*p++ = val2;
*p++ = val3;
}
}

public virtual void SetVertFloat(uint vertexindex, uint byteoffset, float val1, float val2, float val3, float val4)
{
unsafe
{
if (vertexindex > mNumVerts)
return;
char* pp = (char*)pVBuffStart;
pp += (mVertexSize * vertexindex) + byteoffset;
float* p = (float*)pp;
*p++ = val1;
*p++ = val2;
*p++ = val3;
*p++ = val4;
}
}


public virtual void CreateIndexBuffer(uint triaglecount,
HardwareIndexBuffer.IndexType itype,
HardwareBuffer.Usage usage)
{
CreateIndexBuffer(triaglecount, itype, usage, false);
}

public virtual void CreateIndexBuffer(uint triaglecount,
HardwareIndexBuffer.IndexType itype,
HardwareBuffer.Usage usage,
bool useShadowBuffer)
{

mvbuf.Unlock();
mTriagleCount = triaglecount;
mIndexType = itype;
mSubMesh.vertexData.vertexBufferBinding.SetBinding(0, mvbuf);
mSubMesh.indexData.indexCount = mTriagleCount * 3;
HardwareIndexBufferSharedPtr ibuf = HardwareBufferManager.Singleton
.CreateIndexBuffer(mIndexType, mTriagleCount * 3, usage, useShadowBuffer);
/* TODO. BUG
mSubMesh.indexData.indexBuffer = ibuf;
*/
unsafe
{
pIBuffStart = (void*) ibuf.Lock(HardwareBuffer.LockOptions.HBL_DISCARD);
}
}
public virtual void SetIndex16bit(uint triagleIdx, ushort vidx1, ushort vidx2, ushort vidx3)
{
if (triagleIdx > mTriagleCount)
return;
if (mIndexType != HardwareIndexBuffer.IndexType.IT_16BIT)
return;
unsafe
{
ushort* p = (ushort*)pIBuffStart;
p += (triagleIdx * 3);
*p++ = vidx1;
*p++ = vidx2;
*p++ = vidx3;
}
}
public virtual void SetIndex32bit(uint triagleIdx, uint vidx1, uint vidx2, uint vidx3)
{
if (triagleIdx > mTriagleCount)
return;
if (mIndexType != HardwareIndexBuffer.IndexType.IT_32BIT)
return;

unsafe
{
uint* p = (uint*)pIBuffStart;
p += (triagleIdx * 3);
*p++ = vidx1;
*p++ = vidx2;
*p++ = vidx3;
}
}

public virtual MeshPtr Load(String materialname)
{
mSubMesh.indexData.indexBuffer.Unlock();
mSubMesh.MaterialName = materialname;
mMeshPtr.Load();
return mMeshPtr;
}

#region Protected and Private fields
protected MeshPtr mMeshPtr;
protected SubMesh mSubMesh;
protected String mName, mResourceGroup;
protected uint mVertextStart, mVertexCount;
protected HardwareVertexBufferSharedPtr mvbuf;
protected uint offset;
protected uint mVertexSize;
protected uint mNumVerts;
protected unsafe void* pVBuffStart = (void*)0;
protected uint mTriagleCount;
protected HardwareIndexBuffer.IndexType mIndexType;
protected unsafe void* pIBuffStart = (void*)0;


#endregion

}


I can't test it until Bekas fix the bug. Anyway,
I think there is another bug in VertexElement class. When I call
VertexElement ve = mSubMesh.vertexData.vertexDeclaration.AddElement(0, offset, VertexElementType.VET_FLOAT4, VertexElementSemantic.VES_POSITION);

the vertexelement I've obtained has corrupted data.

Bekas

13-11-2006 18:04:09

I think there is another bug in VertexElement class. When I call
VertexElement ve = mSubMesh.vertexData.vertexDeclaration.AddElement(0, offset, VertexElementType.VET_FLOAT4, VertexElementSemantic.VES_POSITION);

the vertexelement I've obtained has corrupted data.

Yep that's another bug. Too bad it's found just after a new release. It's serious though, after I make sure that MeshBuilderHelper is working fine, I'll release a 0.1.8.1 version just for this.

Bekas

13-11-2006 23:21:18

I fixed the second bug too, go grab the 0.1.8.1 release.

At your MeshBuilderHelper class you've made the mistake to use 'char*' pointers. The C++ char is used for 1 byte, but the C# char is unicode (2 bytes); byte* should be used in its place.

A slightly modified version of MeshBuilderHelper is this:
/// <summary>
/// A helper class for manual mesh
/// This code is based on MeshBuilderHelper.h
/// developed by rastaman 11/16/2005
/// </summary>
unsafe class MeshBuilderHelper
{
public MeshBuilderHelper(String name, String resourcegroup,
bool usesharedvertices, uint vertexstart, uint vertexcount)
{
mName = name;
mResourceGroup = resourcegroup;
mVertextStart = vertexstart;
mVertexCount = vertexcount;

// Return now if already exists
if (MeshManager.Singleton.ResourceExists(name))
return;

mMeshPtr = MeshManager.Singleton.CreateManual(mName, mResourceGroup);
mSubMesh = mMeshPtr.CreateSubMesh();
mSubMesh.useSharedVertices = usesharedvertices;
mSubMesh.vertexData = new VertexData();
mSubMesh.vertexData.vertexStart = mVertextStart;
mSubMesh.vertexData.vertexCount = mVertexCount;
offset = 0;
mIndexType = HardwareIndexBuffer.IndexType.IT_16BIT;
}


public virtual VertexElement AddElement(VertexElementType theType, VertexElementSemantic semantic)
{
VertexElement ve = mSubMesh.vertexData.vertexDeclaration.AddElement(0, offset, theType, semantic);
offset += VertexElement.GetTypeSize(theType);
return ve;
}

public virtual void CreateVertexBuffer(uint numVerts, HardwareBuffer.Usage usage)
{
CreateVertexBuffer(numVerts, usage, false);
}

public virtual void CreateVertexBuffer(uint numVerts, HardwareBuffer.Usage usage, bool useShadowBuffer)
{
mVertexSize = offset;
mNumVerts = numVerts;
mvbuf = HardwareBufferManager.Singleton.CreateVertexBuffer(
mVertexSize, mNumVerts, usage, useShadowBuffer);
pVBuffStart = mvbuf.Lock(HardwareBuffer.LockOptions.HBL_DISCARD);
}

public virtual void SetVertFloat(uint vertexindex, uint byteoffset, float val1)
{
if (vertexindex >= mNumVerts)
throw new IndexOutOfRangeException("'vertexIndex' cannot be greater than the number of vertices.");

byte* pp = (byte*)pVBuffStart;
pp += (mVertexSize * vertexindex) + byteoffset;
float* p = (float*)pp;
*p = val1;
}

public virtual void SetVertFloat(uint vertexindex, uint byteoffset, float val1, float val2)
{
if (vertexindex >= mNumVerts)
throw new IndexOutOfRangeException("'vertexIndex' cannot be greater than the number of vertices.");

byte* pp = (byte*)pVBuffStart;
pp += (mVertexSize * vertexindex) + byteoffset;
float* p = (float*)pp;
*p++ = val1;
*p = val2;
}

public virtual void SetVertFloat(uint vertexindex, uint byteoffset, float val1, float val2, float val3)
{
if (vertexindex >= mNumVerts)
throw new IndexOutOfRangeException("'vertexIndex' cannot be greater than the number of vertices.");

byte* pp = (byte*)pVBuffStart;
pp += (mVertexSize * vertexindex) + byteoffset;
float* p = (float*)pp;
*p++ = val1;
*p++ = val2;
*p = val3;
}

public virtual void SetVertFloat(uint vertexindex, uint byteoffset, float val1, float val2, float val3, float val4)
{
if (vertexindex >= mNumVerts)
throw new IndexOutOfRangeException("'vertexIndex' cannot be greater than the number of vertices.");

byte* pp = (byte*)pVBuffStart;
pp += (mVertexSize * vertexindex) + byteoffset;
float* p = (float*)pp;
*p++ = val1;
*p++ = val2;
*p++ = val3;
*p = val4;
}


public virtual void CreateIndexBuffer(uint triaglecount,
HardwareIndexBuffer.IndexType itype,
HardwareBuffer.Usage usage)
{
CreateIndexBuffer(triaglecount, itype, usage, false);
}

public virtual void CreateIndexBuffer(uint triaglecount,
HardwareIndexBuffer.IndexType itype,
HardwareBuffer.Usage usage,
bool useShadowBuffer)
{

mvbuf.Unlock();
mTriagleCount = triaglecount;
mIndexType = itype;
mSubMesh.vertexData.vertexBufferBinding.SetBinding(0, mvbuf);
mSubMesh.indexData.indexCount = mTriagleCount * 3;
HardwareIndexBufferSharedPtr ibuf = HardwareBufferManager.Singleton
.CreateIndexBuffer(mIndexType, mTriagleCount * 3, usage, useShadowBuffer);
mSubMesh.indexData.indexBuffer = ibuf;
pIBuffStart = ibuf.Lock(HardwareBuffer.LockOptions.HBL_DISCARD);
}

public virtual void SetIndex16bit(uint triagleIdx, ushort vidx1, ushort vidx2, ushort vidx3)
{
if (triagleIdx >= mTriagleCount)
throw new IndexOutOfRangeException("'triagleIdx' cannot be greater than the number of triangles.");
if (mIndexType != HardwareIndexBuffer.IndexType.IT_16BIT)
throw new NotSupportedException("HardwareIndexBuffer.IndexType other than 'IT_16BIT' is not supported.");

ushort* p = (ushort*)pIBuffStart;
p += (triagleIdx * 3);
*p++ = vidx1;
*p++ = vidx2;
*p = vidx3;
}

public virtual void SetIndex32bit(uint triagleIdx, uint vidx1, uint vidx2, uint vidx3)
{
if (triagleIdx >= mTriagleCount)
throw new IndexOutOfRangeException("'triagleIdx' cannot be greater than the number of triangles.");
if (mIndexType != HardwareIndexBuffer.IndexType.IT_16BIT)
throw new NotSupportedException("HardwareIndexBuffer.IndexType other than 'IT_16BIT' is not supported.");

uint* p = (uint*)pIBuffStart;
p += (triagleIdx * 3);
*p++ = vidx1;
*p++ = vidx2;
*p = vidx3;
}

public virtual MeshPtr Load(String materialname)
{
mSubMesh.indexData.indexBuffer.Unlock();
mSubMesh.MaterialName = materialname;
mMeshPtr.Load();
return mMeshPtr;
}

#region Protected and Private fields
protected MeshPtr mMeshPtr;
protected SubMesh mSubMesh;
protected String mName, mResourceGroup;
protected uint mVertextStart, mVertexCount;
protected HardwareVertexBufferSharedPtr mvbuf;
protected uint offset;
protected uint mVertexSize;
protected uint mNumVerts;
protected void* pVBuffStart = (void*)0;
protected uint mTriagleCount;
protected HardwareIndexBuffer.IndexType mIndexType;
protected void* pIBuffStart = (void*)0;
#endregion

}


Give it a test drive to see if it works ok. If you want, add it to the wiki under the 'Snippets' section along with an example and a few guidelines of how it's used.

Auphim

15-11-2006 20:21:31

As Bekas suggested, I added all this stuff to the wiki under the 'Snippets'.

It's the first time I publish in the wiki, so I hope everything is OK.

Thanks Bekas for your support.

Kodachi_Garou

16-11-2006 09:29:37

Great work both of you :D. I think this class should be packed with next releases of MOGRE, as it is extremely useful in a C# environment use of Ogre.