Working with Simplygon objects in Python
Disclaimer: The code in this post is written on version 9.0.11600 of Simplygon. Should you encounter this post running a different version, some of the API calls might differ. However, the core concepts should still remain valid.
The Simplygon Python and C# APIs are wrappers around the core C++ library that contains all the algorithms and tools. This means that the ways of working with the API is inherited from the C++. Both in Python and C# there are some things that might seem odd at first, and this post will shed some light on this. In our example code we will be using Python, but the concepts are the same in C#.
Smart pointers and interfaces
All objects in Simplygon core are so called smart pointers. For those not used to working in C++, this is a common way of simplifying memory allocation and deallocation. Simplygon object classes are defined as interfaces, which are then wrapped by smart pointer object. When you encounter an object which starts with an I (IGeometryData for example) it's an interface class. If the object starts with sp (spScene for example) it is a smart pointer.
In our documentation we always talk about the name of the class, without the prefix. However, when coding in Python or C# there are a number of situations that you need to be aware these prefixes.
There is a hierarchy of inheritance in Simplygon and some functions in the API will return the generic parent of a collection of sub classes (for example GetChild on SceneNode) . If you want to know what type of class an object is, you can use GetClass. For example, after loading a pipeline you can check the type with this code.
serializer = sg.CreatePipelineSerializer() pipeline = serializer.LoadPipelineFromFile("pipeline.json") print(pipeline.GetClass())
If the pipeline is a reduction pipeline, the ouput will be IReductionPipeline , in the case of a remeshing pipeline it would be IRemeshingPipeline and so on.
The previous examples show that the object knows which type it is, but that is not the case for the Python or C# environment. Luckily there is a SafeCast function on in every Simplygon class that can help you with that Let's take the pipeline loading example again. We might assume that we could call, for example, GetReductionSettings directly after loading the pipeline. Like this:
sg = simplygon_loader.init_simplygon() serializer = sg.CreatePipelineSerializer() pipeline = serializer.LoadPipelineFromFile("reduction.json") reduction_settings = pipeline.GetReductionSettings() reduction_settings.SetReductionTargetTriangleRatio(0.5)
But this will result in the following error:
AttributeError: 'spPipeline' object has no attribute 'GetReductionSettings'
Even though you know it's a reduction pipeline, Python doesn't. It believes it's an Pipeline (the parent class). In order to use it, we need to run SafeCast first. By adding this line of code after the loading, the object will be transformed into a reduction pipeline.
pipeline = Simplygon.spReductionPipeline.SafeCast(pipeline)
In this case we are using a static function in the reduction pipeline smart pointer object. All objects in Simplygon has this function.
If the object you're safe casting isn't of the type you assumed, you will get None back. For example, if we would try to cast the object to a remeshing pipeline like below, the object would be None.
serializer = sg.CreatePipelineSerializer() pipeline = serializer.LoadPipelineFromFile("reduction.json") pipeline = Simplygon.spRemeshingPipeline.SafeCast(pipeline)
We can use this behavior of SafeCast in situations where we want to filter out certain object types. For example, if you want to find all meshes in a Simplygon scene, you can use the SafeCast-function to filter out all objects of the type SceneMesh . In the code below we are load a scene and recursively search through all nodes to filter out meshes.
def find_all_meshes(scene_node, mesh_list): for i in range(0, scene_node.GetChildCount()): child = scene_node.GetChild(i) mesh = Simplygon.spSceneMesh.SafeCast(child) if mesh is not None: mesh_list.append(mesh) find_all_meshes(child, mesh_list) sg = simplygon_loader.init_simplygon() scene_importer = sg.CreateSceneImporter() scene_importer.SetImportFilePath("test_asset.fbx") if scene_importer.RunImport(): mesh_list =  find_all_meshes(scene_importer.GetScene().GetRootNode(), mesh_list) print(len(mesh_list))
We could of course apply the same logic if we wanted to filter other types of scene object, for example SceneBones.
When you understand the concepts described above you have a good foundation to continue your work with Simplygon objects in both Python and C#.