I tried to apply skinning to a mesh converted from a ManualObject but that makes the UVs "go nuts".
This effect was first reported in 2006 by dv8 here:
http://www.ogre3d.org/forums/viewtopic.php?t=23360
I took the createGeometry code from there, but unfortunately it doesn't work: I can't see anything in my Ogre window (even if I don't add any skeleton/skinning).
Luckily, I've found then this thread from 2005:
http://www.ogre3d.org/forums/viewtopic.php?f=2&t=16232
There are some differences with the other code:
- it uses a submesh to store the vertex data
- it uses indexed geometry
- it works
This was really a 2 days struggle. Therefore, my suggestion is simply the following: please provide an easy way to add skinning to a ManualObject (by either subclassing or enhancing ManualObject). My Ogre expertise is rather new and limited so I doubt I can jump into this by myself, but I'm sure that would bring to Ogre a smoother way to import content from anywhere. For now, the limitation is: "ManualObject + skinning = broken UVs".
Here is my final code (which includes, additionally, some triangulation algorithm because the input mesh can be an n-gon). Maybe it will help someone in the future:
Code: Select all
static Ogre::MeshPtr _createGeometry2(const sbSimpleNGonMesh& sbmesh)
{
Ogre::String meshName = sbmesh.getName();
Ogre::MeshPtr mesh = Ogre::MeshManager::getSingletonPtr()->createManual(meshName,
Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
// generate mesh vertices
static const unsigned int size_vert = 6; // 3 for position and 3 for normal
static const unsigned int uv_size_vert = 2;
const unsigned int num_verts = sbmesh.getPointCount();
bool vertexShadowBuffer = true;
Ogre::HardwareVertexBufferSharedPtr vbuf;
vbuf = Ogre::HardwareBufferManager::getSingleton().createVertexBuffer
(size_vert*sizeof(float),
size_vert*num_verts,
Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY,vertexShadowBuffer);
// help from http://www.ogre3d.org/phpBB2/viewtopic.php?t=4371
// on how to use two buffers
Ogre::HardwareVertexBufferSharedPtr uv_vbuf =
Ogre::HardwareBufferManager::getSingleton().createVertexBuffer
(uv_size_vert*sizeof(float),
uv_size_vert*num_verts,
Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY,
vertexShadowBuffer);
Ogre::HardwareBuffer::Usage indexBufferUsage = Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY;
// triangulation
std::vector<unsigned int> mIndecies;
{
const unsigned int fc = sbmesh.getFaceCount();
for (unsigned int i=0;i<fc;i++)
{
const sbFace& f = sbmesh.getFace(i);
const unsigned int fec=f.size();
if (fec==3)
{
mIndecies.push_back( f[0].vertexIndex );
mIndecies.push_back( f[1].vertexIndex );
mIndecies.push_back( f[2].vertexIndex );
}
else if (fec==4)
{
// triangle(i0, i1, i2);
mIndecies.push_back( f[0].vertexIndex );
mIndecies.push_back( f[1].vertexIndex );
mIndecies.push_back( f[2].vertexIndex );
// triangle(i2, i3, i0);
mIndecies.push_back( f[2].vertexIndex );
mIndecies.push_back( f[3].vertexIndex );
mIndecies.push_back( f[0].vertexIndex );
}
else if (fec>4)
{
unsigned int idx0 = f[0].vertexIndex;
for (unsigned int j=0;j+2<fec;j++)
{
mIndecies.push_back( idx0 );
mIndecies.push_back( f[j+1].vertexIndex );
mIndecies.push_back( f[j+2].vertexIndex );
}
}
}
}
Ogre::HardwareIndexBufferSharedPtr ibuf = Ogre::HardwareBufferManager::getSingleton().createIndexBuffer(
Ogre::HardwareIndexBuffer::IT_32BIT,
mIndecies.size(),
indexBufferUsage);
float *pFloat = (float*) vbuf->lock(Ogre::HardwareBuffer::HBL_DISCARD);
// Information for calculating bounds.
Ogre::Vector3 min, max, pos;
Ogre::Real maxSquaredRadius = -1;
bool firstVertex = true;
unsigned int i;
// process position and normals buffer
for (i = 0; i < num_verts; i++)
{
const sbVector& v = sbmesh.getVector(i);
*pFloat = v.x; // assign value
++pFloat; // go to the next value
*pFloat = v.y; // assign value
++pFloat; // go to the next value
*pFloat = v.z; // assign value
++pFloat; // go to the next value
// Bounding box calculations.
pos.x = v.x;
pos.y = v.y;
pos.z = v.z;
if (firstVertex)
{
min = max = pos;
maxSquaredRadius = pos.squaredLength();
firstVertex = false;
}
else
{
min.makeFloor(pos);
max.makeCeil(pos);
maxSquaredRadius = std::max(pos.squaredLength(), maxSquaredRadius);
}
const sbVector& n = sbmesh.getNormal(i);
*pFloat = n.x; // assign value
++pFloat; // go to the next value
*pFloat = n.y; // assign value
++pFloat; // go to the next value
*pFloat = n.z; // assign value
++pFloat; // go to the next value
}
// process uv buffer
pFloat = (float*)uv_vbuf->lock(Ogre::HardwareBuffer::HBL_DISCARD);
// writing to a vbuf from uv_verts array
for (i = 0; i < num_verts; i++)
{
const sbUv& uv = sbmesh.getUv(i);
*pFloat = uv.u; // assign value
++pFloat; // go to the next value
*pFloat = 1.0f-uv.v; // assign value
++pFloat; // go to the next value
}
// process indecies buffer
unsigned int* pIndexes = static_cast<unsigned int*> (ibuf->lock(Ogre::HardwareBuffer::HBL_DISCARD) );
for (std::vector<unsigned int>::iterator ind = mIndecies.begin(); ind!=mIndecies.end(); ++ind)
{
// why do I have to use unsigned int here???
// without it I have warning that *ind returns size_t
*pIndexes = (unsigned int)(*ind); // assign value
++pIndexes; // go to the next value
}
// create sub mesh
Ogre::SubMesh * sub = mesh->createSubMesh();
sub->operationType = Ogre::RenderOperation::OT_TRIANGLE_LIST;
sub->useSharedVertices = false;
sub->indexData->indexCount = mIndecies.size();
sub->indexData->indexStart = 0;
sub->indexData->indexBuffer=ibuf;
ibuf->unlock();
vbuf->unlock();
uv_vbuf->unlock();
sub->vertexData = new Ogre::VertexData();
sub->vertexData->vertexStart = 0;
sub->vertexData->vertexCount = num_verts;
Ogre::VertexBufferBinding* bind = sub->vertexData->vertexBufferBinding;
bind->setBinding(0, vbuf);
bind->setBinding(1, uv_vbuf);
size_t offset = 0;
Ogre::VertexDeclaration* decl = sub->vertexData->vertexDeclaration;
decl->addElement(0, offset, Ogre::VET_FLOAT3, Ogre::VES_POSITION);
offset += Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3);
decl->addElement(0, offset, Ogre::VET_FLOAT3, Ogre::VES_NORMAL);
decl->addElement(1, 0, Ogre::VET_FLOAT2, Ogre::VES_TEXTURE_COORDINATES, 0);
// set bounds for the mesh
mesh->_setBounds(Ogre::AxisAlignedBox(min, max), false);
mesh->_setBoundingSphereRadius(Ogre::Math::Sqrt(maxSquaredRadius));
return mesh;
}