@itwin/core-frontend
Version:
iTwin.js frontend components
459 lines • 18.9 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 Rendering
*/
import { assert, expectDefined } from "@itwin/core-bentley";
import { Point2d, Range2d } from "@itwin/core-geometry";
import { ColorDef, FeatureIndexType, FillFlags, QParams2d, QParams3d, QPoint2d, QPoint3dList, } from "@itwin/core-common";
import { AuxChannelTable } from "./AuxChannelTable";
import { computeDimensions } from "./VertexTable";
import { createSurfaceMaterial, SurfaceType } from "./SurfaceParams";
import { VertexIndices } from "./VertexIndices";
import { createEdgeParams } from "./EdgeParams";
/** @internal */
export function createMeshParams(args, maxDimension, enableIndexedEdges) {
const builder = createMeshBuilder(args);
const vertices = builder.build(args.colors, args.features, maxDimension);
const surfaceIndices = VertexIndices.fromArray(args.vertIndices);
const surface = {
type: builder.type,
indices: surfaceIndices,
fillFlags: args.fillFlags ?? FillFlags.ByView,
hasBakedLighting: true === args.hasBakedLighting,
textureMapping: undefined !== args.textureMapping ? { texture: args.textureMapping.texture, alwaysDisplayed: false } : undefined,
material: createSurfaceMaterial(args.material),
};
const channels = undefined !== args.auxChannels ? AuxChannelTable.fromChannels(args.auxChannels, vertices.numVertices, maxDimension) : undefined;
const edges = createEdgeParams({ meshArgs: args, maxWidth: maxDimension, createIndexed: enableIndexedEdges });
return {
vertices,
surface,
edges,
isPlanar: !!args.isPlanar,
auxChannels: channels,
};
}
/** Builds a VertexTable from some data type supplying the vertex data.
* @internal
*/
export class VertexTableBuilder {
data;
_curIndex = 0;
get uvParams() { return undefined; }
appendColorTable(colorIndex) {
if (undefined !== colorIndex.nonUniform) {
for (const color of colorIndex.nonUniform.colors) {
this.appendColor(color);
}
}
}
advance(nBytes) {
this._curIndex += nBytes;
assert(undefined !== this.data);
assert(this._curIndex <= this.data.length);
}
append8(val) {
assert(0 <= val);
assert(val <= 0xff);
assert(val === Math.floor(val));
expectDefined(this.data)[this._curIndex] = val;
this.advance(1);
}
append16(val) {
this.append8(val & 0x00ff);
this.append8(val >>> 8);
}
append32(val) {
this.append16(val & 0x0000ffff);
this.append16(val >>> 16);
}
appendColor(tbgr) {
const colors = ColorDef.getColors(tbgr);
// invert transparency => alpha
colors.t = 255 - colors.t;
// premultiply alpha...
switch (colors.t) {
case 0:
colors.r = colors.g = colors.b = 0;
break;
case 255:
break;
default: {
const f = colors.t / 255.0;
colors.r = Math.floor(colors.r * f + 0.5);
colors.g = Math.floor(colors.g * f + 0.5);
colors.b = Math.floor(colors.b * f + 0.5);
break;
}
}
// Store 32-bit value in little-endian order (red first)
this.append8(colors.r);
this.append8(colors.g);
this.append8(colors.b);
this.append8(colors.t);
}
build(colorIndex, featureIndex, maxDimension) {
const { numVertices, numRgbaPerVertex } = this;
const numColors = colorIndex.isUniform ? 0 : colorIndex.numColors;
const dimensions = computeDimensions(numVertices, numRgbaPerVertex, numColors, maxDimension);
assert(0 === dimensions.width % numRgbaPerVertex || (0 < numColors && 1 === dimensions.height));
const data = new Uint8Array(dimensions.width * dimensions.height * 4);
this.data = data;
for (let i = 0; i < numVertices; i++)
this.appendVertex(i);
this.appendColorTable(colorIndex);
this.data = undefined;
return {
data,
qparams: this.qparams,
usesUnquantizedPositions: this.usesUnquantizedPositions,
width: dimensions.width,
height: dimensions.height,
hasTranslucency: colorIndex.hasAlpha,
uniformColor: colorIndex.uniform,
numVertices,
numRgbaPerVertex,
uvParams: this.uvParams,
featureIndexType: featureIndex.type,
uniformFeatureID: featureIndex.type === FeatureIndexType.Uniform ? featureIndex.featureID : undefined,
};
}
static buildFromPolylines(args, maxDimension) {
const polylines = args.polylines;
if (polylines.length === 0)
return undefined;
const builder = createPolylineBuilder(args);
return builder.build(args.colors, args.features, maxDimension);
}
}
var Quantized;
(function (Quantized) {
/**
* Supplies vertex data from a PolylineArgs or MeshArgs. Each vertex consists of 12 bytes:
* pos.x 00
* pos.y 02
* pos.z 04
* colorIndex 06
* featureIndex 08 (24 bits)
* materialIndex 0B (for meshes that use a material atlas; otherwise unused). NOTE: Currently front-end code does not produce material atlases.
*/
class SimpleBuilder extends VertexTableBuilder {
args;
_qpoints;
constructor(args) {
super();
this._qpoints = args.points;
this.args = args;
assert(undefined !== this.args.points);
}
get numVertices() { return this.args.points.length; }
get numRgbaPerVertex() { return 3; }
get usesUnquantizedPositions() { return false; }
get qparams() {
return this._qpoints.params;
}
appendVertex(vertIndex) {
this.appendPosition(vertIndex);
this.appendColorIndex(vertIndex);
this.appendFeatureIndex(vertIndex);
}
appendPosition(vertIndex) {
this.append16(this._qpoints.list[vertIndex].x);
this.append16(this._qpoints.list[vertIndex].y);
this.append16(this._qpoints.list[vertIndex].z);
}
appendColorIndex(vertIndex) {
if (undefined !== this.args.colors.nonUniform) {
this.append16(this.args.colors.nonUniform.indices[vertIndex]);
}
else {
this.advance(2);
}
}
appendFeatureIndex(vertIndex) {
if (undefined !== this.args.features.featureIDs) {
this.append32(this.args.features.featureIDs[vertIndex]);
}
else {
this.advance(4);
}
}
}
Quantized.SimpleBuilder = SimpleBuilder;
/** Supplies vertex data from a MeshArgs. */
class MeshBuilder extends SimpleBuilder {
type;
constructor(args, type) {
super(args);
this.type = type;
}
static create(args) {
if (args.isVolumeClassifier)
return new MeshBuilder(args, SurfaceType.VolumeClassifier);
const isLit = undefined !== args.normals && 0 < args.normals.length;
const isTextured = undefined !== args.textureMapping;
let uvParams;
if (args.textureMapping) {
const uvRange = Range2d.createNull();
const fpts = args.textureMapping.uvParams;
const pt2d = new Point2d();
if (undefined !== fpts && fpts.length > 0)
for (let i = 0; i < args.points.length; i++)
uvRange.extendPoint(Point2d.create(fpts[i].x, fpts[i].y, pt2d));
uvParams = QParams2d.fromRange(uvRange);
}
if (isLit)
// If isTextured is true, uvParams will always be defined.
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return isTextured ? new TexturedLitMeshBuilder(args, uvParams) : new LitMeshBuilder(args);
else
// If isTextured is true, uvParams will always be defined.
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return isTextured ? new TexturedMeshBuilder(args, uvParams) : new MeshBuilder(args, SurfaceType.Unlit);
}
}
Quantized.MeshBuilder = MeshBuilder;
/** Supplies vertex data from a MeshArgs where each vertex consists of 16 bytes.
* In addition to the SimpleBuilder data, the final 4 bytes hold the quantized UV params
* The color index is left uninitialized as it is unused.
*/
class TexturedMeshBuilder extends MeshBuilder {
_qparams;
_qpoint = new QPoint2d();
constructor(args, qparams, type = SurfaceType.Textured) {
super(args, type);
this._qparams = qparams;
assert(undefined !== args.textureMapping);
}
get numRgbaPerVertex() { return 4; }
get uvParams() { return this._qparams; }
appendVertex(vertIndex) {
this.appendPosition(vertIndex);
this.appendNormal(vertIndex);
this.appendFeatureIndex(vertIndex);
this.appendUVParams(vertIndex);
}
appendNormal(_vertIndex) { this.advance(2); } // no normal for unlit meshes
appendUVParams(vertIndex) {
this._qpoint.init(expectDefined(this.args.textureMapping).uvParams[vertIndex], this._qparams);
this.append16(this._qpoint.x);
this.append16(this._qpoint.y);
}
}
/** As with TexturedMeshBuilder, but the color index is replaced with the oct-encoded normal value. */
class TexturedLitMeshBuilder extends TexturedMeshBuilder {
constructor(args, qparams) {
super(args, qparams, SurfaceType.TexturedLit);
assert(undefined !== args.normals);
}
appendNormal(vertIndex) { this.append16(expectDefined(this.args.normals)[vertIndex].value); }
}
/** 16 bytes. The last 2 bytes are unused; the 2 immediately preceding it hold the oct-encoded normal value. */
class LitMeshBuilder extends MeshBuilder {
constructor(args) {
super(args, SurfaceType.Lit);
assert(undefined !== args.normals);
}
get numRgbaPerVertex() { return 4; }
appendVertex(vertIndex) {
super.appendVertex(vertIndex);
this.append16(expectDefined(this.args.normals)[vertIndex].value);
this.advance(2); // 2 unused bytes
}
}
})(Quantized || (Quantized = {}));
/** Builders in this namespace store vertex positions as 32-bit floats instead of quantizing to 16-bit unsigned integers.
* This is preferred for decoration graphics, which might contain ranges of positions that exceed the limits for quantization; if quantized,
* they could produce visual artifacts.
* Each builder produces a VertexTable that starts with the following layout:
* pos.x: 00
* pos.y: 04
* pos.z: 08
* featureIndex: 0C
* materialIndex:0F (NOTE: frontend code currently doesn't produce material atlases, so this is always zero).
* Followed (by default) by:
* colorIndex: 10
* unused: 12
* Subclasses may add 4 more bytes and/or overwrite the final 4 bytes above.
*/
var Unquantized;
(function (Unquantized) {
const u32Array = new Uint32Array(1);
const f32Array = new Float32Array(u32Array.buffer);
// colorIndex: 10
// unused: 12
class SimpleBuilder extends VertexTableBuilder {
args;
_points;
_qparams3d;
constructor(args) {
super();
assert(!(args.points instanceof QPoint3dList));
this._qparams3d = QParams3d.fromRange(args.points.range);
this.args = args;
this._points = args.points;
}
get numVertices() { return this._points.length; }
get numRgbaPerVertex() { return 5; }
get usesUnquantizedPositions() { return true; }
get qparams() { return this._qparams3d; }
appendVertex(vertIndex) {
this.appendTransposePosAndFeatureNdx(vertIndex);
this.appendColorIndex(vertIndex);
}
appendFloat32(val) {
f32Array[0] = val;
this.append32(u32Array[0]);
}
convertFloat32(val) {
f32Array[0] = val;
return u32Array[0];
}
appendTransposePosAndFeatureNdx(vertIndex) {
// transpose position xyz vals into [0].xyz - [3].xyz, and add feature index at .w
// this is to order things to let shader code access much more efficiently
const pt = this._points[vertIndex];
const x = this.convertFloat32(pt.x);
const y = this.convertFloat32(pt.y);
const z = this.convertFloat32(pt.z);
const featID = (this.args.features.featureIDs) ? this.args.features.featureIDs[vertIndex] : 0;
this.append8(x & 0x000000ff);
this.append8(y & 0x000000ff);
this.append8(z & 0x000000ff);
this.append8(featID & 0x000000ff);
this.append8((x >>> 8) & 0x000000ff);
this.append8((y >>> 8) & 0x000000ff);
this.append8((z >>> 8) & 0x000000ff);
this.append8((featID >>> 8) & 0x000000ff);
this.append8((x >>> 16) & 0x000000ff);
this.append8((y >>> 16) & 0x000000ff);
this.append8((z >>> 16) & 0x000000ff);
this.append8((featID >>> 16) & 0x000000ff);
this.append8(x >>> 24);
this.append8(y >>> 24);
this.append8(z >>> 24);
this.append8(featID >>> 24);
}
appendPosition(vertIndex) {
const pt = this._points[vertIndex];
this.appendFloat32(pt.x);
this.appendFloat32(pt.y);
this.appendFloat32(pt.z);
}
appendFeatureIndex(vertIndex) {
if (this.args.features.featureIDs)
this.append32(this.args.features.featureIDs[vertIndex]);
else
this.advance(4);
}
_appendColorIndex(vertIndex) {
if (undefined !== this.args.colors.nonUniform)
this.append16(this.args.colors.nonUniform.indices[vertIndex]);
else
this.advance(2);
}
appendColorIndex(vertIndex) {
this._appendColorIndex(vertIndex);
this.advance(2);
}
}
Unquantized.SimpleBuilder = SimpleBuilder;
class MeshBuilder extends SimpleBuilder {
type;
constructor(args, type) {
super(args);
this.type = type;
}
static create(args) {
if (args.isVolumeClassifier)
return new MeshBuilder(args, SurfaceType.VolumeClassifier);
const isLit = undefined !== args.normals && 0 < args.normals.length;
const isTextured = undefined !== args.textureMapping;
let uvParams;
if (args.textureMapping) {
const uvRange = Range2d.createNull();
const fpts = args.textureMapping.uvParams;
const pt2d = new Point2d();
if (undefined !== fpts && fpts.length > 0)
for (let i = 0; i < args.points.length; i++)
uvRange.extendPoint(Point2d.create(fpts[i].x, fpts[i].y, pt2d));
uvParams = QParams2d.fromRange(uvRange);
}
if (isLit)
// If isTextured is true, uvParams will always be defined.
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return isTextured ? new TexturedLitMeshBuilder(args, uvParams) : new LitMeshBuilder(args);
else
// If isTextured is true, uvParams will always be defined.
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
return isTextured ? new TexturedMeshBuilder(args, uvParams) : new MeshBuilder(args, SurfaceType.Unlit);
}
}
Unquantized.MeshBuilder = MeshBuilder;
// u: 10
// v: 12
class TexturedMeshBuilder extends MeshBuilder {
_qparams;
_qpoint = new QPoint2d();
constructor(args, qparams, type = SurfaceType.Textured) {
super(args, type);
this._qparams = qparams;
assert(undefined !== args.textureMapping);
}
get uvParams() { return this._qparams; }
appendVertex(vertIndex) {
super.appendVertex(vertIndex);
this._qpoint.init(expectDefined(this.args.textureMapping).uvParams[vertIndex], this._qparams);
this.append16(this._qpoint.x);
this.append16(this._qpoint.y);
}
appendColorIndex() { }
}
// u: 10
// v: 12
// normal: 14
// unused: 16
class TexturedLitMeshBuilder extends TexturedMeshBuilder {
constructor(args, qparams) {
super(args, qparams, SurfaceType.TexturedLit);
assert(undefined !== args.normals);
}
get numRgbaPerVertex() { return 6; }
appendVertex(vertIndex) {
super.appendVertex(vertIndex);
this.append16(expectDefined(this.args.normals)[vertIndex].value);
this.advance(2);
}
}
// color: 10
// normal: 12
class LitMeshBuilder extends MeshBuilder {
constructor(args) {
super(args, SurfaceType.Lit);
assert(undefined !== args.normals);
}
appendColorIndex(vertIndex) {
super._appendColorIndex(vertIndex);
}
appendVertex(vertIndex) {
super.appendVertex(vertIndex);
this.append16(expectDefined(this.args.normals)[vertIndex].value);
}
}
})(Unquantized || (Unquantized = {}));
function createMeshBuilder(args) {
if (args.points instanceof QPoint3dList)
return Quantized.MeshBuilder.create(args);
else
return Unquantized.MeshBuilder.create(args);
}
function createPolylineBuilder(args) {
if (args.points instanceof QPoint3dList)
return new Quantized.SimpleBuilder(args);
else
return new Unquantized.SimpleBuilder(args);
}
//# sourceMappingURL=VertexTableBuilder.js.map