I am attempting to export a LOD group to .fbx, using Blender v3.4, which I can then import in a custom game engine. Following this video I was able to properly create the LOD group on export.
To summarize that video, you just add an empty node that each LOD is parented to, which has a custom property. The custom property has to be of string type, with name fbx_type and value LodGroup. In the Blender .py scripts I can see how that property gets translated to a LodGroup node in the .fbx. However, I can't see what property might translate into a threshold distance on export.
I am trying to use the FBX SDK function GetNumThresholds() to retrieve the number of thresholds, and then will iterate them using GetThreshold() to retrieve the distance for each. The problem is, I can't figure out how to set up the threshold distances in Blender to be properly exported. Regardless of what I've tried with additional custom properties, when I call GetNumThresholds() the return value is always 0.
I have attempted adding various custom properties to both the LodGroup node and to the individual LODs (excluding LOD0), but I can't actually find anything documenting how to do this, and my blind attempts have not panned out. I have tried an array of floats on the LodGroup node, as well as individual float values as custom properties on the individual LODs. I have tried using the names fbx_distance, threshold.
I'm new to FBX and Blender, so any help would be greatly appreciated. Is there a comprehensive spec for the FBX file format that is publicly available?
Update:
I have attempted to use the FBX SDK and FBX Converter tool to discern the properties required. I use the SDK to import the .fbx file created with Blender, add the thresholds, and export a new .fbx. From there I was planning to use Autodesk's FBX Converter tool to convert the binary to ASCII, so I could read it manually to see if it provided any insight as to what properties (node location, name, type, etc.) are expected for proper thresholds. Unfortunately the converter tells me the resulting (binary) .fbx is corrupted, so I can't convert it to ASCII. Despite the converter saying the file is corrupted, I can still read it with the SDK, including the now present threshold data. I can import the "corrupted" .fbx in Blender, but cannot seem to find anything in the Blender UI that indicates threshold distance has been added. I was following examples from the FBX SDK documentation for adding thresholds and exporting.
For reference:
bool create_thresholds(FbxLODGroup* lod_group, bool percentages = false)
{
const int num_nodes{ lod_group->GetNode()->GetChildCount() };
if (num_nodes == 0 || lod_group->GetNumThresholds() == (num_nodes - 1)) return false;
bool result = true;
lod_group->ThresholdsUsedAsPercentage.Set(percentages);
if (percentages)
{
double step{ 100.0 / num_nodes };
for (int i{ 1 }; i < num_nodes; ++i)
{
result &= lod_group->AddThreshold(step * (double)i);
}
}
else
{
for (int i{ 1 }; i < num_nodes; ++i)
{
result &= lod_group->AddThreshold(FbxDistance(5.f * i, "m"));
}
}
return result;
}
void save_fbx_file(FbxManager* mgr, FbxScene* scene)
{
FbxExporter* exporter{ FbxExporter::Create(mgr, "Exporter") };
const char* filename{ "modified.fbx" };
bool status{ exporter->Initialize(filename, -1, mgr->GetIOSettings()) };
if (status) exporter->Export(scene);
exporter->Destroy();
}
... [various snippets to make c/p easier] ...
// Initialization and scene import...
FbxManager* mgr = FbxManager::Create();
FbxIOSettings* ios{ FbxIOSettings::Create(mgr, IOSROOT) };
mgr->SetIOSettings(ios);
FBXScene* scene = FbxScene::Create(mgr, "Import Scene");
FbxImporter* importer{ FbxImporter::Create(mgr, "Importer") };
if (!(importer &&
importer->Initialize("no_thresholds.fbx", -1, mgr->GetIOSettings()) &&
importer->Import(scene))) return;
importer->Destroy();
...
// Example of traversal to find a LOD Group node in relatively flat fbx
// e.g.
// root
// |
// LOD group
// / | \
// LOD0 LOD1 LOD2
FbxNode* root = scene->GetRootNode()
for (int i{ 0 }; i < root->GetChildCount(); ++i)
{
FbxNode* node{ root->GetChild(i) };
for (int j{ 0 }; j < node->GetNodeAttributeCount(); ++j)
{
FbxNodeAttribute* attribute{ node->GetNodeAttributeByIndex(j) };
if (attribute->GetAttributeType() == FbxNodeAttribute::eLODGroup)
{
FbxLODGroup* lod_grp{ (FbxLODGroup*)attribute };
if (create_thresholds(lod_group)) save_fbx_file(mgr, scene);
}
}
}