Created
July 27, 2023 00:32
-
-
Save xeolabs/742f40608fc747903ad5a2cb6390c523 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {ENTITY_FLAGS} from '../../ENTITY_FLAGS.js'; | |
import {RENDER_PASSES} from '../../RENDER_PASSES.js'; | |
import {math} from "../../../math/math.js"; | |
import {RenderState} from "../../../webgl/RenderState.js"; | |
import {geometryCompressionUtils} from "../../../math/geometryCompressionUtils.js"; | |
import {getDataTextureRenderers} from "./TrianglesDataTextureRenderers.js"; | |
import {TrianglesDataTextureBuffer} from "./TrianglesDataTextureBuffer.js"; | |
import {DataTextureState} from "./DataTextureState.js" | |
import {DataTextureGenerator} from "./DataTextureGenerator"; | |
import {dataTextureRamStats} from "./dataTextureRamStats"; | |
/** | |
* 12-bits allowed for object ids. | |
* | |
* Limits the per-object texture height in the layer. | |
*/ | |
const MAX_NUMBER_OF_OBJECTS_IN_LAYER = (1 << 16); | |
/** | |
* 4096 is max data texture height | |
* | |
* Limits the aggregated geometry texture height in the layer. | |
*/ | |
const MAX_DATA_TEXTURE_HEIGHT = (1 << 12); | |
/** | |
* Align `indices` and `edgeIndices` memory layout to 8 elements. | |
* | |
* Used as an optimization for the `...portionIds...` texture, so it | |
* can just be stored 1 out of 8 `portionIds` corresponding to a given | |
* `triangle-index` or `edge-index`. | |
*/ | |
const INDICES_EDGE_INDICES_ALIGNEMENT_SIZE = 8; | |
/** | |
* Number of maximum allowed per-object flags update per render frame | |
* before switching to batch update mode. | |
*/ | |
const MAX_OBJECT_UPDATES_IN_FRAME_WITHOUT_BATCHED_UPDATE = 10; | |
const tempVec4a = math.vec4([0, 0, 0, 1]); | |
const tempVec4b = math.vec4([0, 0, 0, 1]); | |
const tempVec4c = math.vec4([0, 0, 0, 1]); | |
const tempUint8Array4 = new Uint8Array(4); | |
const tempFloat32Array3 = new Float32Array(3); | |
let _numberOfLayers = 0; | |
/** | |
* @private | |
*/ | |
export class TrianglesDataTextureLayer { | |
constructor(model, cfg) { | |
this._layerNumber = _numberOfLayers++; | |
dataTextureRamStats.numberOfLayers++; | |
/** | |
* State sorting key. | |
* @type {string} | |
*/ | |
this.sortId = `TrianglesDataTextureLayer-${this._layerNumber}`; | |
/** | |
* Index of this TrianglesDataTextureLayer in {@link SceneModel#_layerList}. | |
* @type {Number} | |
*/ | |
this.layerIndex = cfg.layerIndex; | |
this._dataTextureRenderers = getDataTextureRenderers(model.scene); | |
this.model = model; | |
this._buffer = new TrianglesDataTextureBuffer(); | |
/** | |
* @type {DataTextureState} | |
*/ | |
this._dataTextureState = new DataTextureState(); | |
/** | |
* @type {DataTextureGenerator} | |
*/ | |
this.dataTextureGenerator = new DataTextureGenerator(); | |
this._state = new RenderState({ | |
origin: math.vec3(cfg.origin), | |
metallicRoughnessBuf: null, | |
positionsDecodeMatrix: math.mat4(), | |
textureState: this._dataTextureState, | |
numIndices8Bits: 0, | |
numIndices16Bits: 0, | |
numIndices32Bits: 0, | |
numEdgeIndices8Bits: 0, | |
numEdgeIndices16Bits: 0, | |
numEdgeIndices32Bits: 0, | |
numVertices: 0, | |
}); | |
// These counts are used to avoid unnecessary render passes | |
this._numPortions = 0; | |
this._numVisibleLayerPortions = 0; | |
this._numTransparentLayerPortions = 0; | |
this._numXRayedLayerPortions = 0; | |
this._numSelectedLayerPortions = 0; | |
this._numHighlightedLayerPortions = 0; | |
this._numClippableLayerPortions = 0; | |
this._numEdgesLayerPortions = 0; | |
this._numPickableLayerPortions = 0; | |
this._numCulledLayerPortions = 0; | |
this._modelAABB = math.collapseAABB3(); // Model-space AABB | |
this._portions = []; | |
this._finalized = false; | |
/** | |
* Due to `index rebucketting` process in ```prepareMeshGeometry``` function, it's possible that a single | |
* portion is expanded to more than 1 real sub-portion. | |
* | |
* This Array tracks the mapping between: | |
* | |
* - external `portionIds` as seen by consumers of this class. | |
* - internal `sub-portionIds` actually managed by this class. | |
* | |
* The outer index of this array is the externally seen `portionId`. | |
* The inner value of the array, are `sub-portionIds` corresponding to the `portionId`. | |
* | |
* @type {Array<Array<int>>} | |
* @private | |
*/ | |
this._bucketPortionIdMapping = []; | |
this._bucketPortionGeometries = {}; | |
/** | |
* The axis-aligned World-space boundary of this TrianglesDataTextureLayer's positions. | |
* @type {*|Float64Array} | |
*/ | |
this.aabb = math.collapseAABB3(); | |
/** | |
* The number of updates in the current frame; | |
* @type {number} | |
*/ | |
this.numUpdatesInFrame = 0; | |
} | |
/** | |
* Returns wheter the ```TrianglesDataTextureLayer``` has room for more portions. | |
* | |
* @param {object} cfg An object containing the geometrical data (`positions`, `indices`, `edgeIndices`) for the portion. | |
* @returns {Boolean} Wheter the requested portion can be created | |
*/ | |
canCreatePortion(cfg) { | |
if (this._finalized) { | |
throw "Already finalized"; | |
} | |
const state = this._state; | |
const numNewPortions = cfg.buckets.length; | |
if ((this._numPortions + numNewPortions) > MAX_NUMBER_OF_OBJECTS_IN_LAYER) { | |
dataTextureRamStats.cannotCreatePortion.because10BitsObjectId++; | |
} | |
let retVal = (this._numPortions + numNewPortions) <= MAX_NUMBER_OF_OBJECTS_IN_LAYER; | |
const alreadyHasPortionGeometry = cfg.geometryId && (cfg.geometryId + "#0") in this._bucketPortionGeometries; | |
if (!alreadyHasPortionGeometry) { | |
const maxIndicesOfAnyBits = Math.max(state.numIndices8Bits, state.numIndices16Bits, state.numIndices32Bits,); | |
let numVertices = 0; | |
let numIndices = 0; | |
cfg.buckets.forEach(bucket => { | |
numVertices += bucket.positionsCompressed.length / 3; | |
numIndices += bucket.indices.length / 3; | |
}); | |
if ((state.numVertices + numVertices) > MAX_DATA_TEXTURE_HEIGHT * 4096 || | |
(maxIndicesOfAnyBits + numIndices) > MAX_DATA_TEXTURE_HEIGHT * 4096) { | |
dataTextureRamStats.cannotCreatePortion.becauseTextureSize++; | |
} | |
retVal &&= | |
(state.numVertices + numVertices) <= MAX_DATA_TEXTURE_HEIGHT * 4096 && | |
(maxIndicesOfAnyBits + numIndices) <= MAX_DATA_TEXTURE_HEIGHT * 4096; | |
} | |
return retVal; | |
} | |
/** | |
* Creates a new portion within this TrianglesDataTextureLayer, returns the new portion ID. | |
* | |
* Gives the portion the specified geometry, color and matrix. | |
* | |
* @param cfg.positions Flat float Local-space positions array. | |
* @param [cfg.normals] Flat float normals array. | |
* @param [cfg.colors] Flat float colors array. | |
* @param cfg.indices Flat int indices array. | |
* @param [cfg.edgeIndices] Flat int edges indices array. | |
* @param cfg.color Quantized RGB color [0..255,0..255,0..255,0..255] | |
* @param cfg.metallic Metalness factor [0..255] | |
* @param cfg.roughness Roughness factor [0..255] | |
* @param cfg.opacity Opacity [0..255] | |
* @param [cfg.meshMatrix] Flat float 4x4 matrix | |
* @param [cfg.worldMatrix] Flat float 4x4 matrix | |
* @param cfg.portionAABB Flat float AABB World-space AABB | |
* @param cfg.pickColor Quantized pick color | |
* @returns {number} Portion ID | |
*/ | |
createPortion(cfg) { | |
if (this._finalized) { | |
throw "Already finalized"; | |
} | |
const portionId = this._bucketPortionIdMapping.length; | |
const portionIdFanout = []; | |
this._bucketPortionIdMapping.push(portionIdFanout); | |
const portionAABB = cfg.portionAABB; | |
cfg.buckets.forEach((bucket, bucketIndex) => { | |
const key = `${cfg.id}#${bucketIndex}`; | |
let bucketPortionGeometry = this._bucketPortionGeometries[key]; | |
if (!bucketPortionGeometry) { | |
bucketPortionGeometry = this._createBucketPortionGeometry(bucket); | |
this._bucketPortionGeometries[key] = bucketPortionGeometry; | |
} | |
const bucketPortionAABB = math.collapseAABB3(); | |
const bucketPortionId = this._createBucketPortion( | |
cfg, | |
bucketPortionGeometry, | |
bucket, | |
// math.addVec3( instancing ? objectCfg.origin : cfg.origin, this._state.origin, math.vec3()), | |
bucketPortionAABB | |
); | |
math.expandAABB3(portionAABB, bucketPortionAABB); | |
portionIdFanout.push(bucketPortionId); | |
}); | |
this.model.numPortions++; | |
return portionId; | |
} | |
_createBucketPortionGeometry(bucket) { | |
// Indices alignement | |
// This will make every mesh consume a multiple of INDICES_EDGE_INDICES_ALIGNEMENT_SIZE | |
// array items for storing the triangles of the mesh, and it supports: | |
// - a memory optimization of factor INDICES_EDGE_INDICES_ALIGNEMENT_SIZE | |
// - in exchange for a small RAM overhead | |
// (by adding some padding until a size that is multiple of INDICES_EDGE_INDICES_ALIGNEMENT_SIZE) | |
if (bucket.indices) { | |
const alignedIndicesLen = Math.ceil((bucket.indices.length / 3) / INDICES_EDGE_INDICES_ALIGNEMENT_SIZE) * INDICES_EDGE_INDICES_ALIGNEMENT_SIZE * 3; | |
dataTextureRamStats.overheadSizeAlignementIndices += 2 * (alignedIndicesLen - bucket.indices.length); | |
const alignedIndices = new Uint32Array(alignedIndicesLen); | |
alignedIndices.fill(0); | |
alignedIndices.set(bucket.indices); | |
bucket.indices = alignedIndices; | |
} | |
// EdgeIndices alignement | |
// This will make every mesh consume a multiple of INDICES_EDGE_INDICES_ALIGNEMENT_SIZE | |
// array items for storing the edges of the mesh, and it supports: | |
// - a memory optimization of factor INDICES_EDGE_INDICES_ALIGNEMENT_SIZE | |
// - in exchange for a small RAM overhead | |
// (by adding some padding until a size that is multiple of INDICES_EDGE_INDICES_ALIGNEMENT_SIZE) | |
if (bucket.edgeIndices) { | |
const alignedEdgeIndicesLen = Math.ceil((bucket.edgeIndices.length / 2) / INDICES_EDGE_INDICES_ALIGNEMENT_SIZE) * INDICES_EDGE_INDICES_ALIGNEMENT_SIZE * 2; | |
dataTextureRamStats.overheadSizeAlignementEdgeIndices += 2 * (alignedEdgeIndicesLen - bucket.edgeIndices.length); | |
const alignedEdgeIndices = new Uint32Array(alignedEdgeIndicesLen); | |
alignedEdgeIndices.fill(0); | |
alignedEdgeIndices.set(bucket.edgeIndices); | |
bucket.edgeIndices = alignedEdgeIndices; | |
} | |
const positionsCompressed = bucket.positionsCompressed; | |
const indices = bucket.indices; | |
const edgeIndices = bucket.edgeIndices; | |
const buffer = this._buffer; | |
const vertexBase = buffer.positions.length / 3; | |
const numVertices = positionsCompressed.length / 3; | |
for (let i = 0, len = positionsCompressed.length; i < len; i++) { | |
buffer.positions.push(positionsCompressed[i]); | |
} | |
let indicesBase; | |
let numTriangles = 0; | |
if (indices) { | |
numTriangles = indices.length / 3; | |
let indicesBuffer; | |
if (numVertices <= (1 << 8)) { | |
indicesBuffer = buffer.indices8Bits; | |
} else if (numVertices <= (1 << 16)) { | |
indicesBuffer = buffer.indices16Bits; | |
} else { | |
indicesBuffer = buffer.indices32Bits; | |
} | |
indicesBase = indicesBuffer.length / 3; | |
for (let i = 0, len = indices.length; i < len; i++) { | |
indicesBuffer.push(indices[i]); | |
} | |
} | |
let edgeIndicesBase; | |
let numEdges = 0; | |
if (edgeIndices) { | |
numEdges = edgeIndices.length / 2; | |
let edgeIndicesBuffer; | |
if (numVertices <= (1 << 8)) { | |
edgeIndicesBuffer = buffer.edgeIndices8Bits; | |
} else if (numVertices <= (1 << 16)) { | |
edgeIndicesBuffer = buffer.edgeIndices16Bits; | |
} else { | |
edgeIndicesBuffer = buffer.edgeIndices32Bits; | |
} | |
edgeIndicesBase = edgeIndicesBuffer.length / 2; | |
for (let i = 0, len = edgeIndices.length; i < len; i++) { | |
edgeIndicesBuffer.push(edgeIndices[i]); | |
} | |
} | |
this._state.numVertices += numVertices; | |
dataTextureRamStats.numberOfGeometries++; | |
return { | |
vertexBase, | |
numVertices, | |
numTriangles, | |
numEdges, | |
indicesBase, | |
edgeIndicesBase | |
}; | |
} | |
_createBucketPortion(cfg, bucketPortionGeometry, bucket, bucketPortionAABB) { | |
const color = cfg.color; | |
const metallic = cfg.metallic; | |
const roughness = cfg.roughness; | |
const colors = cfg.colors; | |
const opacity = cfg.opacity; | |
const meshMatrix = cfg.meshMatrix; | |
const worldMatrix = cfg.worldMatrix; | |
const pickColor = cfg.pickColor; | |
const scene = this.model.scene; | |
const buffer = this._buffer; | |
const state = this._state; | |
buffer.perObjectPositionsDecodeMatrices.push(cfg.positionsDecodeMatrix); | |
buffer.perObjectInstancePositioningMatrices.push(meshMatrix); | |
// const positions = cfg.positions; | |
// const positionsIndex = buffer.positions.length; | |
// const vertsIndex = positionsIndex / 3; | |
// Expand the world AABB with the concrete location of the object | |
const localAABB = math.collapseAABB3(); | |
math.expandAABB3Points3(localAABB, bucket.positionsCompressed); | |
geometryCompressionUtils.decompressAABB(localAABB, cfg.positionsDecodeMatrix); | |
const geometryOBB = math.AABB3ToOBB3(localAABB); | |
for (let i = 0, len = geometryOBB.length; i < len; i += 4) { | |
tempVec4a[0] = geometryOBB[i + 0]; | |
tempVec4a[1] = geometryOBB[i + 1]; | |
tempVec4a[2] = geometryOBB[i + 2]; | |
math.transformPoint4(meshMatrix, tempVec4a, tempVec4b); | |
if (worldMatrix) { | |
math.transformPoint4(worldMatrix, tempVec4b, tempVec4c); | |
math.expandAABB3Point3(bucketPortionAABB, tempVec4c); | |
} else { | |
math.expandAABB3Point3(bucketPortionAABB, tempVec4b); | |
} | |
} | |
// Adjust the world AABB with the object `origin` | |
// if (this._state.origin) { | |
const origin = this._state.origin; | |
bucketPortionAABB[0] += origin[0]; | |
bucketPortionAABB[1] += origin[1]; | |
bucketPortionAABB[2] += origin[2]; | |
bucketPortionAABB[3] += origin[0]; | |
bucketPortionAABB[4] += origin[1]; | |
bucketPortionAABB[5] += origin[2]; | |
// } | |
math.expandAABB3(this.aabb, bucketPortionAABB); | |
buffer.perObjectSolid.push(cfg.solid); | |
if (colors) { | |
buffer.perObjectColors.push([colors[0] * 255, colors[1] * 255, colors[2] * 255, 255]); | |
} else if (color) { // Color is pre-quantized by SceneModel | |
buffer.perObjectColors.push([color[0], color[1], color[2], opacity]); | |
} | |
buffer.perObjectPickColors.push(pickColor); | |
buffer.perObjectVertexBases.push(bucketPortionGeometry.vertexBase); | |
{ | |
let currentNumIndices; | |
if (bucketPortionGeometry.numVertices <= (1 << 8)) { | |
currentNumIndices = state.numIndices8Bits; | |
} else if (bucketPortionGeometry.numVertices <= (1 << 16)) { | |
currentNumIndices = state.numIndices16Bits; | |
} else { | |
currentNumIndices = state.numIndices32Bits; | |
} | |
buffer.perObjectIndexBaseOffsets.push(currentNumIndices / 3 - bucketPortionGeometry.indicesBase); | |
} | |
{ | |
let currentNumEdgeIndices; | |
if (bucketPortionGeometry.numVertices <= (1 << 8)) { | |
currentNumEdgeIndices = state.numEdgeIndices8Bits; | |
} else if (bucketPortionGeometry.numVertices <= (1 << 16)) { | |
currentNumEdgeIndices = state.numEdgeIndices16Bits; | |
} else { | |
currentNumEdgeIndices = state.numEdgeIndices32Bits; | |
} | |
buffer.perObjectEdgeIndexBaseOffsets.push(currentNumEdgeIndices / 2 - bucketPortionGeometry.edgeIndicesBase); | |
} | |
const bucketPortionId = this._portions.length; | |
if (bucketPortionGeometry.numTriangles > 0) { | |
let numIndices = bucketPortionGeometry.numTriangles * 3; | |
let indicesPortionIdBuffer; | |
if (bucketPortionGeometry.numVertices <= (1 << 8)) { | |
indicesPortionIdBuffer = buffer.perTriangleNumberPortionId8Bits; | |
state.numIndices8Bits += numIndices; | |
dataTextureRamStats.totalPolygons8Bits += bucketPortionGeometry.numTriangles; | |
} else if (bucketPortionGeometry.numVertices <= (1 << 16)) { | |
indicesPortionIdBuffer = buffer.perTriangleNumberPortionId16Bits; | |
state.numIndices16Bits += numIndices; | |
dataTextureRamStats.totalPolygons16Bits += bucketPortionGeometry.numTriangles; | |
} else { | |
indicesPortionIdBuffer = buffer.perTriangleNumberPortionId32Bits; | |
state.numIndices32Bits += numIndices; | |
dataTextureRamStats.totalPolygons32Bits += bucketPortionGeometry.numTriangles; | |
} | |
dataTextureRamStats.totalPolygons += bucketPortionGeometry.numTriangles; | |
for (let i = 0; i < bucketPortionGeometry.numTriangles; i += INDICES_EDGE_INDICES_ALIGNEMENT_SIZE) { | |
indicesPortionIdBuffer.push(bucketPortionId); | |
} | |
} | |
if (bucketPortionGeometry.numEdges > 0) { | |
let numEdgeIndices = bucketPortionGeometry.numEdges * 2; | |
let edgeIndicesPortionIdBuffer; | |
if (bucketPortionGeometry.numVertices <= (1 << 8)) { | |
edgeIndicesPortionIdBuffer = buffer.perEdgeNumberPortionId8Bits; | |
state.numEdgeIndices8Bits += numEdgeIndices; | |
dataTextureRamStats.totalEdges8Bits += bucketPortionGeometry.numEdges; | |
} else if (bucketPortionGeometry.numVertices <= (1 << 16)) { | |
edgeIndicesPortionIdBuffer = buffer.perEdgeNumberPortionId16Bits; | |
state.numEdgeIndices16Bits += numEdgeIndices; | |
dataTextureRamStats.totalEdges16Bits += bucketPortionGeometry.numEdges; | |
} else { | |
edgeIndicesPortionIdBuffer = buffer.perEdgeNumberPortionId32Bits; | |
state.numEdgeIndices32Bits += numEdgeIndices; | |
dataTextureRamStats.totalEdges32Bits += bucketPortionGeometry.numEdges; | |
} | |
dataTextureRamStats.totalEdges += bucketPortionGeometry.numEdges; | |
for (let i = 0; i < bucketPortionGeometry.numEdges; i += INDICES_EDGE_INDICES_ALIGNEMENT_SIZE) { | |
edgeIndicesPortionIdBuffer.push(bucketPortionId); | |
} | |
} | |
buffer.perObjectOffsets.push([0, 0, 0]); | |
this._portions.push({ | |
// vertsBase: vertsIndex, | |
numVertices: bucketPortionGeometry.numTriangles | |
}); | |
this._numPortions++; | |
dataTextureRamStats.numberOfPortions++; | |
return bucketPortionId; | |
} | |
// updatePickCameratexture(pickViewMatrix, pickCameraMatrix) { | |
// this._dataTextureState.texturePickCameraMatrices.updateViewMatrix(pickViewMatrix, pickCameraMatrix); | |
// } | |
/** | |
* Builds data textures from the appended geometries and loads them into the GPU. | |
* | |
* No more portions can then be created. | |
*/ | |
finalize() { | |
if (this._finalized) { | |
this.model.error("Already finalized"); | |
return; | |
} | |
const state = this._state; | |
const textureState = this._dataTextureState; | |
const gl = this.model.scene.canvas.gl; | |
const buffer = this._buffer; | |
state.gl = gl; | |
textureState.texturePerObjectIdColorsAndFlags = this.dataTextureGenerator.generateTextureForColorsAndFlags( | |
gl, | |
buffer.perObjectColors, | |
buffer.perObjectPickColors, | |
buffer.perObjectVertexBases, | |
buffer.perObjectIndexBaseOffsets, | |
buffer.perObjectEdgeIndexBaseOffsets, | |
buffer.perObjectSolid | |
); | |
textureState.texturePerObjectIdOffsets = this.dataTextureGenerator.generateTextureForObjectOffsets( | |
gl, | |
this._numPortions | |
); | |
textureState.texturePerObjectIdPositionsDecodeMatrix = this.dataTextureGenerator.generateTextureForPositionsDecodeMatrices( | |
gl, | |
buffer.perObjectPositionsDecodeMatrices, | |
buffer.perObjectInstancePositioningMatrices | |
); | |
// position coordinates texture | |
textureState.texturePerVertexIdCoordinates = this.dataTextureGenerator.generateTextureForPositions( | |
gl, | |
buffer.positions | |
); | |
// portion Id triangles texture | |
textureState.texturePerPolygonIdPortionIds8Bits = this.dataTextureGenerator.generateTextureForPackedPortionIds( | |
gl, | |
buffer.perTriangleNumberPortionId8Bits | |
); | |
textureState.texturePerPolygonIdPortionIds16Bits = this.dataTextureGenerator.generateTextureForPackedPortionIds( | |
gl, | |
buffer.perTriangleNumberPortionId16Bits | |
); | |
textureState.texturePerPolygonIdPortionIds32Bits = this.dataTextureGenerator.generateTextureForPackedPortionIds( | |
gl, | |
buffer.perTriangleNumberPortionId32Bits | |
); | |
// portion Id texture for edges | |
if (buffer.perEdgeNumberPortionId8Bits.length > 0) { | |
textureState.texturePerEdgeIdPortionIds8Bits = this.dataTextureGenerator.generateTextureForPackedPortionIds( | |
gl, | |
buffer.perEdgeNumberPortionId8Bits | |
); | |
} | |
if (buffer.perEdgeNumberPortionId16Bits.length > 0) { | |
textureState.texturePerEdgeIdPortionIds16Bits = this.dataTextureGenerator.generateTextureForPackedPortionIds( | |
gl, | |
buffer.perEdgeNumberPortionId16Bits | |
); | |
} | |
if (buffer.perEdgeNumberPortionId32Bits.length > 0) { | |
textureState.texturePerEdgeIdPortionIds32Bits = this.dataTextureGenerator.generateTextureForPackedPortionIds( | |
gl, | |
buffer.perEdgeNumberPortionId32Bits | |
); | |
} | |
// indices texture | |
if (buffer.indices8Bits.length > 0) { | |
textureState.texturePerPolygonIdIndices8Bits = this.dataTextureGenerator.generateTextureFor8BitIndices( | |
gl, | |
buffer.indices8Bits | |
); | |
} | |
if (buffer.indices16Bits.length > 0) { | |
textureState.texturePerPolygonIdIndices16Bits = this.dataTextureGenerator.generateTextureFor16BitIndices( | |
gl, | |
buffer.indices16Bits | |
); | |
} | |
if (buffer.indices32Bits.length > 0) { | |
textureState.texturePerPolygonIdIndices32Bits = this.dataTextureGenerator.generateTextureFor32BitIndices( | |
gl, | |
buffer.indices32Bits | |
); | |
} | |
// edge indices texture | |
if (buffer.edgeIndices8Bits.length > 0) { | |
textureState.texturePerPolygonIdEdgeIndices8Bits = this.dataTextureGenerator.generateTextureFor8BitsEdgeIndices( | |
gl, | |
buffer.edgeIndices8Bits | |
); | |
} | |
if (buffer.edgeIndices16Bits.length > 0) { | |
textureState.texturePerPolygonIdEdgeIndices16Bits = this.dataTextureGenerator.generateTextureFor16BitsEdgeIndices( | |
gl, | |
buffer.edgeIndices16Bits | |
); | |
} | |
if (buffer.edgeIndices32Bits.length > 0) { | |
textureState.texturePerPolygonIdEdgeIndices32Bits = this.dataTextureGenerator.generateTextureFor32BitsEdgeIndices( | |
gl, | |
buffer.edgeIndices32Bits | |
); | |
} | |
// if (buffer.metallicRoughness.length > 0) { | |
// const metallicRoughness = new Uint8Array(buffer.metallicRoughness); | |
// let normalized = false; | |
// state.metallicRoughnessBuf = new ArrayBuf(gl, gl.ARRAY_BUFFER, metallicRoughness, buffer.metallicRoughness.length, 2, gl.STATIC_DRAW, normalized); | |
// } | |
// Model matrices texture | |
if (!this.model._modelMatricesTexture) { | |
this.model._modelMatricesTexture = this.dataTextureGenerator.generateModelTexture( | |
gl, this.model | |
); | |
} | |
textureState.textureModelMatrices = this.model._modelMatricesTexture; | |
// Camera textures | |
textureState.cameraTexture = this.dataTextureGenerator.generateCameraDataTexture( | |
this.model.scene.canvas.gl, | |
this.model.scene.camera, | |
this.model.scene, | |
this._state.origin.slice() | |
); | |
textureState.textureCameraMatrices = textureState.cameraTexture; | |
textureState.texturePickCameraMatrices = this.dataTextureGenerator.generatePickCameraDataTexture( | |
this.model.scene.canvas.gl, | |
this.model.scene.camera, | |
this._state.origin.slice() | |
); | |
textureState.finalize(); | |
// Free up memory | |
this._buffer = null; | |
this._bucketPortionGeometries = {}; | |
this._finalized = true; | |
} | |
attachToRenderingEvent() { | |
this.model.scene.on("rendering", () => { | |
if (this._deferredSetFlagsDirty) { | |
this.commitDeferredFlags(); | |
} | |
this.numUpdatesInFrame = 0; | |
}); | |
} | |
isEmpty() { | |
return this._numPortions === 0; | |
} | |
initFlags(portionId, flags, meshTransparent) { | |
if (flags & ENTITY_FLAGS.VISIBLE) { | |
this._numVisibleLayerPortions++; | |
this.model.numVisibleLayerPortions++; | |
} | |
if (flags & ENTITY_FLAGS.HIGHLIGHTED) { | |
this._numHighlightedLayerPortions++; | |
this.model.numHighlightedLayerPortions++; | |
} | |
if (flags & ENTITY_FLAGS.XRAYED) { | |
this._numXRayedLayerPortions++; | |
this.model.numXRayedLayerPortions++; | |
} | |
if (flags & ENTITY_FLAGS.SELECTED) { | |
this._numSelectedLayerPortions++; | |
this.model.numSelectedLayerPortions++; | |
} | |
if (flags & ENTITY_FLAGS.CLIPPABLE) { | |
this._numClippableLayerPortions++; | |
this.model.numClippableLayerPortions++; | |
} | |
if (flags & ENTITY_FLAGS.EDGES) { | |
this._numEdgesLayerPortions++; | |
this.model.numEdgesLayerPortions++; | |
} | |
if (flags & ENTITY_FLAGS.PICKABLE) { | |
this._numPickableLayerPortions++; | |
this.model.numPickableLayerPortions++; | |
} | |
if (flags & ENTITY_FLAGS.CULLED) { | |
this._numCulledLayerPortions++; | |
this.model.numCulledLayerPortions++; | |
} | |
if (meshTransparent) { | |
this._numTransparentLayerPortions++; | |
this.model.numTransparentLayerPortions++; | |
} | |
const deferred = true; | |
this._setFlags(portionId, flags, meshTransparent, deferred); | |
this._setFlags2(portionId, flags, deferred); | |
} | |
flushInitFlags() { | |
this._setDeferredFlags(); | |
this._setDeferredFlags2(); | |
} | |
setVisible(portionId, flags, transparent) { | |
if (!this._finalized) { | |
throw "Not finalized"; | |
} | |
if (flags & ENTITY_FLAGS.VISIBLE) { | |
this._numVisibleLayerPortions++; | |
this.model.numVisibleLayerPortions++; | |
} else { | |
this._numVisibleLayerPortions--; | |
this.model.numVisibleLayerPortions--; | |
} | |
this._setFlags(portionId, flags, transparent); | |
} | |
setHighlighted(portionId, flags, transparent) { | |
if (!this._finalized) { | |
throw "Not finalized"; | |
} | |
if (flags & ENTITY_FLAGS.HIGHLIGHTED) { | |
this._numHighlightedLayerPortions++; | |
this.model.numHighlightedLayerPortions++; | |
} else { | |
this._numHighlightedLayerPortions--; | |
this.model.numHighlightedLayerPortions--; | |
} | |
this._setFlags(portionId, flags, transparent); | |
} | |
setXRayed(portionId, flags, transparent) { | |
if (!this._finalized) { | |
throw "Not finalized"; | |
} | |
if (flags & ENTITY_FLAGS.XRAYED) { | |
this._numXRayedLayerPortions++; | |
this.model.numXRayedLayerPortions++; | |
} else { | |
this._numXRayedLayerPortions--; | |
this.model.numXRayedLayerPortions--; | |
} | |
this._setFlags(portionId, flags, transparent); | |
} | |
setSelected(portionId, flags, transparent) { | |
if (!this._finalized) { | |
throw "Not finalized"; | |
} | |
if (flags & ENTITY_FLAGS.SELECTED) { | |
this._numSelectedLayerPortions++; | |
this.model.numSelectedLayerPortions++; | |
} else { | |
this._numSelectedLayerPortions--; | |
this.model.numSelectedLayerPortions--; | |
} | |
this._setFlags(portionId, flags, transparent); | |
} | |
setEdges(portionId, flags, transparent) { | |
if (!this._finalized) { | |
throw "Not finalized"; | |
} | |
if (flags & ENTITY_FLAGS.EDGES) { | |
this._numEdgesLayerPortions++; | |
this.model.numEdgesLayerPortions++; | |
} else { | |
this._numEdgesLayerPortions--; | |
this.model.numEdgesLayerPortions--; | |
} | |
this._setFlags(portionId, flags, transparent); | |
} | |
setClippable(portionId, flags) { | |
if (!this._finalized) { | |
throw "Not finalized"; | |
} | |
if (flags & ENTITY_FLAGS.CLIPPABLE) { | |
this._numClippableLayerPortions++; | |
this.model.numClippableLayerPortions++; | |
} else { | |
this._numClippableLayerPortions--; | |
this.model.numClippableLayerPortions--; | |
} | |
this._setFlags2(portionId, flags); | |
} | |
/** | |
* This will _start_ a "set-flags transaction". | |
* | |
* After invoking this method, calling setFlags/setFlags2 will not update | |
* the colors+flags texture but only store the new flags/flag2 in the | |
* colors+flags texture. | |
* | |
* After invoking this method, and when all desired setFlags/setFlags2 have | |
* been called on needed portions of the layer, invoke `commitDeferredFlags` | |
* to actually update the texture data. | |
* | |
* In massive "set-flags" scenarios like VFC or LOD mechanisms, the combina- | |
* tion of `beginDeferredFlags` + `commitDeferredFlags`brings a speed-up of | |
* up to 80x when e.g. objects are massively (un)culled 🚀. | |
* | |
* @private | |
*/ | |
beginDeferredFlags() { | |
this._deferredSetFlagsActive = true; | |
} | |
/** | |
* This will _commit_ a "set-flags transaction". | |
* | |
* Invoking this method will update the colors+flags texture data with new | |
* flags/flags2 set since the previous invocation of `beginDeferredFlags`. | |
* | |
* @private | |
*/ | |
commitDeferredFlags() { | |
this._deferredSetFlagsActive = false; | |
if (!this._deferredSetFlagsDirty) { | |
return; | |
} | |
this._deferredSetFlagsDirty = false; | |
const gl = this.model.scene.canvas.gl; | |
const textureState = this._dataTextureState; | |
gl.bindTexture(gl.TEXTURE_2D, textureState.texturePerObjectIdColorsAndFlags._texture); | |
gl.texSubImage2D( | |
gl.TEXTURE_2D, | |
0, // level | |
0, // xoffset | |
0, // yoffset | |
textureState.texturePerObjectIdColorsAndFlags._textureWidth, // width | |
textureState.texturePerObjectIdColorsAndFlags._textureHeight, // width | |
gl.RGBA_INTEGER, | |
gl.UNSIGNED_BYTE, | |
textureState.texturePerObjectIdColorsAndFlags._textureData | |
); | |
gl.bindTexture(gl.TEXTURE_2D, textureState.texturePerObjectIdOffsets._texture); | |
gl.texSubImage2D( | |
gl.TEXTURE_2D, | |
0, // level | |
0, // xoffset | |
0, // yoffset | |
textureState.texturePerObjectIdOffsets._textureWidth, // width | |
textureState.texturePerObjectIdOffsets._textureHeight, // width | |
gl.RGB, | |
gl.FLOAT, | |
textureState.texturePerObjectIdOffsets._textureData | |
); | |
} | |
setCulled(portionId, flags, transparent) { | |
if (!this._finalized) { | |
throw "Not finalized"; | |
} | |
if (flags & ENTITY_FLAGS.CULLED) { | |
this._numCulledLayerPortions += this._bucketPortionIdMapping[portionId].length; | |
this.model.numCulledLayerPortions++; | |
} else { | |
this._numCulledLayerPortions -= this._bucketPortionIdMapping[portionId].length; | |
this.model.numCulledLayerPortions--; | |
} | |
this._setFlags(portionId, flags, transparent); | |
} | |
setCollidable(portionId, flags) { | |
if (!this._finalized) { | |
throw "Not finalized"; | |
} | |
} | |
setPickable(portionId, flags, transparent) { | |
if (!this._finalized) { | |
throw "Not finalized"; | |
} | |
if (flags & ENTITY_FLAGS.PICKABLE) { | |
this._numPickableLayerPortions++; | |
this.model.numPickableLayerPortions++; | |
} else { | |
this._numPickableLayerPortions--; | |
this.model.numPickableLayerPortions--; | |
} | |
this._setFlags(portionId, flags, transparent); | |
} | |
setColor(portionId, color) { | |
const subPortionMapping = this._bucketPortionIdMapping[portionId]; | |
for (let i = 0, len = subPortionMapping.length; i < len; i++) { | |
this._subPortionSetColor(subPortionMapping[i], color); | |
} | |
} | |
_subPortionSetColor(portionId, color) { | |
if (!this._finalized) { | |
throw "Not finalized"; | |
} | |
// Color | |
const textureState = this._dataTextureState; | |
const gl = this.model.scene.canvas.gl; | |
tempUint8Array4 [0] = color[0]; | |
tempUint8Array4 [1] = color[1]; | |
tempUint8Array4 [2] = color[2]; | |
tempUint8Array4 [3] = color[3]; | |
// object colors | |
textureState.texturePerObjectIdColorsAndFlags._textureData.set(tempUint8Array4, portionId * 32); | |
if (this._deferredSetFlagsActive) { | |
this._deferredSetFlagsDirty = true; | |
return; | |
} | |
if (++this.numUpdatesInFrame >= MAX_OBJECT_UPDATES_IN_FRAME_WITHOUT_BATCHED_UPDATE) { | |
this.beginDeferredFlags(); | |
} | |
gl.bindTexture(gl.TEXTURE_2D, textureState.texturePerObjectIdColorsAndFlags._texture); | |
gl.texSubImage2D( | |
gl.TEXTURE_2D, | |
0, // level | |
(portionId % 512) * 8, // xoffset | |
Math.floor(portionId / 512), // yoffset | |
1, // width | |
1, //height | |
gl.RGBA_INTEGER, | |
gl.UNSIGNED_BYTE, | |
tempUint8Array4 | |
); | |
// gl.bindTexture (gl.TEXTURE_2D, null); | |
} | |
setTransparent(portionId, flags, transparent) { | |
if (transparent) { | |
this._numTransparentLayerPortions++; | |
this.model.numTransparentLayerPortions++; | |
} else { | |
this._numTransparentLayerPortions--; | |
this.model.numTransparentLayerPortions--; | |
} | |
this._setFlags(portionId, flags, transparent); | |
} | |
_setFlags(portionId, flags, transparent, deferred = false) { | |
const subPortionMapping = this._bucketPortionIdMapping[portionId]; | |
for (let i = 0, len = subPortionMapping.length; i < len; i++) { | |
this._subPortionSetFlags(subPortionMapping[i], flags, transparent); | |
} | |
} | |
_subPortionSetFlags(portionId, flags, transparent, deferred = false) { | |
if (!this._finalized) { | |
throw "Not finalized"; | |
} | |
const visible = !!(flags & ENTITY_FLAGS.VISIBLE); | |
const xrayed = !!(flags & ENTITY_FLAGS.XRAYED); | |
const highlighted = !!(flags & ENTITY_FLAGS.HIGHLIGHTED); | |
const selected = !!(flags & ENTITY_FLAGS.SELECTED); | |
const edges = !!(flags & ENTITY_FLAGS.EDGES); | |
const pickable = !!(flags & ENTITY_FLAGS.PICKABLE); | |
const culled = !!(flags & ENTITY_FLAGS.CULLED); | |
// Color | |
let f0; | |
if (!visible || culled || xrayed) { // Highlight & select are layered on top of color - not mutually exclusive | |
f0 = RENDER_PASSES.NOT_RENDERED; | |
} else { | |
if (transparent) { | |
f0 = RENDER_PASSES.COLOR_TRANSPARENT; | |
} else { | |
f0 = RENDER_PASSES.COLOR_OPAQUE; | |
} | |
} | |
// Silhouette | |
let f1; | |
if (!visible || culled) { | |
f1 = RENDER_PASSES.NOT_RENDERED; | |
} else if (selected) { | |
f1 = RENDER_PASSES.SILHOUETTE_SELECTED; | |
} else if (highlighted) { | |
f1 = RENDER_PASSES.SILHOUETTE_HIGHLIGHTED; | |
} else if (xrayed) { | |
f1 = RENDER_PASSES.SILHOUETTE_XRAYED; | |
} else { | |
f1 = RENDER_PASSES.NOT_RENDERED; | |
} | |
// Edges | |
let f2 = 0; | |
if (!visible || culled) { | |
f2 = RENDER_PASSES.NOT_RENDERED; | |
} else if (selected) { | |
f2 = RENDER_PASSES.EDGES_SELECTED; | |
} else if (highlighted) { | |
f2 = RENDER_PASSES.EDGES_HIGHLIGHTED; | |
} else if (xrayed) { | |
f2 = RENDER_PASSES.EDGES_XRAYED; | |
} else if (edges) { | |
if (transparent) { | |
f2 = RENDER_PASSES.EDGES_COLOR_TRANSPARENT; | |
} else { | |
f2 = RENDER_PASSES.EDGES_COLOR_OPAQUE; | |
} | |
} else { | |
f2 = RENDER_PASSES.NOT_RENDERED; | |
} | |
// Pick | |
let f3 = (visible && pickable) ? RENDER_PASSES.PICK : RENDER_PASSES.NOT_RENDERED; | |
const textureState = this._dataTextureState; | |
const gl = this.model.scene.canvas.gl; | |
tempUint8Array4 [0] = f0; | |
tempUint8Array4 [1] = f1; | |
tempUint8Array4 [2] = f2; | |
tempUint8Array4 [3] = f3; | |
// object flags | |
textureState.texturePerObjectIdColorsAndFlags._textureData.set(tempUint8Array4, portionId * 32 + 8); | |
if (this._deferredSetFlagsActive) { | |
this._deferredSetFlagsDirty = true; | |
return; | |
} | |
if (++this.numUpdatesInFrame >= MAX_OBJECT_UPDATES_IN_FRAME_WITHOUT_BATCHED_UPDATE) { | |
this.beginDeferredFlags(); | |
} | |
gl.bindTexture(gl.TEXTURE_2D, textureState.texturePerObjectIdColorsAndFlags._texture); | |
gl.texSubImage2D( | |
gl.TEXTURE_2D, | |
0, // level | |
(portionId % 512) * 8 + 2, // xoffset | |
Math.floor(portionId / 512), // yoffset | |
1, // width | |
1, //height | |
gl.RGBA_INTEGER, | |
gl.UNSIGNED_BYTE, | |
tempUint8Array4 | |
); | |
// gl.bindTexture (gl.TEXTURE_2D, null); | |
} | |
_setDeferredFlags() { | |
} | |
_setFlags2(portionId, flags, deferred = false) { | |
const subPortionMapping = this._bucketPortionIdMapping[portionId]; | |
for (let i = 0, len = subPortionMapping.length; i < len; i++) { | |
this._subPortionSetFlags2(subPortionMapping[i], flags); | |
} | |
} | |
_subPortionSetFlags2(portionId, flags, deferred = false) { | |
if (!this._finalized) { | |
throw "Not finalized"; | |
} | |
const clippable = !!(flags & ENTITY_FLAGS.CLIPPABLE) ? 255 : 0; | |
const textureState = this._dataTextureState; | |
const gl = this.model.scene.canvas.gl; | |
tempUint8Array4 [0] = clippable; | |
tempUint8Array4 [1] = 0; | |
tempUint8Array4 [2] = 1; | |
tempUint8Array4 [3] = 2; | |
// object flags2 | |
textureState.texturePerObjectIdColorsAndFlags._textureData.set(tempUint8Array4, portionId * 32 + 12); | |
if (this._deferredSetFlagsActive) { | |
this._deferredSetFlagsDirty = true; | |
return; | |
} | |
if (++this.numUpdatesInFrame >= MAX_OBJECT_UPDATES_IN_FRAME_WITHOUT_BATCHED_UPDATE) { | |
this.beginDeferredFlags(); | |
} | |
gl.bindTexture(gl.TEXTURE_2D, textureState.texturePerObjectIdColorsAndFlags._texture); | |
gl.texSubImage2D( | |
gl.TEXTURE_2D, | |
0, // level | |
(portionId % 512) * 8 + 3, // xoffset | |
Math.floor(portionId / 512), // yoffset | |
1, // width | |
1, //height | |
gl.RGBA_INTEGER, | |
gl.UNSIGNED_BYTE, | |
tempUint8Array4 | |
); | |
// gl.bindTexture (gl.TEXTURE_2D, null); | |
} | |
_setDeferredFlags2() { | |
return; | |
} | |
setOffset(portionId, offset) { | |
const subPortionMapping = this._bucketPortionIdMapping[portionId]; | |
for (let i = 0, len = subPortionMapping.length; i < len; i++) { | |
this._subPortionSetOffset(subPortionMapping[i], offset); | |
} | |
} | |
_subPortionSetOffset(portionId, offset) { | |
if (!this._finalized) { | |
throw "Not finalized"; | |
} | |
// if (!this.model.scene.entityOffsetsEnabled) { | |
// this.model.error("Entity#offset not enabled for this Viewer"); // See Viewer entityOffsetsEnabled | |
// return; | |
// } | |
const textureState = this._dataTextureState; | |
const gl = this.model.scene.canvas.gl; | |
tempFloat32Array3 [0] = offset[0]; | |
tempFloat32Array3 [1] = offset[1]; | |
tempFloat32Array3 [2] = offset[2]; | |
// object offset | |
textureState.texturePerObjectIdOffsets._textureData.set(tempFloat32Array3, portionId * 3); | |
if (this._deferredSetFlagsActive) { | |
this._deferredSetFlagsDirty = true; | |
return; | |
} | |
if (++this.numUpdatesInFrame >= MAX_OBJECT_UPDATES_IN_FRAME_WITHOUT_BATCHED_UPDATE) { | |
this.beginDeferredFlags(); | |
} | |
gl.bindTexture(gl.TEXTURE_2D, textureState.texturePerObjectIdOffsets._texture); | |
gl.texSubImage2D( | |
gl.TEXTURE_2D, | |
0, // level | |
0, // x offset | |
portionId, // yoffset | |
1, // width | |
1, // height | |
gl.RGB, | |
gl.FLOAT, | |
tempFloat32Array3 | |
); | |
// gl.bindTexture (gl.TEXTURE_2D, null); | |
} | |
// ---------------------- COLOR RENDERING ----------------------------------- | |
drawColorOpaque(renderFlags, frameCtx) { | |
if (this._numCulledLayerPortions === this._numPortions || this._numVisibleLayerPortions === 0 || this._numTransparentLayerPortions === this._numPortions || this._numXRayedLayerPortions === this._numPortions) { | |
return; | |
} | |
this._updateBackfaceCull(renderFlags, frameCtx); | |
if (frameCtx.withSAO && this.model.saoEnabled) { | |
if (frameCtx.pbrEnabled && this.model.pbrEnabled) { | |
if (this._dataTextureRenderers.colorQualityRendererWithSAO) { | |
this._dataTextureRenderers.colorQualityRendererWithSAO.drawLayer(frameCtx, this, RENDER_PASSES.COLOR_OPAQUE); | |
} | |
} else { | |
if (this._dataTextureRenderers.colorRendererWithSAO) { | |
this._dataTextureRenderers.colorRendererWithSAO.drawLayer(frameCtx, this, RENDER_PASSES.COLOR_OPAQUE); | |
} | |
} | |
} else { | |
if (frameCtx.pbrEnabled && this.model.pbrEnabled) { | |
if (this._dataTextureRenderers.colorQualityRenderer) { | |
this._dataTextureRenderers.colorQualityRenderer.drawLayer(frameCtx, this, RENDER_PASSES.COLOR_OPAQUE); | |
} | |
} else { | |
if (this._dataTextureRenderers.colorRenderer) { | |
this._dataTextureRenderers.colorRenderer.drawLayer(frameCtx, this, RENDER_PASSES.COLOR_OPAQUE); | |
} | |
} | |
} | |
} | |
_updateBackfaceCull(renderFlags, frameCtx) { | |
const backfaces = this.model.backfaces || renderFlags.sectioned; | |
if (frameCtx.backfaces !== backfaces) { | |
const gl = frameCtx.gl; | |
if (backfaces) { | |
gl.disable(gl.CULL_FACE); | |
} else { | |
gl.enable(gl.CULL_FACE); | |
} | |
frameCtx.backfaces = backfaces; | |
} | |
} | |
drawColorTransparent(renderFlags, frameCtx) { | |
if (this._numCulledLayerPortions === this._numPortions || this._numVisibleLayerPortions === 0 || this._numTransparentLayerPortions === 0 || this._numXRayedLayerPortions === this._numPortions) { | |
return; | |
} | |
this._updateBackfaceCull(renderFlags, frameCtx); | |
if (frameCtx.pbrEnabled && this.model.pbrEnabled) { | |
if (this._dataTextureRenderers.colorQualityRenderer) { | |
this._dataTextureRenderers.colorQualityRenderer.drawLayer(frameCtx, this, RENDER_PASSES.COLOR_TRANSPARENT); | |
} | |
} else { | |
if (this._dataTextureRenderers.colorRenderer) { | |
this._dataTextureRenderers.colorRenderer.drawLayer(frameCtx, this, RENDER_PASSES.COLOR_TRANSPARENT); | |
} | |
} | |
} | |
// ---------------------- RENDERING SAO POST EFFECT TARGETS -------------- | |
drawDepth(renderFlags, frameCtx) { | |
if (this._numCulledLayerPortions === this._numPortions || this._numVisibleLayerPortions === 0 || this._numTransparentLayerPortions === this._numPortions || this._numXRayedLayerPortions === this._numPortions) { | |
return; | |
} | |
this._updateBackfaceCull(renderFlags, frameCtx); | |
if (this._dataTextureRenderers.depthRenderer) { | |
this._dataTextureRenderers.depthRenderer.drawLayer(frameCtx, this, RENDER_PASSES.COLOR_OPAQUE); // Assume whatever post-effect uses depth (eg SAO) does not apply to transparent objects | |
} | |
} | |
drawNormals(renderFlags, frameCtx) { | |
if (this._numCulledLayerPortions === this._numPortions || this._numVisibleLayerPortions === 0 || this._numTransparentLayerPortions === this._numPortions || this._numXRayedLayerPortions === this._numPortions) { | |
return; | |
} | |
this._updateBackfaceCull(renderFlags, frameCtx); | |
if (this._dataTextureRenderers.normalsRenderer) { | |
this._dataTextureRenderers.normalsRenderer.drawLayer(frameCtx, this, RENDER_PASSES.COLOR_OPAQUE); // Assume whatever post-effect uses normals (eg SAO) does not apply to transparent objects | |
} | |
} | |
// ---------------------- SILHOUETTE RENDERING ----------------------------------- | |
drawSilhouetteXRayed(renderFlags, frameCtx) { | |
if (this._numCulledLayerPortions === this._numPortions || this._numVisibleLayerPortions === 0 || this._numXRayedLayerPortions === 0) { | |
return; | |
} | |
this._updateBackfaceCull(renderFlags, frameCtx); | |
if (this._dataTextureRenderers.silhouetteRenderer) { | |
this._dataTextureRenderers.silhouetteRenderer.drawLayer(frameCtx, this, RENDER_PASSES.SILHOUETTE_XRAYED); | |
} | |
} | |
drawSilhouetteHighlighted(renderFlags, frameCtx) { | |
if (this._numCulledLayerPortions === this._numPortions || this._numVisibleLayerPortions === 0 || this._numHighlightedLayerPortions === 0) { | |
return; | |
} | |
this._updateBackfaceCull(renderFlags, frameCtx); | |
if (this._dataTextureRenderers.silhouetteRenderer) { | |
this._dataTextureRenderers.silhouetteRenderer.drawLayer(frameCtx, this, RENDER_PASSES.SILHOUETTE_HIGHLIGHTED); | |
} | |
} | |
drawSilhouetteSelected(renderFlags, frameCtx) { | |
if (this._numCulledLayerPortions === this._numPortions || this._numVisibleLayerPortions === 0 || this._numSelectedLayerPortions === 0) { | |
return; | |
} | |
this._updateBackfaceCull(renderFlags, frameCtx); | |
if (this._dataTextureRenderers.silhouetteRenderer) { | |
this._dataTextureRenderers.silhouetteRenderer.drawLayer(frameCtx, this, RENDER_PASSES.SILHOUETTE_SELECTED); | |
} | |
} | |
// ---------------------- EDGES RENDERING ----------------------------------- | |
drawEdgesColorOpaque(renderFlags, frameCtx) { | |
if (this._numCulledLayerPortions === this._numPortions || this._numVisibleLayerPortions === 0 || this._numEdgesLayerPortions === 0) { | |
return; | |
} | |
if (this._dataTextureRenderers.edgesColorRenderer) { | |
this._dataTextureRenderers.edgesColorRenderer.drawLayer(frameCtx, this, RENDER_PASSES.EDGES_COLOR_OPAQUE); | |
} | |
} | |
drawEdgesColorTransparent(renderFlags, frameCtx) { | |
if (this._numCulledLayerPortions === this._numPortions || this._numVisibleLayerPortions === 0 || this._numEdgesLayerPortions === 0 || this._numTransparentLayerPortions === 0) { | |
return; | |
} | |
if (this._dataTextureRenderers.edgesColorRenderer) { | |
this._dataTextureRenderers.edgesColorRenderer.drawLayer(frameCtx, this, RENDER_PASSES.EDGES_COLOR_TRANSPARENT); | |
} | |
} | |
drawEdgesHighlighted(renderFlags, frameCtx) { | |
if (this._numCulledLayerPortions === this._numPortions || this._numVisibleLayerPortions === 0 || this._numHighlightedLayerPortions === 0) { | |
return; | |
} | |
if (this._dataTextureRenderers.edgesRenderer) { | |
this._dataTextureRenderers.edgesRenderer.drawLayer(frameCtx, this, RENDER_PASSES.EDGES_HIGHLIGHTED); | |
} | |
} | |
drawEdgesSelected(renderFlags, frameCtx) { | |
if (this._numCulledLayerPortions === this._numPortions || this._numVisibleLayerPortions === 0 || this._numSelectedLayerPortions === 0) { | |
return; | |
} | |
if (this._dataTextureRenderers.edgesRenderer) { | |
this._dataTextureRenderers.edgesRenderer.drawLayer(frameCtx, this, RENDER_PASSES.EDGES_SELECTED); | |
} | |
} | |
drawEdgesXRayed(renderFlags, frameCtx) { | |
if (this._numCulledLayerPortions === this._numPortions || this._numVisibleLayerPortions === 0 || this._numXRayedLayerPortions === 0) { | |
return; | |
} | |
if (this._dataTextureRenderers.edgesRenderer) { | |
this._dataTextureRenderers.edgesRenderer.drawLayer(frameCtx, this, RENDER_PASSES.EDGES_XRAYED); | |
} | |
} | |
// ---------------------- OCCLUSION CULL RENDERING ----------------------------------- | |
drawOcclusion(renderFlags, frameCtx) { | |
if (this._numCulledLayerPortions === this._numPortions || this._numVisibleLayerPortions === 0) { | |
return; | |
} | |
this._updateBackfaceCull(renderFlags, frameCtx); | |
if (this._dataTextureRenderers.occlusionRenderer) { | |
this._dataTextureRenderers.occlusionRenderer.drawLayer(frameCtx, this, RENDER_PASSES.COLOR_OPAQUE); | |
} | |
} | |
// ---------------------- SHADOW BUFFER RENDERING ----------------------------------- | |
drawShadow(renderFlags, frameCtx) { | |
if (this._numCulledLayerPortions === this._numPortions || this._numVisibleLayerPortions === 0) { | |
return; | |
} | |
this._updateBackfaceCull(renderFlags, frameCtx); | |
if (this._dataTextureRenderers.shadowRenderer) { | |
this._dataTextureRenderers.shadowRenderer.drawLayer(frameCtx, this, RENDER_PASSES.COLOR_OPAQUE); | |
} | |
} | |
//---- PICKING ---------------------------------------------------------------------------------------------------- | |
setPickMatrices(pickViewMatrix, pickProjMatrix) { | |
if (this._numVisibleLayerPortions === 0) { | |
return; | |
} | |
this._dataTextureState.texturePickCameraMatrices.updateViewMatrix(pickViewMatrix, pickProjMatrix); | |
} | |
drawPickMesh(renderFlags, frameCtx) { | |
if (this._numVisibleLayerPortions === 0) { | |
return; | |
} | |
this._updateBackfaceCull(renderFlags, frameCtx); | |
if (this._dataTextureRenderers.pickMeshRenderer) { | |
this._dataTextureRenderers.pickMeshRenderer.drawLayer(frameCtx, this, RENDER_PASSES.PICK); | |
} | |
} | |
drawPickDepths(renderFlags, frameCtx) { | |
if (this._numVisibleLayerPortions === 0) { | |
return; | |
} | |
this._updateBackfaceCull(renderFlags, frameCtx); | |
if (this._dataTextureRenderers.pickDepthRenderer) { | |
this._dataTextureRenderers.pickDepthRenderer.drawLayer(frameCtx, this, RENDER_PASSES.PICK); | |
} | |
} | |
drawSnapDepths(renderFlags, frameCtx) { | |
if (this._numCulledLayerPortions === this._numPortions || this._numVisibleLayerPortions === 0) { | |
return; | |
} | |
this._updateBackfaceCull(renderFlags, frameCtx); | |
if (this._dataTextureRenderers.vertexDepthRenderer) { | |
this._dataTextureRenderers.vertexDepthRenderer.drawLayer(frameCtx, this, RENDER_PASSES.PICK); | |
} | |
} | |
drawSnapInitDepthBuf(renderFlags, frameCtx) { | |
if (this._numCulledLayerPortions === this._numPortions || this._numVisibleLayerPortions === 0) { | |
return; | |
} | |
this._updateBackfaceCull(renderFlags, frameCtx); | |
if (this._dataTextureRenderers.vertexDepthZBufferInitializer) { | |
this._dataTextureRenderers.vertexDepthZBufferInitializer.drawLayer(frameCtx, this, RENDER_PASSES.PICK); | |
} | |
} | |
drawPickNormals(renderFlags, frameCtx) { | |
if (this._numCulledLayerPortions === this._numPortions || this._numVisibleLayerPortions === 0) { | |
return; | |
} | |
this._updateBackfaceCull(renderFlags, frameCtx); | |
if (this._dataTextureRenderers.pickNormalsRenderer) { | |
this._dataTextureRenderers.pickNormalsRenderer.drawLayer(frameCtx, this, RENDER_PASSES.PICK); | |
} | |
} | |
destroy() { | |
const state = this._state; | |
if (state.metallicRoughnessBuf) { | |
state.metallicRoughnessBuf.destroy(); | |
state.metallicRoughnessBuf = null; | |
} | |
state.destroy(); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment