Source: utils/MeshPicker.js

import Class from '../core/Class';
import Color from '../math/Color';
import Framebuffer from '../renderer/Framebuffer';
import BasicMaterial from '../material/BasicMaterial';
import {
    padLeft
} from './util';

const meshPickerMaterial = new BasicMaterial({
    lightType: 'NONE'
});
const clearColor = new Color(1, 1, 1);
const tempColor = new Color();

/**
 * Mesh 选择工具,可以获取画布中某个区域内的Mesh
 * @class
 * @example
 * const picker = new Hilo3d.MeshPicker({
 *     renderer: stage.renderer
 * });
 * picker.getSelection(20, 20, 1, 1);
 */
const MeshPicker = Class.create(/** @lends MeshPicker.prototype */{
    /**
     * @default true
     * @type {boolean}
     */
    isMeshPicker: true,
    /**
     * @default MeshPicker
     * @type {string}
     */
    className: 'MeshPicker',
    /**
     * 是否开启debug,开启后会将mesh以不同的颜色绘制在左下角
     * @default false
     * @type {boolean}
     */
    debug: false,
    /**
     * WebGLRenderer 的实例
     * @default null
     * @type {WebGLRenderer}
     */
    renderer: null,
    colorMeshMap: null,
    /**
     * @constructs
     * @param {object} [params] 创建对象的属性参数,可包含此类的所有属性。
     */
    constructor(params) {
        Object.assign(this, params);
        this.colorMeshMap = {};
        this.init();
    },
    createFramebuffer() {
        if (this.framebuffer) {
            return;
        }

        const renderer = this.renderer;

        this.framebuffer = new Framebuffer(renderer, {
            useVao: renderer.useVao,
            width: renderer.width,
            height: renderer.height
        });
    },

    renderDebug() {
        this.framebuffer.render(0, 0.7, 0.3, 0.3);
    },

    createMeshNumberId(mesh) {
        if (!('numberId' in mesh)) {
            mesh.numberId = Number(mesh.id.replace(/^.*_(\d+)$/, '$1')) * 10;
            mesh.color = padLeft(mesh.numberId.toString(16), 6);
            this.colorMeshMap[mesh.color] = mesh;
        }
    },

    renderColoredMeshes() {
        const {
            renderer,
            framebuffer
        } = this;

        framebuffer.bind();
        renderer.clear(clearColor);
        const currentForceMaterial = renderer.forceMaterial;
        renderer.forceMaterial = meshPickerMaterial;
        renderer.renderList.traverse((mesh) => {
            this.createMeshNumberId(mesh);
            meshPickerMaterial.diffuse.fromHEX(mesh.color);
            meshPickerMaterial.isDirty = true;
            renderer.renderMesh(mesh);
        });
        renderer.forceMaterial = currentForceMaterial;
        framebuffer.unbind();
    },

    /**
     * 获取指定区域内的Mesh,注意无法获取被遮挡的Mesh
     * @param {number} x 左上角的x坐标
     * @param {number} y 左上角的y坐标
     * @param {number} [width=1] 区域的宽
     * @param {number} [height=1] 区域的高
     * @return {Mesh[]} 返回获取的Mesh数组
     */
    getSelection(x, y, width = 1, height = 1) {
        const pixelRatio = this.renderer.pixelRatio;
        const meshes = [];
        const pixels = this.framebuffer.readPixels(x * pixelRatio, y * pixelRatio, width * pixelRatio, height * pixelRatio);
        for (let i = 0; i < pixels.length; i += 4) {
            let color = tempColor.fromUintArray(pixels, i).toHEX();
            if (this.colorMeshMap[color]) {
                meshes.push(this.colorMeshMap[color]);
            }
        }
        return meshes;
    },

    init() {
        this.createFramebuffer();
        this.renderer.on('afterRender', () => {
            this.renderColoredMeshes();
            if (this.debug) {
                this.renderDebug();
            }
        });
    }
});

export default MeshPicker;