Skip to content

Instantly share code, notes, and snippets.

@jbltx
Last active June 10, 2022 10:32
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jbltx/1dee4c0ea2c2bf2f3825336fefb10974 to your computer and use it in GitHub Desktop.
Save jbltx/1dee4c0ea2c2bf2f3825336fefb10974 to your computer and use it in GitHub Desktop.
combineMeshes : Combine two FBX meshes together and returns the new one
/*******************************************************************************
CombineMeshes.cpp
Author : Mickael Bonfill (jbltx)
*******************************************************************************/
/**
* @brief Combine two FBX meshes together and returns the new one.
*
* @note The operation doesn't take in account UVs and Texture layers (yet).
*
* @param manager The FbxManager instance
* @param mesh1 A given FbxMesh instance
* @param mesh2 Another FbxMesh instance
* @return The result mesh
*/
FbxMesh * combineMeshes(FbxManager* manager, FbxMesh *mesh1, FbxNode *mesh2) {
const int mesh1ControlPointsCount = mesh1->GetControlPointsCount();
const int mesh2ControlPointsCount = mesh2->GetControlPointsCount();
const int polyCount1 = mesh1->GetPolygonCount();
const int polyCount2 = mesh2->GetPolygonCount();
const FbxVector4* mesh1Vertices = mesh1->GetControlPoints();
const FbxVector4* mesh2Vertices = mesh2->GetControlPoints();
// create the result mesh
FbxMesh* resultMesh = FbxMesh::Create(manager,"");
// allocate memory for vertices
resultMesh->InitControlPoints(mesh1ControlPointsCount + mesh2ControlPointsCount);
// retrieve the allocated array
FbxVector4 *vertices = resultMesh->GetControlPoints();
// copy vertices data
memcpy((void*)vertices,
(void*)mesh1Vertices,
mesh1ControlPointsCount * sizeof(FbxVector4));
memcpy((void*)vertices + (mesh1ControlPointsCount * sizeof(FbxVector4)),
(void*)mesh2Vertices,
mesh2ControlPointsCount * sizeof(FbxVector4));
// create polygons
for (int i = 0; i < polyCount1 + polyCount2; i++)
{
FbxMesh* currentMesh = (i < polyCount1) ? mesh1 : mesh2;
int polyIndex = (i < polyCount1) ? i : i - polyCount1;
resultMesh->BeginPolygon();
int vertexCount = currentMesh->GetPolygonSize(polyIndex);
for (int j = 0; j < vertexCount; j++)
{
int vertex = currentMesh->GetPolygonVertex(polyIndex, j);
resultMesh->AddPolygon(vertex);
}
resultMesh->EndPolygon();
}
// create normals
const int normalLayerIndex = 0;
FbxGeometryElementNormal* resultNormals = resultMesh->CreateElementNormal();
const FbxGeometryElementNormal* normals1 = mesh1->GetElementNormal(normalLayerIndex);
const FbxGeometryElementNormal* normals2 = mesh2->GetElementNormal(normalLayerIndex);
const EMappingMode mappingMode1 = normals1->GetMappingMode();
const EMappingMode mappingMode2 = normals2->GetMappingMode();
const EReferenceMode refMode1 = normals1->GetReferenceMode();
const EReferenceMode refMode2 = normals2->GetReferenceMode();
if (mappingMode1 == mappingMode2 && refMode1 == refMode2) // if both object use same methods to store normals...
{
resultNormals->SetMappingMode(mappingMode1);
resultNormals->SetReferenceMode(refMode1);
int vertexIndex = 0;
for (int i = 0; i < polyCount1 + polyCount2; i++)
{
FbxMesh* currentMesh = (i < polyCount1) ? mesh1 : mesh2;
FbxGeometryElementNormal* currentNormals = (i < polyCount1) ? normals1 : normals2;
int polyIndex = (i < polyCount1) ? i : i - polyCount1;
if (i == polyCount1) // reset count when we reach second mesh
vertexIndex = 0;
int vertexCount = currentMesh->GetPolygonSize(polyIndex);
for (int j = 0; j < vertexCount; j++)
{
int controlPoint = currentMesh->GetPolygonVertex(polyIndex, j);
FbxVector4 normal;
switch(mappingMode1)
{
case FbxGeometryElement::eByControlPoint:
switch(refMode1)
{
case FbxGeometryElement::eDirect:
normal = currentNormals->GetDirectArray().GetAt(controlPoint);
break;
case FbxGeometryElement::eIndexToDirect:
normal = currentNormals->GetDirectArray().GetAt(currentNormals->GetIndexArray().GetAt(controlPoint));
break;
}
break;
case FbxGeometryElement::eByPolygonVertex:
switch(refMode1)
{
case FbxGeometryElement::eDirect:
normal = currentNormals->GetDirectArray().GetAt(vertexIndex);
break;
case FbxGeometryElement::eIndexToDirect:
normal = currentNormals->GetDirectArray().GetAt(currentNormals->GetIndexArray().GetAt(vertexIndex));
break;
}
break;
}
resultNormals->GetDirectArray().Add(FbxVector4(normal));
vertexIndex++;
}
}
}
else // ... or generate automatically
{
resultMesh->GenerateNormals(true, true, false);
}
// apply materials
applyMaterials(resultMesh, mesh1, 0, polyCount1);
applyMaterials(resultMesh, mesh2, polyCount1, polyCount1+polyCount2);
return resultMesh;
}
/**
* @brief Apply materials from srcMesh to targetMesh.
*
* @details
* Apply all ElementMaterial templated layers from a source mesh
* to a target mesh in a given range of polygons. By default, since the
* operation is limited on specific polygons, each created ElementMaterial
* instances on the target mesh will use a mapping mode "by polygon".
*
* @note
* The reference mode used is always eIndexToDirect, as suggested from the FBX
* SDK documentation. https://shorturl.at/tzJQY
*
* @param[in, out] targetMesh The target mesh which will be modified
* @param[in] srcMesh The source mesh which will be evaluated
* @param[in] offset The first polygon index in the given range
* @param[in] limit The last polygon index in the given range
*/
void applyMaterials(FbxMesh* targetMesh, const FbxMesh* srcMesh, const int offset, const int limit)
{
const int max = targetMesh->GetPolygonCount();
for (int i = 0; i < srcMesh->GetElementMaterialCount(); i++)
{
FbxGeometryElementMaterial* resultMaterials;
if (targetMesh->GetElementMaterialCount() < i + 1)
{
resultMaterials = targetMesh->CreateElementMaterial();
resultMaterials->SetMappingMode(FbxGeometryElement::eByPolygon);
resultMaterials->SetReferenceMode(FbxGeometryElement::eIndexToDirect);
resultMaterials->GetIndexArray().SetCount(max);
}
else
{
resultMaterials = targetMesh->GetElementMaterial(i);
}
FbxGeometryElementMaterial* matElement = srcMesh->GetElementMaterial(i);
switch (matElement->GetMappingMode())
{
case FbxGeometryElement::eAllSame: // only 1 material applied
int matId = matElement->GetIndexArray().GetAt(0);
FbxSurfaceMaterial* mat = srcMesh->GetNode()->GetMaterial(matId);
for (int j = offset; j < limit; j++)
{
resultMaterials->GetIndexArray().SetAt(j, mat);
}
break;
case FbxGeometryElement::eByPolygon:
for (int j = O; j < limit-offset; j++)
{
int matId = matElement->GetIndexArray().GetAt(j);
FbxSurfaceMaterial* mat = srcMesh->GetNode()->GetMaterial(matId);
resultMaterials->GetIndexArray().SetAt(j+offset, mat);
}
break;
}
}
}