Source: objects/Mesh.js

import { Utils } from '../common/Utils'
import { GeometryResource } from '../resources/GeometryResource';
import { MaterialResource } from '../resources/MaterialResource';
import { Object3D } from './Object3D';

const __ = {
	private: Symbol('private'),
}

/**
 * @class Mesh
 * The mesh object.
 * @memberof THING
 * @extends THING.Object3D
 * @public
 */
class Mesh extends Object3D {

	static defaultTagArray = ['Geometry'];

	/**
	 * The mesh object that with custom geometry info in scene.
	 * @param {Object} param The initial parameters.
	 */
	constructor(param = {}) {
		super(param);

		this[__.private] = {};
		let _private = this[__.private];

		_private.geometry = null;
		_private.material = null;

		let data = param['data'];
		if (data) {
			_private.geometry = new GeometryResource(data);
		}
		else if (param['geometry']) {
			_private.geometry = param['geometry'];
			_private.geometry.addRef();
		}

		let material = param['material'];
		if (material) {
			if (material.isMaterialResource) {
				_private.material = material;
				_private.material.addRef();
			}
			else {
				_private.material = new MaterialResource(material);
			}
		}
		else {
			_private.material = new MaterialResource();
		}
	}

	// #region Private

	_createCustomNode() {
		let _private = this[__.private];

		let geometryResource = _private.geometry.resource;
		let materialResource = _private.material.resource;

		return Utils.createObject('CustomNode', { geometryResource, materialResource });
	}

	// #endregion

	// #region Overrides

	onDestroy() {
		let _private = this[__.private];

		if (_private.geometry) {
			_private.geometry.release();
			_private.geometry = null;
		}

		if (_private.material) {
			_private.material.release();
			_private.material = null;
		}

		super.onDestroy();
	}

	onLoadResource(options, resolve, reject) {
		let dynamic = Utils.parseValue(options['dynamic'], false);
		if (!dynamic) {
			Utils.setTimeout(() => {
				if (this.destroyed) {
					reject(`The object had been destroyed, skip to load resources`);
				}
				else {
					let node = this._createCustomNode();

					this.body.setNode(node);

					resolve();
				}
			});
		}
	}

	onCopy(object) {
		super.onCopy(object);

		let resource = object.geometry.resource;
		if (!this.geometry) {
			this.geometry = new GeometryResource({ array: resource._vertexData });
		}
		else {
			this.setNormal(resource.getAttribute('a_Normal'));
			this.setPosition(resource.getAttribute('a_Position'));
			this.setUv(resource.getAttribute('a_Uv'));
			this.setUv2(resource.getAttribute('a_Uv2'));
		}
	}

	/**
	 * Set the vertex of the mesh.Use the current interface when the geometry references only itself, and use the attribute (mesh.geometry.position) interface when the geometry is shared.
	 * @param {Array<Number>|Float32Array} array 
	 * @public
	 */
	setPosition(array) {
		if (this.geometry.refCount == 1) {
			if (array instanceof Array) {
				array = new Float32Array(array);
			}
			this.geometry.resource.setAttribute('a_Position', array);
		}
	}

	/**
	 * Get the vertex of the mesh.
	 * @returns {Float32Array}
	 * @public
	 */
	getPosition() {
		return this.geometry.resource.getAttribute('a_Position');
	}

	/**
	 * Set the vertex uv of the mesh.Use the current interface when the geometry references only itself, and use the attribute (mesh.geometry.uv) interface when the geometry is shared.
	 * @param {Array<Number>|Float32Array} array 
	 * @public
	 */
	setUv(array) {
		if (this.geometry.refCount == 1) {
			if (array instanceof Array) {
				array = new Float32Array(array);
			}
			this.geometry.resource.setAttribute('a_Uv', array);
		}
	}

	/**
	 * Get the vertex uv of the mesh.
	 * @returns {Float32Array}
	 * @public
	 */
	getUv() {
		return this.geometry.resource.getAttribute('a_Uv');
	}


	/**
	* Set the vertex uv2 of the mesh.Use the current interface when the geometry references only itself, and use the attribute (mesh.geometry.uv2) interface when the geometry is shared.
	* @param {Array<Number>|Float32Array} array 
	* @private
	*/
	setUv2(array) {
		if (this.geometry.refCount == 1) {
			if (array instanceof Array) {
				array = new Float32Array(array);
			}
			this.geometry.resource.setAttribute('a_Uv2', array);
		}
	}

	/**
	 * Set the vertex normal of the mesh.Use the current interface when the geometry references only itself, and use the attribute (mesh.geometry.normal) interface when the geometry is shared.
	 * @param {Array<Number>|Float32Array} array 
	 * @public
	 */
	setNormal(array) {
		if (this.geometry.refCount == 1) {
			if (array instanceof Array) {
				array = new Float32Array(array);
			}
			this.geometry.resource.setAttribute('a_Normal', array);
		}
	}

	/**
	 * Get the vertex normal of the mesh.
	 * @returns {Float32Array}
	 * @public
	 */
	getNormal() {
		return this.geometry.resource.getAttribute('a_Normal');
	}

	// #endregion

	// #region Accessor

	/**
	 * Get mesh data.
	 * @type {Object}
	 * @public
	 */
	get data() {
		let _private = this[__.private];

		return {
			geometryData: _private.geometry.vertexData,
			materialData: _private.material.data,
		}
	}

	/**
	 * Get geometry.
	 * @type {GeometryResource}
	 * @private
	 */
	get geometry() {
		let _private = this[__.private];

		return _private.geometry;
	}
	set geometry(value) {
		let _private = this[__.private];

		_private.geometry = value;
	}

	// #endregion

	/**
	 * Check whether it's Mesh type or inherit from it.
	 * @type {Boolean}
	 * @public
	 */
	get isMesh() {
		return true;
	}

}

export { Mesh }