Source: core/Layer.js

/**
 * MLJLib
 * MeshLabJS Library
 * 
 * Copyright(C) 2015
 * Paolo Cignoni 
 * Visual Computing Lab
 * ISTI - CNR
 * 
 * All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it under 
 * the terms of the GNU General Public License as published by the Free Software 
 * Foundation; either version 2 of the License, or (at your option) any later 
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT 
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
 * FOR A PARTICULAR PURPOSE. See theGNU General Public License 
 * (http://www.gnu.org/licenses/gpl.txt) for more details.
 * 
 */

/**
 * @file Defines the Layer class
 * @author Stefano Gabriele
 */

/**         
 * @class Creates a new Layer 
 * @param {String} name The name of the mesh file
 * @param {CppMesh} cppMesh the CppMesh object
 * @memberOf MLJ.core
 */
MLJ.core.Layer = function (name, cppMesh) {
    this.name = name;

    /**
     * @type {String} - Set if a mesh is read from a file
     * (see {@link MLJ.core.File.openMeshFile}), defaults to the empty string
     */
    this.fileName = "";

    this.cppMesh = cppMesh;
    this.cppMesh.setMeshName(name);
    this.VN = this.FN = this.threeMesh = null;
    // Array of booleans that reflect what are the overlays on or off for this layer.
    this.properties = new MLJ.util.AssociativeArray();
    //is updated by MLJ.core.Scene and contains overlay ThreeJS objects
    this.overlays = new MLJ.util.AssociativeArray();
    //contains overlaying mesh parameters
    this.overlaysParams = new MLJ.util.AssociativeArray();

    var _this = this;

    this.initializeRenderingAttributes = function () {
        for(var pname in MLJ.core.defaults) {
            _this.overlaysParams.set(pname,
                jQuery.extend(true, {}, MLJ.core.defaults[pname]));                        
        }

        // Select the appropriate color mode
        var cw = _this.overlaysParams.getByKey("ColorWheel");
        var useIndex = true;
        if (cppMesh.hasPerVertexColor()) {
            cw.mljColorMode = MLJ.ColorMode.Vertex;
        } else if (cppMesh.hasPerFaceColor()) {
            cw.mljColorMode = MLJ.ColorMode.Face;
            useIndex = false;
        }

        console.time("Time to create mesh: ");
        // This Threejs object is used as a 'group' 
        // This node will have as children all the overlays of this layer.
        // It is also used to store in a single place the geometry that the overlay node will refer. 
        // So for example point overlay will use/refer the geometry buffers here defined
        // The node itself will never been rendered (material has the visibile flag == false).
        //  
        _this.threeMesh = new THREE.Mesh(new THREE.BufferGeometry(),new THREE.MeshBasicMaterial({visible: false}));
        _this.updateMeshGeometryData(useIndex);
        _this.updateMeshColorData(cw.colorMode);
        
        console.timeEnd("Time to create mesh: ");

        // If the mesh is a point cloud, enable the "Points" rendering mode
        if (_this.VN > 0 && _this.FN === 0) {
            _this.properties.set("Filled", false);
            _this.properties.set("Points", true);
        }
    };

    this.updateMeshGeometryData = function (indexed) {
        if (indexed === undefined) {
            throw new Error("MLJ.core.Layer.updateMeshGeometryData(): 'indexed' parameter required");
        }

        _this.VN = cppMesh.VN();
        _this.FN = cppMesh.FN();

        var geometry = _this.threeMesh.geometry;
        var bufferptr, bufferData;

        var positionAttrib = null, normalAttrib = null, indexAttrib = null;

        if (indexed) {
            bufferptr = cppMesh.getVertexVector(true);
            bufferData = new Float32Array(new Float32Array(Module.HEAPU8.buffer, bufferptr, _this.VN*3));
            positionAttrib = new THREE.BufferAttribute(bufferData, 3);
            Module._free(bufferptr);

            if (_this.FN > 0) { // if the mesh has faces, load per vertex normals and the index
                bufferptr = cppMesh.getVertexNormalVector(true);
                bufferData = new Float32Array(new Float32Array(Module.HEAPU8.buffer, bufferptr, _this.VN*3));
                normalAttrib = new THREE.BufferAttribute(bufferData, 3);
                Module._free(bufferptr);

                bufferptr = cppMesh.getFaceIndex();
                bufferData = new Uint32Array(new Uint32Array(Module.HEAPU8.buffer, bufferptr, _this.FN*3));
                indexAttrib = new THREE.BufferAttribute(bufferData, 3);
                Module._free(bufferptr);
            }
        } else { // load disconnected triangles
            if (_this.FN === 0) {
                bufferptr = cppMesh.getVertexVector(true); // if there are no faces load unique vertices
                bufferData = new Float32Array(new Float32Array(Module.HEAPU8.buffer, bufferptr, _this.VN*3));
                positionAttrib = new THREE.BufferAttribute(bufferData, 3);
                Module._free(bufferptr);
            } else {
                bufferptr = cppMesh.getVertexVector(false);
                bufferData = new Float32Array(new Float32Array(Module.HEAPU8.buffer, bufferptr, _this.FN*9));
                positionAttrib = new THREE.BufferAttribute(bufferData, 3);
                Module._free(bufferptr);

                bufferptr = cppMesh.getVertexNormalVector(false);
                bufferData = new Float32Array(new Float32Array(Module.HEAPU8.buffer, bufferptr, _this.FN*9));
                normalAttrib = new THREE.BufferAttribute(bufferData, 3)
                Module._free(bufferptr);
            }
        }

        if (geometry.getAttribute('position') !== undefined) {
            delete geometry.attributes.position;
        }
        if (positionAttrib !== null) geometry.addAttribute('position', positionAttrib);

        if (geometry.getAttribute('normal') !== undefined) {
            delete geometry.attributes.normal;
        }
        if (normalAttrib !== null) geometry.addAttribute('normal', normalAttrib);

        if (geometry.getAttribute('index') !== undefined) {
            delete geometry.attributes.index;
        }
        if (indexAttrib !== null) geometry.addAttribute('index', indexAttrib);
		geometry.computeBoundingBox (); //update the boundingbox
    };

    this.updateMeshColorData = function (colorMode) {
        var geometry = _this.threeMesh.geometry;
        var colorptr, colorData;

        if (colorMode === MLJ.ColorMode.Face) {
            if (_this.usingIndexedGeometry()) { // rebuild geometry as disconnected triangles
                _this.updateMeshGeometryData(false);
            }
            colorptr = cppMesh.getFaceColors();
            colorData = new Float32Array(new Float32Array(Module.HEAPU8.buffer, colorptr, _this.FN*9));
        } else {
            if (!_this.usingIndexedGeometry()) { // rebuild geometry with index
                _this.updateMeshGeometryData(true);
            }
            colorptr = cppMesh.getVertexColors();
            colorData = new Float32Array(new Float32Array(Module.HEAPU8.buffer, colorptr, _this.VN*3));
        }

        if (geometry.getAttribute('VCGColor') !== undefined) {
            delete geometry.attributes.VCGColor;
        }
        geometry.addAttribute('VCGColor', new THREE.BufferAttribute(colorData, 3));
        Module._free(colorptr);
    };

    this.usingIndexedGeometry = function () {
        return (_this.threeMesh.geometry.getAttribute('index') !== undefined);
    };

    this.updateThreeMesh = function () {
        var cm = _this.overlaysParams.getByKey("ColorWheel").colorMode;
        var useIndex = (cm !== MLJ.ColorMode.Face);
        this.updateMeshGeometryData(useIndex);
        this.updateMeshColorData(cm);
    };
        
    /**
     * Returns this THREE.Mesh object
     * @returns {THREE.Mesh} this THREE.Mesh object
     * @author Stefano Gabriele     
     */
    this.getThreeMesh = function () {
        return this.threeMesh;
    };

    /**
     * Returns the ptr to the cppMesh object
     * @returns {Number} An emscripten pointer to the VCG mesh of this layer
     */
    this.ptrMesh = function () {
        return _this.cppMesh.getMeshPtr();
    };
    
    /**
     * Removes the object from memory
     * @author Stefano Gabriele     
     */
    this.dispose = function () {
               
        var iter = _this.overlays.iterator();
        var mesh;
        while(iter.hasNext()) {
            mesh = iter.next();
            mesh.geometry.dispose();
            mesh.material.dispose();
            
            if (mesh.texture) {
                mesh.texture.dispose();            
                mesh.texture = null;
            }
        }
        
        _this.threeMesh.geometry.dispose();
        _this.threeMesh.material.dispose();
        
        if (_this.threeMesh.texture) {
            _this.threeMesh.texture.dispose();            
            _this.threeMesh.texture = null;
        }
        
        _this.cppMesh.delete();
        
        _this.name = _this.VN = _this.FN =
        _this.threeMesh = _this.properties = _this.overlays = 
        _this.overlaysParams =  _this = null;       
    };
}