Plugins

Plugins are the building blocks of features in a 3D Viewer. Each plugin handles its own individual feature along with serialisation and lifecycle management.
WebGi viewer uses a plugin system to add new options, rendering styles, post processing passes, and more functionality.
The plugin architecture is designed similar to other js frameworks like vue or webpack (but for 3d rendering).

All plugins follow the same basic structure, independent of the logic, with the API to add and remove plugins being always consistent (and one-liner). This makes it easy to debug, bundle, tree-shake, serialisation/deserialisation and extend functionality to the 3d viewer. It is also recommended to keep individual plugins small and handle one specific functionality.

Plugins can be dependant on other plugins. These dependencies are automatically resolved and added to the viewer at runtime. eg. SSAOPlugin is dependant on GBufferPlugin to get the depth and normal data. So, when SSAOPlugin is added to the viewer, it automatically adds GBufferPlugin before that.

Note: Plugin dependencies are different from pass/filter dependencies, which specifies how passes should be arranged in the render pipeline (effect composer).
More information about this in later articles.

The webgi project ships with a library of plugins to achieve photorealistic rendering, generating user interfaces, handling events, loading and exporting assets, building 3d models etc.

Core plugins

The following plugins are available in webgi and can be added by importing them similar to the viewer.

Note: Checkout the plugin specific pages(coming soon) to get more details on the usage and API.

Adding a plugin

Plugins first need to be imported in your app to use. For that, simply import the named plugin in es6 import syntax.

// All plugins can be imported from `webgi`
import { 
	ViewerApp,
	ProgressivePlugin, AssetManagerPlugin, TonemapPlugin, // ...others
} from "webgi";

Instantiating a plugin can be done manually(before/after creating the viewer) or it can be left to the viewer to instantiate a provided viewer class.

// creating a plugin.
const ssaa = new ProgressivePlugin(16); // 16 is an optional argument that specifies the number of frames to render before stopping.
// add to the viewer
await viewer.addPlugin(ssaa);

// or let the viewer initialize
const ssaa = await viewer.addPlugin(ProgressivePlugin, 16);

// remove the plugin when required
await viewer.removePlugin(ssaa)

Note: it is not recommended to remove and re-add plugins that have custom passes or filters once is the pipeline has been built. To disable a plugin, set plugin.enable to false if it’s supported. In the case where we need a completely new set of plugins it’s best to initialise a new viewer

Writing custom plugins

Plugins need to implement a simple interface to be attached to the viewer:

interface IViewerPlugin extends IEventDispatcher<string>, IUiConfigContainer, Partial<IJSONSerializable> {  
  // all classes must have this static property with a unique identifier value for this plugin
  static readonly PluginType: string
  
  // these plugins will be added automatically(with default settings), if they are not added yet.
  dependencies?: Class<IViewerPlugin<any>>[]  
  
  // the viewer will render the next frame if this is set to true
  dirty?: boolean;  
  
  // Called when this plug-in is added to the viewer  
  onAdded(viewer: TViewer): Promise<void>;  
  
  // Called when this plug-in is removed from the viewer  
  onRemove(viewer: TViewer): Promise<void>;  
  
  // Called when the viewer is disposed  
  onDispose(viewer: TViewer): Promise<void>;  
}

There are abstract classes to create new viewer plugins with minimal boilerplate. This includes cases for plugins with a single or multiple pre/post processing pass, connecting with the DOM, serialisation and handling user input/interactions.

Check the Writing custom plugins guide and the source of existing plugins to know more.


Back to home