Using Python API to Decimate Models at Runtime of Other Applications

Hello all!

I am currently in a group working on a project that involves server side decimation of objects using the Blender 2.90 Python API. Our current script involves decimation and conversion into gltf format for use with the Sceneform AR engine in Android. When converting, we cannot seem to pin down exactly what causes longer or shorter decimation times. Our first theory had to do with face/tri count, but it seems that the results vary from model to model, even when considering tri count. For example, we tried to decimate a rose (~166k tris), a jet fighter (~146k tris), and a car (~180k tris) and the results came back as approximately 5 seconds for both the rose and the jet, but 11 seconds for the car. Our best theory is that decimation time depends on the objects geometry, but wanted to know if the community had more insight on the entire process. Does decimation time have anything to do with things like geometry, size, textures, etc? Does anyone have any suggestions regarding reducing decimation time outside of raw computing power? Are there any other methods that could be used to achieve the same result?

The current script we use for conversion can be found here converted to the 2.8x API and exporting as gltf rather than obj. Measured times are times recorded for running said script. If needed, I can post the modified code or the code used to test the system.

Just in case, here are a few of the specifications on the computer used for testing:
Ubuntu 20.04.1
Blender 2.90.1
Python 3.8.5
Intel i5-4670K CPU @ 3.4 GHz
GeForce GTX 1070
All done on an HDD

Let me know if I can provide any further information!

I don’t think there is any quick-fix for speeding up the current decimate logic - as you seem to be looking for.

Variation in decimation time is likely caused by the topology of the mesh as collapsing one edge needs to update the error values of connected geometry.

I see from your github that you’ve done something similar with Meshlab. Out of curiosity, how do they compare? Blender’s decimate modifier is largely single-threaded IIRC, and I expect that it is slower.

Anyways, the collapse decimation method is iterative as far as I know, and it works by collapsing short edges until the correct number of faces is reached. I think the overhead is due to the algorithm having to choose which edges to collapse.

It might be worth recording the import time, too, as that may contribute significantly. I don’t think the fast .obj importer/exporter has made its way into master yet. Actually, this is my best guess as to where the time is spent.

EDIT: brecht got to it first with a more correct answer!

We suspected as much, I appreciate the response!

We used Meshlab for a small amount of time, but the decimated models turned out a lot nicer in Blender. I’ll have to do some poking around though, it might be worth switching back over if we can find an increase in performance. I’ll also look into recording input times, is there an ideal format to import the models in? Or could it possibly be worth it to run a development build to use the fast importer?

The development build doesn’t have the new importer yet, either – you’d have to apply two patches from Arcanist (the importer and exporter are separate patches) and build it yourself from source.

As far as I know, all of Blender’s importers are Python except Alembic and (in the future, USD).

If you are doing something server-side, I wonder if you might be better off using C++ libraries in a custom program, using e.g. Assimp for faster I/O and Meshlab or CGAL for geometry processing… but you should be sure that it is all compatible with whatever licensing model your Android app is using. Same goes for using Blender, actually. Anyways, as long as it all remains GPL2 compatible it’s OK to re-implement or outright copy Blender’s decimate implementation for whatever you want to do.

I don’t know enough about your software background to know if I’m offering a helpful advice here. I would have enough trouble doing the above that I might shrug my shoulders and accept things as they stand, but if you can handle server stuff maybe you’ll have an easier time of it.

To be fair, the server stuff we’re doing isn’t anything too complex. We’re mostly looking for a proof of concept with some small functionality. I wasn’t exactly sure where to go from here, so this helps a bunch. I’m off to go start doing some digging. Thanks a bunch!

Some other suggestions:

  • Separate loose parts before running decimate (so it can be executed in parallel).
  • If you can accept much lower quality, you could perform weld to merge very small edges.
  • If you wanted to edit the code, you could avoid re-calculating normals/error values… YMMV.