|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130 |
- ---
- prev:
- text: 'Serialization'
- link: './serialization'
-
- #next:
- # text: 'UI Configuration'
- # link: './ui-config'
- ---
-
- # Plugin System
-
- Plugins are the building blocks of features in a 3D Viewer. Each plugin handles its own individual feature along with serialisation and lifecycle management. Threepipe 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).
-
- ::: tip
- Check the pages on [Core Plugins](./core-plugins) and [@threepipe Packages](./threepipe-packages) for a list of available plugins.
- :::
-
- 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 dependent on other plugins. These dependencies are automatically resolved and added to the viewer at runtime. e.g. `SSAOPlugin` depends on `GBufferPlugin` to get the depth and normal data. So, when `SSAOPlugin` is added to the viewer, it automatically adds `GBufferPlugin` before that (if not added already).
-
- ::: note
- Plugin dependencies are different from pass/filter dependencies, which specifies how passes should be arranged in the render pipeline (effect composer).
- :::
-
- Threepipe ships with a library of internal and external plugins to achieve photorealistic rendering, generating user interfaces, handling events, loading and exporting assets, building 3d models etc.
-
- The plugins can be added synchronously or asynchronously using `viewer.addPluginSync` and `viewer.addPlugin` methods respectively.
-
- It is recommended to create custom plugins for reusable features, as they provide built-in features for ui configuration, serialization, integration with editors etc. and are easy to manage and tree-shake in the code.
-
- Check out the list of plugins in the [Core Plugin](./core-plugins) and [@threepipe Packages](./threepipe-packages) pages.
-
- To create new plugins, simply implement the `IViewerPlugin` interface or extend the [AViewerPluginSync](https://threepipe.org/docs/classes/AViewerPluginSync.html) or [AViewerPluginAsync](https://threepipe.org/docs/classes/AViewerPluginAsync.html) classes.
- The only difference is that in async the `onAdded` and `onRemove` functions are async.
-
- Here is a sample plugin
- ```typescript
- @uiFolder("Sample Plugin") // This creates a folder in the Ui. (Supported by TweakpaneUiPlugin)
- export class SamplePlugin extends AViewerPluginSync<"sample-1" | "sample-2"> {
- // These are the list of events that this plugin can dispatch.
- static readonly PluginType = "SamplePlugin"; // This is required for serialization and handling plugins. Also used in viewer.getPluginByType()
-
- @uiToggle() // This creates a checkbox in the Ui. (Supported by TweakpaneUiPlugin)
- @serialize() // Adds this property to the list of serializable. This is also used when serializing to glb in AssetExporter.
- enabled = true;
-
- // A plugin can have custom properties.
-
- @uiSlider("Some Number", [0, 100], 1) // Adds a slider to the Ui, with custom bounds and step size (Supported by TweakpaneUiPlugin)
- @serialize("someNumber")
- @onChange(SamplePlugin.prototype._updateParams) // this function will be called whenevr this value changes.
- val1 = 0;
-
- // A plugin can have custom properties.
- @uiInput("Some Text") // Adds a slider to the Ui, with custom bounds and step size (Supported by TweakpaneUiPlugin)
- @onChange(SamplePlugin.prototype._updateParams) // this function will be called whenevr this value changes.
- @serialize()
- val2 = "Hello";
-
- @uiButton("Print Counters") // Adds a button to the Ui. (Supported by TweakpaneUiPlugin)
- public printValues = () => {
- console.log(this.val1, this.val2);
- this.dispatchEvent({ type: "sample-1", detail: { sample: this.val1 } }); // This will dispatch an event.
- }
-
- constructor() {
- super();
- this._updateParams = this._updateParams.bind(this);
- }
-
- private _updateParams() {
- console.log("Parameters updated.");
- this.dispatchEvent({ type: "sample-2" }); // This will dispatch an event.
- }
-
- onAdded(v: ThreeViewer): void {
- super.onAdded(v);
-
- // Do some initialization here.
- this.val1 = 0;
- this.val2 = "Hello";
-
- v.addEventListener("preRender", this._preRender);
- v.addEventListener("postRender", this._postRender);
- v.addEventListener("preFrame", this._preFrame);
- v.addEventListener("postFrame", this._postFrame);
-
- this._viewer!.scene.addEventListener("addSceneObject", this._objectAdded); // this._viewer can also be used while this plugin is attached.
- }
-
- onRemove(v: ThreeViewer): void {
- // remove dispose objects
-
- v.removeEventListener("preRender", this._preRender);
- v.removeEventListener("postRender", this._postRender);
- v.removeEventListener("preFrame", this._preFrame);
- v.removeEventListener("postFrame", this._postFrame);
-
- this._viewer!.scene.removeEventListener("addSceneObject", this._objectAdded); // this._viewer can also be used while this plugin is attached.
-
- super.onRemove(v);
- }
-
- private _objectAdded = (ev: IEvent<any>) => {
- console.log("A new object, texture or material is added to the scene.", ev.object);
- };
- private _preFrame = (ev: IEvent<any>) => {
- // This function will be called before each frame. This is called even if the viewer is not dirty, so it's a good place to do viewer.setDirty()
- };
- private _preRender = (ev: IEvent<any>) => {
- // This is called before each frame is rendered, only when the viewer is dirty.
- };
- // postFrame and postRender work the same way as preFrame and preRender.
- }
- ```
-
- Notes:
- * All plugins that are present in the dependencies array when the plugin is added to the viewer, are created and attached to the viewer in `super.onAdded`
- * Custom events can be dispatched with `this.dispatchEvent`, and subscribed to with `plugin.addEventListener`. The event type must be described in the class signature for typescript autocomplete to work.
- * Event listeners and other hooks can be added and removed in `onAdded` and `onRemove` functions for the viewer and other plugins.
- * To the viewer render the next frame, `viewer.setDirty()` can be called, or set `this.dirty = true` in preFrame and reset in postFrame to stop the rendering. (Note that rendering may continue if some other plugin sets the viewer dirty like `ProgressivePlugin` or any of the animation plugins). Check `isConverged` in `ProgressivePlugin` to check if it's the final frame.
- * All Plugins which inherit from AViewerPlugin support serialisation. Create property `serializeWithViewer = false` to disable serialisation with the viewer in config and glb or `toJSON: any = undefined` to disable serialisation entirely
- * `plugin.toJSON()` and `plugin.fromJSON()` or `ThreeSerialization` can be used to serialize and deserialize plugins. `viewer.exportPluginConfig` and `viewer.importPluginConfig` also exist for this.
- * @serialize('label') decorator can be used to mark any public/private variable as serializable. label (optional) corresponds to the key in JSON.
- * @serialize supports instances of ITexture, IMaterial, all primitive types, simple JS objects, three.js math classes(Vector2, Vector3, Matrix3...), and some more.
- * uiDecorators can be used to mark properties and functions that will be shown in the Ui. The Ui shows up automatically when TweakpaneUiPlugin/BlueprintJsUiPlugin is added to the viewer. Plugins have special features in the UI for download preset and saving state.
-
- Check various plugins in the source code for more examples.
|