UNPKG

@xeokit/xeokit-sdk

Version:

3D BIM IFC Viewer SDK for AEC engineering applications. Open Source JavaScript Toolkit based on pure WebGL for top performance, real-world coordinates and full double precision

319 lines (283 loc) 11.2 kB
import {Geometry} from './Geometry.js'; import {RenderState} from '../webgl/RenderState.js'; import {ArrayBuf} from '../webgl/ArrayBuf.js'; import {math} from '../math/math.js'; import {stats} from '../stats.js'; import {buildEdgeIndices} from '../math/buildEdgeIndices.js'; import {geometryCompressionUtils} from '../math/geometryCompressionUtils.js'; const memoryStats = stats.memory; const tempAABB = math.AABB3(); /** * @desc A {@link Geometry} that keeps its geometry data solely in GPU memory, without retaining it in browser memory. * * VBOGeometry uses less memory than {@link ReadableGeometry}, which keeps its geometry data in both browser and GPU memory. * * ## Usage * * Creating a {@link Mesh} with a VBOGeometry that defines a single triangle, plus a {@link PhongMaterial} with diffuse {@link Texture}: * * [[Run this example](/examples/index.html#geometry_VBOGeometry)] * * ````javascript * import {Viewer, Mesh, VBOGeometry, PhongMaterial, Texture} from "xeokit-sdk.es.js"; * * const viewer = new Viewer({ * canvasId: "myCanvas" * }); * * new Mesh(viewer.scene, { * geometry: new VBOGeometry(viewer.scene, { * primitive: "triangles", * positions: [0.0, 3, 0.0, -3, -3, 0.0, 3, -3, 0.0], * normals: [0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0], * uv: [0.0, 0.0, 0.5, 1.0, 1.0, 0.0], * indices: [0, 1, 2] * }), * material: new PhongMaterial(viewer.scene, { * diffuseMap: new Texture(viewer.scene, { * src: "textures/diffuse/uvGrid2.jpg" * }), * backfaces: true * }) * }); * ```` */ class VBOGeometry extends Geometry { /** @private */ get type() { return "VBOGeometry"; } /** * @private * @returns {Boolean} */ get isVBOGeometry() { return true; } /** * @constructor * @param {Component} owner Owner component. When destroyed, the owner will destroy this component as well. * @param {*} [cfg] Configs * @param {String} [cfg.id] Optional ID, unique among all components in the parent {@link Scene}, generated automatically when omitted. * @param {String} [cfg.primitive="triangles"] The primitive type. Accepted values are 'points', 'lines', 'line-loop', 'line-strip', 'triangles', 'triangle-strip' and 'triangle-fan'. * @param {Number[]} [cfg.positions] Positions array. * @param {Number[]} [cfg.normals] Vertex normal vectors array. * @param {Number[]} [cfg.uv] UVs array. * @param {Number[]} [cfg.colors] Vertex colors. * @param {Number[]} [cfg.indices] Indices array. * @param {Number} [cfg.edgeThreshold=10] When autogenerating edges for supporting {@link Drawable#edges}, this indicates the threshold angle (in degrees) between the face normals of adjacent triangles below which the edge is discarded. */ constructor(owner, cfg = {}) { super(owner, cfg); this._state = new RenderState({ // Arrays for emphasis effects are got from xeokit.GeometryLite friend methods compressGeometry: true, primitive: null, // WebGL enum primitiveName: null, // String positionsDecodeMatrix: null, // Set when compressGeometry == true uvDecodeMatrix: null, // Set when compressGeometry == true positionsBuf: null, normalsBuf: null, colorsbuf: null, uvBuf: null, indicesBuf: null, hash: "" }); this._numTriangles = 0; this._edgeThreshold = cfg.edgeThreshold || 10.0; this._aabb = null; this._obb = math.OBB3(); const state = this._state; const gl = this.scene.canvas.gl; cfg.primitive = cfg.primitive || "triangles"; switch (cfg.primitive) { case "points": state.primitive = gl.POINTS; state.primitiveName = cfg.primitive; break; case "lines": state.primitive = gl.LINES; state.primitiveName = cfg.primitive; break; case "line-loop": state.primitive = gl.LINE_LOOP; state.primitiveName = cfg.primitive; break; case "line-strip": state.primitive = gl.LINE_STRIP; state.primitiveName = cfg.primitive; break; case "triangles": state.primitive = gl.TRIANGLES; state.primitiveName = cfg.primitive; break; case "triangle-strip": state.primitive = gl.TRIANGLE_STRIP; state.primitiveName = cfg.primitive; break; case "triangle-fan": state.primitive = gl.TRIANGLE_FAN; state.primitiveName = cfg.primitive; break; default: this.error("Unsupported value for 'primitive': '" + cfg.primitive + "' - supported values are 'points', 'lines', 'line-loop', 'line-strip', 'triangles', " + "'triangle-strip' and 'triangle-fan'. Defaulting to 'triangles'."); state.primitive = gl.TRIANGLES; state.primitiveName = cfg.primitive; } if (!cfg.positions) { this.error("Config expected: positions"); return; // TODO: Recover? } if (!cfg.indices) { this.error("Config expected: indices"); return; // TODO: Recover? } var positions; { const positionsDecodeMatrix = cfg.positionsDecodeMatrix; if (positionsDecodeMatrix) { // Compressed positions } else { // Uncompressed positions const bounds = geometryCompressionUtils.getPositionsBounds(cfg.positions); const result = geometryCompressionUtils.compressPositions(cfg.positions, bounds.min, bounds.max); positions = result.quantized; state.positionsDecodeMatrix = result.decodeMatrix; state.positionsBuf = new ArrayBuf(gl, gl.ARRAY_BUFFER, positions, positions.length, 3, gl.STATIC_DRAW); memoryStats.positions += state.positionsBuf.numItems; math.positions3ToAABB3(cfg.positions, this._aabb); math.positions3ToAABB3(positions, tempAABB, state.positionsDecodeMatrix); math.AABB3ToOBB3(tempAABB, this._obb); } } if (cfg.colors) { const colors = cfg.colors.constructor === Float32Array ? cfg.colors : new Float32Array(cfg.colors); state.colorsBuf = new ArrayBuf(gl, gl.ARRAY_BUFFER, colors, colors.length, 4, gl.STATIC_DRAW); memoryStats.colors += state.colorsBuf.numItems; } if (cfg.uv) { const bounds = geometryCompressionUtils.getUVBounds(cfg.uv); const result = geometryCompressionUtils.compressUVs(cfg.uv, bounds.min, bounds.max); const uv = result.quantized; state.uvDecodeMatrix = result.decodeMatrix; state.uvBuf = new ArrayBuf(gl, gl.ARRAY_BUFFER, uv, uv.length, 2, gl.STATIC_DRAW); memoryStats.uvs += state.uvBuf.numItems; } if (cfg.normals) { const normals = geometryCompressionUtils.compressNormals(cfg.normals); let normalized = state.compressGeometry; state.normalsBuf = new ArrayBuf(gl, gl.ARRAY_BUFFER, normals, normals.length, 3, gl.STATIC_DRAW, normalized); memoryStats.normals += state.normalsBuf.numItems; } { const indices = (cfg.indices.constructor === Uint32Array || cfg.indices.constructor === Uint16Array) ? cfg.indices : new Uint32Array(cfg.indices); state.indicesBuf = new ArrayBuf(gl, gl.ELEMENT_ARRAY_BUFFER, indices, indices.length, 1, gl.STATIC_DRAW); memoryStats.indices += state.indicesBuf.numItems; const edgeIndices = buildEdgeIndices(positions, indices, state.positionsDecodeMatrix, this._edgeThreshold); this._edgeIndicesBuf = new ArrayBuf(gl, gl.ELEMENT_ARRAY_BUFFER, edgeIndices, edgeIndices.length, 1, gl.STATIC_DRAW); if (this._state.primitiveName === "triangles") { this._numTriangles = (cfg.indices.length / 3); } } this._buildHash(); memoryStats.meshes++; } _buildHash() { const state = this._state; const hash = ["/g"]; hash.push("/" + state.primitive + ";"); if (state.positionsBuf) { hash.push("p"); } if (state.colorsBuf) { hash.push("c"); } if (state.normalsBuf || state.autoVertexNormals) { hash.push("n"); } if (state.uvBuf) { hash.push("u"); } hash.push("cp"); // Always compressed hash.push(";"); state.hash = hash.join(""); } _getEdgeIndices() { return this._edgeIndicesBuf; } /** * Gets the primitive type. * * Possible types are: 'points', 'lines', 'line-loop', 'line-strip', 'triangles', 'triangle-strip' and 'triangle-fan'. * * @type {String} */ get primitive() { return this._state.primitiveName; } /** * Gets the local-space axis-aligned 3D boundary (AABB). * * The AABB is represented by a six-element Float64Array containing the min/max extents of the axis-aligned volume, ie. ````[xmin, ymin,zmin,xmax,ymax, zmax]````. * * @type {Number[]} */ get aabb() { return this._aabb; } /** * Gets the local-space oriented 3D boundary (OBB). * * The OBB is represented by a 32-element Float64Array containing the eight vertices of the box, where each vertex is a homogeneous coordinate having [x,y,z,w] elements. * * @type {Number[]} */ get obb() { return this._obb; } /** * Approximate number of triangles in this VBOGeometry. * * Will be zero if {@link VBOGeometry#primitive} is not 'triangles', 'triangle-strip' or 'triangle-fan'. * * @type {Number} */ get numTriangles() { return this._numTriangles; } /** @private */ _getState() { return this._state; } /** * Destroys this component. */ destroy() { super.destroy(); const state = this._state; if (state.indicesBuf) { state.indicesBuf.destroy(); } if (state.positionsBuf) { state.positionsBuf.destroy(); } if (state.normalsBuf) { state.normalsBuf.destroy(); } if (state.uvBuf) { state.uvBuf.destroy(); } if (state.colorsBuf) { state.colorsBuf.destroy(); } if (this._edgeIndicesBuf) { this._edgeIndicesBuf.destroy(); } state.destroy(); memoryStats.meshes--; } } export {VBOGeometry};