@itwin/core-frontend
Version:
iTwin.js frontend components
373 lines • 17.2 kB
JavaScript
/*---------------------------------------------------------------------------------------------
* Copyright (c) Bentley Systems, Incorporated. All rights reserved.
* See LICENSE.md in the project root for license terms and full copyright notice.
*--------------------------------------------------------------------------------------------*/
/** @packageDocumentation
* @module WebGL
*/
import { assert, dispose } from "@itwin/core-bentley";
import { Point3d, Range3d, Transform } from "@itwin/core-geometry";
import { AttributeMap } from "./AttributeMap";
import { CachedGeometry } from "./CachedGeometry";
import { GL } from "./GL";
import { BufferHandle, BufferParameters, BuffersContainer } from "./AttributeBuffers";
import { Matrix4 } from "./Matrix";
import { _featureTable, _implementationProhibited, _transformCenter, _transforms } from "../../../common/internal/Symbols";
import { BatchType, PackedFeatureTable } from "@itwin/core-common";
/** @internal */
export function isInstancedGraphicParams(params) {
return typeof params === "object" && typeof params.count === "number" && params.transforms instanceof Float32Array && params.transformCenter instanceof Point3d;
}
class InstanceData {
numInstances;
// A transform including only rtcCenter.
_rtcOnlyTransform;
// A transform from _rtcCenter including model matrix
_rtcModelTransform;
// The model matrix from which _rtcModelTransform was previously computed. If it changes, _rtcModelTransform must be recomputed.
_modelMatrix = Transform.createIdentity();
constructor(numInstances, rtcCenter) {
this.numInstances = numInstances;
this._rtcOnlyTransform = Transform.createTranslation(rtcCenter);
this._rtcModelTransform = this._rtcOnlyTransform.clone();
}
getRtcModelTransform(modelMatrix) {
if (!this._modelMatrix.isAlmostEqual(modelMatrix)) {
modelMatrix.clone(this._modelMatrix);
modelMatrix.multiplyTransformTransform(this._rtcOnlyTransform, this._rtcModelTransform);
}
return this._rtcModelTransform;
}
getRtcOnlyTransform() {
return this._rtcOnlyTransform;
}
static _noFeatureId = new Float32Array([0, 0, 0]);
get patternFeatureId() {
return InstanceData._noFeatureId;
}
}
/** @internal */
export class InstanceBuffersData extends InstanceData {
transforms;
featureIds;
symbology;
_noDispose = false;
constructor(count, transforms, rtcCenter, symbology, featureIds, disableDisposal = false) {
super(count, rtcCenter);
this.transforms = transforms;
this.featureIds = featureIds;
this.symbology = symbology;
this._noDispose = disableDisposal;
}
static create(params, disableDisposal = false) {
const { count, featureIds, symbologyOverrides, transforms } = params;
assert(count > 0 && Math.floor(count) === count);
assert(count === transforms.length / 12);
assert(undefined === featureIds || count === featureIds.length / 3);
assert(undefined === symbologyOverrides || count * 8 === symbologyOverrides.length);
let idBuf;
if (undefined !== featureIds && undefined === (idBuf = BufferHandle.createArrayBuffer(featureIds)))
return undefined;
let symBuf;
if (undefined !== symbologyOverrides && undefined === (symBuf = BufferHandle.createArrayBuffer(symbologyOverrides)))
return undefined;
const tfBuf = BufferHandle.createArrayBuffer(transforms);
const transformCenter = params.transformCenter instanceof Point3d ? params.transformCenter : Point3d.fromJSON(params.transformCenter);
if (!tfBuf) {
return undefined;
}
return new InstanceBuffersData(count, tfBuf, transformCenter, symBuf, idBuf, disableDisposal);
}
[Symbol.dispose]() {
if (!this._noDispose) {
dispose(this.transforms);
dispose(this.featureIds);
dispose(this.symbology);
}
}
get isDisposed() {
return this.transforms.isDisposed && (!this.featureIds || this.featureIds.isDisposed) && (!this.symbology || this.symbology.isDisposed);
}
}
/** @internal */
export var RenderInstancesImpl;
(function (RenderInstancesImpl) {
function create(params) {
const buffers = InstanceBuffersData.create(params.instances, true);
if (!buffers) {
return undefined;
}
let featureTable;
if (params.features) {
// ###TODO permit user to specify batch type and other batch options...
featureTable = new PackedFeatureTable(params.features.data, params.features.modelId, params.features.count, BatchType.Primary);
}
return {
[_implementationProhibited]: "renderInstances",
buffers,
[_transforms]: params.instances.transforms,
[_transformCenter]: params.instances.transformCenter,
[_featureTable]: featureTable,
};
}
RenderInstancesImpl.create = create;
})(RenderInstancesImpl || (RenderInstancesImpl = {}));
/** @internal */
export class InstanceBuffers {
static _patternParams = new Float32Array([0, 0, 0, 0]);
_data;
patternParams = InstanceBuffers._patternParams;
patternTransforms = undefined;
viewIndependentOrigin = undefined;
range;
get numInstances() { return this._data.numInstances; }
get transforms() { return this._data.transforms; }
get featureIds() { return this._data.featureIds; }
get symbology() { return this._data.symbology; }
get hasFeatures() { return undefined !== this.featureIds; }
get patternFeatureId() { return this._data.patternFeatureId; }
getRtcModelTransform(modelMatrix) { return this._data.getRtcModelTransform(modelMatrix); }
getRtcOnlyTransform() { return this._data.getRtcOnlyTransform(); }
constructor(data, range) {
this._data = data;
this.range = range;
}
static createTransformBufferParameters(techniqueId) {
const params = [];
const numRows = 3;
let row = 0;
while (row < numRows) {
// 3 rows per instance; 4 floats per row; 4 bytes per float.
const floatsPerRow = 4;
const bytesPerVertex = floatsPerRow * 4;
const offset = row * bytesPerVertex;
const stride = 3 * bytesPerVertex;
const name = `a_instanceMatrixRow${row}`;
const details = AttributeMap.findAttribute(name, techniqueId, true);
assert(details !== undefined);
const bParams = {
glAttribLoc: details.location,
glSize: floatsPerRow,
glType: GL.DataType.Float,
glNormalized: false,
glStride: stride,
glOffset: offset,
glInstanced: true,
};
params.push(bParams);
row++;
}
return params;
}
static fromParams(params, computeReprRange) {
const data = InstanceBuffersData.create(params);
if (!data) {
return undefined;
}
const range = params.range ?? this.computeRange(computeReprRange(), params.transforms, params.transformCenter);
return new InstanceBuffers(data, range);
}
static fromRenderInstances(arg, reprRange) {
const instances = arg;
const range = this.computeRange(reprRange, instances[_transforms], instances[_transformCenter]);
return new InstanceBuffers(instances.buffers, range);
}
[Symbol.dispose]() {
dispose(this._data);
}
get isDisposed() {
return this._data.isDisposed;
}
collectStatistics(stats) {
const featureBytes = undefined !== this.featureIds ? this.featureIds.bytesUsed : 0;
const symBytes = undefined !== this.symbology ? this.symbology.bytesUsed : 0;
const bytesUsed = this.transforms.bytesUsed + symBytes + featureBytes;
stats.addInstances(bytesUsed);
}
static extendTransformedRange(tfs, i, range, x, y, z) {
range.extendXYZ(tfs[i + 3] + tfs[i + 0] * x + tfs[i + 1] * y + tfs[i + 2] * z, tfs[i + 7] + tfs[i + 4] * x + tfs[i + 5] * y + tfs[i + 6] * z, tfs[i + 11] + tfs[i + 8] * x + tfs[i + 9] * y + tfs[i + 10] * z);
}
static computeRange(reprRange, tfs, rtcCenter, out) {
const range = out ?? new Range3d();
const numFloatsPerTransform = 3 * 4;
assert(0 === tfs.length % (3 * 4));
for (let i = 0; i < tfs.length; i += numFloatsPerTransform) {
this.extendTransformedRange(tfs, i, range, reprRange.low.x, reprRange.low.y, reprRange.low.z);
this.extendTransformedRange(tfs, i, range, reprRange.low.x, reprRange.low.y, reprRange.high.z);
this.extendTransformedRange(tfs, i, range, reprRange.low.x, reprRange.high.y, reprRange.low.z);
this.extendTransformedRange(tfs, i, range, reprRange.low.x, reprRange.high.y, reprRange.high.z);
this.extendTransformedRange(tfs, i, range, reprRange.high.x, reprRange.low.y, reprRange.low.z);
this.extendTransformedRange(tfs, i, range, reprRange.high.x, reprRange.low.y, reprRange.high.z);
this.extendTransformedRange(tfs, i, range, reprRange.high.x, reprRange.high.y, reprRange.low.z);
this.extendTransformedRange(tfs, i, range, reprRange.high.x, reprRange.high.y, reprRange.high.z);
}
range.low.addInPlace(rtcCenter);
range.high.addInPlace(rtcCenter);
return range.clone(out);
}
}
/** @internal */
export class PatternBuffers extends InstanceData {
range;
patternParams;
origin;
orgTransform;
localToModel;
symbolToLocal;
offsets;
viewIndependentOrigin;
_featureId;
[_implementationProhibited] = "renderAreaPattern";
constructor(count, rtcCenter, range, patternParams, // [ isAreaPattern, spacingX, spacingY, scale ]
origin, // [ x, y ]
orgTransform, localToModel, symbolToLocal, offsets, featureId, viewIndependentOrigin) {
super(count, rtcCenter);
this.range = range;
this.patternParams = patternParams;
this.origin = origin;
this.orgTransform = orgTransform;
this.localToModel = localToModel;
this.symbolToLocal = symbolToLocal;
this.offsets = offsets;
this.viewIndependentOrigin = viewIndependentOrigin;
this.patternTransforms = this;
if (undefined !== featureId) {
this._featureId = new Float32Array([
(featureId & 0x0000ff) >>> 0,
(featureId & 0x00ff00) >>> 8,
(featureId & 0xff0000) >>> 16,
]);
}
}
static create(params) {
const count = params.xyOffsets.byteLength / 2;
assert(Math.floor(count) === count);
const offsets = BufferHandle.createArrayBuffer(params.xyOffsets);
if (!offsets)
return undefined;
return new PatternBuffers(count, new Point3d(), params.range, new Float32Array([1, params.spacing.x, params.spacing.y, params.scale]), new Float32Array([params.origin.x, params.origin.y]), Matrix4.fromTransform(params.orgTransform), Matrix4.fromTransform(params.patternToModel), Matrix4.fromTransform(Transform.createTranslation(params.symbolTranslation)), offsets, params.featureId, params.viewIndependentOrigin);
}
patternTransforms;
get hasFeatures() {
return undefined !== this._featureId;
}
get patternFeatureId() {
return this._featureId ?? super.patternFeatureId;
}
get isDisposed() {
return this.offsets.isDisposed;
}
[Symbol.dispose]() {
dispose(this.offsets);
}
collectStatistics(stats) {
stats.addInstances(this.offsets.bytesUsed);
}
}
/** @internal */
export class InstancedGeometry extends CachedGeometry {
_buffersContainer;
_buffers;
_repr;
_ownsBuffers;
getRtcModelTransform(modelMatrix) { return this._buffers.getRtcModelTransform(modelMatrix); }
getRtcOnlyTransform() { return this._buffers.getRtcOnlyTransform(); }
get viewIndependentOrigin() { return this._buffers.viewIndependentOrigin; }
get asInstanced() { return this; }
get asLUT() { return this._repr.asLUT; }
get asMesh() { return this._repr.asMesh; }
get asSurface() { return this._repr.asSurface; }
get asEdge() { return this._repr.asEdge; }
get asSilhouette() { return this._repr.asSilhouette; }
get asIndexedEdge() { return this._repr.asIndexedEdge; }
get renderOrder() { return this._repr.renderOrder; }
get isLitSurface() { return this._repr.isLitSurface; }
get hasBakedLighting() { return this._repr.hasBakedLighting; }
get hasAnimation() { return this._repr.hasAnimation; }
get usesQuantizedPositions() { return this._repr.usesQuantizedPositions; }
get qOrigin() { return this._repr.qOrigin; }
get qScale() { return this._repr.qScale; }
get materialInfo() { return this._repr.materialInfo; }
get polylineBuffers() { return this._repr.polylineBuffers; }
get isEdge() { return this._repr.isEdge; }
get hasFeatures() { return this._buffers.hasFeatures; }
get techniqueId() { return this._repr.techniqueId; }
get supportsThematicDisplay() { return this._repr.supportsThematicDisplay; }
getPass(target) { return this._repr.getPass(target); }
wantWoWReversal(params) { return this._repr.wantWoWReversal(params); }
getLineCode(params) { return this._repr.getLineCode(params); }
getLineWeight(params) { return this._repr.getLineWeight(params); }
wantMonochrome(target) { return this._repr.wantMonochrome(target); }
wantMixMonochromeColor(target) { return this._repr.wantMixMonochromeColor(target); }
static create(repr, ownsBuffers, buffers) {
const techId = repr.techniqueId;
const container = BuffersContainer.create();
container.appendLinkages(repr.lutBuffers.linkages);
container.addBuffer(buffers.transforms, InstanceBuffers.createTransformBufferParameters(repr.techniqueId));
if (buffers.symbology) {
const attrInstanceOverrides = AttributeMap.findAttribute("a_instanceOverrides", techId, true);
const attrInstanceRgba = AttributeMap.findAttribute("a_instanceRgba", techId, true);
assert(attrInstanceOverrides !== undefined);
assert(attrInstanceRgba !== undefined);
container.addBuffer(buffers.symbology, [
BufferParameters.create(attrInstanceOverrides.location, 4, GL.DataType.UnsignedByte, false, 8, 0, true),
BufferParameters.create(attrInstanceRgba.location, 4, GL.DataType.UnsignedByte, false, 8, 4, true),
]);
}
if (buffers.featureIds) {
const attrFeatureId = AttributeMap.findAttribute("a_featureId", techId, true);
assert(attrFeatureId !== undefined);
container.addBuffer(buffers.featureIds, [BufferParameters.create(attrFeatureId.location, 3, GL.DataType.UnsignedByte, false, 0, 0, true)]);
}
return new this(repr, ownsBuffers, buffers, container);
}
static createPattern(repr, ownsBuffers, buffers) {
const techId = repr.techniqueId;
const container = BuffersContainer.create();
container.appendLinkages(repr.lutBuffers.linkages);
const attrX = AttributeMap.findAttribute("a_patternX", techId, true);
const attrY = AttributeMap.findAttribute("a_patternY", techId, true);
assert(undefined !== attrX && undefined !== attrY);
container.addBuffer(buffers.offsets, [
BufferParameters.create(attrX.location, 1, GL.DataType.Float, false, 8, 0, true),
BufferParameters.create(attrY.location, 1, GL.DataType.Float, false, 8, 4, true),
]);
return new this(repr, ownsBuffers, buffers, container);
}
constructor(repr, ownsBuffers, buffers, container) {
super();
this._repr = repr;
this._ownsBuffers = ownsBuffers;
this._buffers = buffers;
this._buffersContainer = container;
}
get isDisposed() {
if (!this._repr.isDisposed)
return false;
return !this._ownsBuffers || this._buffers.isDisposed;
}
[Symbol.dispose]() {
this._repr[Symbol.dispose]();
if (this._ownsBuffers)
dispose(this._buffers);
}
_wantWoWReversal(_target) {
assert(false, "Should never be called");
return false;
}
draw() {
this._repr.drawInstanced(this._buffers.numInstances, this._buffersContainer);
}
computeRange(output) {
return this._buffers.range.clone(output);
}
collectStatistics(stats) {
this._repr.collectStatistics(stats);
if (this._ownsBuffers)
this._buffers.collectStatistics(stats);
}
get patternParams() { return this._buffers.patternParams; }
get patternTransforms() { return this._buffers.patternTransforms; }
get patternFeatureId() { return this._buffers.patternFeatureId; }
}
//# sourceMappingURL=InstancedGeometry.js.map