entity_GeoObject.ts

import * as utils from "../utils/shared";
import {Entity} from "./Entity";
import {Quat, Vec3, Vec4} from "../math/index";
import {GeoObjectHandler, InstanceData} from "./GeoObjectHandler";
import {NumberArray3} from "../math/Vec3";
import {NumberArray4} from "../math/Vec4";
import {Object3d} from "../Object3d";
import {RADIANS} from "../math";

export interface IGeoObjectParams {
    object3d?: Object3d;
    objSrc?: string;
    textureSrc?: string;
    tag?: string;
    position?: Vec3 | NumberArray3;
    pitch?: number;
    yaw?: number;
    roll?: number;
    scale?: number | Vec3 | NumberArray3;
    translate?: Vec3 | NumberArray3;
    color?: Vec4 | NumberArray4 | string;
    visibility?: boolean;
}

/**
 * @class
 * @param {Object} options -  Geo object parameters:
 * @param {Vec3} [options.position] - Geo object position.
 *
 * @todo: GeoObject and GeoObjectHandler provides instanced objects only.
 * It would be nice if it could provide not instanced rendering loop too.
 */
class GeoObject {
    protected _tag: string;

    public instanced: boolean;

    /**
     * Entity instance that holds this geo object.
     * @public
     * @type {Entity}
     */
    public _entity: Entity | null;

    /**
     * Geo object center cartesian position.
     * @protected
     * @type {Vec3}
     */
    protected _position: Vec3;
    public _positionHigh: Vec3;
    public _positionLow: Vec3;

    protected _pitch: number;
    protected _yaw: number;
    protected _roll: number;

    protected _pitchRad: number;
    protected _yawRad: number;
    protected _rollRad: number;

    protected _scale: Vec3;
    protected _translate: Vec3;

    /**
     * RGBA color.
     * @public
     * @type {Vec4}
     */
    public _color: Vec4;

    public _qRot: Quat;

    protected _direction: Vec3;

    public _handler: GeoObjectHandler | null;
    public _handlerIndex = -1;

    public _tagData: InstanceData | null;
    public _tagDataIndex: number;

    protected _object3d: Object3d;

    protected _visibility: boolean;

    protected _qNorthFrame: Quat;
    private _textureSrc?: string;
    public _objectSrc?: string;

    protected _children: GeoObject[];

    constructor(options: IGeoObjectParams) {

        this._tag = options.tag || "none";

        this.instanced = true;

        this._entity = null;

        this._position = utils.createVector3(options.position);

        this._positionHigh = new Vec3();
        this._positionLow = new Vec3();
        Vec3.doubleToTwoFloats(this._position, this._positionHigh, this._positionLow);

        this._pitch = options.pitch || 0.0;
        this._yaw = options.yaw || 0.0;
        this._roll = options.roll || 0.0;

        this._pitchRad = this._pitch * RADIANS;
        this._yawRad = this._yaw * RADIANS;
        this._rollRad = this._roll * RADIANS;

        this._scale = utils.createVector3(options.scale, new Vec3(1, 1, 1));
        this._translate = utils.createVector3(options.translate, new Vec3());

        this._color = utils.createColorRGBA(options.color);

        this._qRot = Quat.IDENTITY;

        this._handler = null;
        this._handlerIndex = -1;

        this._tagData = null;
        this._tagDataIndex = -1;
        let object3d = options.object3d;
        if ((!options.object3d || options.object3d?.vertices.length === 0)) {
            object3d = new Object3d();
        }
        if (options.objSrc) {
            this.setObjectSrc(options.objSrc)
            this._objectSrc = options.objSrc;
        }
        this._object3d = object3d as Object3d;
        if (options.textureSrc) {
            this.setTextureSrc(options.textureSrc)
        }
        this._visibility = (options.visibility != undefined ? options.visibility : true);

        this._children = [];

        this._direction = new Vec3();

        this._qNorthFrame = new Quat();
    }

    public get tag() {
        return this._tag;
    }

    public getPosition(): Vec3 {
        return this._position;
    }

    public getPitch(): number {
        return this._pitch;
    }

    public getYaw(): number {
        return this._yaw;
    }

    public getRoll(): number {
        return this._roll;
    }

    public get object3d(): Object3d {
        return this._object3d;
    }

    public get vertices(): number[] {
        return this._object3d.vertices;
    }

    public get normals(): number[] {
        return this._object3d.normals;
    }

    public get texCoords(): number[] {
        return this._object3d.texCoords;
    }

    public get indices(): number[] {
        return this._object3d.indices;
    }

    /**
     * Sets geo object opacity.
     * @public
     * @param {number} a - Billboard opacity.
     */
    public setOpacity(a: number) {
        this._color.w = a;
        this.setColor(this._color.x, this._color.y, this._color.z, a);
    }

    /**
     * Sets color.
     * @public
     * @param {number} r - Red.
     * @param {number} g - Green.
     * @param {number} b - Blue.
     * @param {number} [a] - Alpha.
     */
    public setColor(r: number, g: number, b: number, a?: number) {
        this._color.x = r;
        this._color.y = g;
        this._color.z = b;
        a != undefined && (this._color.w = a);
        this._handler && this._handler.setRgbaArr(this._tagData!, this._tagDataIndex, this._color);
    }

    /**
     * Sets color.
     * @public
     * @param {Vec3 | Vec4} color - RGBA vector.
     */
    public setColor4v(color: Vec3 | Vec4) {
        this._color.x = color.x;
        this._color.y = color.y;
        this._color.z = color.z;
        (color as Vec4).w != undefined && (this._color.w = (color as Vec4).w);
        this._handler && this._handler.setRgbaArr(this._tagData!, this._tagDataIndex, this._color);
    }

    /**
     * Sets geo object visibility.
     * @public
     * @param {boolean} visibility - Visibility flag.
     */
    public setVisibility(visibility: boolean) {
        this._visibility = visibility;
        this._handler && this._handler.setVisibility(this._tagData!, this._tagDataIndex, visibility);
    }

    /**
     * Returns geo object visibility.
     * @public
     * @returns {boolean}
     */
    public getVisibility(): boolean {
        return this._visibility;
    }

    /**
     * Sets geo object position.
     * @public
     * @param {number} x - X coordinate.
     * @param {number} y - Y coordinate.
     * @param {number} z - Z coordinate.
     */
    public setPosition(x: number, y: number, z: number) {
        this._position.x = x;
        this._position.y = y;
        this._position.z = z;
        Vec3.doubleToTwoFloats(this._position, this._positionHigh, this._positionLow);
        this._handler &&
        this._handler.setPositionArr(this._tagData!, this._tagDataIndex, this._positionHigh, this._positionLow);
        this.updateRotation();
    }

    /**
     * Sets geo object position.
     * @public
     * @param {Vec3} position - Cartesian coordinates.
     */
    public setPosition3v(position: Vec3) {
        this._position.x = position.x;
        this._position.y = position.y;
        this._position.z = position.z;
        Vec3.doubleToTwoFloats(position, this._positionHigh, this._positionLow);
        this._handler && this._handler.setPositionArr(this._tagData!, this._tagDataIndex, this._positionHigh, this._positionLow);
        this.updateRotation();
    }

    public setYaw(yaw: number) {
        this._yaw = yaw;
        this._yawRad = yaw * RADIANS;
        this.updateRotation();
    }

    public setObject(object: Object3d) {
        this._object3d = object;
    }

    public setObjectSrc(src: string) {
        this._objectSrc = src;
        this._handler && this._handler.setObjectSrc(src, this.tag);
    }

    public setTextureSrc(src: string) {
        this._textureSrc = src;
        this._object3d && (this._object3d.src = src);
        this._handler && this._handler.setTextureTag(src, this.tag);
    }

    public setColorHTML(color: string) {
        this.setColor4v(utils.htmlColorToRgba(color));
    }

    /**
     *
     * @param {number} pitch - Pitch in radians
     */
    public setPitch(pitch: number) {
        this._pitch = pitch;
        this._pitchRad = pitch * RADIANS;
        this.updateRotation();
    }

    public setRoll(roll: number) {
        this._roll = roll;
        this._rollRad = roll * RADIANS;
        this.updateRotation();
    }

    public setPitchYawRoll(pitch: number, yaw: number, roll: number) {
        this._pitch = pitch;
        this._yaw = yaw;
        this._roll = roll;

        this._pitchRad = pitch * RADIANS;
        this._yawRad = yaw * RADIANS;
        this._rollRad = roll * RADIANS;

        this.updateRotation();
    }

    public setScale(scale: number) {
        this._scale.x = this._scale.y = this._scale.z = scale;
        this._handler && this._handler.setScaleArr(this._tagData!, this._tagDataIndex, this._scale);
    }

    public setScale3v(scale: Vec3) {
        this._scale.copy(scale);
        this._handler && this._handler.setScaleArr(this._tagData!, this._tagDataIndex, scale);
    }

    public getScale(): Vec3 {
        return this._scale;
    }

    public setTranslate3v(translate: Vec3) {
        this._translate.copy(translate);
        this._handler && this._handler.setTranslateArr(this._tagData!, this._tagDataIndex, translate);
    }

    public getTranslate(): Vec3 {
        return this._translate;
    }

    /**
     * Removes geo object from handler.
     * @public
     */
    public remove() {
        this._entity = null;
        this._handler && this._handler.remove(this);
    }

    /**
     * Sets billboard picking color.
     * @public
     * @param {Vec3} color - Picking color.
     */
    public setPickingColor3v(color: Vec3) {
        this._handler && this._handler.setPickingColorArr(this._tagData!, this._tagDataIndex, color);
    }

    public updateRotation() {

        if (this._handler && this._handler._planet) {
            this._qNorthFrame = this._handler._planet.getNorthFrameRotation(this._position);

            let qp = Quat.xRotation(-this._pitchRad);
            let qy = Quat.yRotation(this._yawRad);
            let qr = Quat.zRotation(-this._rollRad);

            this._qRot = qr.mul(qp).mul(qy).mul(this._qNorthFrame).conjugate();

            this._direction = this._qRot.mulVec3(new Vec3(0.0, 0.0, -1.0)).normalize();

            this._handler.setQRotArr(this._tagData!, this._tagDataIndex, this._qRot);
        }
    }

    public getDirection(): Vec3 {
        return this._direction.clone();
    }
}

export {GeoObject};