Home Reference Source

src/components/AtlasSprite.js

import Sprite from './Sprite';
import System from '../systems/System';

/**
 * Atlas sprite renderer.
 *
 * @example
 * const component = new AtlasSprite();
 * component.deserialize({ shader: 'shader://sprite.json', atlas: 'atlas://sheet.json:box.png' });
 */
export default class AtlasSprite extends Sprite {

  /**
   * Component factory.
   *
   * @return {AtlasSprite} Component instance.
   */
  static factory() {
    return new AtlasSprite();
  }

  /** @type {*} */
  static get propsTypes() {
    return {
      visible: Sprite.propsTypes.visible,
      shader: Sprite.propsTypes.shader,
      overrideUniforms: Sprite.propsTypes.overrideUniforms,
      overrideSamplers: Sprite.propsTypes.overrideSamplers,
      layers: Sprite.propsTypes.layers,
      xOffset: Sprite.propsTypes.xOffset,
      yOffset: Sprite.propsTypes.yOffset,
      xOrigin: Sprite.propsTypes.xOrigin,
      yOrigin: Sprite.propsTypes.yOrigin,
      color: Sprite.propsTypes.color,
      overrideBaseFiltering: Sprite.propsTypes.overrideBaseFiltering,
      atlas: 'asset(atlas?:.*$)',
      scale: 'number'
    };
  }

  /** @type {string|null} */
  get atlas() {
    return this._atlas;
  }

  /** @type {string|null} */
  set atlas(value) {
    if (!value || value === '') {
      this._atlas = value;
      this.overrideBaseTexture = '';
      this.width = 0;
      this.height = 0;
      this.frameTopLeft = [0, 0];
      this.frameBottomRight = [0, 0];
      return;
    }

    if (typeof value !== 'string') {
      throw new Error('`value` is not type of String!');
    }

    const found = value.indexOf(':');
    if (found < 0) {
      throw new Error('`value` does not conform rule of "atlas:frame" naming!');
    }

    const original = value;
    const frame = value.substr(found + 1);
    value = value.substr(0, found);

    const assets = System.get('AssetSystem');
    if (!assets) {
      throw new Error('There is no registered AssetSystem!');
    }

    const atlas = assets.get(`atlas://${value}`);
    if (!atlas) {
      throw new Error(`There is no atlas asset loaded: ${value}`);
    }

    const { meta, frames } = atlas.data.descriptor;
    if (!meta || !frames) {
      throw new Error(`There is either no metadata or frames in atlas: ${value}`);
    }

    const info = frames[frame];
    if (!info || !info.frame) {
      throw new Error(`There is no frame information in atlas: ${value} (${frame})`);
    }

    const { size } = meta;
    const { x, y, w, h } = info.frame;
    const { _scale } = this;

    this._atlas = original;
    this.overrideBaseTexture = meta.image;
    this.width = w * _scale;
    this.height = h * _scale;
    this.frameTopLeft = [
      x / size.w,
      y / size.h
    ];
    this.frameBottomRight = [
      (x + w) / size.w,
      (y + h) / size.h
    ];
  }

  /** @type {number} */
  get scale() {
    return this._scale;
  }

  /** @type {number} */
  set scale(value) {
    if (typeof value !== 'number') {
      throw new Error('`value` is not type of Number!');
    }

    this._scale = value;
    this.atlas = this.atlas;
  }

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

    this._atlas = null;
    this._scale = 1;
  }

}