Problems with buffers

Numsgil

27-09-2007 10:13:40

I'm trying to convert a mesh I made with ManualObject to one using buffers. I've done this before in C++, but it's been a while. The code below is my go at it in C# with Mogre.

It's causing all sorts of problems. Various "the data you are trying to read or write to is protected" problems. Trying to uncomment the second set of texture coordinates amplifies the problems further. Tweaking it I've managed to get it to run in OpenGL and verify that the mesh looks right, so it's mostly working. But even then, when I close the program down, it'll throw the old "protected memory" problem again.

I'm guessing it has to do with the VertexData. When it gets disposed it seems to cause the protected memory error.

I'm not even really sure what I'm doing here, or if I'm doing something really wrong. I'd appreciate any insight anyone can offer.


public MeshPtr BuildMainPlanetMeshHardwareBuffer(int Face, int Quad)
{
//1. Create the mesh
MeshPtr Mesh = Mogre.MeshManager.Singleton.CreateManual("Planet Mesh " + Face + "," + Quad, "Planet Mesh");
//2. Create a sub mesh
SubMesh SubMesh = Mesh.CreateSubMesh();
//3. Create a shared vertex data?
Mesh.sharedVertexData = new VertexData();
VertexData vertexData = Mesh.sharedVertexData;

//4. define the vertex format
VertexDeclaration VertexDecl = vertexData.vertexDeclaration;
uint currOffset = 0;
// positions
VertexDecl.AddElement(0, currOffset, VertexElementType.VET_FLOAT3, VertexElementSemantic.VES_POSITION);
currOffset += VertexElement.GetTypeSize(VertexElementType.VET_FLOAT3);
// normals
VertexDecl.AddElement(0, currOffset, VertexElementType.VET_FLOAT3, VertexElementSemantic.VES_NORMAL);
currOffset += VertexElement.GetTypeSize(VertexElementType.VET_FLOAT3);
// two dimensional texture coordinates
VertexDecl.AddElement(0, currOffset, VertexElementType.VET_FLOAT2, VertexElementSemantic.VES_TEXTURE_COORDINATES);
currOffset += VertexElement.GetTypeSize(VertexElementType.VET_FLOAT2);
//second texture coordinates
//VertexDecl.AddElement(0, currOffset, VertexElementType.VET_FLOAT2, VertexElementSemantic.VES_TEXTURE_COORDINATES);
//currOffset += VertexElement.GetTypeSize(VertexElementType.VET_FLOAT2);

//5. allocate the vertex buffer
vertexData.vertexCount =
(uint)(Data.Lithosphere.HeightMap.Subdivisions + 1) *
(uint)(Data.Lithosphere.HeightMap.Subdivisions + 1);

HardwareVertexBufferSharedPtr vBuf = HardwareBufferManager.Singleton.CreateVertexBuffer(
VertexDecl.GetVertexSize(0),
vertexData.vertexCount,
HardwareBuffer.Usage.HBU_DYNAMIC_WRITE_ONLY,
false); //No shadow buffer

//6. Bind to "source"?
VertexBufferBinding binding = vertexData.vertexBufferBinding;
binding.SetBinding(0, vBuf);

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

// allocate index buffer
SubMesh.indexData.indexCount =
(uint)(Data.Lithosphere.HeightMap.Subdivisions) *
(uint)(Data.Lithosphere.HeightMap.Subdivisions) * 2 * 3;

SubMesh.indexData.indexBuffer = HardwareBufferManager.Singleton.CreateIndexBuffer(
HardwareIndexBuffer.IndexType.IT_16BIT,
SubMesh.indexData.indexCount,
HardwareBuffer.Usage.HBU_STATIC_WRITE_ONLY,
false); //don't use shadow buffer

HardwareIndexBufferSharedPtr iBuf = SubMesh.indexData.indexBuffer;
ushort* pIndices = (ushort*)(iBuf.Lock(HardwareBuffer.LockOptions.HBL_DISCARD));

//...

int Size = Data.Lithosphere.HeightMap.Subdivisions;

Vector3[,] Positions = new Vector3[Size + 3, Size + 3];
Vector3[,] Normals = new Vector3[Size + 3, Size + 3];
Iterator<Object> DummyIter = new Iterator<object>(Size, Face, 0, 1);

//Set up initial data
for (int Y = 0; Y <= Size + 2; ++Y)
for (int X = -1; X <= Size + 1; ++X)
{
Positions[X + 1, Y] = Data.Lithosphere.ActualPosition(DummyIter._Set(Face, X + Quad * Size, Y));
Normals[X + 1, Y] = new Vector3(0, 1, 0);
}

//Find normals
for (int Y = 1; Y <= Size + 1; ++Y)
for (int X = -1; X <= Size; ++X)
{
//Foreach tris
//add tris.normal to a, b, c

Vector3 a = Positions[(X + 1) + 1, Y];
Vector3 b = Positions[(X + 1), Y + 1];
Vector3 c = Positions[(X + 1) + 1, Y + 1];
Vector3 d = Positions[(X + 1), Y];

Vector3 NormalBottom = new Geometry.Triangle(d, b, a).Normal();
Vector3 NormalTop = new Geometry.Triangle(a, b, c).Normal();

Normals[(X + 1) + 1, Y] += NormalTop + NormalBottom; //a
Normals[(X + 1), Y + 1] += NormalTop + NormalBottom; //b
Normals[(X + 1) + 1, Y + 1] += NormalTop; //c
Normals[(X + 1), Y] += NormalBottom; //d
}

//Add triangles
for (int Y = 0; Y < Size; ++Y)
for (int X = 0; X < Size; ++X)
{
ushort a = (ushort)((Size + 1) * (Y) + X + 1);
ushort b = (ushort)((Size + 1) * (Y + 1) + X);
ushort c = (ushort)((Size + 1) * (Y + 1) + X + 1);
ushort d = (ushort)((Size + 1) * (Y) + X);

*pIndices++ = c;
*pIndices++ = b;
*pIndices++ = a;

*pIndices++ = a;
*pIndices++ = b;
*pIndices++ = d;
}


for (int Y = 1; Y <= Size + 1; ++Y)
for (int X = 0; X <= Size; ++X)
{
//Update bounding box, etc.
*pVertex++ = Positions[X + 1, Y].x;
*pVertex++ = Positions[X + 1, Y].y;
*pVertex++ = Positions[X + 1, Y].z;

*pVertex++ = Normals[X + 1, Y].NormalisedCopy.x;
*pVertex++ = Normals[X + 1, Y].NormalisedCopy.y;
*pVertex++ = Normals[X + 1, Y].NormalisedCopy.z;

*pVertex++ = (X + Y / 2.0f);
*pVertex++ = Y;

//*pVertex++ = (X + Y / 2.0f) / (float)(Data.Lithosphere.HeightMap.Subdivisions + 1);
//*pVertex++ = Y / (float)(Data.Lithosphere.HeightMap.Subdivisions + 1);
}

Mesh._setBounds(new AxisAlignedBox(-10000,-10000,-10000,10000,10000,10000));
Mesh._setBoundingSphereRadius((float)System.Math.Sqrt(3*10000*10000));

// Unlock
vBuf.Unlock();
iBuf.Unlock();
}//end unsafe

// Generate face list
SubMesh.useSharedVertices = true;

Mesh.Load();

return Mesh;
}


elswhere...


Mogre.MeshPtr mesh = Meshes[0].BuildMainPlanetMeshHardwareBuffer(0, 0);

Mogre.Entity entity = Data.Ogre.OgreManager.SceneManager.CreateEntity("The Planet Sort Of", "Planet Mesh 0,0");
entity.SetMaterialName("TestMaterial");
Data.Ogre.OgreManager.SceneManager.RootSceneNode.CreateChildSceneNode().AttachObject(entity);

bleubleu

18-10-2007 16:57:07

Sorry I am kind of in a rush so I dont have time to read your code.

BUT! To create a mesh with manual vertices, I would recommend using the ManualObject and the simply call the GetMesh() method! Voila! This way, you dont have to manipulate buffers which are very error-prone.

Also, I am very confident that this is not a but in Mogre since I have done all sorts of strange Vertex/Pixel buffer manipulation in Mogre and never hit a bug.

Mat

Numsgil

18-10-2007 18:58:11

I did managed to solve this, though I don't remember what the solution was. Ogre is extremely sensitive to problems with hardware buffers, and Mogre just amplifies the problem.

Manual objects are over-rated, honestly, for all but the simplest tasks. It's much slower than raw hardware buffers, and it doesn't let you share single hardware buffer instances across multiple meshes. Working with raw hardware buffers is a huge pain, especially with Mogre when you need to declare safe/unsafe contexts, but it still beats out ManualObject half the time.

bleubleu

18-10-2007 22:41:55

Agreed about your comment about buffers. But ManualObject are still useful for single-use geometry and for quickly creating an object "OpenGL-style".

Glad you solve your problem.

Mat