Importing a skeletal mesh using the Cineware SDK
-
Hello, I'm trying to get skeletal meshes imported into our software by using the Cineware SDK. I've finally got most of the transforms and keyframe handling in order, but it appears that the vertex data already has some sort of a transform baked in.
I'm attaching a screenshot to show you what I mean. That leg should not be twisted like that in the bind pose.
Is there a way to get the raw non-transformed vertex data somehow? If not, what transform is being applied there and what do I need to do to get a clean bind pose object for our animation system?
Thanks!
-
Hi @manvydas,
Welcome to the Maxon developers forum and its community, it is great to have you with us!
Getting Started
Before creating your next postings, we would recommend making yourself accustomed with our forum and support procedures. You did not do anything wrong, we point all new users to these rules.
- Forum Overview: Provides a broad overview of the fundamental structure and rules of this forum, such as the purpose of the different sub-forums or the fact that we will ban users who engage in hate speech or harassment.
- Support Procedures: Provides a more in detail overview of how we provide technical support for APIs here. This topic will tell you how to ask good questions and limits of our technical support.
- Forum Features: Provides an overview of the technical features of this forum, such as Markdown markup or file uploads.
It is strongly recommended to read the first two topics carefully, especially the section Support Procedures: Asking Questions.
About your First Question
Please elaborate on details of your setup and ways for us to reproduce your issue. Generally speaking there are so many points where something can go wrong, so it's absolutely necessary to reduce the complexity of the setup in order to pin point the issue. For example, the keyframe handling has nothing to do with the skeleton itself and only increases the complexity of your setup.
Is your C4D scene exported with polygon cache enabled? Polygon and Object Caches.
Does doc->HasCaches() actually report there is a polygon cache?
In general, if bind transforms are baked into polygon points data, then you should also see these artefacts when loading the scene normally in Cinema 4D.Please provide the sample scene that produces such behavior. In case of any confidential information involved, feel free to reach out to us using Contact Form.
Cheers,
Ilia -
Hello. Thanks for the reply!
Sorry if I wasn't clear enough. We have a working importer already, but the requirement to import skeletal meshes from C4D files is a new one. I'm certain that most of the code in the importer is good already, since it's not a new thing and only skeletal meshes are causing us trouble. Also, we always check for the presence of caches. That's why I went straight to something that looked odd to me.
I was comparing everything (bind pose matrices, keyframes, etc.) to our FBX importer which has always supported skeletal meshes. After we convert the C4D data to our own coordinate spaces, the values read from the FBX and the C4D files are almost identical. Bind pose matrices and final keyframe transforms match closely. The only thing that appears to be significantly different is the vertex data.
I don't think I can share the complex models, but I have a simpler one that we made just for testing. Please excuse the programmer "art" , but it's simple and still shows what I mean. We imported an FBX to C4D and exported the file again. Both as an FBX and as a C4D file. I'm attaching the C4D file.
I've also saved the vertices right after import to OBJ files. These are pre-converted to our own coordinate spaces, but, other than some axis flips or swaps, there should be no additional transforms on top of this vertex data. I'm attaching those OBJs as well.
As you can see, with the FBX import, I get the vertices in the bind pose. With the C4D import, I get an already deformed object.
To read the vertices, we use a function that overrides
PolygonObjectData::Execute()
. Inside it, we just access the vertex data through these functions (after a bunch of asserts and checks):PolygonObject *op = static_cast<PolygonObject*>(bo); const Vector *vertices = op->GetPointR(); Int32 vertexCount = op->GetPointCount(); const CPolygon *polygonIndices = op->GetPolygonR(); Int32 polygonCount = op->GetPolygonCount();
The rest is just regular vertex data reading, followed by some coordinate space changes. I'm not seeing any transforms being applied on our end.
Any ideas how to solve this would be greatly appreciated! Thanks!
-
Hi @manvydas,
Assuming you're using CreateSceneFromC4D() function for traversing the document, I suspect that your PolygonObjectData::Execute() is running on a deform cache, while you're expecting it to be an object itself. The CreateSceneFromC4D() function traverses the scene tree all the way down until it finds the last cache object in a chain, in your case it's the object with the applied skin deformer on it. This is done intentionally as this is the fastest way to extract the actual geometry of the object. Of cource, if your application is capable of handling more complex data than polygons and points (e.g. mesh skeleton), you're more interested in the structural data of the object.
To handle this I see two opportunities for you.
- IMO the right way (at least the one that gives you the most control over the process and hence the most flexibility) is to implement CreateSceneFromC4D() function yourself. There's no rocket science there, you just need to traverse document branches (aka Root Classes) yourself. This allows you to distinguish, which parts of the c4d document you app can understand (e.g. joints deformations) from those that you prefer to import as a plain geometry data from Cache or DeformerCache. For example, the traversal can look like this:
Bool CreateObjectFromC4DRecursive(BaseObject* op) { BaseObject* cOp = op->GetCache(); // object cache BaseObject* dcOp = op->GetDeformCache(); // deform cache, if the object is deformed // FIXME: here's the point where you decide, depending on what's supported in your software, // which of these three you want to execute: op->Execute(), cOp->Execute() or dcOp->Execute() // Recursively process children // please also check example at: https://developers.maxon.net/docs/cw/22_004/classcineware_1_1_base_object.html#a7c857d92833c4ddb48a0c105db52531b BaseObject* o = op->GetDown(); for(; o; o = (BaseObject*)o->GetNext()) CreateObjectFromC4DRecursive(o); return true; } Bool CreateSceneFromC4D(BaseDocument* doc) { doc->Execute(); // Traversing material branch BaseMaterial* mat = doc->GetFirstMaterial(); for(; mat; mat = (BaseMaterial*)mat->GetNext()) { // FIXME: do something with the material } // Traversing layers BaseObject* layer = doc->GetFirstLayer(); for(; layer; layer = (BaseObject*)layer->GetNext()) { // FIXME: do something with the layer } // Traversing object branch BaseObject* op = doc->GetFirstObject(); for(; op; op = (BaseObject*)op->GetNext()) CreateObjectFromC4DRecursive(op); return true; }
- The easier way (or may be not). You can keep using CreateSceneFromC4D() function, but whenever you receive execution in the PolygonObjectData::Execute(), you will do some work to figure out if it's a deformer cache of the skin modified object that you're currently working with. For example, you can traverse the tree upwards (using op->GetUp()), until you find the actual object, check if this object is a polygon object with an active skin deformer and weight tag, and if so, process points from the actual polygon object.
As you can see, there's no built-in way to check if your object is skin-deformed, so you'll need to implement this yourself in both cases.
Note: you can explore your C4D-document using C++ SDK Demo - Active Object Dialog from our sdk examples, this becomes pretty handy when figuring out object's underlying structure. For example, you spike_new.c4d scene looks like this, and what you get in the Execute() function is "DEFORM: Cube" instead of "Cube":
Please also be aware that the object may not contain cache at all, may only contain cache or deform cache or may contain both in the hierarchy, or even a list of caches. There's a nice explanation about it in the GetDeformCache(). Please check the example screenshot below.
Cheers,
IliaExample scene with cloner creates list of cache object each of which has its own cache -> deform_cache structure:
-
Thank you for the valuable information.
Implementing CreateSceneFromC4D() appears to be what I am missing. Since we never cared about skeletal transforms, we were relying on, as you put it, the fastest way to extract the actual geometry of the object, which was working really well for the previous simpler cases.
I'm currently working on other tasks, so, I haven't validated everything yet, but this seems to be the solution. I won't hesitate to ask if I run into other problems when I return to the importer, but this looks doable now. Thanks again for the help!