How to rectangularize a skinned mesh?

Started by
4 comments, last by anders211 10 years, 3 months ago

I am trying to generate rectangularized skinned mesh (box per bone), because it is better to do collision test with some simpler geometry rather than with original mesg with 10 thousands vertices. However this is too complex for me, I thought it would be easy but this is very complex to implement something like that, I almost cracked up with debugging this. Here is the effect which I got:

http://img845.imageshack.us/img845/2290/37ui.jpg

Could anybody tell me where I can find the ready solution, because for sure somebody in the past had to implement this what I do now. I cannot find through the website such code so I had to discover the America like Columb again, however my work is nonsense and waste of time (which I don't have in fact), because for sure this must be something in the website. If anybody hear about such code please give the hint where I can find it in the website.

Advertisement

Hi there.

your all most there. why are the boxes offset. post some code.

Do you make the boxes in model space(bone space) then transform them.

It maybe faster to have a boxed skinned mesh as well made in model program like 3Dsmax to match you other model.

I make a collision mesh.

I placed the code below, however it is not possible to understand this by anyone than me because I used a lot of my internal functions.

I would prefer to use some ready solution because I am very far from the aim. From theoretical pov it is easy to rectangularize mesh. And in practise it is hard.

I rectangularize vertices of the mesh like they are in x file so not in bone space. I have commented out line (*):


                   // Get pointer to vertex coordinates
                   D3DXVECTOR3 *vecPtr = (D3DXVECTOR3*)(pVertices + Vertices[j]*Stride);


                   // Transform vertex by bone offset transformation, so vertex are in bone space
                   D3DXVECTOR3 vecPos;
                   //D3DXVec3TransformCoord(&vecPos, vecPtr, pSkin->GetBoneOffsetMatrix(i)); (*)
                   vecPos = *vecPtr; //(-)

and use just original vertex coordinates. The effect is the mesh as in the last attached link. If I comment out line (-) and uncomment (*) the effect is as below:

http://img11.imageshack.us/img11/8338/ewzi.jpg

All boxes are placed in one place.

When I run animation then the effect is that boxes are dispersed on the whole screen:

http://img571.imageshack.us/img571/2139/04wj.jpg

This happens with both versions (*) and (-). With version (*) boxes are much more separately. However in each case boxes of animated bones are moving and boxes of not animated bones are not moving.

More code:


void Ragdoll::init(FrameEx* root, MeshContainerEx* meshContainer, IDirect3DDevice9* pDev)
{
   if(!root || !meshContainer || !pDev)
   {
  MessageBox(NULL,"Ragdoll::init() - invalid arguments", "Warning", MB_OK|MB_ICONWARNING);
  return;
   }


   ID3DXSkinInfo *pSkin = meshContainer->pSkinInfo;
   u32 numBones = pSkin->GetNumBones();
   mBox.resize(numBones);
   for(u32 i=0; i<numBones; ++i)
   {
       const char* boneName = pSkin->GetBoneName(i);
       D3DXFRAME* frame = D3DXFrameFind(root, boneName);
       if(frame)
       {
           u32 numVertices = pSkin->GetNumBoneInfluences(i);
           if(numVertices)
  {
               // Get the bone influcences
               DWORD *Vertices = new DWORD[numVertices];
               float *Weights  = new float[numVertices];
               pSkin->GetBoneInfluence(i, Vertices, Weights);
               
  // Get stride of vertex data
               u32 Stride = meshContainer->mSkinnedMesh->GetNumBytesPerVertex();


               // Lock vertex buffer and go through all of the vertices that are connected to bone
               char *pVertices = NULL;
               meshContainer->mSkinnedMesh->LockVertexBuffer(D3DLOCK_READONLY, (void**)&pVertices);


  mBox[i].min.x = d3d::INFINITY;
  mBox[i].min.y = d3d::INFINITY;
  mBox[i].min.z = d3d::INFINITY;
  mBox[i].max.x = -d3d::INFINITY;
  mBox[i].max.y = -d3d::INFINITY;
  mBox[i].max.z = -d3d::INFINITY;


               for(u32 j=0;j<numVertices;++j) 
  {
                   // Get pointer to vertex coordinates
                   D3DXVECTOR3 *vecPtr = (D3DXVECTOR3*)(pVertices + Vertices[j]*Stride);


                   // Transform vertex by bone offset transformation, so vertex are in bone space
                   D3DXVECTOR3 vecPos;
                   //D3DXVec3TransformCoord(&vecPos, vecPtr, pSkin->GetBoneOffsetMatrix(i));
                   vecPos = *vecPtr;


                   // Get min/max values
                   mBox[i].min.x = min(mBox[i].min.x, vecPos.x);
                   mBox[i].min.y = min(mBox[i].min.y, vecPos.y);
                   mBox[i].min.z = min(mBox[i].min.z, vecPos.z);


                   mBox[i].max.x = max(mBox[i].max.x, vecPos.x);
                   mBox[i].max.y = max(mBox[i].max.y, vecPos.y);
                   mBox[i].max.z = max(mBox[i].max.z, vecPos.z);
               }
               meshContainer->mSkinnedMesh->UnlockVertexBuffer();


               delete [] Vertices;
               delete [] Weights;
  }
  }
   }


   if(!mBox.empty())
   {
       mObject = new Object(pDev);
  srand(static_cast<u32>(time(0)));


  u32 sumVertices = 0;
  u32 sumBones = 0;


  for(u32 i=0; i<numBones; ++i)
       {
           const char* boneName = pSkin->GetBoneName(i);
           D3DXFRAME* frame = D3DXFrameFind(root, boneName);
           if(frame)
  {
               u32 numVertices = pSkin->GetNumBoneInfluences(i);
               if(numVertices)
  {   
  const D3DMATERIAL9* mat = NULL;
      switch(rand()%7)
      {
      case 0: mat = &material::RED; break;
      case 1: mat = &material::GREEN; break;
      case 2: mat = &material::BROWN; break;
      case 3: mat = &material::BLACK; break;
      case 4: mat = &material::WHITE; break;
      case 5: mat = &material::BLUE; break;
      case 6: mat = &material::YELLOW; break;
      }
      mObject->set(Szescian,mat,mBox[i].min.x, mBox[i].min.z, mBox[i].min.y, mBox[i].extent().x, mBox[i].extent().z, mBox[i].extent().y, NULL, NULL, MESH);
  
      sumVertices += 8;
                   mBoneIndices.resize(sumVertices, sumBones);
  }
  }
  ++sumBones;
  }


  if(mObject->createMesh(&D3DXVECTOR3(0,0,0), &mModel, NONEFLAGS, vertNormal2Blending1, NULL, &mBoneIndices) == NOK) //vertNormal2Blending1 //vertNormal2
  //if(mObject->createMesh(&D3DXVECTOR3(0,0,0)) == NOK) 
  MessageBox(NULL,"Ragdoll::init() - new object not created", "Warning", MB_OK|MB_ICONWARNING);


  //DWORD vertc = mModel->mesh->GetNumVertices();
  //DWORD faces = mModel->mesh->GetNumFaces();
  //DWORD bytesPerVert = mModel->mesh->GetNumBytesPerVertex();


  //HRESULT hr = mModel->mesh->CloneMeshFVF(D3DXMESH_DYNAMIC | D3DXMESH_32BIT, mModel->mesh->GetFVF(), pDev, &mMeshOryg);
  //if(FAILED(hr))
//d3d::CheckError(hr, "CloneMesh(FVF)()");


  //vertc = mMeshOryg->GetNumVertices();
  //faces = mMeshOryg->GetNumFaces();
  //bytesPerVert = mMeshOryg->GetNumBytesPerVertex();
   }


}
void Ragdoll::draw(IDirect3DDevice9* pDev, D3DXMATRIX* Finals)
{
    VertexShader *vShader = VertexShader::getShaders(Shader::blend1);
vShader->setVertexShader();


vShader->setMatrixArray(Shader::p_F, &Finals[0], static_cast<u32>(mBox.size()));


for(u32 i=0; i<mModel->subsets; ++i)
{
Mtrl mtrl = Mtrl(mModel->material[i]->Ambient, mModel->material[i]->Diffuse, mModel->material[i]->Specular, mModel->material[i]->Power);
vShader->setValue(Shader::p_Mtrl, (void *) &mtrl, sizeof(Mtrl));


D3DXMATRIX mat;
D3DXMatrixIdentity(&mat);
pDev->SetTransform(D3DTS_WORLD, &mat);
mat._41 = 10;
vShader->setMatrix(Shader::p_MatWorld, &mat);


pDev->SetTexture(0, mModel->texture[i]);
//Draw mesh subset
mModel->mesh->DrawSubset(i);
}


    vShader->setDefaultVertexShader();
}

My vertex shader code:


uniform extern float4x4 MatWorld; //or uniform extern x, y, z; //not used
uniform extern float4x4 MatWorldInvTrans;
//uniform extern float4x4 MatView; //not used
uniform extern float4x4 MatWVP;
//uniform extern float4x4 MatViewProj; //not used
uniform extern float4x3 F[62]; //Final transformation matrix for each bone (bind space -> root space)


struct Mtrl
{
float4 Ambient;
float4 Diffuse;
float4 Specular; //not used
float  Power; //not used
};
struct LightStr
{
float4 Ambient;
float4 Diffuse;
float4 Specular; //not used
float3 Direction; //earlier was float4 and opposite direction (now float3 and also opposite direction)
};


uniform extern Mtrl     mtrl;
uniform extern LightStr lightStr;


struct VS_INPUT
{
    float3 position    : POSITION; //bind space
int   boneIndices  : BLENDINDICES;
    float3 normal      : NORMAL;
    float2 coord       : TEXCOORD;
    //float  weights   : BLENDWEIGHT;
};


struct VS_OUTPUT
{
    vector position  : POSITION;
    vector diffuse   : COLOR;
    float2 coord     : TEXCOORD;
};


VS_OUTPUT Main(VS_INPUT input)
{
      VS_OUTPUT output = (VS_OUTPUT)0;


      //output position
      //Transform root space -> root space acc. to weights
      //float3 pos3 =  mul(mul(float4(input.position, 1.0f), MatWorld), F[input.boneIndices]);
 float3 pos3 =  mul(float4(input.position, 1.0f), F[input.boneIndices]);
 float4 pos = float4(pos3, 1.0f);


 // Transform to homogeneous clip space.
 output.position = mul(pos, MatWVP);


      //Output diffuse
      //Transform root space -> root space acc. to weights
      //float3 normal3 = mul(mul(float4(input.normal, 0.0f), MatWorld), F[input.boneIndices]);
 float3 normal3 = mul(float4(input.normal, 0.0f), F[input.boneIndices]);
 float4 normal = float4(normal3, 0.0f);


      // Transform to World space
      normal = mul(normal, MatWorldInvTrans);
 normal = normalize(normal);


      // Light calculation
      float4 LightDirection = float4(lightStr.Direction, 0.0f);
      float s = dot(LightDirection, normal);
      if(s < 0.0f) s = 0.0f; //cosinus <-1,1>


      output.diffuse = (mtrl.Ambient * lightStr.Ambient) +
                     (s * (lightStr.Diffuse * mtrl.Diffuse)); 


      // Forward texture coordinate
      output.coord = input.coord;


      return output;
}

So in other words I determine each box per bone (min.x,y,z and max.x,y,z coordinates in root (-) or bone space (*)) and then from all the boxes I make a mesh. FVF of this mesh is:

float3 position : POSITION;
int boneIndices : BLENDINDICES;
float3 normal : NORMAL;
float2 coord : TEXCOORD;
There is not a need to have weight because there is only one bone index per separate box vertices. I used Final matrices determined from original mesh.
I think in order to investigate the problem I have to put focus only on one bone for example head bone. So I made a mesh only from head bone and I see that rotation works:
The problem is that box is not in the place of head. The example above is from the case when box is made in bone space, not from original vertex coordinates from x file.

Ok.

each block is created from each frame in the skinned mesh now for each block have a pointer to the frame that the box is for

then when you update you model each boxes frame is also updated and use that to render the boxes. should work then.ph34r.png

After nervous time I succeeded at all:

http://img440.imageshack.us/img440/7770/pr88.jpg

However making collision mesh based on bone influences is not a good solution because the effect is that boxes are very large. Maybe another solution - skip from box calculations vertices which weight is less than 0.2 or something like that and in this way collision mesh would not have such large boxes.

This topic is closed to new replies.

Advertisement