SkinnableManualObject?

What it says on the tin: a place to discuss proposed new features.
Post Reply
lonestarr
Gnoblar
Posts: 6
Joined: Fri Dec 06, 2002 10:46 am
Location: Montreuil, France

SkinnableManualObject?

Post by lonestarr » Fri Sep 24, 2010 4:38 pm

Hello,

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 :) if you just add the mesh Bounds calculation from dv8 above.

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;
}
-lonestarr
0 x

Post Reply