Monday, May 24, 2010

Scaling and Centering of ID3DXMesh Geometry in DX9

Introduction


Frequently I have received requests for functions that will allow a mesh to be rescaled to fit within specific bounds and/or centered at origin, so I decided to take some time to publish source code to accomplish this task.  It is often necessary to do this when dealing with arbitrary content such as in a mesh viewer, or as a means to verify the validity of imported meshes that may be arbitrarily scaled or offset from origin.  In that context it may also be useful for rescaling mesh geometry so that it can be saved back to a geometry file properly scaled for later use.

Normalizing a Mesh

Rather than scaling the mesh by a fixed amount, the first function that I am going to show here "normalizes" a mesh - that is, it scales the mesh based on the dimensions of the mesh, so that it will fit inside of a bounding sphere of a specified size.  Normalization typically refers to scaling to unit (1.0) dimensions, but in the case of our function we will allow you to specify the final dimensions of the bounding sphere.
Before we can write our mesh normalization function, we will first need a couple of supporting functions, which will allow us to measure the mesh and to apply scaling and offset.

    Measuring the Mesh

Our first function will allow us to compute the bounding sphere of the mesh, by wrapping the D3DXComputeBoundingSphere() function:
HRESULT CalcBounds(ID3DXMesh *pMesh, D3DXVECTOR3 *vCenter, float *radius)
{
 BYTE *ptr=NULL;
 HRESULT hr;

 // return failure if no mesh pointer provided
 if (!pMesh)
  return D3DERR_INVALIDCALL;

 // get the face count
 DWORD numVerts=pMesh->GetNumVertices();

 // get the FVF flags
 DWORD fvfSize=D3DXGetFVFVertexSize(pMesh->GetFVF());  // See DX8 Version

 // lock the vertex buffer
 if (FAILED(hr=pMesh->LockVertexBuffer(0,&ptr)))

  // return on failure
  return hr;

 // compute bounding sphere
 if (FAILED(hr=D3DXComputeBoundingSphere((D3DXVECTOR3 *) ptr, 
      numVerts, 
      fvfSize,   // See DX8 Version
      vCenter, radius )))
  // return on failure
  return hr;

 // unlock the vertex buffer
 if (FAILED(hr=pMesh->UnlockVertexBuffer()))

  // return on failure
  return hr;
  
 // return success to caller
 return S_OK;
}

    Scaling the Mesh

Our next function allows us to scale and offset the vertices of a mesh:
HRESULT ScaleMesh(ID3DXMesh *pMesh, float scale, D3DXVECTOR3 *offset=NULL)
{
 BYTE *ptr=NULL;
 HRESULT hr;
 D3DXVECTOR3 vOff;

 // return failure if no mesh pointer set
 if (!pMesh)
  return D3DERR_INVALIDCALL;

 // select default or specified offset vector
 if (offset)
  vOff=*offset;
 else
  vOff=D3DXVECTOR3(0.0f,0.0f,0.0f);

 // get the face count
 DWORD numVerts=pMesh->GetNumVertices();

 // get the FVF flags
 DWORD fvf=pMesh->GetFVF();

 // calculate vertex size
 DWORD vertSize=D3DXGetFVFVertexSize(fvf);

 // lock the vertex buffer
 if (FAILED(hr=pMesh->LockVertexBuffer(0,&ptr)))
 
  // return on failure
  return hr;

 // loop through the vertices
 for (DWORD i=0;ix*=scale;
  vPtr->y*=scale;
  vPtr->z*=scale;

  // increment pointer to next vertex
  ptr+=vertSize;
 }

 // unlock the vertex buffer
 if (FAILED(hr=pMesh->UnlockVertexBuffer()))

  // return on failure
  return hr;
  
 // return success to caller
 return S_OK;
}

    Our Normalization Function

Finally, we get to our normalization function, which will utilize the two functions we saw above:
  1. First, it will find the bounding sphere of the mesh, as a radius and center.
  2. Next, it will calculate a scaling factor based on the radius calculated and the desired mesh size.
  3. If centering of the mesh is requested, the center will be negated for use as an offset.
  4. The scaling function we wrote will then be called with the calculated scaling factor and offset.
HRESULT NormalizeMesh(ID3DXMesh *pMesh, float scaleTo=1.0f, BOOL bCenter=TRUE)
{
 D3DXVECTOR3 vCenter;
 float radius;
 HRESULT hr;

 // calculate bounds of mesh
 if (FAILED(hr=CalcBounds(pMesh,&vCenter,&radius)))
  return hr;

 // calculate scaling factor
 float scale=scaleTo/radius;

 // calculate offset if centering requested
 D3DXVECTOR3 vOff;
 if (bCenter) 
  vOff=-vCenter;
 else
  vOff=D3DXVECTOR3(0.0f,0.0f,0.0f);

 // scale and offset mesh
 return ScaleMesh(pMesh,scale,&vOff);
}

No comments:

Post a Comment