Three Pipe

ThreePipe

A new way to work with three.js, 3D models and rendering on the web.

ThreePipe — Github — Examples — API Reference — WebGi

NPM Package Discord Server License: Apache 2.0 Twitter

ThreePipe is a 3D framework built on top of three.js in TypeScript with a focus on rendering quality, modularity, and extensibility.

Key features include:

Examples

Code samples and demos covering various usecases and test are present in the examples folder.

Try them: https://threepipe.org/examples/

View the source code by pressing the code button on the top left of the example page.

To make changes and run the example, click on the CodePen button on the top right of the source code.

Table of Contents

Getting Started

HTML/JS Quickstart (CDN)


<canvas id="three-canvas" style="width: 800px; height: 600px;"></canvas>
<script type="module">
  import {ThreeViewer, DepthBufferPlugin} from 'https://threepipe.org/dist/index.mjs'

  const viewer = new ThreeViewer({canvas: document.getElementById('three-canvas')})

  // Add some plugins 
  viewer.addPluginSync(new DepthBufferPlugin())
  
  // Load an environment map
  const envPromise = viewer.setEnvironmentMap('https://threejs.org/examples/textures/equirectangular/venice_sunset_1k.hdr')
  const modelPromise = viewer.load('https://threejs.org/examples/models/gltf/DamagedHelmet/glTF/DamagedHelmet.gltf', {
    autoCenter: true,
    autoScale: true,
  })

  Promise.all([envPromise, modelPromise]).then(([env, model]) => {
    console.log('Loaded', model, env, viewer)
  })
</script>

Check it in action: https://threepipe.org/examples/#html-js-sample/

Check out the details about the ThreeViewer API and more plugins below.

React

A sample react component in tsx to render a model with an environment map.

import React from 'react'
function ThreeViewerComponent({src, env}: {src: string, env: string}) {
  const canvasRef = React.useRef(null)
  React.useEffect(() => {
    const viewer = new ThreeViewer({canvas: canvasRef.current})

    const envPromise = viewer.setEnvironmentMap(env)
    const modelPromise = viewer.load(src)
    Promise.all([envPromise, modelPromise])
    return () => {
      viewer.dispose()
    }
  }, [])
  return (
     <canvas id="three-canvas" style={{width: 800, height: 600}} ref={canvasRef} />
  )
}

Check it in action: https://threepipe.org/examples/#react-tsx-sample/

Other examples in js: https://threepipe.org/examples/#react-js-sample/ and jsx: https://threepipe.org/examples/#react-jsx-sample/

Vue.js

A sample vue.js component in js to render a model with an environment map.

const ThreeViewerComponent = {
  setup() {
    const canvasRef = ref(null);

    onMounted(() => {
      const viewer = new ThreeViewer({ canvas: canvasRef.value });

      const envPromise = viewer.setEnvironmentMap('https://threejs.org/examples/textures/equirectangular/venice_sunset_1k.hdr');
      const modelPromise = viewer.load('https://threejs.org/examples/models/gltf/DamagedHelmet/glTF/DamagedHelmet.gltf');

      Promise.all([envPromise, modelPromise])

      onBeforeUnmount(() => {
        viewer.dispose();
      });
    });

    return { canvasRef };
  },
};

Check it in action: https://threepipe.org/examples/#vue-html-sample/

Another example with Vue SFC(Single file component): https://threepipe.org/examples/#vue-sfc-sample/

Svelte

A sample svelte component in js to render a model with an environment map.

<script>
    import {onDestroy, onMount} from 'svelte';
    import {ThreeViewer} from 'threepipe'; 

    let canvasRef;
    let viewer;
    onMount(() => {
        viewer = new ThreeViewer({canvas: canvasRef});

        const envPromise = viewer.setEnvironmentMap('https://threejs.org/examples/textures/equirectangular/venice_sunset_1k.hdr');
        const modelPromise = viewer.load('https://threejs.org/examples/models/gltf/DamagedHelmet/glTF/DamagedHelmet.gltf');

        Promise.all([envPromise, modelPromise])
    });
    onDestroy(() => viewer.dispose())
</script>

<canvas bind:this={canvasRef} id="three-canvas" style="width: 800px; height: 600px"></canvas>

Check it in action: https://threepipe.org/examples/#svelte-sample/

NPM/YARN

Installation

npm install threepipe

Loading a 3D Model

First, create a canvas element in your HTML page:

<canvas id="three-canvas" style="width: 800px; height: 600px;"></canvas>

Then, import the viewer and create a new instance:

import {ThreeViewer, IObject3D} from 'threepipe'

// Create a viewer
const viewer = new ThreeViewer({canvas: document.getElementById('three-canvas') as HTMLCanvasElement})

// Load an environment map
await viewer.setEnvironmentMap('https://threejs.org/examples/textures/equirectangular/venice_sunset_1k.hdr')

// Load a model
const result = await viewer.load<IObject3D>('https://threejs.org/examples/models/gltf/DamagedHelmet/glTF/DamagedHelmet.gltf', {
    autoCenter: true,
    autoScale: true,
})

That's it! You should now see a 3D model on your page.

The 3D model can be opened in the editor to view and edit the scene settings, objects, materials, lights, cameras, post-processing, etc. and exported as a GLB file. All settings are automatically serialized and saved in the GLB file, which can be loaded into the viewer. Any plugins used in the editor can be added to the viewer to add the same functionality. The plugin data is automatically loaded(if the plugin is added) when the model is added to the scene.

The viewer initializes with a Scene, Camera, Camera controls(Orbit Controls), several importers, exporters and a default rendering pipeline. Additional functionality can be added with plugins.

Check out the GLTF Load example to see it in action or to check the JS equivalent code: https://threepipe.org/examples/#gltf-load/

Check out the Plugins section below to learn how to add additional functionality to the viewer.

License

The core framework(src, dist, examples folders) and any plugins without a separate license are under the Free Apache 2.0 license.

Some plugins(in the plugins folder) might have different licenses. Check the individual plugin documentation and the source folder/files for more details.

Status

The project is in alpha stage and under active development. Many features will be added but the core API will not change significantly in future releases.

Check out WebGi for an advanced tailor-made solution for e-commerce, jewelry, automobile, apparel, furniture etc.

Documentation

Check the list of all functions, classes and types in the API Reference Docs.

WebGi

Check out WebGi - Premium Photo-realistic 3D rendering framework and tools for web applications and online commerce along with custom modules and rendering solutions for e-commerce, jewelry, automobile, apparel, furniture and other retail applications.

Homepage — Docs

Twitter

Contributing

Contributions to ThreePipe are welcome and encouraged! Feel free to open issues and pull requests on the GitHub repository.

Features

File Formats

ThreePipe Asset Manager supports the import of the following file formats out of the box:

Plugins can add additional formats:

Plugins to support more model formats are available in the package @threepipe/plugins-extra-importers including .3ds, .3mf, .collada, .amf, .bvh, .vox, .gcode, .mdd, .pcd, .tilt, .wrl, .mpd, .vtk, .xyz

Loading files

ThreePipe uses the AssetManager to load files. The AssetManager has support for loading files from URLs, local files and data URLs. The AssetManager also adds support for loading files from a zip archive. The zip files are automatically unzipped, and the files are loaded from the zip archive.

viewer.load() is a high-level wrapper for loading files from the AssetManager. It automatically adds the loaded object to the scene and returns a promise that resolves to the loaded object, the materials are also automatically registered to the material manager.

AssetManager internally uses AssetImporter, which provides a low-level API for managing three.js LoadingManager and adding and registering loaders for different file types.

If the purpose is not to add files to the scene then viewer.assetManager.importer.import() method can be used to import files from the AssetImporter. viewer.assetManager.loadImported()](https://threepipe.org/docs/classes/AssetManager.html#loadImported) can then be called to load the imported files after any processing. The viewer.load(), viewer.assetManager.addAsset() and viewer.assetManager.addAssetSingle() methods perform combination of import and loadImported.

3D models

The 3d models are added to viewer.scene.modelRoot on viewer.load unless some option is specified.

const objectGlb = await viewer.load<IObject3D>('https://example.com/file.glb')
const objectFbx = await viewer.load<IObject3D>('https://example.com/file.fbx')
const objectObj = await viewer.load<IObject3D>('https://example.com/file.obj') // .mtl referenced in obj is automatically loaded
// ... load any 3d model file as an object

Here, we are casting to IObject3D type to get the proper type and autocomplete for the object. IObject3D inherits Object3D from three.js and adds some additional properties.

For JavaScript, the type can be omitted.

const objectGlb = await viewer.load('https://example.com/file.glb')

When loading models, several options can be passed to automatically process the model first time, like autoScale, autoCenter, addToRoot etc. Check AddObjectOptions and ImportAddOptions for more details.

Materials

The materials downloaded as PMAT/BMAT/JSON etc from threepipe, webgi or the editor can be loaded and registered with the MaterialManager using the viewer.load method.

Custom material types can also be registered by plugins(like dmat for diamonds), which can also be loaded automatically using the viewer.load method.

const pMaterial = await viewer.load<PhysicalMaterial>('https://example.com/file.pmat')
const bMaterial = await viewer.load<UnlitMaterial>('https://example.com/file.bmat')
// ... load any material file as a material

Casting to PhysicalMaterial or UnlitMaterial is optional but recommended to get the proper type and autocomplete for the material.

To assign the material on any object, set it to object.material

// find a loaded mesh in the scene
const object = viewer.scene.getObjectByName('objectName');
// assign the material
object.material = pMaterial;

To copy the properties without changing the material reference, use material.copy() or material.setValues() methods.

object.material.copy(pMaterial);

// or use material manager to apply to multiple materials.
viewer.assetManager.materialManager.applyMaterial(pMaterial, 'METAL') // apply props to all materials/objects with the name METAL

TODO: add examples for material load and copy

Images/Textures

Images can be loaded using the viewer.load method. There is built-in support for loading all image formats supported by the browser (webp, png, jpeg, jpg, svg, ico, avif) and hdr, exr, hdr.png formats for all browsers. More formats like ktx2, ktx, etc. can be added using plugins.

const texture = await viewer.load<ITexture>('https://example.com/file.png')
// ... load any image file as a texture

Casting to ITexture is optional but recommended to get the proper type and autocomplete for the texture. It inherits from three.js Texture and adds some additional properties.

To assign the texture on any material, set it to material.map

// find a loaded mesh in the scene
const object = viewer.scene.getObjectByName('objectName');
const material = object.material as PhysicalMaterial;
// assign the texture
material.map = texture;
material.setDirty() // to let the viewer know that the material has changed and needs to re-render the scene. This will also trigger fade effect if FrameFadePlugin is added.

Check out the image load example to see it in action or to check the JS equivalent code: https://threepipe.org/examples/#image-load/

Zip files

.zip files are automatically unzipped and the files are sent to re-load recursively when loaded with viewer.load. Any level of zip hierarchy is flattened. Loading files like .gltf with references to assets inside the zip file, any relative references are also automatically resolved. This is supported for file types like gltf, glb, obj, etc which support references to external files and has root set to `true in IImporter.

const objectGltf = await viewer.load<IObject3D>('https://example.com/model.gltf.zip')

If we know that the zip file contains a single gltf with all the assets, we can cast the result to IObject3D type.

To load multiple assets from zip files like multiple textures or materials, use viewer.assetManager.addAsset method which returns a promise of array of loaded assets.

const textures = await viewer.assetManager.addAsset<ITexture[]>('https://example.com/textures.zip')
const materials = await viewer.assetManager.addAsset<IMaterial[]>('https://example.com/materials.zip')

The auto import of zip contents can be disabled to get the files and blobs in the zip

const zip = await viewer.load<any>('https://example.com/file.zip', {autoImportZipContents: false})

TODO - add example for loading zip files.

txt, json files

Text and JSON files can be loaded using the viewer.load method and return strings and objects respectively.

const text = await viewer.load<string>('https://example.com/file.txt')
const json = await viewer.load<any>('https://example.com/file.json')

Data URLs

Data URLs can be loaded using the viewer.load method. The correct mime-type is required to be set in the data URL for finding the correct importer.

const dataUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA' // ... some data url
const texture = await viewer.load<ITexture>(dataUrl)

Local files, File and Blob

Local files can be loaded using the viewer.load method by passing a IAsset object with File or Blob object.

const file: File|Blob = fileObject // create a new file, blob or get from input element 
const text = await viewer.load<IObject>({
  // a path/name is required to determine the proper importer by extension. `file.name` can also be used if available
  path: 'file.glb', 
  file
})

The same can be done for any file type.

To load a Map of files(like when multiple files are dragged and dropped on the webpage) with internal references to other files, use viewer.assetManager.importer.importFiles method. Check the source for DropzonePlugin for an example.

Background, Environment maps

The background and environment maps can be set using the viewer.setBackgroundMap and viewer.setEnvironmentMap methods respectively. These accept both loaded textures from viewer.load and direct URLs. Files can be of any image format including hdr, exr.

await viewer.setEnvironmentMap('https://example.com/file.hdr')
await viewer.setBackgroundMap('https://example.com/file.png')

The same texture can be set to both by setting setBackground or setEnvironment to true in the options:

await viewer.setEnvironmentMap('https://example.com/file.hdr', {setBackground: true})

Check the HDR Load example to see it in action: https://threepipe.org/examples/#hdr-load/

SVG strings

SVG strings can be converted to data urls using the svgUrl string template function

const svgDataUrl = svgUrl`<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg"> ... </svg>`;
const texture = await viewer.load<ITexture>(dataUrl)

Custom file types

Custom file importers/loaders can be registered to the AssetImporter using the addImporter method.

class CustomLoader extends FileLoader implements ILoader{
    constructor(manager?: LoadingManager) {
        super(manager);
    }
    load(url: string, onLoad: (data: any) => void, onProgress?: (event: ProgressEvent) => void, onError?: (event: ErrorEvent) => void): Mesh {
      this.setResponseType('json')
      return super.load(url, (json: any)=>{
        const mat = new PhysicalMaterial(json)
        onLoad?.(mat)
      }, onProgress, onError)
    }
}

viewer.assetManager.importer.addImporter(new Importer(CustomLoader, ['ext'], ['mime/type'], false))

// load the file
const mat = await viewer.load<PhysicalMaterial>('https://example.com/file.ext')

Exporting files

Threepipe has support for exporting various asset type with AssetManager, as well as support to export viewer and plugin configuration, arbitrary objects etc using the serialization system.

viewer.export() is a high-level wrapper for exporting scene objects, materials, textures, render targets, viewer/scene configuration and plugin configurations.

AssetManager internally uses AssetExporter to export files. AssetExporter includes some basic exporters for glb, exr, textures, and materials and a system to register exporters for different file types with plugins or custom exporters.

Exporting 3D models

Export the root scene as glb

const blob = await viewer.exportScene({
  viewerConfig: true, // default = true. export all viewer and plugin configuration. if false only the model root object is exported.
})
// download the file
downloadBlob(blob, 'scene.glb')

Export a single object from the scene as glb

const object = viewer.scene.getObjectByName('objectName');
const glb: Blob = await viewer.export(object, {
  exportExt: 'glb', // default = glb for models
  embedUrlImages: true, // default = false. embed images in glb even when url is available.
})
// download the file
downloadBlob(glb, 'object.glb')

Check the example glb-export to see a demo.

Exporting Materials

Export a material

const material = viewer.assetManager.materialManager.findMaterialsByName('materialName')[0];
// or 
// const material = viewer.scene.getObjectByName('objectName').material;
const blob = await viewer.export(material)
// download the file
downloadBlob(blob, 'material.' + blob.ext)

Check the example pmat-material-export to see a demo.

Exporting Canvas Images

Canvas Screenshot/snapshot can be exported as png, jpeg or webp(if supported by the browser)

const blob = await viewer.getScreenshotBlob({mimeType: 'image/' + type, quality: 0.85})
// or to get data url:
// const dataUrl = await viewer.getScreenshotDataUrl({mimeType: 'image/' + type, quality: 0.85})
// download the file
downloadBlob(blob, 'screenshot.' + blob.ext)

Check the example image-snapshot-export to see a demo.

Exporting Textures

Textures can be exported to JSON using viewer.export or AssetExporter

const texture = await viewer.load('https://example.com/file.jpeg')
const blob = await viewer.export(texture)
downloadBlob(blob, texture.name + '.' + blob.ext)

Render target textures can be exported with viewer.renderManager.exportRenderTarget or viewer.export, read about Exporting Render Targets below.

TODO: add examples for texture export

Textures and Uint8 Data Textures can be exported as a data url or copied to a new canvas

// get a base64 data url
const dataUrl = textureToDataUrl(texture, 4096, false, 'image/png') // texture or data texture, max-size, flipY, mimeType
// or copy to a new canvas
const canvas = textureToCanvas(texture, 4096) // texture or data texture, max-size

Data Textures of type Half float and Float can be exported with viewer.export

const dataTex = await viewer.load('https://example.com/file.hdr')
const blob = await viewer.export(dataTexture, {exportExt: 'exr'})

Check the example hdr-to-exr to see a demo of HDR to EXR conversion.

TODO: add support to export unsigned byte textures as png, jpeg, webp

Exporting Images/Textures

Exporting Textures as Images with image of types ImageBitmap, HTMLImageElement, HTMLOrSVGImageElement, CanvasImageSource, HTMLCanvasElement, OffscreenCanvas can be exported to png data urls with imageBitmapToBase64 function.

const texture = await viewer.load('https://example.com/file.jpeg')

const dataUrl = await imageBitmapToBase64(texture.image, 'image/png', 0.85);

TODO: add support for texture export as images in AssetExporter

Exporting Render Targets

Unsigned byte render targets can be exported as png, jpeg or webp(if supported by the browser)

const depthPlugin = viewer.addPluginSync(DepthBufferPlugin, UnsignedByteType)
// wait for the first render
const blob = await viewer.export(depthPlugin.target!, {exportExt: 'png'})
if (blob) downloadBlob(blob, target.texture.name + '.' + blob.ext)

Half float and float render targets can be exported as exr

const depthPlugin = viewer.addPluginSync(DepthBufferPlugin, HalfFloatType)
// wait for the first render
const blob = await viewer.export(depthPlugin.target!, {exportExt: 'exr'})
if (blob) downloadBlob(blob, target.texture.name + '.' + blob.ext)

Note: exportExt is determined automatically if not specified.

Render pipeline

Threepipe includes a RenderManager for managing the composition pipeline, and provides helpers for rendering and render target management.

The RenderManager includes a EffectComposer from three.js for rendering passes and a WebGLRenderer for rendering, but the pass management and sorting is managed by the RenderManager itself.

The RenderManager inherits from RenderTargetManager which provides utilities for creating, tracking and destroying dedicated and temporary render targets.

Render Targets

Render targets can be created using the viewer.renderManager.createTarget and viewer.renderManager.createTargetCustom methods. These can then be disposed using the viewer.renderManager.disposeTarget method when not needed anymore.

Or to create temp targets for one time use viewer.renderManager.getTempTarget and viewer.renderManager.releaseTempTarget methods. can be used. All created render targets are tracked in the RenderManager, and are resized and disposed automatically when needed along with the viewer.

const newTarget = viewer.renderManager.createTarget({sizeMultiplier: 1})
// or
const newTarget2 = viewer.renderManager.createTarget({size: {
    width: 1024,
    height: 1024,
  },
  type: HalfFloatType
})
// or clone an existing target
const newTarget3 = viewer.renderManager.composerTarget.clone()
// for multi-sample render target
const newTarget4 = viewer.renderManager.createTarget({sizeMultiplier: 1, samples: 4})

// or create a custom target
const newTarget5 = viewer.renderManager.createTargetCustom(
    {width: 1024, height: 1024},
    {type: HalfFloatType},
    WebGLCubeRenderTarget
)

// dispose targets
viewer.renderManager.disposeTarget(newTarget)
viewer.renderManager.disposeTarget(newTarget2)
viewer.renderManager.disposeTarget(newTarget3)
viewer.renderManager.disposeTarget(newTarget4)
viewer.renderManager.disposeTarget(newTarget5)

// get a temporary target
const tempTarget = viewer.renderManager.getTempTarget({sizeMultiplier: 1})
// release the temporary target
viewer.renderManager.releaseTempTarget(tempTarget)

Note: Render targets created with a sizeMultiplier are automatically resized when the canvas is resized.

Passes

By default, the render pipeline includes 2 passes - RenderPass for rendering the scene hierarchy and ScreenPass for rendering the final output on the canvas.

More passes can be added and removed from the pipeline using the registerPass and unregisterPass methods.

The pipeline passes need to follow the interface of IPipelinePass and IPipelinePassPlugin. Which adds some important parameters over the three.js Pass, like pass id and support for defining where the pass should be added in the pipeline and it's dependants.

const pass = new GBufferRenderPass('customPass', viewer.renderManager.createTarget({sizeMultiplier: 1}))
pass.before = ['render'] // Add the pass before the render pass
pass.after = [] // Add the pass after these passes (none in this case)
pass.required = ['render'] // render pass is required to be in the pipeline for this
viewer.renderManager.registerPass(pass)

Note: See PipelinePassPlugin for an abstract plugin that provides the boilerplate to create a plugin that registers a custom pass in the pipeline. Check NormalBufferPlugin for an example of that.

Note: All effects in post-processing or material extension need not be a separate pass in the pipeline. Most effects can be achieved with either extending the scene object material shaders or the Screen Pass material shader using Material extension system

Material Extension

Threepipe includes a Material extension system along with a material manager. The material manager is used to register materials and material extensions.

The material extensions are used to extend any material in the scene, or any plugin/pass with additional uniforms, defines, shader snippets and provides hooks.

The material extensions are automatically applied to all materials in the scene that are compatible, when the extension is registered or when the material is added to the scene.

Threepipe includes several built-in materials like PhysicalMaterial, UnlitMaterial, ExtendedShaderMaterial, LegacyPhongMaterial, that include support for extending the material. Any three.js material can be made extendable, check the ShaderPass2 class for a simple example that adds support for material extension to three.js ShaderPass.

The material extensions must follow the MaterialExtension interface. Many plugins create their own material extensions either for the scene materials or shader passes(like the screen pass). Some plugins like DepthBufferPlugin also provides helper material extensions for other custom plugins to fetch value in the depth buffer.

A sample material extension

const extension: MaterialExtension = {
    shaderExtender: (shader)=> {
        // change the shader properties like shader.fragmentShader, etc
    },
    parsFragmentSnippet: ` // add some code before the main function in the fragment shader
    uniform sampler2D tTexture;
    uniform float opacity;
    `,
    extraUniforms: {
      tTexture: ()=>({value: getTexture()}),
      opacity: {value: 1}
      // add additional uniforms, these can be IUniform or functions that return IUniform
    },
    extraDefines: {
      ['DEPTH_PACKING']: BasicDepthPacking,
      ['SOME_DEFINE']: ()=>"1",
      // add additional defines, these can be values or functions that return values 
    },
    priority: 100, // priority when using multiple extensions on the same material
    isCompatible: (material) => material.isMeshBasicMaterial, // check if the material is compatible with this extension,
    computeCacheKey: (material) => material.uuid, // a custom cache key for the material extension. Shader is recompiled when this is changed
    onObjectRender: (object: Object3D, material: IMaterial) => {
      // called when some object is rendererd which has a material with this extension.
    },
    // uiConfig
    // check more properties and hooks in the MaterialExtension interface
}

// The extension can be registered to all the materials using the MaterialManager
viewer.assetManager.materialManager.registerMaterialExtension(extension)

// or register it on a single material (like the Screen Pass)
viewer.renderManager.screenPass.material.registerMaterialExtensions([extension])

UI Configuration

Most of the classes and plugins in Threepipe include uiconfig.js support and can be used to create configuration UIs, 3d configurators and even full-editors. The UIs are automatically generated based on the configuration object under .uiConfig property on all objects. These are of type UiObjectConfig. In some classes, the ui configs are also generated using typescript decorators.

The uiConfig is also added to all three.js objects and materials when they are added to the scene.

The UIs can be generated at runtime using any of the UI plugins like TweakpaneUIPlugin, BlueprintJsUiPlugin

An example showing how to create a UI for a material

const ui = viewer.addPluginSync(TweakpaneUiPlugin)

const object = viewer.scene.getObjectByName('objectName');
const material = object.material as PhysicalMaterial;

ui.appendChild(material.uiConfig)

See it in action: https://threepipe.org/examples/#material-uiconfig/

Check more examples showing Viewer UI, Scene UI, Object UI, Camera UI

TweakpaneEditorPlugin further uses the Tweakpane configuration panel along with various plugins to create an 3d editor.

Custom UI configuration can be created to generate custom UI for the editor or tweaking. This can be done by using typescript decorators or defining the UI in javascript as a UiObjectConfig object.

Here is a sample of extending the orbit controls class with decorators to automatically generate UI.

@uiPanelContainer('Orbit Controls')
export class OrbitControlsWithUi extends OrbitControls implements IUiConfigContainer {
  // for autocomplete
  uiConfig?: UiObjectConfig<void, 'panel'> 

  @uiToggle() enabled = true

  @uiToggle() dollyZoom = false
  @uiToggle() enableDamping = true
  @uiInput() dampingFactor = 0.08

  @uiToggle() autoRotate = false
  @uiInput() autoRotateSpeed = 2.0

  @uiToggle() enableZoom = true
  @uiInput() zoomSpeed = 0.15
  @uiInput() maxZoomSpeed = 0.20

  @uiToggle() enableRotate = true
  @uiInput() rotateSpeed = 2.0

  @uiToggle() enablePan = true
  @uiInput() panSpeed = 1.0

  @uiInput() autoPushTarget = false
  @uiInput() autoPullTarget = false
  @uiInput() minDistance = 0.35
  @uiInput() maxDistance = 1000

  @uiInput() minZoom = 0.01
  @uiInput() maxZoom = 1000

  @uiInput() minPolarAngle = 0
  @uiInput() maxPolarAngle = Math.PI

  @uiInput() minAzimuthAngle = -10000 // should be -Infinity but this breaks the UI
  @uiInput() maxAzimuthAngle = 10000

}

Check out the full source code: ./src/three/controls/OrbitControls3.ts for proper implementation

See it in action: https://threepipe.org/examples/#camera-uiconfig/ Open the Camera UI and click on the Orbit Controls panel.

There are many available decorators like uiToggle, uiSlider, uiInput, uiNumber, uiColor, uiImage. Check the complete list in the uiconfig.js documentation.

The UI configuration can also be created using json objects in both typescript and javascript

const viewer = new ThreeViewer({...})

const ui = viewer.addPluginSync(TweakpaneUiPlugin)

const state = {
    position: new Vector3(),
    scale: 1,
}

ui.appendChild({
  type: 'folder',
  label: 'Custom UI',
  children: [
    {
        type: 'vec3',
        label: 'Position',
        property: [state, 'position']
    },
    {
        type: 'slider',
        label: ()=>'Scale', // everything can be a function as well.
        property: [state, 'scale'],
        bounds: [1, 2],
        stepSize: 0.1,
    }
  ]
})

TODO: create example/codepen for this

Serialization

Easy serialization of all threepipe and most three.js objects are supported out of the box using the Asset Manager. Fine control over serialization is also supported using the ThreeSerialization class

Call ThreeSerialization.serialize on any object to serialize it. and ThreeSerialization.deserialize to deserialize the serialized object.

This is done by performing a nested serialization of all the properties of the object. It's possible to implement custom serializers for custom types and classes and is done for three.js primitives, objects and plugins in threepipe

To make a custom data class that is serializable, mark it using @serializable decorator and any properties using @serialize decorator.

@serializable('DataClass')
class DataClass{
    @serialize() prop1 = 1
    @serialize() prop2 = 'string'
    @serialize() prop3 = new Vector3()
    @serialize() prop4 = new PhysicalMaterial()
    @serialize() prop4 = {
        prop1: 1,
        prop2: 'string',
        prop3: new Vector3(),
        prop4: new PhysicalMaterial(),
    }
}

const data = new DataClass()
const serialized = ThreeSerialization.serialize(data)
const deserialized = ThreeSerialization.deserialize(serialized)

The classes without a @serializable decorator are serialized as plain objects. These can still include @serialize decorator to mark the properties are serializable but these classes cannot be deserialized into a new instance of the class. The ThreeViewer and plugins are an example of these. When deserialized they need an object to deserialize into. This ensures there is always just one instance. With this, the serialization system works like toJSON and fromJSON methods in three.js.

Check the plugin system below for more details on how to mark properties as serializable for plugins.

class CustomClass{
    @serialize() prop1 = 1
    @serialize() prop2 = 'string'
    @serialize() prop3 = new Vector3()
    @serialize() prop4 = new PhysicalMaterial()
}
const obj = new DataClass()
const serialized = ThreeSerialization.serialize(data)
// now to deserialize we need to pass in the object to deserialize into
ThreeSerialization.deserialize(serialized, obj)

Plugin System

Threepipe includes a plugin system for adding additional features to the viewer in a modular way. 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 Plugin List section below.

To create new plugins, simply implement the IViewerPlugin interface or extend the AViewerPluginSync or AViewerPluginAsync classes. The only difference is that in async the onAdded and onRemove functions are async

Here is a sample plugin

@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:

Check various plugins in the source code for more examples.

Viewer API

ThreeViewer - is the main entry point to 3d rendering on the canvas.

ThreeViewer

Source Code: src/viewer/ThreeViewer.ts

API Reference: ThreeViewer

ThreeViewer is the main entry point to the viewer. It provides all the API for managing the scene, camera, rendering, plugins, etc.

It is initialized with either a canvas element or a HTMLElement for the container. The canvas element is used for rendering, and the options are used to configure the viewer. If the canvas element is not provided, a new canvas element is created and appended to the container.

More options can be passed in the constructor to configure various built-in plugins and rendering features in the viewer.

Constructor

import {ThreeViewer, CameraViewPlugin} from 'threepipe'

// Create a viewer. All options except canvas/container are optional
const viewer = new ThreeViewer({
  canvas: document.getElementById('mcanvas') as HTMLCanvasElement,
  // or a container like: 
  // container: document.getElementById('mcontainer'),
  // container: document.body,

  // Set the render scale to render at device resolution and clamp to max 2.
  renderScale: 'auto',
  // or Set the render scale to render at device resolution
  // renderScale: window.devicePixelRatio,
  // modify the screen shader: See ScreenPass and ScreenPass.glsl for more details
  screenShader: `diffuseColor = diffuseColor * 2.0;`,
  
  // Add TonemapPlugin
  tonemap: true,
  // Use MSAA(anti-aliasing)
  msaa: false,
  // Use Uint8 RGBM HDR Render Pipeline. Provides better performance with post-processing. RenderManager Uses Half-float if set to false. 
  rgbm: true,
  // Use rendered gbuffer as depth-prepass / z-prepass.
  zPrepass: false,

  // Options for AssetManager
  assetManager: {
      // Use a custom CacheStorage
      storage: caches.open('threepipe-assetmanager'),
  },

  // Use DropzonePlugin to add support for file drag and drop
  // Enable and set properties
  dropzone: {
    // Set allowed extensions
    allowedExtensions: ['png', 'glb', 'gltf'],
    // Automatically add downloaded assets
    autoAdd: true
    // autoImport: true,
    // domElement: document.body,
    // addOptions: { ... }
    // importOptions: { ... }
  },
  // By default its false
  // dropzone: false,
  // To Enable without options
  // dropzone: true
  
  // Add some plugins after viewer creation.
  plugins: [CameraViewPlugin, new CustomPlugin()],
  
  // Shorthand to load files immediately after viewer initialization
  load: {
      src: 'https://example.com/file.glb',
      environment: 'https://example.com/file.hdr',
      background: 'https://example.com/file.png',
  },
  onLoad: (viewer) => {
      // Called when all the files are loaded
  },
})

Check the interface ThreeViewerOptions for all the options.

To dispose off the viewer and all its resources call viewer.dispose() method.

To dispose only the scene objects and not the complete viewer, use viewer.scene.disposeSceneModels()

Plugin Functions

import {ThreeViewer, TonemapPlugin, DepthBufferPlugin, NormalBufferPlugin} from 'threepipe'

const viewer = new ThreeViewer({...})

// Add a plugin
const plugin = viewer.addPluginSync(new TonemapPlugin())
// plugins can be added with just the class also
const plugin2 = viewer.addPluginSync(TonemapPlugin)

// Add multiple plugins at once
viewer.addPluginsSync([
  TonemapPlugin,
  new NormalBufferPlugin(),
  DepthBufferPlugin,
  //  ...
])

// Get a plugin
const plugin3 = viewer.getPlugin(TonemapPlugin)
        
// Get or add a plugin, when not sure if the plugin is already added
const plugin4 = viewer.getOrAddPluginSync(TonemapPlugin)

// Remove a plugin
viewer.removePluginSync(TonemapPlugin)

Note: all sync functions above have async counterparts like addPlugin, getOrAddPlugin, removePlugin that are used for async plugins. There are no async plugins built-in to threepipe yet.

Import/Export Functions

import {ThreeViewer} from 'threepipe'

const viewer = new ThreeViewer({...})

// Load a 3d model
const object = await viewer.load<IObject3D>('https://example.com/file.glb')

// Load a material
const material = await viewer.load<PhysicalMaterial>('https://example.com/file.pmat')

// Load an image
const texture = await viewer.load<ITexture>('https://example.com/file.png')

// Import a model without adding to the scene
const imported = await viewer.import('https://example.com/file.glb')

// Export the complete scene with viewer configuraion
const exportedScene = await viewer.exportScene({})

// Export an object
const exported = await viewer.export(object)

// Export a material
const exportedMaterial = await viewer.export(material)

// Export a texture
const exportedTexture = await viewer.export(texture)

// Export viewer and plugins configurations
const exportedConfig = await viewer.export(viewer)

// Export plugin configuration
const exportedPlugin = await viewer.exportPlugin(viewer.getPlugin(PluginClass))

// Set Background Image
await viewer.setBackgroundMap('https://example.com/file.png')

// Set Environment Map
await viewer.setEnvironmentMap('https://example.com/file.hdr')

// Add an imported object or a created three.js object to the scene
viewer.addSceneObject(imported)

viewer.load - Loads a single asset by path or IAsset object, and adds to the scene if its 3d object or loads it if it's a configuration It is the same as AssetManager.addAssetSingle. Use AssetManager.addAsset to load multiple assets from the same path like in case of zip bundles.

viewer.import - Load a single asset but does not add to the scene or load the configuration. It is the same as AssetManager.importer.importSingle. Use AssetManager.importer.import to import multiple assets from the same path like in case of zip bundles.

viewer.export - Exports an object, material, texture, render target or plugin configuration and returns a Blob. It is similar to AssetManager.exporter.exportObject but adds support for exporting plugin and self(viewer) configs.

viewer.exportScene - Exports the scene model root and all configurations into a bundled glb file and returns a blob.

viewer.exportPlugin - Exports a plugin configuration and returns a blob.

viewer.setBackgroundMap - Sets the background map to the given texture or url. Also sets it as environment map if setEnvironment is true in the options.

viewer.setEnvironmentMap - Sets the environment map to the given texture or url. Also sets it as background if setBackground is true in the options.

viewer.addSceneObject - Adds an imported object or a created three.js object to the scene model root. If an imported scene model root is passed, it will be loaded with viewer configuration, unless importConfig is false in the options.

Frame/Rendering Events

import {ThreeViewer} from 'threepipe'

const viewer = new ThreeViewer({...})

// Add a callback to be called before every frame, irrespective of whether enything is being rendered that frame
viewer.addEventListener('preFrame', (ev)=>{
    console.log(ev);
    // change something 
    viewer.setDirty() // let the viewer know to re-render the scene from this frame
})

// Add a callback to be called after every frame, irrespective of whether enything was rendered that frame
viewer.addEventListener('postFrame', (ev)=>{
    console.log(ev);
    // change something 
    viewer.setDirty() // let the viewer know to re-render the scene from next frame
})

// Add a callback to be called before every render, only if something is being rendered that frame
viewer.addEventListener('preRender', (ev)=>{
    // canvas is about to be rendered, or re-rendered for progressive rendering
    console.log(ev, viewer.renderManager.frameCount);
})

// Add a callback to be called after every render, only if something was rendered that frame
viewer.addEventListener('postRender', (ev)=>{
    // canvas is rendered, or re-rendered for progressive rendering
    console.log(ev);
})

// Listen to viewer.setDirty() calls 
viewer.addEventListener('update', (ev)=>{
    // viewer.setDirty() was called by some plugin or code
    console.log(ev);
})

// to remove an event listener, first keep a reference to the callback
const callback = (ev)=>{ return }
viewer.addEventListener('preFrame', callback)
// then remove it
viewer.removeEventListener('preFrame', callback)

// Add a callback to be called only once for an event
viewer.doOnce('postFrame', () => viewer.canvas.toDataURL('image/png'))

// Enable/disable rendering in the viewer
viewer.renderEnabled = false
// do something with the canvas or load assets 
await viewer.load('https://example.com/file.glb')
await viewer.load('https://example.com/file1.glb')
await viewer.load('https://example.com/file2.glb')
viewer.renderEnabled = true // all the assets will be rendered together in the next frame

Check the IViewerEvent interface for all the event types.

viewer.addEventListener - Adds a callback to be called on the given event. The callback is called with an IViewerEvent object.

viewer.removeEventListener - Removes a callback from the given event.

viewer.doOnce - Adds a callback to be called only once for the given event. The listener will be added and automatically removed after the first call.

Utility Functions

import {ThreeViewer} from 'threepipe'

const viewer = new ThreeViewer({...})

// Set the final render size directly and fit in container based on mode.
viewer.setRenderSize({width: 800, height: 600}, 'cover')

// Set size of the canvas
viewer.setSize({width: 800, height: 600})
// Set the render scale
viewer.renderManager.renderScale = Math.min(window.devicePixelRatio, 2)


// Traverse scene objects
viewer.traverseSceneObjects((object) => {
    console.log(object)
  // do something with object
})

// If the size is set by css or manually by javascript use `resize` to update the viewer
viewer.resize()

// Trigger re-render with `setDirty` when something changes and viewer is not notified internally
viewer.setDirty()

// Get snapshot of the canvas as a blob 
const blob = await viewer.getScreenshotBlob({mimeTye: 'image/png', quality: 90})

// Get snapshot of the canvas as a data url
const dataUrl = await viewer.getScreenshotDataUrl({mimeTye: 'image/jpeg', quality: 85})

// Dispose viewer and all resources
viewer.canvas.remove() // canvas needs to be removed separately from the DOM
viewer.dispose()

viewer.setRenderSize - Sets the rendering resolution and fits the canvas in container based on the mode. The modes are cover, contain, fill, scale-down and none. The canvas size and render scale is calculated automatically to match the render render.

viewer.setSize - Sets the size of the canvas and updates the renderer and the camera. If no width/height is passed, canvas is set to 100% of the container.

viewer.traverseSceneObjects - Loop through all the objects in the scene model root hierarchy and calls the callback function with each object.

viewer.resize - Mark that the canvas is resized. If the size is changed, the renderer and all render targets are resized.

viewer.setDirty - Triggers re-render on next requestAnimationFrame call.

viewer.getScreenshotBlob - Returns a blob of the canvas screenshot. The blob can be used to download the screenshot or upload to a server.

viewer.getScreenshotDataUrl - Returns a data url of the canvas screenshot. The data url can be used to display the screenshot in an image element.

viewer.dispose - Disposes the viewer and all its resources. Use this to dispose the viewer when it is no longer needed. Note: the canvas element is not removed from the DOM and needs to be removed separately.

RenderManager

Source Code: src/viewer/ViewerRenderManager.ts, src/rendering/RenderManager.ts, src/rendering/RenderTargetManager.ts

API Reference: ViewerRenderManager, RenderManager, RenderTargetManager

It manages the rendering, composition/postprocessing of the scene and provides helpers for rendering and render target management.

const viewer = new ThreeViewer({...})

const renderManager = viewer.renderManager

// Get the effect composer
const composer = renderManager.composer

// Get the three.js webgl renderer
const renderer = renderManager.renderer

// Get the main Render Pass in the pipeline
const renderPass = renderManager.renderPass

// Get the main Screen Pass in the pipeline
const screenPass = renderManager.screenPass

// Set the render scale
renderManager.renderScale = Math.min(window.devicePixelRatio, 2)

// Register a custom composer pass
const customPass: IPipelinePass = new CustomPass()
renderManager.registerPass(customPass)

// Unregister a custom composer pass
renderManager.unregisterPass(customPass)

// The pipeline is automatically created and sorted based on dependencies in pipeline pass. 
// To check the built pipeline
console.log(renderManager.pipeline)

// Set a custom render pipeline 
renderManager.autoBuildPipeline = false
renderManager.pipeline = ['depth', 'render', 'screen']

// Check the total frames rendererd
console.log(renderManager.totalFrameCount)

// Get the current frame count in progressive rendering
console.log(renderManager.frameCount)

// Force reset shadows when some object is moved
renderManager.resetShadows()

// Render Target Management

// Get the main composer target
const composerTarget = renderManager.composerTarget
// clone the target to get a copy of the target with all the default options
const clonedTarget = composeTarget.clone()

// Create a render target, same size as the canvas. The target are automatically resized when the canvas is resized
const renderTarget = renderManager.createTarget({
  sizeMultiplier: 1, // size multiplier from the canvas. 0.5 will be half width and height of the canvas
  type: UnsignedByteType,
  // ... // See CreateRenderTargetOptions for all options
})

// Create a render target of custom size
const renderTarget = renderManager.createTarget({
  size: {
    width: 1024,
    height: 1024,
  },
  type: HalfFloatType,
  // ...
})

// Dispose the render target
renderManager.disposeTarget(renderTarget)

// Create and release temporary targets for in-pass rendering.
const tempTarget = renderManager.getTempTarget({
  sizeMultiplier: 0.5,
  type: HalfFloatType,
  // ...
})
// do something
renderManager.releaseTempTarget(tempTarget)

// Set how many temporary targets can remain in memory for a specific set of options
renderManager.maxTempPerKey = 10 // default = 5

// Copy/Blit a texture to the canvas or another render target with standard or a custom material
renderManager.blit(destination, {source: sourceTexture})

// Clear color of the canvas
renderManager.clearColor({r: 0, g: 0, b: 0, a: 1, depth: true, viewport: new Vector4(...)})

// Clear of a render target
renderManager.clearColor(renderTarget, {r: 0, g: 0, b: 0, a: 1, target: renderTarget})

// Export a render target to a blob. The type is automatically picked from exr to png based on the render target
const blob = renderManager.exportRenderTarget(renderTarget, 'auto')

// Export a render target to png/jpeg data url. This will clamp any floating point targets to fit in png/jpeg
const dataUrl = renderManager.renderTargetToDataUrl(renderTarget, 'image/png')

// Read render target pixels to array buffer. Returns Uint8Array|Uint16Array|Float32Array based on the render target type
const buffer = renderManager.renderTargetToBuffer(renderTarget)

renderManager.composer - The three.js EffectComposer used for rendering the pipeline.

renderManager.renderer - The three.js WebGLRenderer used for rendering

renderManager.context - Access the WebGL rendering context for the canvas.

renderManager.renderPass - The main render pass used in the render pipeline. Instance of three.js RenderPass with extra features like z prepass, rgbm rendering, blurred transmission, msaa and other optimizations.

renderManager.screenPass - The main screen pass used in the render pipeline. Instance of three.js ShaderPass with extra features like material extension, custom fragment, overriding read buffer, re-render to screen on change, etc

renderManager.renderScale - Sets the render scale. All targets are scaled by this factor. This is equivalent to calling EffectComposer.setPixelRatio and WebGLRenderer.setPixelRatio in three.js.

renderManager.resetShadows - Resets all shadow maps in the scene. This is useful when some object is moved and the shadows need to be updated. This is automatically called when scene.setDirty or any object.setDirty is called, and during animation with plugins.

Rendering Pipeline

renderManager.registerPass - Registers a custom composer pass to the render pipeline. See IPipelinePass interface.

renderManager.unregisterPass - Unregisters a custom composer pass from the render pipeline.

renderManager.pipeline - The render pipeline array. The array is automatically sorted based on dependencies in the pipeline passes.

renderManager.autoBuildPipeline - If true, the pipeline is automatically created and sorted based on dependencies in pipeline pass. If false, the pipeline is built only once and is not changed after that. The default value is true.

renderManager.totalFrameCount - The total frames rendered since the render manager was created.

renderManager.frameCount - The current frame count in progressive rendering. This is useful for progressive rendering effects like progressive shadows, gi, denoising, baking, anti-aliasing, and many other effects.

Render Targets management

renderManager.composerTarget, renderManager.composerTarget1 - The main targets used in EffectComposer2

renderManager.createTarget - Creates a render target with the given options. The render target is automatically resized when the canvas is resized if sizeMultiplier is used. See CreateRenderTargetOptions for options

renderManager.disposeTarget - Disposes a render target and removes it from the render target manager.

renderManager.getTempTarget - Gets a temporary render target with the given options. The render target is automatically resized when the canvas is resized if sizeMultiplier is used. See CreateRenderTargetOptions for options

renderManager.releaseTempTarget - Releases a temporary render target and adds it back to the render target manager.

renderManager.maxTempPerKey - Sets how many temporary targets can remain in memory for a specific set of options. The default value is 5.

Helpers

renderManager.blit - Blits a texture to the canvas or another render target with standard or a custom material. See RendererBlitOptions for options.

renderManager.clearColor - Clears the color of the canvas or a render target.

renderManager.exportRenderTarget - Exports a render target to a blob. The type is automatically picked from exr to png based on the render target.

renderManager.renderTargetToDataUrl - Exports a render target to png/jpeg data url string. This will clamp any floating point targets to fit in png/jpeg. See RenderTargetToDataUrlOptions for options.

renderManager.renderTargetToBuffer - Reads render target pixels to a Uint8Array|Uint16Array|Float32Array array buffer.

RootScene

Source Code: src/core/object/RootScene.ts

API Reference: RootScene

RootScene is the main scene that is rendered in the canvas. It is an instance of three.js Scene with extra features including separation between model root and others, backgroundColor, background map and background intensity, environment map rotation and intensity, fixed env map direction patch, event bubbling in the scene hierarchy, scene config serialization, main camera management, automatic active near far management based on scene bounds, disposing complete scene hierarchy, etc


const viewer = new ThreeViewer({...})

const scene = viewer.scene

// List oll loaded objects in the model root
console.log(scene.modelRoot.children)

// Set the background color
scene.setBackgroundColor('#ffffff')
// or 
// scene.backgroundColor = new Color('#ffffff')
// or
// scene.backgroundColor.set('#ffffff')
// scene.onBackgroundChange() // this must be called when not using a setter or set function.

// Set a texture as background (or use viewer.setBackgroundMap). When both color and texture are set, they are multiplied
scene.background = customTexture

// Set the background same as the environment map
scene.background = 'environment' // background is automatically changed when the environment changes.
// or
// scene.background = scene.environment

// Set the background intensity
scene.backgroundIntensity = 2

// Set a texture as environment map (or use viewer.setEnvironmentMap)
scene.environment = customTexture

// Set the environment intensity
scene.envMapIntensity = 2

// Set the environment map rotation
scene.environment.rotation = Math.PI / 2

// Set Fixed env direction (rotate environment according the the camera)
scene.fixedEnvMapDirection = true

// Get the main camera used for rendering
const camera: PerspectiveCamera2 = scene.mainCamera
// Set camera props
camera.position.set(0, 0, 10)
camera.target.set(0, 0, 0)
camera.setDirty() // this needs to be called to notify the viewer to re-render the scene

// Traverse the model root hierarchy (or just use viewer.traverseSceneObjects)
scene.modelRoot.traverse((object) => {
  console.log(object)
  // do something with object
})

// Access the default camera (same as mainCamera if not changed explicitly)
const defaultCamera: ICamera = scene.defaultCamera

// Dispose all assets in the modelRoot
scene.disposeSceneModels()

// Remove all objects from the modelRoot
scene.clearSceneModels()

// Dispose the scene and all its resources and children
scene.dispose()

// Get bounds of the scene model root
const bounds = scene.getBounds(true, true) // true for precise bounds and ignore invisible 

// Add an object to the scene or its model root depending on the options (or just use viewer.addSceneObject)
scene.addObject(object, {addToRoot: false}) // adds to the scene directly when addToRoot is true

// Load an imported model root from the asset importer
scene.loadModelRoot(object)

// notify that something has changed in the scene for re-render
scene.setDirty()

// notify that some object has changed in the scene for scene refresh(like shadow refresh, ground etc) and re-render
scene.refreshScene()
// or 
scene.setDirty({refreshScene: true})

// set geometryChanged: false to prevent shadow recompute
scene.refreshScene({geometryChanged: false})

// refresh active near far. (in most cases this is done automatically and need not be called)
scene.refreshActiveNearFar()

scene.modelRoot - The root object. All the objects loaded in the viewer are added to this object. And this is exported when exporting the gltf. Everything else like meta or UI objects can be added outside this.

scene.backgroundColor - The background color of the scene. Can be a Color | null. This is not the same as scene.background. When both backgroundColor and background are set, they are multiplied.

scene.background - The background of the scene. This is the same as scene.background in three.js. This can be a texture or a color or null, but it's preferred to use scene.backgroundColor for color, and this for texture, then both can be used together.

scene.setBackgroundColor - Set the background color from a string, number or color. Same as setting backgroundColor to a new color value.

scene.backgroundIntensity - The background intensity of the scene. This is the same as scene.backgroundIntensity in three.js.

scene.environment - The environment map of the scene. This is the same as scene.environment in three.js.

scene.environment.rotation - The rotation of the environment map around the y-axis.

scene.envMapIntensity - The environment intensity of the scene.

scene.fixedEnvMapDirection - If true, the environment map is rotated according to the camera. This is the same as scene.fixedEnvMapDirection in three.js.

scene.mainCamera - The main camera used for rendering. This is the same as scene.mainCamera in three.js.

scene.defaultCamera - The default camera used for rendering. This is the same as scene.defaultCamera in three.js.

scene.disposeSceneModels - Disposes all assets in the modelRoot.

scene.clearSceneModels - Removes all objects from the modelRoot.

scene.dispose - Disposes the scene and all its resources and children.

scene.getBounds - Gets the bounds of the scene model root. Returns an instance of three.js Box3 with min and max bounds according to parameters.

scene.addObject - Adds an object to the scene or its model root depending on the options. If addToRoot is true, the object is added to the model root, else it is added to the scene directly.

scene.setDirty - Notifies that something has changed in the scene for re-render.

scene.refreshScene - Notifies that some object has changed in the scene for scene refresh(like shadow refresh, ground etc) and re-render. Slower than setDirty, as it refreshes shadows, updates bounds, etc.

scene.refreshActiveNearFar - Refreshes active near far. (in most cases this is done automatically and need not be called)

scene.loadModelRoot - Loads an imported model root from the asset importer. This is used internally and in most cases, you don't need to call this.

Scene Events

RootScene dispatches many events that are useful when writing app logic or plugins

'sceneUpdate' - Listen to refreshScene called in RootScene. When some object changes.

'addSceneObject' - When a new object is added to the scene

'mainCameraChange' - When the main camera is changed to some other camera.

'mainCameraUpdate' - When some properties in the current main camera has changed.

'environmentChanged' - When the environment map changes

'backgroundChanged' - When the background map or color changes

'materialUpdate' - When a material in the scene has updated. (setDirty called on the material)

'objectUpdate' - When a object in the scene has updated. (setDirty called on the object)

'textureUpdate' - When a texture in the scene has updated. (setDirty called on the texture)

'cameraUpdate' - When a camera in the scene has updated. (setDirty called on the camera)

'geometryUpdate' - When a geometry in the scene has updated. (setDirty called on the geometry)

'geometryChanged' - When a geometry is changed on any mesh

'materialChanged' - When a material is changed on any mesh

Check IObject3DEventTypes andISceneEventTypes for more information.

ICamera

Source Code: src/core/camera/PerspectiveCamera2.ts, src/core/ICamera.ts

API Reference: PerspectiveCamera2, ICamera

ICamera is an interface for a camera that extends the three.js Camera. PerspectiveCamera2 implements the interface, extending from three.js PerspectiveCamera with extra features like target, automatic aspect management, automatic near far management(from RootScene), camera control attachment and hooks(like OrbitControls) and the ability to set as the main camera in the root scene.

import {OrbitControls3} from './OrbitControls3'

const viewer = new ThreeViewer({...})

const camera: PerspectiveCamera2 = viewer.scene.mainCamera

// Set the camera position
camera.position.set(0, 0, 10)

// Set the camera target (where the camera looks at)
camera.target.set(0, 0, 0)

// Set the camera fov
camera.fov = 45

// Set the camera aspect ratio
camera.autoAspect = false // disable automatic aspect management based on the canvas size
camera.aspect = 1

// Set camera near far bounds. 
// Near, Far plane will be calculated automatically within these limits
// Try changing these values when encountering z-fighting issues or far-plane clipping
camera.minNearPlane = 0.5 // min near plane
camera.maxFarPlane = 10 // max far plane

// Set a custom camera near far
camera.autoNearFar = false // disable automatic near far management based on the scene bounds
camera.minNearPlane = 0.1 // near plane
camera.maxFarPlane = 1000 // far plane

// this needs to be called to notify the viewer to re-render the scene
// in most cases this is done internally. But calling this does not have much impact
camera.setDirty()

// Check if user can interact with the camera. Also checks if its the main camera.
console.log(camera.canUserInteract)

// Get the camera controls (orbit controls for the default camera)
const controls: OrbitControls3 = camera.controls
// Disable controls
controls.enabled = false

// Change the controls mode
camera.controlsMode = 'none' // this will remove the current controls.

// Register a custom camera controls
camera.setControlsCtor('customOrbit', (camera, domElement) => new CustomOrbitControls(camera, domElement))

// Set the camera controls
camera.controlsMode = 'customOrbit' // this will initialize the controls with the customOrbit constructor and set it on the camera

// Disable interactions to the camera. (eg when animating)
camera.setInteractions(false, 'animation')
// Enable interactions back 
camera.setInteractions(true, 'animation') // this will enable interactions when all the keys have been set to true(which were set to false earlier)

// Force refresh aspect ratio (this is done automatically with a ResizeObserver on the canvas in the viewer)
camera.refreshAspect()

// Set the camera as the main camera
camera.activateMain()
// Deactivate the camera as the main camera
camera.deactivateMain()

camera.target - The target of the camera. This is the same as controls.target in three.js. The target is always in world-space coordinates, as opposed to position, rotation, and scale which are always relative to their parent.

camera.autoAspect - If true, the aspect ratio is automatically calculated based on the canvas size.

camera.aspect - The aspect ratio of the camera. This is the same as camera.aspect in three.js. This is only used when camera.autoAspect is false.

camera.minNearPlane - The minimum near plane of the camera. This is the same as camera.near in three.js when camera.autoNearFar is false, otherwise it is the minimum near plane distance from the camera allowed when computing automatically

camera.maxFarPlane - The maximum far plane of the camera. This is the same as camera.far in three.js when camera.autoNearFar is false, otherwise it is the maximum far plane distance from the camera allowed when computing automatically

camera.autoNearFar - If true (default), the near and far planes are automatically calculated based on the scene bounds.

camera.setDirty - Notifies that something has changed in the camera for re-render.

camera.canUserInteract - Checks if user can interact with the camera. Also checks if it's the main camera.

camera.controls - Get the current camera controls set on the camera. This can be changed by changing controlsMode

camera.controlsMode - Get or set the current controls that are attached to the camera. More modes can be registered with setControlsCtor. Default for the default camera is orbit and none for any new cameras.

camera.setControlsCtor - Register a custom camera controls constructor. The controls can be set by setting controlsMode to the key/name of the controls.

camera.setInteractions - If true, the camera can be interacted with. This is useful when animating the camera or using the window scroll or programmatically automating the viewer. Using this multiple plugins can disable interactions and it will be enabled again when all of them enable it back.

camera.refreshAspect - Force refresh aspect ratio (this is done automatically with a ResizeObserver on the canvas in the viewer or when viewer.resize() is called)

camera.activateMain - Set the camera as the main camera. This is the same as doing scene.mainCamera = camera. The camera needs to be in the scene hierarchy for this to work.

camera.deactivateMain - Deactivate the camera as the main camera.

See also CameraViewPlugin for camera focus animation.

Note: The constructor signature of PerspectiveCamera2 is different PerspectiveCamera(from three.js), since it requires the canvas and the controlsMode during creation. Because of this PerspectiveCamera0 is provided with the same signature as PerspectiveCamera for compatibility, in case the controls functionality is not required.

AssetManager

Source Code: src/assetmanager/AssetManager.ts

API Reference: AssetManager

AssetManager is a class that manages the loading and exporting of assets and provides helpers for caching assets. It is used internally in the viewer and can be used to load assets outside the viewer. It provides a modular framework for adding more asset loaders and exporters.

const viewer = new ThreeViewer({...})

const assetManager = viewer.assetManager

// Add an asset or an asset bundle
const assets = await assetManager.addAsset('https://example.com/model.zip')
// or
const assets = await assetManager.addAsset({
  path: 'https://example.com/model.zip',
  file: blob,
})

// Get the storage used in the asset manager for caching
const storage: Cache | Storage = assetManager.storage

// Get the importer. Provides low level functions to import assets
const importer = assetManager.importer

// import a file by asset or url
const file = await importer.import('https://example.com/model.gltf')

// Import a Map<string, File> from drag drop
const res = await importer.importFiles(mapOfFiles, {})

// Register a custom path that maps to a File object. Useful when some file types have path references to other files.
importer.registerFile('/myFile.png', file) // this returns the three.js loader for that kind of file.
// Unregister the path 
importer.unregisterFile('/myFile.png')

// Unregister all files and clear the loader cache(memory, not storage)
importer.clearCache()

// Add custom importers (check extra load plugins for more examples)
importer.addImporter(new Importer(class extends PLYLoader implements ILoader {
  transform(res: BufferGeometry, _: AnyOptions): Mesh | undefined {
    return res ? new Mesh(res, new PhysicalMaterial({color: new Color(1, 1, 1)})) : undefined
  }
}, ['ply'], ['text/plain+ply'], false))

// Get the exporter. Provides low level functions to export assets
const exporter = assetManager.exporter

// Export any IObject3D, IMaterial, ITexture, IRenderTarget
const exported = exporter.exportObject(obj, options)

// Add a custom exporter
exporter.addExporter({
  ext: ['gltf', 'glb'], // file extensions
  extensions: [], // extensions for the exporter
  ctor: (assetExporter, iexporter) => {
    return new GLTFExporter2()
  }
})


// Material Manager

const materialManager = assetManager.materialManager

// Create a material of type
const mat = materialManager.create('physical')
const mat2 = materialManager.create('unlit')

// find or create a material by uuid
const mat = materialManager.findOrCreate('00000000-0000-0000-0000-000000000000', {color: '#ffffff'})

// find a material creation template 
const template = materialManager.findTemplate('physical')

// Get all materials 
const materials = materialManager.getAllMaterials()

// Get all materials of type
const materials = materialManager.getMaterialsOfType(PhysicalMaterial.TypeSlug)

// register a custom material to the manager for tracking and extensions
// Note all materials created in threepipe internally are registered automatically on creation or when added to any scene object.
materialManager.registerMaterial(customMat)
// unregister
materialManager.unregisterMaterial(customMat)

// clear all material references
materialManager.clearMaterials()

// Register a custom material template
materialManager.registerTemplate({
  generator: (params: any) => new PhysicalMaterial(params),
  name: 'custom',
  materialType: PhysicalMaterial.TYPE, 
  params: {
    color: '#ffffff',
    roughness: 0.5,
    metalness: 0.5,
  },
})
const mat3 = materialManager.create('custom')

// convert a standard three.js material to threepipe material 
const mat4 = materialManager.convertToIMaterial(new ShadowMaterial(), {materialTemplate: 'test'})

// register a custom material extension for all materials in the viewer
materialManager.registerMaterialExtension(customExtension)
// unregister
materialManager.unregisterMaterialExtension(customExtension)
// remove all extensions
materialManager.clearExtensions()

// Apply a material properties to other material(s) in the scene by name or uuid
materialManager.applyMaterial(goldMaterial, 'METAL') // this will copy the properties from goldMaterial to all the materials(or objects) in the viewer with the name METAL.

// export a material as JSON. Note: use AssetExporter instead to export all the embedded assets and properties properly.
const blob = materialManager.exportMaterial(mat)

// dispose manager and all materials.
materialManager.dispose()

assetManager.addAsset - Add an asset or an asset bundle. Returns a promise that resolves to an array of asset objects. An asset can contain multiple objects, hence an array is returned. Use shorthand viewer.load(path) to load a single asset from a single file.

assetManager.storage - Get the storage used in the asset manager for caching. This is the storage that can be passed in the ThreeViewer contructor options.

assetManager.importer - Get the importer. Provides low-level functions to import assets. This is an instance of AssetImporter.

assetManager.exporter - Get the exporter. Provides low-level functions to export assets. This is an instance of AssetExporter.

AssetImporter

importer.import - Import a file by asset or url. Returns a promise that resolves to a File object.

importer.importFiles - Import a Map<string, File> from drag and drop. Returns a promise that resolves to a Map<string, any[]> of imported object arrays.

importer.registerFile - Register a custom path that maps to a File object. Useful when some file types have path references to other files. Like when importing a .zip with a .gltf file that references a .bin file, or when loading remote files from custom local cache implementation.

importer.unregisterFile - Unregister the path.

importer.clearCache - Unregister all the registered files and clear the loader cache(memory cache, not cache-storage).

importer.addImporter - Add custom importers (check extra load plugins for more examples). This allows to pass a class to a ILoader, that is used to import assets. Loaders are only created when a file is being loaded of that type. And they remain in the loader cache until clearCache or clearLoaderCache is called.

AssetExporter

exporter.exportObject - Export any IObject3D, IMaterial, ITexture, IRenderTarget. Returns a promise that resolves to an exported object. Use viewer.export or AssetExporterPlugin, which provide more features and shortcuts to export viewer, scene and plugins as well.

exporter.addExporter - Add a custom exporter for a custom file type.

MaterialManager

materialManager.create - Create a new material of a given type and with passed properties. Returns an implementation of IMaterial.

materialManager.findOrCreate - Find or create a material by uuid. Returns an instance of IMaterial. If a material with the uuid exists, it is returned, else a new material is created with the passed properties.

materialManager.findTemplate - Find a material creation template. Returns an instance of IMaterialTemplate. Material templates are used to create materials of a given type with default properties.

materialManager.getAllMaterials - Get all materials registered with the manager.

materialManager.getMaterialsOfType - Get all materials of a specific type. Pass in the typeslug from the class like pmat or dmat to identify the material.

materialManager.registerMaterial - Register a new material to the manager for tracking and extensions. Note: all materials created in threepipe internally or any that are set to any object in the scene are registered automatically on creation or when used

materialManager.unregisterMaterial - Unregister a material from the manager.

materialManager.clearMaterials - Clear all registered material references.

materialManager.registerTemplate - Register a custom material template. Requires an instance of IMaterialTemplate. Material templates are used to create materials of a given type with default properties.

materialManager.convertToIMaterial - Convert/upgrade a standard three.js material to threepipe material, by making it conform to IMaterial.

materialManager.registerMaterialExtension - Register a custom material extension for all materials in the viewer. Requires an instance of IMaterialExtension. Material extensions are used to add custom properties, methods, uniforms, defines, shader patches etc to predefined materials. The extensions are added to the material when the mateiral or extension is registered to the manager.

materialManager.unregisterMaterialExtension - Unregister a material extension from the manager.

materialManager.clearExtensions - Remove all material extensions from the manager.

materialManager.applyMaterial - Apply a material properties to other material(s) in the scene by name or uuid. This is useful when you want to change the properties of all materials with a given name or uuid. This can also be a regex, in that case a regex.match will be performed on the material/object name.

materialManager.exportMaterial - Export a material as JSON. Note: use viewer.export or AssetExporter instead to export all the embedded assets properly.

materialManager.dispose - Dispose manager and all materials.

Other classes and interfaces

Threepipe provides various interfaces and classes for for three.js objects with upgraded features like UI events, serialization, etc. These can be used while developing new apps to get better developer experience and features. When standard three.js instances are added to the scene, they are automatically upgraded automatically at runtime to make them work with the rest of the framework.

Some important interfaces:

Some important classes

Threepipe Plugins

ThreePipe has a simple plugin system that allows you to easily add new features to the viewer. Plugins can be added to the viewer using the addPlugin and addPluginSync methods. The plugin system is designed to be modular and extensible. Plugins can be added to the viewer at any time and can be removed using the removePlugin and removePluginSync methods.

TonemapPlugin

Example — Source Code — API Reference

TonemapPlugin adds a post-processing material extension to the ScreenPass in render manager that applies tonemapping to the color. The tonemapping operator can be changed by setting the toneMapping property of the plugin. The default tonemapping operator is ACESFilmicToneMapping.

Other Tonemapping properties can be like exposure, contrast and saturation

TonemapPlugin is added by default in ThreeViewer unless tonemap is set to false in the options.

DropzonePlugin

Example — Source Code — API Reference

DropzonePlugin adds support for drag and drop of local files to automatically import, process and load them into the viewer.

DropzonePlugin can be added by default in ThreeViewer by setting the dropzone property to true or an object of DropzonePluginOptions in the options.

import {DropzonePlugin, ThreeViewer} from 'threepipe'
const viewer = new ThreeViewer({
  canvas: document.getElementById('mcanvas') as HTMLCanvasElement,
  dropzone: true, // just set to true to enable drag drop functionatility in the viewer
})

To set custom options, pass an object of DropzonePluginOptions type to the dropzone property.

import {DropzonePlugin, ThreeViewer} from 'threepipe'
const viewer = new ThreeViewer({
  canvas: document.getElementById('mcanvas') as HTMLCanvasElement,
  dropzone: { // this can also be set to true and configured by getting a reference to the DropzonePlugin
    allowedExtensions: ['gltf', 'glb', 'hdr', 'png', 'jpg', 'json', 'fbx', 'obj', 'bin', 'exr'], // only allow these file types. If undefined, all files are allowed.
    addOptions: {
      disposeSceneObjects: true, // auto dispose of old scene objects
      autoSetEnvironment: true, // when hdr is dropped
      autoSetBackground: true, // when any image is dropped
      autoCenter: true, // auto center the object
      autoScale: true, // auto scale according to radius
      autoScaleRadius: 2,
      license: 'Imported from dropzone', // Any license to set on imported objects
      importConfig: true, // import config from file
    },
    // check more options in the DropzonePluginOptions interface
  },
})

ProgressivePlugin

Example — Source Code — API Reference

Progressive Plugin adds a post-render pass to blend the last frame with the current frame.

This is used as a dependency in other plugins for progressive rendering effect which is useful for progressive shadows, gi, denoising, baking, anti-aliasing, and many other effects. The helper function convergedPromise returns a new promise that can be used to wait for the progressive rendering to converge.

DepthBufferPlugin

Example — Source Code — API Reference

Depth Buffer Plugin adds a pre-render pass to the render manager and renders a depth buffer to a target. The render target can be accessed by other plugins throughout the rendering pipeline to create effects like depth of field, SSAO, SSR, etc.

import {ThreeViewer, DepthBufferPlugin} from 'threepipe'

const viewer = new ThreeViewer({...})

const depthPlugin = viewer.addPluginSync(new DepthBufferPlugin(HalfFloatType))

const depthTarget = depthPlugin.target;

// Use the depth target by accesing `depthTarget.texture`.

The depth values are based on camera near far values, which are controlled automatically by the viewer. To manually specify near, far values and limits, it can be set in the camera userData. Check the example for more details.

NormalBufferPlugin

Example — Source Code — API Reference

Normal Buffer Plugin adds a pre-render pass to the render manager and renders a normal buffer to a target. The render target can be accessed by other plugins throughout the rendering pipeline to create effects like SSAO, SSR, etc.

Note: Use GBufferPlugin if using both DepthBufferPlugin and NormalBufferPlugin to render both depth and normal buffers in a single pass.

import {ThreeViewer, NormalBufferPlugin} from 'threepipe'

const viewer = new ThreeViewer({...})

const normalPlugin = viewer.addPluginSync(new NormalBufferPlugin())

const normalTarget = normalPlugin.target;

// Use the normal target by accessing `normalTarget.texture`.

GBufferPlugin

Example — Source Code — API Reference

GBuffer Plugin adds a pre-render pass to the render manager and renders depth+normals to a target and some customizable flags to another. The multiple render target and textures can be accessed by other plugins throughout the rendering pipeline to create effects like SSAO, SSR, etc.

import {ThreeViewer, GBufferPlugin} from 'threepipe'

const viewer = new ThreeViewer({...})

const gBufferPlugin = viewer.addPluginSync(new GBufferPlugin())

const gBuffer = gBufferPlugin.target;
const normalDepth = gBufferPlugin.normalDepthTexture;
const gBufferFlags = gBufferPlugin.flagsTexture;

SSAOPlugin

Example — Source Code — API Reference

SSAO Plugin adds support for Screen Space Ambient Occlusion to the viewer. Simply add the plugin to the viewer to use it.

This is done by adding a pre-render pass to the render manager which renders SSAO to a custom render target. SSAOPlugin depends on GBufferPlugin, and is automatically added if not already.

This render target is then used by all PhysicalMaterial(s) in the scene during the main RenderPass to get the AO data. SSAO can also be disabled from the UI of the material.

Note: Use with ProgressivePlugin and TemporalAAPlugin for best results.

import {ThreeViewer, SSAOPlugin} from 'threepipe'

const viewer = new ThreeViewer({...})

const ssaoPlugin = viewer.addPluginSync(new SSAOPlugin())

// get the buffer. 
console.log(ssaoPlugin.target);

// disable ssao for a material in the scene
material.userData.ssaoDisabled = true

In the target/buffer - The ssao data is in the r channel to remain compatible with ORM. gba contains the depth in vec3(xyz) format. Note that its ssaoDisabled, so setting it to true will disable the effect.

CanvasSnapshotPlugin

Example — Source Code — API Reference

Canvas Snapshot Plugin adds support for taking snapshots of the canvas and exporting them as images and data urls. It includes options to take snapshot of a region, mime type, quality render scale and scaling the output image. Check out the interface CanvasSnapshotOptions for more details.

import {ThreeViewer, CanvasSnapshotPlugin} from 'threepipe'

const viewer = new ThreeViewer({...})

const snapshotPlugin = viewer.addPluginSync(new CanvasSnapshotPlugin())

// download a snapshot.
await snapshotPlugin.downloadSnapshot('image.webp', { // all parameters are optional
  scale: 1, // scale the final image
  timeout: 0, // wait before taking the snapshot, in ms
  quality: 0.9, // quality of the image (0-1) only for jpeg and webp
  displayPixelRatio: 2, // render scale 
  mimeType: 'image/webp', // mime type of the image
  waitForProgressive: true, // wait for progressive rendering to finish (ProgressivePlugin). true by default
  rect: { // region to take snapshot. eg. crop center of the canvas
    height: viewer.canvas.clientHeight / 2,
    width: viewer.canvas.clientWidth / 2,
    x: viewer.canvas.clientWidth / 4,
    y: viewer.canvas.clientHeight / 4,
  },
})

// get data url (string)
const dataUrl = await snapshotPlugin.getDataUrl({ // all parameters are optional
  displayPixelRatio: 2, // render scale 
  mimeType: 'image/webp', // mime type of the image
})

// get File
const file = await snapshotPlugin.getFile('file.jpeg', { // all parameters are optional
  mimeType: 'image/jpeg', // mime type of the image
})

PickingPlugin

Example — Source Code — API Reference

Picking Plugin adds support for selecting and hovering over objects in the viewer with user interactions and selection widgets.

When the plugin is added to the viewer, it starts listening to the mouse move and click events over the canvas. When an object is clicked, it is selected, and if a UI plugin is added, the uiconfig for the selected object is populated in the interface. The events selectedObjectChanged, hoverObjectChanged, and hitObject can be listened to on the plugin.

Picking plugin internally uses ObjectPicker, check out the documentation or source code for more information.

import {ThreeViewer, PickingPlugin} from 'threepipe'

const viewer = new ThreeViewer({...})

const pickingPlugin = viewer.addPluginSync(new PickingPlugin())

// Hovering events are also supported, but since its computationally expensive for large scenes it is disabled by default.
pickingPlugin.hoverEnabled = true

pickingPlugin.addEventListener('hitObject', (e)=>{
  // This is fired when the user clicks on the canvas.
  // The selected object hasn't been changed yet, and we have the option to change it or disable selection at this point.
    
  // e.intersects.selectedObject contains the object that the user clicked on.
  console.log('Hit: ', e.intersects.selectedObject)
  // It can be changed here 
  // e.intersects.selectedObject = e.intersects.selectedObject.parent // select the parent
  // e.intersects.selectedObject = null // unselect
  
  // Check other properties on the event like intersects, mouse position, normal etc.
  console.log(e)
})

pickingPlugin.addEventListener('selectedObjectChanged', (e)=>{
  // This is fired when the selected object is changed.
  // e.object contains the new selected object. It can be null if nothing is selected.
  console.log('Selected: ', e.object)
})

// Objects can be programmatically selected and unselected

// to select
pickingPlugin.setSelectedObject(object)

// get the selected object
console.log(pickingPlugin.getSelectedObject())
// to unselect
pickingPlugin.setSelectedObject(null)

// Select object with camera animation to the object
pickingPlugin.setSelectedObject(object, true)

pickingPlugin.addEventListener('hoverObjectChanged', (e)=>{
  // This is fired when the hovered object is changed.
  // e.object contains the new hovered object.
  console.log('Hovering: ', e.object)
})

TransformControlsPlugin

Example — Source Code — API Reference

Transform Controls Plugin adds support for moving, rotating and scaling objects in the viewer with interactive widgets.

Under the hood, TransformControlsPlugin uses TransformControls2 to provide the interactive controls, it is a extended version of three.js TransformControls.

When the plugin is added to the viewer, it interfaces with the PickingPlugin and shows the control gizmos when an object is selected and hides them when the object is unselected.

If the PickingPlugin is not added to the viewer before the TransformControlsPlugin, it is added automatically with the plugin.

import {ThreeViewer, TransformControlsPlugin} from 'threepipe'

const viewer = new ThreeViewer({...})

const transfromControlsPlugin = viewer.addPluginSync(new TransformControlsPlugin())

// Get the underlying transform controls
console.log(transfromControlsPlugin.transformControls)

ContactShadowGroundPlugin

Example — Source Code — API Reference

Contact Shadow Ground Plugin adds a ground plane with three.js contact shadows to the viewer scene.

The plane is added to the scene root at runtime and not saved with scene export. Instead the plugin settings are saved with the scene.

It inherits from the base class BaseGroundPlugin which provides generic ground plane functionality. Check the source code for more details. With the property autoAdjustTransform, the ground plane is automatically adjusted based on the bounding box of the scene.

import {ThreeViewer, ContactShadowGroundPlugin} from 'threepipe'

const viewer = new ThreeViewer({...})

viewer.addPluginSync(new ContactShadowGroundPlugin())

GLTFAnimationPlugin

Example — Source Code — API Reference

Manages playback of GLTF animations.

The GLTF animations can be created in any 3d software that supports GLTF export like Blender. If animations from multiple files are loaded, they will be merged in a single root object and played together.

The time playback is managed automatically, but can be controlled manually by setting {@link autoIncrementTime} to false and using {@link setTime} to set the time.

This plugin is made for playing, pausing, stopping, all the animations at once, while it is possible to play individual animations, it is not recommended.

To play individual animations, with custom choreography, use the {@link GLTFAnimationPlugin.animations} property to get reference to the animation clips and actions. Create your own mixers and control the animation playback like in three.js

PopmotionPlugin

Example — Source Code — API Reference

Provides animation/tweening capabilities to the viewer using the popmotion.io library.

Overrides the driver in popmotion to sync with the viewer and provide ways to store and stop animations.

import {PopmotionPlugin, ThreeViewer} from 'threepipe'

const viewer = new ThreeViewer({...})

const cube = viewer.scene.getObjectByName('cube');

const popmotion = viewer.addPluginSync(new PopmotionPlugin())

// Move the object cube 1 unit up.
const anim = popmotion.animateTarget(cube, 'position', {
  to: cube.position.clone().add(new Vector3(0,1,0)),
  duration: 500, // ms
  onComplete: () => isMovedUp = true,
  onStop: () => throw(new Error('Animation stopped')),
})

// Alternatively, set the property directly in onUpdate.
const anim1 = popmotion.animate({
  from: cube.position.y,
  to: cube.position.y + 1,
  duration: 500, // ms
  onUpdate: (v) => {
    cube.position.setY(v)
    cube.setDirty()
  },
  onComplete: () => isMovedUp = true,
  onStop: () => throw(new Error('Animation stopped')),
  onEnd: () => console.log('Animation ended'), // This runs after both onComplete and onStop
})

// await for animation. This promise will reject only if an exception is thrown in onStop or onComplete. onStop rejects if throwOnStop is true
await anim.promise.catch((e)=>{
  console.log(e, 'animation stopped before completion')
});

// or stop the animation
// anim.stop()

// Animate the color
await popmotion.animateAsync({ // Also await for the animation.
  from: '#' + cube.material.color.getHexString(),
  to: '#' + new Color().setHSL(Math.random(), 1, 0.5).getHexString(),
  duration: 1000, // 1s
  onUpdate: (v) => {
    cube.material.color.set(v)
    cube.material.setDirty()
  },
})

Note: The animation is started when the animate or animateAsync function is called.

CameraViewPlugin

Example — Source Code — API Reference

CameraViewPlugin adds support to save and load camera views, which can then be animated to. It uses PopmotionPlugin internally to animate any camera to a saved view or to loop through all the saved views.

It also provides a UI to manage the views.

import {CameraViewPlugin, ThreeViewer, CameraView, Vector3, Quaternion, EasingFunctions, timeout} from 'threepipe'

const viewer = new ThreeViewer({...})

const cameraViewPlugin = viewer.addPluginSync(new CameraViewPlugin())

const intialView = cameraViewPlugin.getView()
// or = viewer.scene.mainCamera.getView()

// create a new view
const view = new CameraView(
    'My View', // name
    new Vector3(0, 0, 10), // position
    new Vector3(0, 0, 0), // target
    new Quaternion(0, 0, 0, 1), // quaternion rotation
    1 // zoom
)

// or clone a view
const view2 = intialView.clone()
view2.position.add(new Vector3(0, 5, 0)) // move up 5 units

// animate the main camera to a view
await cameraViewPlugin.animateToView(
    view,
    2000, // in ms, = 2sec
    EasingFunctions.easeInOut,
).catch(()=>console.log('Animation stopped'))

// stop any/all animations
cameraViewPlugin.stopAllAnimations()

// add views to the plugin
cameraViewPlugin.addView(view)
cameraViewPlugin.addView(view2)
cameraViewPlugin.addView(intialView)
cameraViewPlugin.addCurrentView() // adds the current view of the main camera

// loop through all the views once
cameraViewPlugin.animDuration = 2000 // default duration
cameraViewPlugin.animEase = EasingFunctions.easeInOutSine // default easing
await cameraViewPlugin.animateAllViews()

// loop through all the views forever
cameraViewPlugin.viewLooping = true
await timeout(10000) // wait for some time
// stop looping
cameraViewPlugin.viewLooping = false

RenderTargetPreviewPlugin

Example — Source Code — API Reference

RenderTargetPreviewPlugin is a useful development and debugging plugin that renders any registered render-target to the screen in small collapsable panels.

import {ThreeViewer, RenderTargetPreviewPlugin, NormalBufferPlugin} from 'threepipe'

const viewer = new ThreeViewer({...})

const normalPlugin = viewer.addPluginSync(new NormalBufferPlugin(HalfFloatType))

const previewPlugin = viewer.addPluginSync(new RenderTargetPreviewPlugin())

// Show the normal buffer in a panel
previewPlugin.addTarget(()=>normalPlugin.target, 'normal', false, false)

GeometryUVPreviewPlugin

Example — Source Code — API Reference

GeometryUVPreviewPlugin is a useful development and debugging plugin that adds a panel to the viewer to show the UVs of a geometry.

import {ThreeViewer, GeometryUVPreviewPlugin, SphereGeometry} from 'threepipe'

const viewer = new ThreeViewer({...})

const previewPlugin = viewer.addPluginSync(new GeometryUVPreviewPlugin())

const geometry = new SphereGeometry(1, 32, 32)
// Show the normal buffer in a panel
previewPlugin.addGeometry(geometry, 'sphere')

FrameFadePlugin

Example — Source Code — API Reference

FrameFadePlugin adds a post-render pass to the render manager and blends the last frame with the current frame over time. This is useful for creating smooth transitions between frames for example when changing the camera position, material, object properties, etc to avoid a sudden jump.

import {ThreeViewer, FrameFadePlugin} from 'threepipe'

const viewer = new ThreeViewer({...})

const fadePlugin = viewer.addPluginSync(new FrameFadePlugin())

// Make some changes in the scene (any visual change that needs to be faded)

// Start transition and wait for it to finish
await fadePlugin.startTransition(400) // duration in ms

To stop a transition, call fadePlugin.stopTransition(). This will immediately set the current frame to the last frame and stop the transition. The transition is also automatically stopped when the camera is moved or some pointer event occurs on the canvas.

The plugin automatically tracks setDirty() function calls in objects, materials and the scene. It can be triggerred by calling setDirty on any material or object in the scene. Check the example for a demo. This can be disabled by options in the plugin.

VignettePlugin

Example — Source Code — API Reference

VignettePlugin adds a post-processing material extension to the ScreenPass in render manager that applies a vignette effect to the final render. The parameters power and color can be changed to customize the effect.

import {ThreeViewer, VignettePlugin} from 'threepipe'

const viewer = new ThreeViewer({...})

const vignettePlugin = viewer.addPluginSync(VignettePlugin)

// Change the vignette color
vignettePlugin.power = 1
vignettePlugin.color = new Color(0.5, 0, 0)

// or 
// vignettePlugin.color.set('#ff0000'); vignettePlugin.setDirty() // Call setDirty to tell the plugin that color has changed

ChromaticAberrationPlugin

Example — Source Code — API Reference

ChromaticAberrationPlugin adds a post-processing material extension to the ScreenPass in render manager that applies a chromatic-aberration effect to the final render. The parameter intensity can be changed to customize the effect.

import {ThreeViewer, ChromaticAberrationPlugin} from 'threepipe'

const viewer = new ThreeViewer({...})

const chromaticAberrationPlugin = viewer.addPluginSync(ChromaticAberrationPlugin)

// Change the chromaticAberration color
chromaticAberrationPlugin.intensity = 0.5

FilmicGrainPlugin

Example — Source Code — API Reference

FilmicGrainPlugin adds a post-processing material extension to the ScreenPass in render manager that applies a filmic-grain effect to the final render. The parameters power and color can be changed to customize the effect.

import {ThreeViewer, FilmicGrainPlugin} from 'threepipe'

const viewer = new ThreeViewer({...})

const filmicGrainPlugin = viewer.addPluginSync(FilmicGrainPlugin)

// Change the filmicGrain color
filmicGrainPlugin.intensity = 10
filmicGrainPlugin.multiply = false

NoiseBumpMaterialPlugin

Example — Source Code — API Reference

NoiseBumpMaterialPlugin adds a material extension to PhysicalMaterial to add support for sparkle bump / noise bump by creating procedural bump map from noise to simulate sparkle flakes. It uses voronoise function from blender along with several additions to generate the noise for the generation. It also adds a UI to the material to edit the settings. It uses WEBGI_materials_noise_bump glTF extension to save the settings in glTF/glb files.

import {ThreeViewer, NoiseBumpMaterialPlugin} from 'threepipe'

const viewer = new ThreeViewer({...})

const noiseBump = viewer.addPluginSync(NoiseBumpMaterialPlugin)

// Add noise bump to a material
NoiseBumpMaterialPlugin.AddNoiseBumpMaterial(material, {
  flakeScale: 300,
})

// Change properties with code or use the UI
material.userData._noiseBumpMat!.bumpNoiseParams = [1, 1]
material.setDirty()

// Disable
material.userData._noiseBumpMat!.hasBump = false
material.setDirty()

CustomBumpMapPlugin

Example — Source Code — API Reference

CustomBumpMapPlugin adds a material extension to PhysicalMaterial to support custom bump maps. A Custom bump map is similar to the built-in bump map, but allows using an extra bump map and scale to give a combined effect. This plugin also has support for bicubic filtering of the custom bump map and is enabled by default. It also adds a UI to the material to edit the settings. It uses WEBGI_materials_custom_bump_map glTF extension to save the settings in glTF/glb files.

import {ThreeViewer, CustomBumpMapPlugin} from 'threepipe'

const viewer = new ThreeViewer({...})

const customBump = viewer.addPluginSync(CustomBumpMapPlugin)

// Add noise bump to a material
customBump.enableCustomBump(material, bumpMap, 0.2)

// Change properties with code or use the UI
material.userData._customBumpMat = texture
material.setDirty()

// Disable
material.userData._hasCustomBump = false
// or 
material.userData._customBumpMat = null
material.setDirty()

ClearcoatTintPlugin

Example — Source Code — API Reference

ClearcoatTintPlugin adds a material extension to PhysicalMaterial which adds tint and thickness to the built-in clearcoat properties. It also adds a UI to the material to edit the settings. It uses WEBGI_materials_clearcoat_tint glTF extension to save the settings in glTF/glb files.

import {ThreeViewer, ClearcoatTintPlugin} from 'threepipe'

const viewer = new ThreeViewer({...})

const clearcoatTint = viewer.addPluginSync(ClearcoatTintPlugin)

material.clearcoat = 1
// add initial properties
ClearcoatTintPlugin.AddClearcoatTint(material, {
  tintColor: '#ff0000',
  thickness: 1,
})

// Change properties with code or use the UI
material.userData._clearcoatTint!.tintColor = '#ff0000'
material.setDirty()

// Disable
material.userData._clearcoatTint.enableTint = false
material.setDirty()

FragmentClippingExtensionPlugin

Example — Source Code — API Reference

FragmentClippingExtensionPlugin adds a material extension to PhysicalMaterial to add support for fragment clipping. Fragment clipping allows to clip fragments of the material in screen space or world space based on a circle, rectangle, plane, sphere, etc. It uses fixed SDFs with params defined by the user for clipping. It also adds a UI to the material to edit the settings. It uses WEBGI_materials_fragment_clipping_extension glTF extension to save the settings in glTF/glb files.

import {ThreeViewer, FragmentClippingExtensionPlugin} from 'threepipe'

const viewer = new ThreeViewer({...})

const fragmentClipping = viewer.addPluginSync(FragmentClippingExtensionPlugin)

// add initial properties
FragmentClippingExtensionPlugin.AddFragmentClipping(material, {
  clipPosition: new Vector4(0.5, 0.5, 0, 0),
  clipParams: new Vector4(0.1, 0.05, 0, 1),
})

// Change properties with code or use the UI
material.userData._fragmentClipping!.clipPosition.set(0, 0, 0, 0)
material.setDirty()

// Disable
material.userData._clearcoatTint.clipEnabled = false
material.setDirty()

HDRiGroundPlugin

Example — Source Code — API Reference

HDRiGroundPlugin patches the background shader in the renderer to add support for ground projected environment map/skybox. Works simply by setting the background same as the environemnt and enabling the plugin.

The world radius, tripod height, and origin position(center offset) can be set in the plugin.

The plugin is disabled by default when added. Set .enabled to enable it or pass true in the constructor. If the background is not the same as the environment when enabled, the user will be prompted for this, unless promptOnBackgroundMismatch is set to false in the plugin.

import {ThreeViewer, HDRiGrounPlugin} from 'threepipe'

const viewer = new ThreeViewer({...})

const hdriGround = viewer.addPluginSync(new HDRiGrounPlugin())

// Load an hdr environment map
await viewer.setEnvironmentMap('https://threejs.org/examples/textures/equirectangular/venice_sunset_1k.hdr')
// set background to environment
viewer.scene.background = 'environment'
// or 
// viewer.scene.background = viewer.scene.environemnt

// enable the plugin
hdriGround.enabled = true

Check the example for a demo.

VirtualCamerasPlugin

Example — Source Code — API Reference

VirtualCamerasPlugin adds support for rendering to multiple virtual cameras in the viewer. These cameras are rendered in preRender callback just before the main camera is rendered. The virtual cameras can be added to the plugin and removed from it.

The feed to the virtual camera is rendered to a Render Target texture which can be accessed and re-rendered in the scene or used in other plugins.

import {ThreeViewer, VirtualCamerasPlugin} from 'threepipe'

const viewer = new ThreeViewer({...})

const virtualCameras = viewer.addPluginSync(new VirtualCamerasPlugin())

const camera = new PerspectiveCamera2('orbit', viewer.canvas, false, 45, 1)
camera.name = name
camera.position.set(0, 5, 0)
camera.target.set(0, 0.25, 0)
camera.userData.autoLookAtTarget = true // automatically look at the target (in setDirty)
camera.setDirty()
camera.addEventListener('update', ()=>{
  viewer.setDirty() // if the camera is not added to the scene it wont update automatically when camera.setDirty is called(like from the UI)
})

const vCam = virtualCameras.addCamera(camera)
console.log(vCam.target) // target is a WebGLRenderTarget/IRenderTarget

Check the virtual camera example for using the texture in the scene.

EditorViewWidgetPlugin

Example — Source Code — API Reference

EditorViewWidgetPlugin adds a ViewHelper in the parent of the viewer canvas to show the current camera view and allow the user to change the camera view to one of the primary world axes.

Simply add the plugin to the viewer to see the widget.

import {ThreeViewer, EditorViewWidgetPlugin} from 'threepipe'

const viewer = new ThreeViewer({...})

const plugin = viewer.addPluginSync(new EditorViewWidgetPlugin())

// to hide the widget
plugin.enabled = false

Object3DWidgetsPlugin

Example — Source Code — API Reference

Object3DWidgetsPlugin adds support for light and camera helpers/gizmos in the viewer. A helper is automatically created when any supported light or camera is added to the scene. Simply add the plugin to the viewer to see the widget.

Support for additional types of helpers can be added dynamically or by other plugins by pushing a helper constructor to the Object3DWidgetsPlugin.helpers array, and calling Object3DWidgetsPlugin.refresh().

The helper class prototype should implement the IObject3DHelper interface. Check DirectionalLightHelper2 for an example.

import {ThreeViewer, Object3DWidgetsPlugin, Object3DGeneratorPlugin} from 'threepipe'

const viewer = new ThreeViewer({...})

// Add the plugin to add support
const plugin = viewer.addPluginSync(new Object3DWidgetsPlugin())

// Add some lights or cameras to the scene. (This can be done before adding the plugin as well)
// Using Object3DGeneratorPlugin to create a camera and add it to the scene.
const generator = viewer.getOrAddPluginSync(Object3DGeneratorPlugin)
generator.generate('camera-perspective', {
  position: new Vector3(5, 5, 0),
  name: 'My Camera'
})

// to hide the widgets
plugin.enabled = false

// to add support for a custom helper
plugin.helpers.push(MyCustomHelper)
plugin.refresh()

Object3DGeneratorPlugin

Example — Source Code — API Reference

Object3DGeneratorPlugin adds support for creating different types of lights and camera objects in the viewer. Call the generate method with any type to generate a type of object(like lights, cameras, mesh etc).

Support for the following types of generators is included in the plugin:

Additional types of generators can be added dynamically or by other plugins by adding a custom generator function to the Object3DGeneratorPlugin.generators object. This is done by GeometryGeneratorPlugin to add various type of primitive objects like plane, sphere, etc A custom generator can take in any kind object as parameters and should return an IObject3D.

Sample Usage

import {ThreeViewer, Object3DWidgetsPlugin, Object3DGeneratorPlugin, Mesh2} from 'threepipe'

const viewer = new ThreeViewer({...})

const generator = viewer.addPluginSync(Object3DGeneratorPlugin)
generator.generate('camera-perspective', {
  position: new Vector3(5, 5, 0),
  name: 'My Camera'
})
const light = generator.generate('light-spot', {
  position: new Vector3(5, 0, 0),
})

// to add support for a custom helper
plugin.generators['custom-object'] = (params)=>{
  const object = new Mesh2(new PlaneGeometry(1,1), new PhysicalMaterial())
  object.name = params.name ?? 'Custom Mesh'
  if(params.position) object.position.copy(params.position)
  return object
}
const obj = generator.generate('custom-object', {
  position: new Vector3(5, 0, 0),
})

// Add Object3DWidgetsPlugin to see the added lights and cameras.
viewer.addPluginSync(new Object3DWidgetsPlugin())

Check the example for the UI.

DeviceOrientationControlsPlugin

Example — Source Code — API Reference

DeviceOrientationControlsPlugin enables controlling the main camera rotation in the scene with device orientation. This only works on devices which have a gyroscope(but can also be emulated in devtools in chrome). After the plugin is added, it adds support for setting deviceOrientation as the key in scene.mainCamera.controlMode.

When the controls is started (for the first time), the current camera rotation is and the device orientation is saved and used as reference. To reset the saved device orientation, call resetView in the controls.

Sample Usage

import {ThreeViewer, DeviceOrientationControlsPlugin, Mesh2} from 'threepipe'

const viewer = new ThreeViewer({...})

viewer.addPluginSync(DeviceOrientationControlsPlugin)

// after some user action
viewer.scene.mainCamera.controlsMode = 'deviceOrientation'

// to reset the saved device orientation
viewer.scene.mainCamera.controls.resetView()

// switch back to default orbit controls
viewer.scene.mainCamera.controlsMode = 'orbit'

PointerLockControlsPlugin

Example — Source Code — API Reference

PointerLockControlsPlugin adds support for using PointerLockControls from three.js. It works similar to controls in first person shooter, captures the mouse pointer and uses it to look around with the camera.

After the plugin is added, it adds support for setting pointerLock as the key in scene.mainCamera.controlMode.

Sample Usage

import {ThreeViewer, PointerLockControlsPlugin, Mesh2} from 'threepipe'

const viewer = new ThreeViewer({...})

viewer.addPluginSync(PointerLockControlsPlugin)

// after some user action
viewer.scene.mainCamera.controlsMode = 'pointerLock'

// listen to lock/unlock events 
viewer.scene.mainCamera.controls?.addEventListener('lock', ()=> console.log('pointer locked'))
viewer.scene.mainCamera.controls?.addEventListener('unlock', ()=> console.log('pointer unlocked'))

// switch back to default orbit controls
viewer.scene.mainCamera.controlsMode = 'orbit'

ThreeFirstPersonControlsPlugin

Example — Source Code — API Reference

ThreeFirstPersonControlsPlugin adds support for using FirstPersonControls from three.js. It works similar to idle look around in first person games, it does not captures the mouse pointer.

After the plugin is added, it adds support for setting threeFirstPerson as the key in scene.mainCamera.controlMode.

Sample Usage

import {ThreeViewer, ThreeFirstPersonControlsPlugin, Mesh2} from 'threepipe'

const viewer = new ThreeViewer({...})

viewer.addPluginSync(ThreeFirstPersonControlsPlugin)

// after some user action
viewer.scene.mainCamera.controlsMode = 'threeFirstPerson'

// switch back to default orbit controls
viewer.scene.mainCamera.controlsMode = 'orbit'

Rhino3dmLoadPlugin

Example — Source Code — API Reference

Adds support for loading .3dm files generated by Rhino 3D. This plugin includes some changes with how 3dm files are loaded in three.js. The changes are around loading layer and primitive properties when set as reference in the 3dm files.

It also adds some helpful options to process the model after load.

import {Rhino3dmLoadPlugin} from 'threepipe'
const rhino3dmPlugin = viewer.addPluginSync(new Rhino3dmLoadPlugin())

rhino3dmPlugin.importMaterials = true // import materials source from 3dm file
rhino3dmPlugin.forceLayerMaterials = true // force material source to be layer in 3dm file.
rhino3dmPlugin.hideLineMesh = true // hide all lines and points in the model.
rhino3dmPlugin.replaceWithInstancedMesh = true // replace meshes with the same parent, geometry and material with a single instance mesh.

const mesh = await viewer.load('file.3dm')

PLYLoadPlugin

Example — Source Code — API Reference

Adds support for loading .ply (Polygon file format) files.

import {PLYLoadPlugin} from 'threepipe'
viewer.addPluginSync(new PLYLoadPlugin())

const mesh = await viewer.load('file.ply')

USDZLoadPlugin

Example — Source Code — API Reference

Adds support for loading .usdz and .usda (Universal Scene Description) files.

import {USDZLoadPlugin} from 'threepipe'
viewer.addPluginSync(new USDZLoadPlugin())

const mesh = await viewer.load('file.usdz')
const mesh2 = await viewer.load('file.usda')

STLLoadPlugin

Example — Source Code — API Reference

Adds support for loading .stl (Stereolithography) files.

import {STLLoadPlugin} from 'threepipe'
viewer.addPluginSync(new STLLoadPlugin())

const mesh = await viewer.load('file.stl')

KTX2LoadPlugin

Example — Source Code — API Reference

Adds support for loading .ktx2 (Khronos Texture files.

KTX2LoadPlugin also adds support for exporting loaded .ktx2 files in glTF files with the KHR_texture_basisu extension.

import {KTX2LoadPlugin} from 'threepipe'
viewer.addPluginSync(new KTX2LoadPlugin())

const texture = await viewer.load('file.ktx2')

KTXLoadPlugin

Example — Source Code — API Reference

Adds support for loading .ktx (Khronos Texture files.

Note: This plugin only adds support for loading .ktx file, and not exporting them in the bundled .glb. Use .ktx2 files instead of .ktx files for better compression and performance.

import {KTXLoadPlugin} from 'threepipe'
viewer.addPluginSync(new KTXLoadPlugin())

const texture = await viewer.load('file.ktx')

SimplifyModifierPlugin

Example — Source Code — API Reference

Boilerplate for implementing a plugin for simplifying geometries. This is a base class and cannot be used directly.

A sample to use it:

class SimplifyModifierPluginImpl extends SimplifyModifierPlugin {
  protected _simplify(geometry: IGeometry, count: number) {
    return new SimplifyModifier().modify(geometry, count) as IGeometry
  }
}

const plugin = viewer.addPluginSync(new SimplifyModifierPluginImpl())

const root = await viewer.load('file.glb')
plugin.simplifyAll(root, {factor: 0.75})

Check the example for full implementation.

MeshOptSimplifyModifierPlugin

Example — Source Code — API Reference

Simplify modifier using meshoptimizer library. It Loads the library at runtime from a customisable CDN URL.

Note: It does not guarantee that the geometry will be simplified to the exact target count.

const simplifyModifier = viewer.addPluginSync(new MeshOptSimplifyModifierPlugin())

const root = await viewer.load('file.glb')
simplifyModifier.simplifyAll(root, {factor: 0.75})

@threepipe Packages

Additional plugins can be found in the plugins directory. These add support for integrating with other libraries, adding new features, and other functionality with different licenses.

@threepipe/plugin-tweakpane

Tweakpane UI plugin for ThreePipe

Example — Source Code — API Reference

NPM: npm install @threepipe/plugin-tweakpane

CDN: https://threepipe.org/plugins/tweakpane/dist/index.mjs

TweakpaneUiPlugin adds support for using uiconfig-tweakpane to create a configuration UI in applications using the Tweakpane library.

The plugin takes the uiconfig that's defined in the viewer and all the objects to automatically render a UI in the browser.

import {IObject3D, ThreeViewer, TonemapPlugin} from 'threepipe'
import {TweakpaneUiPlugin} from '@threepipe/plugin-tweakpane'

const viewer = new ThreeViewer({...})

// Add the plugin
const plugin = viewer.addPluginSync(new TweakpaneUiPlugin(true)) // true to show expanded the UI by default

// Add the UI for the viewer
plugin.appendChild(viewer.uiConfig)
// Add UI for some plugins
plugin.setupPlugins(TonemapPlugin, DropzonePlugin)

@threepipe/plugin-blueprintjs

Blueprint.js UI plugin for ThreePipe

Example — Source Code — API Reference

NPM: npm install @threepipe/plugin-blueprintjs

CDN: https://threepipe.org/plugins/blueprintjs/dist/index.mjs

BlueprintJsUiPlugin adds support for using uiconfig-blueprint to create a configuration UI in applications using the BlueprintJs library.

The plugin takes the uiconfig that's defined in the viewer and all the objects to automatically render a UI in the browser.

import {IObject3D, ThreeViewer, TonemapPlugin} from 'threepipe'
import {BlueprintJsUiPlugin} from '@threepipe/plugin-blueprintjs'

const viewer = new ThreeViewer({...})

// Add the plugin
const plugin = viewer.addPluginSync(new BlueprintJsUiPlugin(true)) // true to show expanded the UI by default

// Add the UI for the viewer
plugin.appendChild(viewer.uiConfig)
// Add UI for some plugins
plugin.setupPlugins(TonemapPlugin, DropzonePlugin)

@threepipe/plugin-tweakpane-editor

Tweakpane Editor Plugin for ThreePipe

Example — Source Code — [API Reference](https://threepipe.org/plugins/tweakpane-editor/docs/classes/TweakpaneEditorPlugi —

NPM: npm install @threepipe/plugin-tweakpane-editor

CDN: https://threepipe.org/plugins/tweakpane-editor/dist/index.mjs

TweakpaneEditorPlugin uses TweakpaneUiPlugin to create an editor for editing viewer, plugins, model and material configurations in the browser.

import {IObject3D, ThreeViewer, TonemapPlugin} from 'threepipe'
import {TweakpaneEditorPlugin} from '@threepipe/plugin-tweakpane-editor'

const viewer = new ThreeViewer({...})

viewer.addPluginSync(new TweakpaneUiPlugin(true))
const editor = viewer.addPluginSync(new TweakpaneEditorPlugin())

// Add some plugins to the viewer
await viewer.addPlugins([
    new ViewerUiConfigPlugin(),
    // new SceneUiConfigPlugin(), // this is already in ViewerUiPlugin
    new DepthBufferPlugin(HalfFloatType, true, true),
    new NormalBufferPlugin(HalfFloatType, false),
    new RenderTargetPreviewPlugin(false),
])

// Load the plugin UI in the editor and tweakpane ui with categories.
editor.loadPlugins({
    ['Viewer']: [ViewerUiConfigPlugin, SceneUiConfigPlugin, DropzonePlugin, FullScreenPlugin],
    ['GBuffer']: [DepthBufferPlugin, NormalBufferPlugin],
    ['Post-processing']: [TonemapPlugin],
    ['Debug']: [RenderTargetPreviewPlugin],
})

@threepipe/plugins-extra-importers

Exports several plugins to add support for various file types.

Example — Source Code — API Reference

NPM: npm install @threepipe/plugins-extra-importers

CDN: https://threepipe.org/plugins/extra-importers/dist/index.mjs

This package exports several plugins to add support for several file types using the following plugins

To add all the plugins at once use extraImporters. This adds support for loading all the above file types.

import {ThreeViewer} from 'threepipe'
import {extraImporters} from '@threepipe/plugins-extra-importers'

const viewer = new ThreeViewer({...})
viewer.addPluginsSync(extraImporters)

// Now load any file as is.
const model = await viewer.load<IObject3D>('file.3mf')

// To load the file as a data url, use the correct mimetype
const model1 = await viewer.load<IObject3D>('data:model/3mf;base64,...')

Remove the <IObject3D> if using javascript and not typescript.

@threepipe/plugin-blend-importer

Exports BlendImporterPlugin which adds support for loading .blend files.

It uses js.blend for parsing blend file structure.

Note: This is still a WIP. Currently working: Mesh, BufferGeometry and basic PointLight. To be added: PhysicalMaterial, UnlitMaterial (similar to blender-gltf-io plugin)

Example — Source Code — API Reference

NPM: npm install @threepipe/plugin-blend-importer

import {ThreeViewer} from 'threepipe'
import {BlendLoadPlugin} from '@threepipe/plugin-blend-importer'

const viewer = new ThreeViewer({...})
viewer.addPluginSync(BlendLoadPlugin)

// Now load any .blend file.
const model = await viewer.load<IObject3D>('path/to/file.blend')

// To load the file as a data url, use the correct mimetype
const model1 = await viewer.load<IObject3D>('data:application/x-blender;base64,...')

@threepipe/plugin-geometry-generator

Exports GeometryGeneratorPlugin with several Geometry generators to create parametric and updatable geometries like plane, circle, sphere, box, torus, cylinder, cone etc.

Example — Source Code — API Reference

NPM: npm install @threepipe/plugin-geometry-generator

The generated geometries/meshes include the parameters in the userData and can be re-generated by changing the parameters from the UI or the plugin API.

Includes the following generator which inherit from AGeometryGenerator:

Sample Usage:

import {ThreeViewer, UnlitMaterial} from 'threepipe'
import {GeometryGeneratorPlugin} from '@threepipe/plugin-geometry-generator'

const viewer = new ThreeViewer({...})
const generator = viewer.addPluginSync(GeometryGeneratorPlugin)

const sphere = generator.generateObject('sphere', {radius: 3})
viewer.scene.addObject(sphere)

// to update the geometry
generator.updateGeometry(sphere.geometry, {radius: 4, widthSegments: 100})

// to add a custom generator
generator.generators.custom = new CustomGenerator('custom') // Extend from AGeometryGenerator or implement GeometryGenerator interface
// refresh the ui so the new generator is available to select.
generator.uiConfig.uiRefresh?.()

// change the material type for all objects
generator.defaultMaterialClass = UnlitMaterial // by default its PhysicalMaterial
viewer.scene.addObject(generator.generateObject('box', {width: 2, height: 2, depth: 2}))

@threepipe/plugin-gaussian-splatting

Exports GaussianSplattingPlugin which adds support for loading .blend files.

It uses three-gaussian-splat, a rewrite of @zappar/three-guassian-splat (and gsplat.js and antimatter15/splat) for loading splat files and rendering gaussian splats.

Example — Source Code — API Reference

NPM: npm install @threepipe/plugin-gaussian-splatting

Note: This is still a WIP.

Currently working:

TBD:

import {ThreeViewer} from 'threepipe'
import {GaussianSplattingPlugin} from '@threepipe/plugin-gaussian-splatting'

const viewer = new ThreeViewer({...})
viewer.addPluginSync(GaussianSplattingPlugin)

// Now load any .splat file.
const model = await viewer.load<GaussianSplatMesh>('path/to/file.splat')

@threepipe/plugin-svg-renderer

Exports ThreeSVGRendererPlugin and BasicSVGRendererPlugin which provide support for rendering the 3d scene as SVG(Scalable Vector Graphics). The generated SVG is compatible with browser rendering and other software like figma, illustrator etc.

Example — Source Code — API Reference — GPLV3 License

NPM: npm install @threepipe/plugin-svg-renderer

Note: This is still a WIP. API might change slightly

ThreeSVGRendererPlugin uses three-svg-renderer, which is a modified version of three-svg-renderer (GPLV3 Licenced). The plugin renderers meshes in the viewer scene to svg objects by computing polygons and contours of the geometry in view space. Check LokiResearch/three-svg-renderer for more details. In the modified version that is used here, support for some types of geometries is added and a rendered image in screen-space is used to create raster fill images for paths along with some other small changes. Check out the Example for demo. See also svg-geometry-playground example for usage with other plugins PickingPlugin, TransformControlsPlugin and GeometryGeneratorPlugin.

Note that this does not support all the features of three.js and may not work with all types of materials and geometries. Check the examples for a list of sample models that do and don't work.

BasicSVGRendererPlugin is a sample plugin using SVGRenderer from three.js addons. This renders all triangles in the scene to separate svg paths. Check the three.js docs for more details. Check out the Example for demo.

import {ThreeViewer} from 'threepipe'
import {ThreeSVGRendererPlugin} from '@threepipe/plugin-svg-renderer'

const viewer = new ThreeViewer({...})
const svgRender = viewer.addPluginSync(ThreeSVGRendererPlugin)
svgRender.autoRender = true // automatically render when camera or any object changes.
svgRender.autoMakeSvgObjects = true // automatically create SVG objects for all meshes in the scene.
// svgRender.makeSVGObject(object) // manually create SVG object for an object. (if autoMakeSvgObjects is false) 

// Now load or generate any 3d model. Make sure its not very big. And the meshes are optimized.
const model = await viewer.load<IOBject3D>('path/to/file.glb')

// clear the background of the viewer 
// this is only required if rgbm = false in the viewer
viewer.scene.backgroundColor = null
// this is only required if rgbm = true in the viewer
viewer.renderManager.screenPass.clipBackground = true
        
// disable damping to get better experience.
viewer.scene.mainCamera.controls!.enableDamping = false

// hide the canvas to see the underlying svg node.
// note: do not set the display to none or remove the canvas as OrbitControls and other plugins might still be tracking the canvas.
viewer.canvas.style.opacity = '0'

// 3d pipeline can also be disabled like this if `drawImageFills` is `false` to get better performance. Do this only after loading the model.
// await viewer.doOnce('postFrame') // wait for the first frame to be rendered (for autoScale etc)
// viewer.renderManager.autoBuildPipeline = false
// viewer.renderManager.pipeline = [] // this will disable main viewer rendering