UNPKG

@vrame/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

518 lines (490 loc) 23.8 kB
import {BindableDataTexture} from "./../../BindableDataTexture.js"; import {dataTextureRamStats} from "./dataTextureRamStats.js"; /** * @private */ export class DTXTrianglesTextureFactory { constructor() { } /** * Enables the currently binded ````WebGLTexture```` to be used as a data texture. * * @param {WebGL2RenderingContext} gl * * @private */ disableBindedTextureFiltering(gl) { gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); } /** * This will generate an RGBA texture for: * - colors * - pickColors * - flags * - flags2 * - vertex bases * - vertex base offsets * * The texture will have: * - 4 RGBA columns per row: for each object (pick) color and flags(2) * - N rows where N is the number of objects * * @param {WebGL2RenderingContext} gl * @param {ArrayLike<ArrayLike<int>>} colors Array of colors for all objects in the layer * @param {ArrayLike<ArrayLike<int>>} pickColors Array of pickColors for all objects in the layer * @param {ArrayLike<int>} vertexBases Array of position-index-bases foteh all objects in the layer * @param {ArrayLike<int>} indexBaseOffsets For triangles: array of offests between the (gl_VertexID / 3) and the position where the indices start in the texture layer * @param {ArrayLike<int>} edgeIndexBaseOffsets For edges: Array of offests between the (gl_VertexID / 2) and the position where the edge indices start in the texture layer * @param {ArrayLike<boolean>} solid Array is-solid flag for all objects in the layer * * @returns {BindableDataTexture} */ createTextureForColorsAndFlags(gl, colors, pickColors, vertexBases, indexBaseOffsets, edgeIndexBaseOffsets, solid) { const numPortions = colors.length; // The number of rows in the texture is the number of // objects in the layer. this.numPortions = numPortions; const textureWidth = 512 * 8; const textureHeight = Math.ceil(numPortions / (textureWidth / 8)); if (textureHeight === 0) { throw "texture height===0"; } // 8 columns per texture row: // - col0: (RGBA) object color RGBA // - col1: (packed Uint32 as RGBA) object pick color // - col2: (packed 4 bytes as RGBA) object flags // - col3: (packed 4 bytes as RGBA) object flags2 // - col4: (packed Uint32 bytes as RGBA) vertex base // - col5: (packed Uint32 bytes as RGBA) index base offset // - col6: (packed Uint32 bytes as RGBA) edge index base offset // - col7: (packed 4 bytes as RGBA) is-solid flag for objects const texArray = new Uint8Array(4 * textureWidth * textureHeight); dataTextureRamStats.sizeDataColorsAndFlags += texArray.byteLength; dataTextureRamStats.numberOfTextures++; for (let i = 0; i < numPortions; i++) { // object color texArray.set(colors [i], i * 32 + 0); texArray.set(pickColors [i], i * 32 + 4); // object pick color texArray.set([0, 0, 0, 0], i * 32 + 8); // object flags texArray.set([0, 0, 0, 0], i * 32 + 12); // object flags2 // vertex base texArray.set([ (vertexBases[i] >> 24) & 255, (vertexBases[i] >> 16) & 255, (vertexBases[i] >> 8) & 255, (vertexBases[i]) & 255, ], i * 32 + 16 ); // triangles index base offset texArray.set( [ (indexBaseOffsets[i] >> 24) & 255, (indexBaseOffsets[i] >> 16) & 255, (indexBaseOffsets[i] >> 8) & 255, (indexBaseOffsets[i]) & 255, ], i * 32 + 20 ); // edge index base offset texArray.set( [ (edgeIndexBaseOffsets[i] >> 24) & 255, (edgeIndexBaseOffsets[i] >> 16) & 255, (edgeIndexBaseOffsets[i] >> 8) & 255, (edgeIndexBaseOffsets[i]) & 255, ], i * 32 + 24 ); // is-solid flag texArray.set([solid[i] ? 1 : 0, 0, 0, 0], i * 32 + 28); } const texture = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, texture); gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGBA8UI, textureWidth, textureHeight); gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, textureWidth, textureHeight, gl.RGBA_INTEGER, gl.UNSIGNED_BYTE, texArray, 0); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); gl.bindTexture(gl.TEXTURE_2D, null); return new BindableDataTexture(gl, texture, textureWidth, textureHeight, texArray); } /** * This will generate a texture for all object offsets. * * @param {WebGL2RenderingContext} gl * @param {int[]} offsets Array of int[3], one XYZ offset array for each object * * @returns {BindableDataTexture} */ createTextureForObjectOffsets(gl, numOffsets) { const textureWidth = 512; const textureHeight = Math.ceil(numOffsets / textureWidth); if (textureHeight === 0) { throw "texture height===0"; } const texArray = new Float32Array(3 * textureWidth * textureHeight).fill(0); dataTextureRamStats.sizeDataTextureOffsets += texArray.byteLength; dataTextureRamStats.numberOfTextures++; const texture = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, texture); gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGB32F, textureWidth, textureHeight); gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, textureWidth, textureHeight, gl.RGB, gl.FLOAT, texArray, 0); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); gl.bindTexture(gl.TEXTURE_2D, null); return new BindableDataTexture(gl, texture, textureWidth, textureHeight, texArray); } /** * This will generate a texture for all positions decode matrices in the layer. * * The texture will have: * - 4 RGBA columns per row (each column will contain 4 packed half-float (16 bits) components). * Thus, each row will contain 16 packed half-floats corresponding to a complete positions decode matrix) * - N rows where N is the number of objects * * @param {WebGL2RenderingContext} gl * @param {ArrayLike<Matrix4x4>} instanceMatrices Array of geometry instancing matrices for all objects in the layer. Null if the objects are not instanced. * * @returns {BindableDataTexture} */ createTextureForInstancingMatrices(gl, instanceMatrices) { const numMatrices = instanceMatrices.length; if (numMatrices === 0) { throw "num instance matrices===0"; } // in one row we can fit 512 matrices const textureWidth = 512 * 4; const textureHeight = Math.ceil(numMatrices / (textureWidth / 4)); const texArray = new Float32Array(4 * textureWidth * textureHeight); // dataTextureRamStats.sizeDataPositionDecodeMatrices += texArray.byteLength; dataTextureRamStats.numberOfTextures++; for (let i = 0; i < instanceMatrices.length; i++) { // 4x4 values texArray.set(instanceMatrices[i], i * 16); } const texture = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, texture); gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGBA32F, textureWidth, textureHeight); gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, textureWidth, textureHeight, gl.RGBA, gl.FLOAT, texArray, 0); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); gl.bindTexture(gl.TEXTURE_2D, null); return new BindableDataTexture(gl, texture, textureWidth, textureHeight, texArray); } /** * This will generate a texture for all positions decode matrices in the layer. * * The texture will have: * - 4 RGBA columns per row (each column will contain 4 packed half-float (16 bits) components). * Thus, each row will contain 16 packed half-floats corresponding to a complete positions decode matrix) * - N rows where N is the number of objects * * @param {WebGL2RenderingContext} gl * @param {ArrayLike<Matrix4x4>} positionDecodeMatrices Array of positions decode matrices for all objects in the layer * * @returns {BindableDataTexture} */ createTextureForPositionsDecodeMatrices(gl, positionDecodeMatrices) { const numMatrices = positionDecodeMatrices.length; if (numMatrices === 0) { throw "num decode+entity matrices===0"; } // in one row we can fit 512 matrices const textureWidth = 512 * 4; const textureHeight = Math.ceil(numMatrices / (textureWidth / 4)); const texArray = new Float32Array(4 * textureWidth * textureHeight); dataTextureRamStats.sizeDataPositionDecodeMatrices += texArray.byteLength; dataTextureRamStats.numberOfTextures++; for (let i = 0; i < positionDecodeMatrices.length; i++) { // 4x4 values texArray.set(positionDecodeMatrices[i], i * 16); } const texture = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, texture); gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGBA32F, textureWidth, textureHeight); gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, textureWidth, textureHeight, gl.RGBA, gl.FLOAT, texArray, 0); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); gl.bindTexture(gl.TEXTURE_2D, null); return new BindableDataTexture(gl, texture, textureWidth, textureHeight); } /** * @param {WebGL2RenderingContext} gl * @param indicesArrays * @param lenIndices * * @returns {BindableDataTexture} */ createTextureFor8BitIndices(gl, indicesArrays, lenIndices) { if (lenIndices === 0) { return {texture: null, textureHeight: 0,}; } const textureWidth = 4096; const textureHeight = Math.ceil(lenIndices / 3 / textureWidth); if (textureHeight === 0) { throw "texture height===0"; } const texArraySize = textureWidth * textureHeight * 3; const texArray = new Uint8Array(texArraySize); dataTextureRamStats.sizeDataTextureIndices += texArray.byteLength; dataTextureRamStats.numberOfTextures++; for (let i = 0, j = 0, len = indicesArrays.length; i < len; i++) { const pc = indicesArrays[i]; texArray.set(pc, j); j += pc.length; } const texture = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, texture); gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGB8UI, textureWidth, textureHeight); gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, textureWidth, textureHeight, gl.RGB_INTEGER, gl.UNSIGNED_BYTE, texArray, 0); this.disableBindedTextureFiltering(gl); gl.bindTexture(gl.TEXTURE_2D, null); return new BindableDataTexture(gl, texture, textureWidth, textureHeight); } /** * @param {WebGL2RenderingContext} gl * @param indicesArrays * @param lenIndices * * @returns {BindableDataTexture} */ createTextureFor16BitIndices(gl, indicesArrays, lenIndices) { if (lenIndices === 0) { return {texture: null, textureHeight: 0,}; } const textureWidth = 4096; const textureHeight = Math.ceil(lenIndices / 3 / textureWidth); if (textureHeight === 0) { throw "texture height===0"; } const texArraySize = textureWidth * textureHeight * 3; const texArray = new Uint16Array(texArraySize); dataTextureRamStats.sizeDataTextureIndices += texArray.byteLength; dataTextureRamStats.numberOfTextures++; for (let i = 0, j = 0, len = indicesArrays.length; i < len; i++) { const pc = indicesArrays[i]; texArray.set(pc, j); j += pc.length; } const texture = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, texture); gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGB16UI, textureWidth, textureHeight); gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, textureWidth, textureHeight, gl.RGB_INTEGER, gl.UNSIGNED_SHORT, texArray, 0); this.disableBindedTextureFiltering(gl); gl.bindTexture(gl.TEXTURE_2D, null); return new BindableDataTexture(gl, texture, textureWidth, textureHeight); } /** * @param {WebGL2RenderingContext} gl * @param indicesArrays * @param lenIndices * * @returns {BindableDataTexture} */ createTextureFor32BitIndices(gl, indicesArrays, lenIndices) { if (lenIndices === 0) { return {texture: null, textureHeight: 0,}; } const textureWidth = 4096; const textureHeight = Math.ceil(lenIndices / 3 / textureWidth); if (textureHeight === 0) { throw "texture height===0"; } const texArraySize = textureWidth * textureHeight * 3; const texArray = new Uint32Array(texArraySize); dataTextureRamStats.sizeDataTextureIndices += texArray.byteLength; dataTextureRamStats.numberOfTextures++; for (let i = 0, j = 0, len = indicesArrays.length; i < len; i++) { const pc = indicesArrays[i]; texArray.set(pc, j); j += pc.length; } const texture = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, texture); gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGB32UI, textureWidth, textureHeight); gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, textureWidth, textureHeight, gl.RGB_INTEGER, gl.UNSIGNED_INT, texArray, 0); this.disableBindedTextureFiltering(gl); gl.bindTexture(gl.TEXTURE_2D, null); return new BindableDataTexture(gl, texture, textureWidth, textureHeight); } /** * @param {WebGL2RenderingContext} gl * @param indicesArrays * @param lenIndices * * @returns {BindableDataTexture} */ createTextureFor8BitsEdgeIndices(gl, indicesArrays, lenIndices) { if (lenIndices === 0) { return {texture: null, textureHeight: 0,}; } const textureWidth = 4096; const textureHeight = Math.ceil(lenIndices / 2 / textureWidth); if (textureHeight === 0) { throw "texture height===0"; } const texArraySize = textureWidth * textureHeight * 2; const texArray = new Uint8Array(texArraySize); dataTextureRamStats.sizeDataTextureEdgeIndices += texArray.byteLength; dataTextureRamStats.numberOfTextures++; for (let i = 0, j = 0, len = indicesArrays.length; i < len; i++) { const pc = indicesArrays[i]; texArray.set(pc, j); j += pc.length; } const texture = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, texture); gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RG8UI, textureWidth, textureHeight); gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, textureWidth, textureHeight, gl.RG_INTEGER, gl.UNSIGNED_BYTE, texArray, 0); this.disableBindedTextureFiltering(gl); gl.bindTexture(gl.TEXTURE_2D, null); return new BindableDataTexture(gl, texture, textureWidth, textureHeight); } /** * @param {WebGL2RenderingContext} gl * @param indicesArrays * @param lenIndices * * @returns {BindableDataTexture} */ createTextureFor16BitsEdgeIndices(gl, indicesArrays, lenIndices) { if (lenIndices === 0) { return {texture: null, textureHeight: 0,}; } const textureWidth = 4096; const textureHeight = Math.ceil(lenIndices / 2 / textureWidth); if (textureHeight === 0) { throw "texture height===0"; } const texArraySize = textureWidth * textureHeight * 2; const texArray = new Uint16Array(texArraySize); dataTextureRamStats.sizeDataTextureEdgeIndices += texArray.byteLength; dataTextureRamStats.numberOfTextures++; for (let i = 0, j = 0, len = indicesArrays.length; i < len; i++) { const pc = indicesArrays[i]; texArray.set(pc, j); j += pc.length; } const texture = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, texture); gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RG16UI, textureWidth, textureHeight); gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, textureWidth, textureHeight, gl.RG_INTEGER, gl.UNSIGNED_SHORT, texArray, 0); this.disableBindedTextureFiltering(gl); gl.bindTexture(gl.TEXTURE_2D, null); return new BindableDataTexture(gl, texture, textureWidth, textureHeight); } /** * @param {WebGL2RenderingContext} gl * @param indicesArrays * @param lenIndices * * @returns {BindableDataTexture} */ createTextureFor32BitsEdgeIndices(gl, indicesArrays, lenIndices) { if (lenIndices === 0) { return {texture: null, textureHeight: 0,}; } const textureWidth = 4096; const textureHeight = Math.ceil(lenIndices / 2 / textureWidth); if (textureHeight === 0) { throw "texture height===0"; } const texArraySize = textureWidth * textureHeight * 2; const texArray = new Uint32Array(texArraySize); dataTextureRamStats.sizeDataTextureEdgeIndices += texArray.byteLength; dataTextureRamStats.numberOfTextures++; for (let i = 0, j = 0, len = indicesArrays.length; i < len; i++) { const pc = indicesArrays[i]; texArray.set(pc, j); j += pc.length; } const texture = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, texture); gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RG32UI, textureWidth, textureHeight); gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, textureWidth, textureHeight, gl.RG_INTEGER, gl.UNSIGNED_INT, texArray, 0); this.disableBindedTextureFiltering(gl); gl.bindTexture(gl.TEXTURE_2D, null); return new BindableDataTexture(gl, texture, textureWidth, textureHeight); } /** * @param {WebGL2RenderingContext} gl * @param {ArrayLike<int>} positionsArrays Arrays of quantized positions in the layer * @param lenPositions * * This will generate a texture for positions in the layer. * * The texture will have: * - 1024 columns, where each pixel will be a 16-bit-per-component RGB texture, corresponding to the XYZ of the position * - a number of rows R where R*1024 is just >= than the number of vertices (positions / 3) * * @returns {BindableDataTexture} */ createTextureForPositions(gl, positionsArrays, lenPositions) { const numVertices = lenPositions / 3; const textureWidth = 4096; const textureHeight = Math.ceil(numVertices / textureWidth); if (textureHeight === 0) { throw "texture height===0"; } const texArraySize = textureWidth * textureHeight * 3; const texArray = new Uint16Array(texArraySize); dataTextureRamStats.sizeDataTexturePositions += texArray.byteLength; dataTextureRamStats.numberOfTextures++; for (let i = 0, j = 0, len = positionsArrays.length; i < len; i++) { const pc = positionsArrays[i]; texArray.set(pc, j); j += pc.length; } const texture = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, texture); gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGB16UI, textureWidth, textureHeight); gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, textureWidth, textureHeight, gl.RGB_INTEGER, gl.UNSIGNED_SHORT, texArray, 0); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); gl.bindTexture(gl.TEXTURE_2D, null); return new BindableDataTexture(gl, texture, textureWidth, textureHeight); } /** * @param {WebGL2RenderingContext} gl * @param {ArrayLike<int>} portionIdsArray * * @returns {BindableDataTexture} */ createTextureForPackedPortionIds(gl, portionIdsArray) { if (portionIdsArray.length === 0) { return {texture: null, textureHeight: 0,}; } const lenArray = portionIdsArray.length; const textureWidth = 4096; const textureHeight = Math.ceil(lenArray / textureWidth); if (textureHeight === 0) { throw "texture height===0"; } const texArraySize = textureWidth * textureHeight; const texArray = new Uint16Array(texArraySize); texArray.set(portionIdsArray, 0); dataTextureRamStats.sizeDataTexturePortionIds += texArray.byteLength; dataTextureRamStats.numberOfTextures++; const texture = gl.createTexture(); gl.bindTexture(gl.TEXTURE_2D, texture); gl.texStorage2D(gl.TEXTURE_2D, 1, gl.R16UI, textureWidth, textureHeight); gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, textureWidth, textureHeight, gl.RED_INTEGER, gl.UNSIGNED_SHORT, texArray, 0); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); gl.bindTexture(gl.TEXTURE_2D, null); return new BindableDataTexture(gl, texture, textureWidth, textureHeight); } }