Particle Reader Plugins

The PtServer supports dynamic loading of reader plugins, allowing you to extend the server with custom particle file format readers. A reader plugin acts as a provider of particle data to the server, enabling in-house file formats to be served with the same ease of use and performance as the built-in PTFX and VTP readers.

Particle Plugin Discovery

To use reader plugins with the PtServer, set the CEW_PT_READER_PLUGIN_FOLDER environment variable to point to the folder containing your plugin shared libraries. The PtServer will scan this folder and load all libraries matching the naming convention on startup.

Plugins must be named with the cpt_ prefix (case-insensitive):

cpt_{PLUGIN_NAME}.[dll|so|dylib]

Examples:

cpt_MyCustomFormat.dll     (Windows)
cpt_MyCustomFormat.so      (Linux)
cpt_MyCustomFormat.dylib   (macOS)

The PtServer checks for duplicates and only loads files that match the required prefix and platform extension. Subdirectories are not recursively scanned.

Particle Reader Plugin API (C ABI)

Reader plugins expose a single C entry point and register themselves with the framework via a callback.

Required export:

extern "C" CPT_PLUGIN_EXPORT CPT_PluginExitFunc initializeReaderPlugin(
    const CPT_FrameworkServices* frameworkServices);

The initializeReaderPlugin function is called once when the plugin is loaded. The plugin must:

  1. Check API version compatibility (major version must match).
  2. Optionally call setPluginInfoFunc to report plugin metadata (name, version, author).
  3. Call registerReaderFunc to register one or more reader factories.
  4. Return a cleanup function (or nullptr if no cleanup is needed).

Framework services provided to the plugin:

struct CPT_FrameworkServices {
    int                     pluginApiMajorVer;   // Currently 1
    int                     pluginApiMinorVer;   // Currently 0
    CPT_RegisterReaderFunc  registerReaderFunc;  // Register reader factories
    CPT_LogFunc             logFunc;             // Server-side logging
    CPT_SetPluginInfoFunc   setPluginInfoFunc;   // Report plugin metadata
};

Reader registration parameters:

struct CPT_RegisterReaderParams {
    int                     pluginApiMajorVer;
    int                     pluginApiMinorVer;
    CPT_CreateReaderFunc    createReaderFunc;   // Factory: create a reader instance
    CPT_DestroyReaderFunc   destroyReaderFunc;  // Destroy a reader instance
    CPT_CanReadFunc         canReadFunc;        // Test if a file path is supported
};

Function signatures:

typedef cee::pt::ParticleDatasetReader* (*CPT_CreateReaderFunc)();
typedef void (*CPT_DestroyReaderFunc)(cee::pt::ParticleDatasetReader*);
typedef bool (*CPT_CanReadFunc)(const char* filePath);

The canReadFunc is called to determine whether the plugin can handle a given file path (typically by checking the file extension). The createReaderFunc returns a new instance of your reader that implements the ParticleDatasetReader interface.

Implementing ParticleDatasetReader

Your plugin’s reader must derive from cee::pt::ParticleDatasetReader and implement the following virtual methods:

class ParticleDatasetReader {
public:
    virtual bool open(const cee::Str& filePath, Error* error = nullptr) = 0;
    virtual bool setActiveScalarField(const cee::Str& fieldName) = 0;

protected:
    virtual std::unique_ptr<FrameData> readFrame(
        size_t frameIndex, const cee::Str& activeScalar) = 0;
    virtual void resetDerivedState() {}
};
  • open(): Open the dataset at the given path. Populate the dataset header (frame count, bounding box, particle count, scalar field names) and return true on success.
  • setActiveScalarField(): Switch the active scalar field for subsequent frame reads.
  • readFrame(): Read a single frame’s particle data (positions, IDs, and optional scalar values) and return it as a FrameData object.
  • resetDerivedState(): Optional cleanup hook called when the reader is closed.

The base class provides thread-safe caching, so readFrame() is only called on cache misses. The base class also handles close(), clearActiveScalarField(), and LRU cache eviction.

Particle Reader Plugin SDK

To create your own reader plugin, download HOOPS Envision for Desktop from https://developer.techsoft3d.com. The particle reader plugin framework is found in the CeeParticleModel folder in the distribution, which includes:

  • ParticleReaderPluginApi.h: the C ABI header your plugin must include.
  • ParticleDatasetReader.h: the base class your reader must implement.
  • FrameData.h / DatasetHeader.h: data structures for frame and header information.

The framework is provided as source code so you can compile with your preferred compiler and settings.

For the full desktop API reference, see the HOOPS Envision for Desktop documentation: https://docs.techsoft3d.com/hoops/latest/envision-desktop/api-ref/components.html

In-Process Custom Particle Readers (Desktop)

In addition to the plugin mechanism (shared libraries loaded at runtime), the desktop ParticleModel API also supports injecting a custom reader directly via:

bool ParticleModel::open(std::unique_ptr<ParticleDatasetReader> reader, Error* error = nullptr);

This transfers ownership of your reader instance to the model. Use this approach when:

  • You want to embed particle reading in your own application (no separate server).
  • Your reader requires application-specific context (e.g. database connections) that cannot be passed through the plugin C ABI.
  • You are prototyping a new format before packaging it as a plugin.

Version Compatibility

The plugin API uses semantic versioning:

  • Major version (CPT_API_MAJOR_VER): Must match between plugin and host. A mismatch causes the plugin to be rejected at load time.
  • Minor version (CPT_API_MINOR_VER): Additive changes only. A plugin compiled against an older minor version will still load on a newer host.

Current version: 1.0.

Cross-References