Home Reference Source

src/components/MultipassRenderer.js

import Component from '../systems/EntitySystem/Component';
import { Command, Pipeline, RenderFullscreenCommand } from '../systems/RenderSystem';
import Camera from './Camera';

export class MultipassPipeline extends Command {

  get passes() {
    return this._passes;
  }

  set passes(value) {
    if (!Array.isArray(value)) {
      throw new Error('`value` is not type of Array!');
    }
    this._passes = value || [];
    this._dirty = true;
  }

  get fullscreen() {
    return this._fullscreen;
  }

  constructor(owner) {
    super();

    this._owner = owner;
    this._renderer = null;
    this._fullscreen = new RenderFullscreenCommand();
    this._dirty = true;
    this._passes = [];
    this._passesTargets = new Set();
  }

  dispose() {
    super.dispose();

    const { _renderer, _fullscreen } = this;
    if (!!_renderer && !!this._passesTargets) {
      for (const target of this._passesTargets.values()) {
        _renderer.unregisterRenderTarget(target);
      }
    }
    if (!!_fullscreen) {
      _fullscreen.dispose();
    }
    this._owner = null;
    this._renderer = null;
    this._fullscreen = null;
    this._passes = null;
    this._passesTargets = null;
  }

  onRender(gl, renderer, deltaTime, mainLayer) {
    this._ensureState(renderer);

    const { _owner, _passes, _passesTargets } = this;
    const { entity } = _owner;
    const rtt = renderer.activeRenderTarget;
    for (const pass of _passes) {
      const {
        layer,
        buffer,
        persistentBuffer,
        captureEntity,
        shader,
        overrideUniforms,
        overrideSamplers
      } = pass;
      if (!!shader) {
        this._fullscreen.shader = shader;
        const uniforms = this._fullscreen.overrideUniforms;
        const samplers = this._fullscreen.overrideSamplers;
        uniforms.clear();
        samplers.clear();
        if (!!overrideUniforms) {
          for (const name in overrideUniforms) {
            uniforms.set(name, overrideUniforms[name]);
          }
        }
        if (!!overrideSamplers) {
          for (const name in overrideSamplers) {
            samplers.set(name, overrideSamplers[name]);
          }
        }
        if (!!rtt) {
          renderer.enableRenderTarget(rtt, false);
        } else {
          renderer.disableRenderTarget();
        }
        this._fullscreen.onRender(gl, renderer, deltaTime, mainLayer);
      } else {
        const target = !!captureEntity
          ? entity.findEntity(captureEntity)
          : entity;
        if (!!buffer) {
          renderer.enableRenderTarget(buffer, !persistentBuffer);
        } else if (!!rtt) {
          renderer.enableRenderTarget(rtt, false);
        } else {
          renderer.disableRenderTarget();
        }
        if (!!layer) {
          target.performAction('render-layer', gl, renderer, deltaTime, layer);
        } else {
          target.performAction('render', gl, renderer, deltaTime, null);
        }
      }
    }
    if (!!rtt) {
      renderer.enableRenderTarget(rtt, false);
    } else {
      renderer.disableRenderTarget();
    }
  }

  onResize(width, height) {
    this._dirty = true;
  }

  _ensureState(renderer) {
    if (!this._dirty) {
      return;
    }
    const { _passes, _passesTargets } = this;
    for (const target of _passesTargets.values()) {
      renderer.unregisterRenderTarget(target);
    }

    this._passesTargets = new Set();
    for (const pass of _passes) {
      const { buffer, targets, floatPointData } = pass;
      this._passesTargets.add(buffer);
      if (!targets) {
        renderer.registerRenderTarget(
          buffer,
          renderer.canvas.width,
          renderer.canvas.height,
          floatPointData
        );
      } else {
        renderer.registerRenderTargetMulti(
          buffer,
          renderer.canvas.width,
          renderer.canvas.height,
          targets
        );
      }
    }

    this._renderer = renderer;
    this._dirty = false;
  }

}

export default class MultipassRenderer extends Component {

  static get propsTypes() {
    return {
      passes: 'array(any)',
    };
  }

  static factory() {
    return new MultipassRenderer();
  }

  /** @type {*} */
  get passes() {
    return this._pipeline.passes;
  }

  /** @type {*} */
  set passes(value) {
    this._pipeline.passes = value;
  }

  /**
   * Constructor.
   */
  constructor() {
    super();

    this._camera = null;
    this._pipeline = new MultipassPipeline(this);
  }

  /**
   * @override
   */
  dispose() {
    if (!!this._pipeline) {
      this._pipeline.dispose();
      this._pipeline = null;
    }
    this._camera = null;

    super.dispose();
  }

  /**
   * @override
   */
  onAttach() {
    const camera = this._camera = this.entity.getComponent(Camera);
    if (!camera) {
      throw new Error('There is no component of Camera type!');
    }

    camera.command = this._pipeline;
  }

  /**
   * @override
   */
  onDetach() {
    if (!!this._camera) {
      this._camera.command = null;
      this._camera = null;
    }
  }

}