Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hey, Nick! Awesome addon :) This issue is about slow performance... #24

Open
redgpu opened this issue Feb 23, 2025 · 2 comments
Open

Hey, Nick! Awesome addon :) This issue is about slow performance... #24

redgpu opened this issue Feb 23, 2025 · 2 comments

Comments

@redgpu
Copy link

redgpu commented Feb 23, 2025

Hi, Nick!

First of all, thank you for this addon! I use it for my OF-based app called Game Script (script version, native C/C++ version) that allows to code in C and C++ and compile the code at runtime.

Here's an example of code that draws a Mixamo FBX mesh using your addon:

https://pastebin.com/raw/Cnkbg7Zt

Image

As you can see at the top left corner, the printed time difference for fbxLateUpdate(fbx) (which is a Game Script function that directly calls fbx->lateUpdate() method here) is 7 milliseconds, which is... a lot! 😄

All 7 milliseconds are spent here, in this Mesh::updateMesh function alone:

void Mesh::updateMesh( ofMesh* aMesh, FbxTime& pTime, FbxAnimLayer * pAnimLayer, FbxPose* pPose ) {
const bool lHasShape = fbxMesh->GetShapeCount() > 0;
const bool lHasSkin = fbxMesh->GetDeformerCount(FbxDeformer::eSkin) > 0;
const bool lHasVertexCache = fbxMesh->GetDeformerCount(FbxDeformer::eVertexCache);
const bool lHasDeformation = lHasShape || lHasSkin;
const int lVertexCount = fbxMesh->GetControlPointsCount();
// cout << "ofxFBXMesh :: " << getName() << " vertices: " << lVertexCount << " has blend shape: " << lHasShape << " has skin: " << lHasSkin << " has def: " << lHasDeformation << " has vertex cache: " << lHasVertexCache << " | " << ofGetFrameNum() << endl;
if(!lHasDeformation || lVertexCount < 3) return;
FbxAMatrix lGlobalOffPosition = mFbxGlobalPosition * mGeometryOffset;
// FbxAMatrix lGlobalPosition = GetGlobalPosition( fbxMesh->GetNode(), pTime, pPose );
// FbxAMatrix lGlobalPosition = GetGlobalPosition( fbxMesh->GetNode(), pTime );
// Geometry offset.
// it is not inherited by the children.
// this is now set in the setup function
// FbxAMatrix lGeometryOffset = GetGeometry( fbxMesh->GetNode() );//, FbxNode::eDestinationPivot );
// FbxAMatrix lGlobalOffPosition = lGlobalPosition * mGeometryOffset;//lGeometryOffset;
FbxVector4* lVertexArray = NULL;
lVertexArray = new FbxVector4[ lVertexCount ];
memcpy( lVertexArray, fbxMesh->GetControlPoints(), lVertexCount * sizeof(FbxVector4) );
FbxVector4* lNormalArray = NULL;
if( aMesh->hasNormals() && aMesh->usingNormals() ) {
if( mNormalsArray != NULL ) {
// cout << "updateMesh :: aMesh " << aMesh->hasNormals() << " using: " << aMesh->usingNormals() << endl;
lNormalArray = new FbxVector4[ lVertexCount ];
memcpy( lNormalArray, mNormalsArray, lVertexCount * sizeof(FbxVector4) );
}
}
if(lHasShape) {
computeBlendShapes( aMesh, pTime, pAnimLayer );
}
if(lHasSkin) {
//we need to get the number of clusters. which are controlled by bones //
// const int lSkinCount = fbxMesh->GetDeformerCount(FbxDeformer::eSkin);
// int lClusterCount = 0;
// for (int lSkinIndex = 0; lSkinIndex < lSkinCount; ++lSkinIndex) {
// lClusterCount += ((FbxSkin *)(fbxMesh->GetDeformer(lSkinIndex, FbxDeformer::eSkin)))->GetClusterCount();
// }
int lClusterCount = getNumClusters();
if (lClusterCount) {
computeSkinDeformation( lGlobalOffPosition, pTime, pAnimLayer, lVertexArray, lNormalArray, pPose );
}
}
// cout << "Calling update mesh lHasShape = " << lHasShape << " skin = " << lHasSkin << " lHasDeformation = " << lHasDeformation << endl;
vector< glm::vec3 >& amverts = aMesh->getVertices();
vector< glm::vec3 >& amnormals = aMesh->getNormals();
bool bUpdateNormals = lNormalArray != NULL && amnormals.size() > 1;
const int controlPointCount = fbxMesh->GetControlPointsCount();
// update the vertices //
for(int i = 0; i < controlPointCount; i++ ) {
amverts[i] = glm::vec3( lVertexArray[i][0], lVertexArray[i][1], lVertexArray[i][2] );
}
if( bUpdateNormals ) {
if( mNormalMappingMode == FbxGeometryElement::eByControlPoint ) {
for(int i = 0; i < controlPointCount; i++ ) {
amnormals[i] = glm::vec3( lNormalArray[i][0], lNormalArray[i][1], lNormalArray[i][2] );
}
} else if( mNormalMappingMode == FbxGeometryElement::eByPolygonVertex ) {
const int lPolygonCount = fbxMesh->GetPolygonCount();
int polysize = 3;
for (int lPolygonIndex = 0; lPolygonIndex < lPolygonCount; ++lPolygonIndex) {
polysize = fbxMesh->GetPolygonSize( lPolygonIndex );
for (int lVerticeIndex = 0; lVerticeIndex < polysize; ++lVerticeIndex) {
const int lControlPointIndex = fbxMesh->GetPolygonVertex( lPolygonIndex, lVerticeIndex );
amnormals[lControlPointIndex] = glm::vec3(lNormalArray[ lControlPointIndex ][0],
lNormalArray[ lControlPointIndex ][1],
lNormalArray[ lControlPointIndex ][2] );
}
}
}
}
delete [] lVertexArray;
if( lNormalArray != NULL ) {
delete [] lNormalArray;
}
}

I know it's the heaviest function, but it can take less than 7 milliseconds... I will rewrite it at some point, just letting you know about this issue!

Thank you again for this awesome addon! 😃

@redgpu
Copy link
Author

redgpu commented Feb 23, 2025

Here I recorded a video where I measured it with a simple time difference and my C/C++ profiling library 🙂

https://www.youtube.com/watch?v=0x55Tl7nErY

@NickHardeman
Copy link
Owner

Hi @redgpu,
Thank you for using the add on and reporting the issue. It is indeed a heavy function; the computation of the bones and its influence on each vertex is computationally expensive. Could probably be improved by using shaders for a GPU skinning type approach.
I welcome any PRs improving and optimizing the function :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants