Modules provide easy integration with components. They can make a lot of complex things much simpler to other developers (such as adding 3D physics).

Simple module

Let's create a simple module that will add .alertRandom() method to component, in which this module is used.

export default class SimpleModule {
  integrate() {
    this.alertRandom = function () {
      alert(Math.random());
    }
  }
}

📘

integrate()

In this method of any module, this is replaced with component's instance.
integrate() is executed once when component instance is created with new keyword.

import SimpleModule from './SimpleModule';

const sphere = new WHS.Sphere({
  modules: [
    new SimpleModule()
  ]
});

sphere.alertRandom(); // will alert a random number.

Advanced module

This example demonstrates the power of using modules.

export default class AdvancedModule {
  constructor(params = {}) {
    this.params = Object.assign({
      color: 0xff0000 // red
    }, params); // Polyfill params with default values
  }
  
  bridge = {
    material(inputMaterial, self) { // self - module scope
      const outputMaterial = inputMaterial.clone(); // You know, it's not required
      
      outputMaterial.color.setHex(self.params.color);
      
      return outputMaterial;
    }
  }
  
  integrate(self) { // self - module scope
    this.checkForColor = function () {
      if (this.material.color.getHex() === self.params.color) {
        alert("color is the same");
      } else {
        alert("???");
      }
    }
  }
  
  manager(manager) {
    manager.add('usedColor', this.params.color); // other modules can access this
  }
}

📘

constructor()

Used to handle input parameters that define module's behavior

📘

bridge

  • Object that handles functions called from component's code with .applyBridge() method.
  • Used to overwrite some parts of component.
  • Functions will be called only if specific component provides API for this "bridge" (using .applyBridge({bridgeName: inputData}).bridgeName).

📘

integrate()

In this method of any module, this is replaced with component's instance.
integrate() is executed once when component instance is created with new keyword.

📘

manager(manager)

Allows components communicate with each other.
manager - is a ModuleManager instance provided by Component or App, where module is used

import {MeshComponent} from WHS;
import AdvancedModule from './AdvancedModule';

class MyComponent extends MeshComponent {
  build() {
    return new THREE.Mesh(
      new THREE.SphereGeometry(),
      this.applyBridge({material: new THREE.MeshBasicMaterial()}).material
    )
  }
}

const myInstance = new MyComponent({
  modules: [
    new AdvancedModule({color: 0x0000ff}) // blue color
  ]
});

myInstance.checkForColor(); // alerts "color is the same"

Module LifeCycle

Module methods are executed in this order:

  1. constructor()
  2. if ModuleManager is provided by component & .manager() exists -> .manager(manager)
  3. if .integrate() exists -> .integrate()
  4. bridges are executed if such API is provided by components.

👍

Only constructor() is executed once when you create a module instance. All other methods are executed once per component, where they are used.

Therefore, you can use one module instance per multiple components in performance reasons (Recommended)

Bridges provided by included components (WHS.Sphere, ...)

Bridge name

Value provided

geometry

THREE.Geometry instance

material

THREE.Material instance

mesh

THREE.Mesh instance