Reference Source

src/viewer/scene/materials/PointsMaterial.js

import {Material} from './Material.js';
import {RenderState} from '../webgl/RenderState.js';

const PRESETS = {
    "default": {
        pointSize: 4,
        roundPoints: true,
        perspectivePoints: true
    },
    "square": {
        pointSize: 4,
        roundPoints: false,
        perspectivePoints: true
    },
    "round": {
        pointSize: 4,
        roundPoints: true,
        perspectivePoints: true
    }
};

/**
 * @desc Configures the size and shape of "points" geometry primitives.
 *
 * * Located at {@link Scene#pointsMaterial}.
 * * Supports round and square points.
 * * Optional perspective point scaling.
 * * Globally configures "points" primitives for all {@link VBOSceneModel}s.
 *
 * ## Usage
 *
 * In the example below, we'll customize the {@link Scene}'s global ````PointsMaterial````, then use
 * an {@link XKTLoaderPlugin} to load a model containing a point cloud.
 *
 * [[Run this example](/examples/index.html#materials_PointsMaterial)]
 *
 * ````javascript
 * import {Viewer, XKTLoaderPlugin} from "xeokit-sdk.es.js";
 *
 * const viewer = new Viewer({
 *     canvasId: "myCanvas",
 *     transparent: true
 * });
 *
 * viewer.scene.camera.eye = [0, 0, 5];
 * viewer.scene.camera.look = [0, 0, 0];
 * viewer.scene.camera.up = [0, 1, 0];
 *
 * viewer.scene.pointsMaterial.pointSize = 2;
 * viewer.scene.pointsMaterial.roundPoints = true;
 * viewer.scene.pointsMaterial.perspectivePoints = true;
 * viewer.scene.pointsMaterial.minPerspectivePointSize = 1;
 * viewer.scene.pointsMaterial.maxPerspectivePointSize = 6;
 * viewer.scene.pointsMaterial.filterIntensity = true;
 * viewer.scene.pointsMaterial.minIntensity = 0.0;
 * viewer.scene.pointsMaterial.maxIntensity = 1.0;
 *
 * const xktLoader = new XKTLoaderPlugin(viewer);
 *
 * const model = xktLoader.load({
 *      id: "myModel",
 *      src: "../assets/models/xkt/MAP-PointCloud.xkt"
 * });
 * ````
 */
class PointsMaterial extends Material {

    /**
     @private
     */
    get type() {
        return "PointsMaterial";
    }

    /**
     * Gets available PointsMaterial presets.
     *
     * @type {Object}
     */
    get presets() {
        return PRESETS;
    };

    /**
     * @constructor
     * @param {Component} owner Owner component. When destroyed, the owner will destroy this component as well.
     * @param {*} [cfg] The PointsMaterial configuration
     * @param {String} [cfg.id] Optional ID, unique among all components in the parent {@link Scene}, generated automatically when omitted.
     * @param {Number} [cfg.pointSize=2] Point size in pixels.
     * @param {Boolean} [cfg.roundPoints=true] Whether points are round (````true````) or square (````false````).
     * @param {Boolean} [cfg.perspectivePoints=true] Whether apparent point size reduces with distance when {@link Camera#projection} is set to "perspective".
     * @param {Number} [cfg.minPerspectivePointSize=1] When ````perspectivePoints```` is ````true````, this is the minimum rendered size of each point in pixels.
     * @param {Number} [cfg.maxPerspectivePointSize=6] When ````perspectivePoints```` is ````true````, this is the maximum rendered size of each point in pixels.
     * @param {Boolean} [cfg.filterIntensity=false] When this is true, points are only rendered when their intensity value falls within the range given in {@link }
     * @param {Number} [cfg.minIntensity=0] When ````filterIntensity```` is ````true````, points with intensity below this value will not be rendered.
     * @param {Number} [cfg.maxIntensity=1] When ````filterIntensity```` is ````true````, points with intensity above this value will not be rendered.
     * @param {String} [cfg.preset] Selects a preset PointsMaterial configuration - see {@link PointsMaterial#presets}.
     */
    constructor(owner, cfg = {}) {

        super(owner, cfg);

        this._state = new RenderState({
            type: "PointsMaterial",
            pointSize: null,
            roundPoints: null,
            perspectivePoints: null,
            minPerspectivePointSize: null,
            maxPerspectivePointSize: null,
            filterIntensity: null,
            minIntensity: null,
            maxIntensity: null
        });

        if (cfg.preset) { // Apply preset then override with configs where provided
            this.preset = cfg.preset;
            if (cfg.pointSize !== undefined) {
                this.pointSize = cfg.pointSize;
            }
            if (cfg.roundPoints !== undefined) {
                this.roundPoints = cfg.roundPoints;
            }
            if (cfg.perspectivePoints !== undefined) {
                this.perspectivePoints = cfg.perspectivePoints;
            }
            if (cfg.minPerspectivePointSize !== undefined) {
                this.minPerspectivePointSize = cfg.minPerspectivePointSize;
            }
            if (cfg.maxPerspectivePointSize !== undefined) {
                this.maxPerspectivePointSize = cfg.minPerspectivePointSize;
            }
        } else {
            this._preset = "default";
            this.pointSize = cfg.pointSize;
            this.roundPoints = cfg.roundPoints;

            this.perspectivePoints = cfg.perspectivePoints;
            this.minPerspectivePointSize = cfg.minPerspectivePointSize;
            this.maxPerspectivePointSize = cfg.maxPerspectivePointSize;
        }

        this.filterIntensity = cfg.filterIntensity;
        this.minIntensity = cfg.minIntensity;
        this.maxIntensity = cfg.maxIntensity;
    }

    /**
     * Sets point size.
     *
     * Default value is ````2.0```` pixels.
     *
     * @type {Number}
     */
    set pointSize(value) {
        this._state.pointSize = value || 2.0;
        this.glRedraw();
    }

    /**
     * Gets point size.
     *
     * Default value is ````2.0```` pixels.
     *
     * @type {Number}
     */
    get pointSize() {
        return this._state.pointSize;
    }


    /**
     * Sets if points are round or square.
     *
     * Default is ````true```` to set points round.
     *
     * @type {Boolean}
     */
    set roundPoints(value) {
        value = (value !== false);
        if (this._state.roundPoints === value) {
            return;
        }
        this._state.roundPoints = value;
        this.scene._needRecompile = true;
        this.glRedraw();
    }

    /**
     * Gets if points are round or square.
     *
     * Default is ````true```` to set points round.
     *
     * @type {Boolean}
     */
    get roundPoints() {
        return this._state.roundPoints;
    }

    /**
     * Sets if rendered point size reduces with distance when {@link Camera#projection} is set to ````"perspective"````.
     *
     * Default is ````true````.
     *
     * @type {Boolean}
     */
    set perspectivePoints(value) {
        value = (value !== false);
        if (this._state.perspectivePoints === value) {
            return;
        }
        this._state.perspectivePoints = value;
        this.scene._needRecompile = true;
        this.glRedraw();
    }

    /**
     * Gets if rendered point size reduces with distance when {@link Camera#projection} is set to "perspective".
     *
     * Default is ````false````.
     *
     * @type {Boolean}
     */
    get perspectivePoints() {
        return this._state.perspectivePoints;
    }

    /**
     * Sets the minimum rendered size of points when {@link PointsMaterial#perspectivePoints} is ````true````.
     *
     * Default value is ````1.0```` pixels.
     *
     * @type {Number}
     */
    set minPerspectivePointSize(value) {
        this._state.minPerspectivePointSize = value || 1.0;
        this.scene._needRecompile = true;
        this.glRedraw();
    }

    /**
     * Gets the minimum rendered size of points when {@link PointsMaterial#perspectivePoints} is ````true````.
     *
     * Default value is ````1.0```` pixels.
     *
     * @type {Number}
     */
    get minPerspectivePointSize() {
        return this._state.minPerspectivePointSize;
    }

    /**
     * Sets the maximum rendered size of points when {@link PointsMaterial#perspectivePoints} is ````true````.
     *
     * Default value is ````6```` pixels.
     *
     * @type {Number}
     */
    set maxPerspectivePointSize(value) {
        this._state.maxPerspectivePointSize = value || 6;
        this.scene._needRecompile = true;
        this.glRedraw();
    }

    /**
     * Gets the maximum rendered size of points when {@link PointsMaterial#perspectivePoints} is ````true````.
     *
     * Default value is ````6```` pixels.
     *
     * @type {Number}
     */
    get maxPerspectivePointSize() {
        return this._state.maxPerspectivePointSize;
    }

    /**
     * Sets if rendered point size reduces with distance when {@link Camera#projection} is set to ````"perspective"````.
     *
     * Default is ````false````.
     *
     * @type {Boolean}
     */
    set filterIntensity(value) {
        value = (value !== false);
        if (this._state.filterIntensity === value) {
            return;
        }
        this._state.filterIntensity = value;
        this.scene._needRecompile = true;
        this.glRedraw();
    }

    /**
     * Gets if rendered point size reduces with distance when {@link Camera#projection} is set to "perspective".
     *
     * Default is ````false````.
     *
     * @type {Boolean}
     */
    get filterIntensity() {
        return this._state.filterIntensity;
    }

    /**
     * Sets the minimum rendered size of points when {@link PointsMaterial#perspectivePoints} is ````true````.
     *
     * Default value is ````0````.
     *
     * @type {Number}
     */
    set minIntensity(value) {
        this._state.minIntensity = (value !== undefined && value !== null) ? value: 0.0;
        this.glRedraw();
    }

    /**
     * Gets the minimum rendered size of points when {@link PointsMaterial#filterIntensity} is ````true````.
     *
     * Default value is ````0````.
     *
     * @type {Number}
     */
    get minIntensity() {
        return this._state.minIntensity;
    }

    /**
     * Sets the maximum rendered size of points when {@link PointsMaterial#filterIntensity} is ````true````.
     *
     * Default value is ````1````.
     *
     * @type {Number}
     */
    set maxIntensity(value) {
        this._state.maxIntensity = (value !== undefined && value !== null) ? value: 1.0;
        this.glRedraw();
    }

    /**
     * Gets the maximum rendered size of points when {@link PointsMaterial#filterIntensity} is ````true````.
     *
     * Default value is ````1````.
     *
     * @type {Number}
     */
    get maxIntensity() {
        return this._state.maxIntensity;
    }

    /**
     * Selects a preset ````PointsMaterial```` configuration.
     *
     * Default value is ````"default"````.
     *
     * @type {String}
     */
    set preset(value) {
        value = value || "default";
        if (this._preset === value) {
            return;
        }
        const preset = PRESETS[value];
        if (!preset) {
            this.error("unsupported preset: '" + value + "' - supported values are " + Object.keys(PRESETS).join(", "));
            return;
        }
        this.pointSize = preset.pointSize;
        this.roundPoints = preset.roundPoints;
        this.perspectivePoints = preset.perspectivePoints;
        this.minPerspectivePointSize = preset.minPerspectivePointSize;
        this.maxPerspectivePointSize = preset.maxPerspectivePointSize;
        this._preset = value;
    }

    /**
     * The current preset ````PointsMaterial```` configuration.
     *
     * Default value is ````"default"````.
     *
     * @type {String}
     */
    get preset() {
        return this._preset;
    }

    /**
     * @private
     * @return {string}
     */
    get hash() {
        return [
            this.pointSize,
            this.roundPoints,
            this.perspectivePoints,
            this.minPerspectivePointSize,
            this.maxPerspectivePointSize,
            this.filterIntensity
        ].join((";"));
    }

    /**
     * Destroys this ````PointsMaterial````.
     */
    destroy() {
        super.destroy();
        this._state.destroy();
    }
}

export {PointsMaterial};