Progressive Mesh Partial Loading

Egor Slupko

November 12, 2021

Introduction

A progressive mesh is a special object that can be used to improve render performance. For example, a big shell with a large number of faces can be very small on the screen, and usually all faces are rendered independently, regardless of how small they (and the whole shell) are on the screen.

A progressive mesh allows you to change the Level of Detail (LOD) and obtain a more appropriate version of the original shell. For example, it may decrease the current LOD so the shell contains significantly fewer faces, and since it is small on the screen no visual defects are noticed.

In contrast to other LOD-related techniques, a progressive mesh allows LOD switching in real-time to any LOD starting from 0 (the minimum available representation) to the maximum LOD (original shell representation).

From the memory usage perspective, occupied memory can be divided into two groups: current LOD representation and a list of LOD information that provides switching between LODs.

  • The first group size depends on the current LOD: if zero, the representation is very small and grows linearly as the LOD increases.
  • The second group size is constant for each LOD, and its size is comparable to the maximum LOD representation memory usage.

ODA supports partial loading of progressive meshes starting with version 22.10. During partial progressive mesh loading, the described list of LOD information is not stored in RAM but kept in the file stream, which significantly reduces memory usage. In this case, LOD switching becomes slower but the performance decrease may be insignificant.

Use OdGiProgressiveMesh for Partial Loading

The OdGiProgressiveMesh interface is extended to support progressive mesh partial loading. It does not keep LOD information in memory but instead uses a customer-implemented instance of OdGiDataExtractor to access LOD data when needed.

OdGiProgressiveMesh Methods for Partial Loading

For partial loading, the OdGiProgressiveMeshGenerator has the following method:

virtual OdGiProgressiveMeshPtr createPartialProgressiveMeshFrom( OdStreamBuf* pBuff, OdGiDataExtractor* pDataExtractor,
  const OdGiProgressiveMeshObjectIdConverter* pConverter = NULL,
  OdGiProgressiveMesh::ProgressiveMeshStreamVersion version = OdGiProgressiveMesh::kVersionActual ) const = 0;

In addition, the OdGiProgressiveMeshEx has the following method:

virtual bool readPartialProgressiveMeshExFrom( OdStreamBuf* pBuff, OdGiDataExtractor* pDataExtractor, const OdGiProgressiveMeshObjectIdConverter* pConverter = NULL,
  OdGiProgressiveMesh::ProgressiveMeshStreamVersion version = OdGiProgressiveMesh::kVersionActual ) = 0;

These methods are similar to the original createProgressiveMeshFrom and readProgressiveMeshExFrom methods, but they use an instance of OdGiDataExtractor instead of keeping LOD information in memory. OdGiProgressiveMesh also has several methods:

  • virtual bool isInPartialMode() const = 0;
    Defines whether a progressive mesh is in partial mode.
  • virtual void endPartialMode() = 0;
    Forces loading of LOD information into memory and ends partial mode. This may be helpful when re-writing a file that is used for loading a progressive mesh in partial mode; in this case there is no guarantee that the extractor and calculated offsets are valid since the stream can be changed.
Use OdGiDataExtractor for Partial Loading

The OdGiDataExtractor class defines the data extraction interface. Each object may use an extractor in different ways, so the extractor implementation cannot know what exact data it is currently processing. Instead, objects register different data types in the extractor and notify it when they want to extract registered data.

Data registration uses two OdGiDataExtractor methods that register the beginning of the dataId and register its end:

virtual bool registerDataBegin( OdUInt8 dataId, OdUInt64 localOffset ) = 0;
virtual bool registerDataEnd( OdUInt8 dataId, OdUInt64 localOffset ) = 0;

Before an object can extract data, it needs to start extraction using the following method:

virtual bool beginExtraction( OdUInt8 dataId ) = 0;

Usually in this method, the extractor should look at the beginning of the registered dataId, but it may differ among implementations. After an object extracts all necessary data, it has to end extraction using the following method:

virtual bool endExtraction( OdUInt8 dataId ) = 0;

Usually this method resets the extractor. During beginExtraction() and endExtraction(), the object may extract data using the following method:

virtual void extractBytes( void* buffer, OdUInt32 numBytes ) = 0;

OdGiDataExtractor also has some helper methods with a default implementation, based on extractBytes().

Also, the object may use:

virtual bool seekFromLocalOffset( OdUInt64 offset ) = 0;

Note that the offset value in this case is a local dataId offset which is calculated using dataId beginning at 0.

Use OdTvProgressiveMesh for Partial Loading

Each OdTvProgressiveMesh that is loaded from a VSFX file supports two versions of partial progressive mesh loading. The readVSFX methods of the OdTvFactoryId class have a OdTvVSFXReadOptions options parameter. These options have the progressiveMeshMode field, which can be one of the following:

  1. OdTvVSFXReadOptions::kNormalMode — Reads the progressive mesh in the usual mode.
  2. OdTvVSFXReadOptions::kPartialMode — Reads the progressive mesh in pure partial mode.
  3. OdTvVSFXReadOptions::kFastPartialMode — Reads the progressive mesh in a fast hybrid of partial mode. Note that OdTvProgressiveMesh from VSF files does not support partial loading.
OdTvVSFXReadOptions::kPartialMode

This is pure partial mode. The progressive mesh does not store LOD information in memory, and it extracts each LOD detail separately, one by one. So memory usage is minimal all the time. On the other hand, LOD switching is very slow since extraction of each LOD detail requires file stream access. However, it can be a good solution in critical memory usage cases.

OdTvVSFXReadOptions::kFastPartialMode

This is a fast hybrid of partial mode. The progressive mesh does not store LOD information in memory. However, when it needs to switch the LOD, it loads from the file stream the whole list of LOD information. After LOD switching is complete, it unloads the list of LOD information. So when performing LOD switching, memory usage grows, but after it is complete, memory usage is minimal again. This approach reduces performance loss to be almost imperceptible.