Last active
June 10, 2022 10:32
-
-
Save jbltx/1dee4c0ea2c2bf2f3825336fefb10974 to your computer and use it in GitHub Desktop.
combineMeshes : Combine two FBX meshes together and returns the new one
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/******************************************************************************* | |
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; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Help