USD export tasks for meshes (either static, geometry cache, skeletal etc.) seem to utilize the “render mesh” to do so which is the triangulated mesh the engine seems to be using. We desired the ability to create proxy purpose versions of the meshes, but could find no way to do this with the current USD export options. However, nanite allowed us a workaround since it has a fallback triangles percentage which seems to affect the render mesh prior to it being processed by the USD export task. This has worked decently so far, but has a few shortcomings. 1) you have to activate or modify nanite settings during the export process and 2) you cannot use this method to make a proxy mesh for anything that cannot use nanite (e.g. Geometry Caches).
It would be extremely useful to simply have the ability to decimate triangle count as part of the USD mesh export task directly. Additionally, if there is some existing C++ code that we could use in the meantime to do whatever nanite is doing to to produce the render mesh that would be helpful as well. Perhaps we could run something prior to the mesh and pipe that into the export task.
Hi Chris!
To pull back a step for a moment just to get some extra context, are these meshes that you’re exporting the result of geometry operations and/or modeling in the engine, or were they previously imported from somewhere (may or may not have been USD) and are now being re-exported? And is this an export just of assets (i.e. from the Content Browser), or are these meshes being exported in the context of a level? Also, I would guess that you may be post-processing the exported USD? For example, I don’t believe there’s a way to specify the purpose the USD mesh should be assigned when a UE mesh is exported, so are you authoring purpose post-export?
We try to keep the translation during export from uassets to USD as literal as possible, so geometry processing is kept to a minimum and there are indeed not many export options that would influence the geometry/topology. As a result, I would say that if you’re able to produce whatever geometry you intend to export before initiating the export process, whether that’s in-engine or otherwise, that might fit best with how the export process works.
Here are a few ideas along that vein:
If using the auto mesh reduction and automatic LOD generation provide acceptable results for you, you could turn up the number of LODs on the UE mesh and then make sure that your USD export is setup to export the full range of those generated LODs. This will produce a LOD variant set on the exported Mesh prim with “LODX” variants for each level of detail. In your USD post-processing, you could extract one of these LODs into a standalone Mesh prim that you then author with purpose=proxy.
In UE 5.5, there’s a new option to allow you to choose between exporting the mesh source data (if available) versus the render mesh:
https://github.com/EpicGames/UnrealEngine/commit/dc9bd881ac089675b40bfbdf051435819cd75064
If your geometry was previously imported from USD, hopefully this source data would be in place, and exporting with that option turned on would give you as close to the original mesh as possible. If you’re not able to move to 5.5, you may be able to patch that change back to 5.4, though there may have been some additional earlier changes that it depends on.
Let me know if any of that helps. Otherwise we can see if there are any pointers the Nanite and/or geometry teams might be able to provide.
Thanks!
I caught up with Chris on Slack, but wanted to make sure that information was reflected here.
We do not currently have any plans to do much in the way of geometry processing within the USD export process, again with the intention of keeping it as literal a translation as possible. So the only real options for fine tuning what ultimately winds up in USD are to process the geometry either before or after the export. Prior to export, the meshes could be setup with auto-generated LODs or otherwise manipulated into the form you want, and/or post-export you could process the USD however you’d like, whether that’s in-engine still using the pxr API or in another DCC.
Outside of that, here are a few pointers to things that might be helpful if you want to assemble your own custom export process. These are all in terms of UE 5.5+, and code links are to the dev trunk (the ue5-main branch on GitHub), though you may be able to find them in earlier releases.
For Nanite specifically, the fallback mesh generation that makes use of the “Fallback Triangle Percent” property drew some interest. A good entry point to see how that process works would be the BuildFallbackMesh() function here:
https://github.com/EpicGames/UnrealEngine/blob/82a3df7f27815c2f3c9ac1■■■120d91694834228/Engine/Source/Developer/NaniteBuilder/Private/NaniteBuilder.cpp\#L582
There is also the Geometry Scripting toolset that might prove useful if you wanted to avoid needing to drop down to C++:
https://dev.epicgames.com/documentation/en\-us/unreal\-engine/geometry\-scripting\-users\-guide\-in\-unreal\-engine
Maybe not as accessible through scripting, but some of the Mesh operations in modeling mode may fit the bill, in particular ones like “Simplify”. If using those tools interactively isn’t feasible, then the underlying code they invoke might be interesting:
https://github.com/EpicGames/UnrealEngine/tree/252cd315339f4b3eb35acae275438b841bcfa4bd/Engine/Plugins/Runtime/GeometryScripting
Then on the USD export side, the UnrealToUsd::ConvertStaticMesh() function here is probably a good place to look for how we currently extract data from StaticMeshes for export:
https://github.com/EpicGames/UnrealEngine/blob/252cd315339f4b3eb35acae275438b841bcfa4bd/Engine/Plugins/Runtime/USDCore/Source/USDUtilities/Private/USDGeomMeshConversion.cpp\#L4448
The analog to that for skeletal meshes would be UnrealToUsd::ConvertSkeletalMesh() here:
https://github.com/EpicGames/UnrealEngine/blob/252cd315339f4b3eb35acae275438b841bcfa4bd/Engine/Plugins/Runtime/USDCore/Source/USDUtilities/Private/USDSkeletalDataConversion.cpp\#L3601
There isn’t currently a clean way to swap those out or extend them with your own code, but you may be able to lift pieces from them to implement your own export solution.
Hope that helps!
Hi Matt!
Thanks for the reply, to answer your questions. This geometry is simply being imported from FBX or other sources and not modified in the engine (aside from Nanite being turned on). So we are basically just converting it to USD as part of the export process without any changes done in the engine. There is indeed post-process being done, I wrote my own USD exporter, but we do leverage the USD export tasks for meshes and materials to include in the stages we create.
I think your comment about the export being literal to the mesh is a totally fair one. A lot of the meshes do not have LODs (and one can argue that this would be the proper way to handle lower-resolution proxies), so that is why I was leveraging nanite as a way to get some triangle decimation for these meshes. It was simply a matter of convenience for artists (especially when getting the source scenes from vendors) so that we can get USD scenes with proxies regardless of whether someone had authored true LODs.
I may very well look into the multiple LOD exports as a replacement for the current nanite solution (especially in 5.5 when I can use the source geometry option) for meshes that have them. However, there may always be some that do not have LOD and we still need a solution for that. I was hoping that since clearly the engine does have the ability to do some decimation (as nanite is doing this), we could leverage that directly in the USD export itself too.