Skip to content

Optimizing Objects

James Walker edited this page Sep 1, 2019 · 2 revisions

Prefer TriMesh for 2D Geometry

Quesa supports 21 geometry object types, but the OpenGL renderer only has direct support for 4 kinds of geometries: Point, Line, PolyLine, and TriMesh. Every other kind of geometry is rendered using Quesa's decomposition and caching mechanisms. For example, a Box geometry is decomposed to a group containing an attribute set and 6 TriMeshes, one for each face. Consequently, it is generally best to use a TriMesh for each of your 2D geometries.

TriMesh Attribute Data

OpenGL requires vertex normal vectors in order to compute lighting. If these are not provided, the OpenGL renderer can compute and cache a version of the TriMesh that does have vertex normals, but it is better to provide them.

OpenGL can handle vertex colors but not face colors. If a TriMesh has face colors but not vertex colors, the renderer will compute and cache an optimized version that does have vertex colors. If a TriMesh has face textures, it will need to be rendered one triangle at a time, which will be much slower.

To render transparent objects with OpenGL, the triangles must be sorted from back to front. Therefore, you get relatively slow rendering when there is any kind of transparency: Transparency colors on vertices, faces, or the geometry as a whole, or a texture with an alpha channel.

Emissive colors on vertices or faces will also knock a TriMesh off the "fast path".

Simpler Structure is Better

When Quesa renders a complex group hierarchy, it involves a significant amount of overhead beyond actual rendering, such as traversing groups, updating the current transform, and updating the current attributes. Furthermore, OpenGL can more efficiently render one array of many triangles than many small arrays of triangles. Therefore, you should strive for simple hierarchies of a few TriMeshes.

Algorithms for Simplification

The Quesa SDK includes source code for several algorithms that can help simplify object hierarchies. Some of the algorithms are used in the Geom Test sample program, showing that these algorithms can speed the rendering of its "multi-box" object by orders of magnitude. The original multi-box object consists of 1000 subgroups, each containing a transform and a reference to a box. The fact that there is only one geometry that is referenced many times may seem memory-efficient, but does not help the renderer.

The first optimization step is the function ApplyTransformsToGeometries, which removes the transform objects by applying the transforms to the data of the Box geometries. Of necessity, it turns the 1000 references to one Box to 1000 individual Box geometries.

The next step uses the function DecomposeGeometries, which replaces each Box by its representation as a group of TriMeshes. This function must be called inside a submitting loop; the Geom Test example uses a bounding loop.

Incidentally, if we had called DecomposeGeometries before ApplyTransformsToGeometries, the end result would have been the same.

So far, we have replaced a group hierarchy containing 1000 Box geometries by a deeper hierarchy containing 6000 TriMesh geometries. Next we use the FlattenHierarchy function, producing a hierarchy of 2 levels. In general, FlattenHierarchy may produce many groups at the second level. But in our specific example, since there are no transforms, styles, free-floating attribute sets, or shaders, we get only one group at the second level, and all 6000 TriMeshes are siblings.

Finally the function MergeTriMeshes merges sibling TriMeshes with compatible attributes, resulting in only 6 TriMeshes, one for each face color.