@itwin/core-frontend
Version:
iTwin.js frontend components
297 lines • 11.6 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 } from "@itwin/core-bentley";
import { AuxChannel, AuxChannelData } from "@itwin/core-geometry";
import { ColorIndex, EdgeArgs, FeatureIndex, FeatureIndexType, LinePixels, PolylineEdgeArgs, PolylineTypeFlags, QParams3d, QPoint3dList, SilhouetteEdgeArgs, } from "@itwin/core-common";
import { ColorMap } from "./ColorMap";
import { DisplayParams } from "./DisplayParams";
import { MeshPrimitiveType } from "./MeshPrimitive";
import { TriangleList } from "./Primitives";
export function createPolylineArgs(mesh) {
if (!mesh.polylines || mesh.polylines.length === 0)
return undefined;
const polylines = [];
for (const polyline of mesh.polylines)
if (polyline.indices.length > 0)
polylines.push(polyline.indices);
if (polylines.length === 0)
return undefined;
const flags = {
is2d: mesh.is2d,
isPlanar: mesh.isPlanar,
isDisjoint: mesh.type === MeshPrimitiveType.Point,
};
if (mesh.displayParams.regionEdgeType === DisplayParams.RegionEdgeType.Outline) {
// This polyline is behaving as the edges of a region surface.
if (!mesh.displayParams.gradient || mesh.displayParams.gradient.isOutlined)
flags.type = PolylineTypeFlags.Edge;
else
flags.type = PolylineTypeFlags.Outline; // edges only displayed if fill undisplayed
}
const colors = new ColorIndex();
mesh.colorMap.toColorIndex(colors, mesh.colors);
const features = new FeatureIndex();
mesh.toFeatureIndex(features);
return {
width: mesh.displayParams.width,
linePixels: mesh.displayParams.linePixels,
flags,
polylines,
points: mesh.points,
colors,
features,
};
}
/** The vertices of the edges are shared with those of the surface. */
export class MeshArgsEdges {
edges = new EdgeArgs();
silhouettes = new SilhouetteEdgeArgs();
polylines = new PolylineEdgeArgs();
width = 0;
linePixels = LinePixels.Solid;
color;
clear() {
this.edges.clear();
this.silhouettes.clear();
this.polylines.clear();
this.width = 0;
this.linePixels = LinePixels.Solid;
}
get isValid() { return this.edges.isValid || this.silhouettes.isValid || this.polylines.isValid; }
}
export function createMeshArgs(mesh) {
if (!mesh.triangles || mesh.triangles.isEmpty || mesh.points.length === 0)
return undefined;
const texture = mesh.displayParams.textureMapping?.texture;
const textureMapping = texture && mesh.uvParams.length > 0 ? { texture, uvParams: mesh.uvParams } : undefined;
const colors = new ColorIndex();
mesh.colorMap.toColorIndex(colors, mesh.colors);
const features = new FeatureIndex();
mesh.toFeatureIndex(features);
let edges;
if (mesh.edges) {
edges = new MeshArgsEdges();
edges.width = mesh.edges.appearance?.width ?? mesh.displayParams.width;
edges.linePixels = mesh.edges.appearance?.linePixels ?? mesh.displayParams.linePixels;
edges.edges.init(mesh.edges);
edges.silhouettes.init(mesh.edges);
edges.color = mesh.edges.appearance?.color;
edges.polylines.init(mesh.edges.polylineGroups);
}
return {
vertIndices: mesh.triangles.indices,
points: mesh.points,
normals: !mesh.displayParams.ignoreLighting && mesh.normals.length > 0 ? mesh.normals : undefined,
textureMapping,
colors,
features,
material: mesh.displayParams.material,
fillFlags: mesh.displayParams.fillFlags,
isPlanar: mesh.isPlanar,
is2d: mesh.is2d,
hasBakedLighting: true === mesh.hasBakedLighting,
isVolumeClassifier: true === mesh.isVolumeClassifier,
edges,
auxChannels: mesh.auxChannels,
};
}
export class Mesh {
_data;
points;
normals = [];
uvParams = [];
colorMap = new ColorMap(); // used to be called ColorTable
colors = [];
edges;
features;
type;
is2d;
isPlanar;
hasBakedLighting;
isVolumeClassifier;
displayParams;
_auxChannels;
constructor(props) {
const { displayParams, features, type, range, is2d, isPlanar } = props;
this._data = MeshPrimitiveType.Mesh === type ? new TriangleList() : [];
this.displayParams = displayParams;
this.features = features ? new Mesh.Features(features) : undefined;
this.type = type;
this.is2d = is2d;
this.isPlanar = isPlanar;
this.hasBakedLighting = (true === props.hasBakedLighting);
this.isVolumeClassifier = (true === props.isVolumeClassifier);
if (props.quantizePositions) {
this.points = new QPoint3dList(QParams3d.fromRange(range));
}
else {
const points = [];
points.range = range;
const center = range.center;
points.add = (pt) => {
// assert(range.containsPoint(pt)); rounding error triggers this sometimes...
points.push(pt.minus(center));
};
this.points = points;
}
}
static create(props) { return new Mesh(props); }
get triangles() {
return MeshPrimitiveType.Mesh === this.type ? this._data : undefined;
}
get polylines() {
return MeshPrimitiveType.Mesh !== this.type ? this._data : undefined;
}
get auxChannels() {
return this._auxChannels;
}
addAuxChannels(channels, srcIndex) {
// The native version of this function appears to assume that all polyfaces added to the Mesh will have
// the same number + type of aux channels.
// ###TODO We should really produce a separate Mesh for each unique combination. For now just bail on mismatch.
if (this._auxChannels) {
if (this._auxChannels.length !== channels.length)
return;
for (let i = 0; i < channels.length; i++) {
const src = channels[i];
const dst = this._auxChannels[i];
if (src.dataType !== dst.dataType || src.name !== dst.name || src.inputName !== dst.inputName)
return;
}
}
if (!this._auxChannels) {
// Copy the channels, leaving each AuxData's values array empty.
this._auxChannels = channels.map((x) => new AuxChannel(x.data.map((y) => new AuxChannelData(y.input, [])), x.dataType, x.name, x.inputName));
}
// Append the value at srcIndex from each source channel's data to our channels.
for (let channelIndex = 0; channelIndex < channels.length; channelIndex++) {
const srcChannel = channels[channelIndex];
const dstChannel = this._auxChannels[channelIndex];
const dstIndex = dstChannel.valueCount;
for (let dataIndex = 0; dataIndex < srcChannel.data.length; dataIndex++) {
const dstData = dstChannel.data[dataIndex];
dstData.copyValues(srcChannel.data[dataIndex], dstIndex, srcIndex, dstChannel.entriesPerValue);
}
}
}
toFeatureIndex(index) {
if (undefined !== this.features)
this.features.toFeatureIndex(index);
}
toMeshArgs() {
return createMeshArgs(this);
}
toPolylineArgs() {
return createPolylineArgs(this);
}
addPolyline(poly) {
const { type, polylines } = this;
assert(MeshPrimitiveType.Polyline === type || MeshPrimitiveType.Point === type);
assert(undefined !== polylines);
if (MeshPrimitiveType.Polyline === type && poly.indices.length < 2)
return;
if (undefined !== polylines)
polylines.push(poly);
}
addTriangle(triangle) {
const { triangles, type } = this;
assert(MeshPrimitiveType.Mesh === type);
assert(undefined !== triangles);
if (undefined !== triangles)
triangles.addTriangle(triangle);
}
addVertex(props) {
const { feature, position, normal, uvParam, fillColor } = props;
this.points.add(position);
if (undefined !== normal)
this.normals.push(normal);
if (undefined !== uvParam)
this.uvParams.push(uvParam);
if (feature) {
assert(undefined !== this.features);
this.features.add(feature, this.points.length);
}
// Don't allocate color indices until we have non-uniform colors
if (0 === this.colorMap.length) {
this.colorMap.insert(fillColor);
assert(this.colorMap.isUniform);
assert(0 === this.colorMap.indexOf(fillColor));
}
else if (!this.colorMap.isUniform || !this.colorMap.hasColor(fillColor)) {
// Back-fill uniform value (index=0) for existing vertices if previously uniform
if (0 === this.colors.length)
this.colors.length = this.points.length - 1;
this.colors.push(this.colorMap.insert(fillColor));
assert(!this.colorMap.isUniform);
}
return this.points.length - 1;
}
}
(function (Mesh) {
class Features {
table;
indices = [];
uniform = 0;
initialized = false;
constructor(table) { this.table = table; }
add(feat, numVerts) {
const index = this.table.insert(feat);
if (!this.initialized) {
// First feature - uniform.
this.uniform = index;
this.initialized = true;
}
else if (0 < this.indices.length) {
// Already non-uniform
this.indices.push(index);
}
else {
// Second feature - back-fill uniform for existing verts
while (this.indices.length < numVerts - 1)
this.indices.push(this.uniform);
this.indices.push(index);
}
}
setIndices(indices) {
this.indices.length = 0;
this.uniform = 0;
this.initialized = 0 < indices.length;
assert(0 < indices.length);
if (1 === indices.length)
this.uniform = indices[0];
else if (1 < indices.length)
this.indices = indices;
}
toFeatureIndex(output) {
const index = output ?? new FeatureIndex();
if (!this.initialized) {
index.type = FeatureIndexType.Empty;
}
else if (this.indices.length === 0) {
index.type = FeatureIndexType.Uniform;
index.featureID = this.uniform;
}
else {
index.type = FeatureIndexType.NonUniform;
index.featureIDs = new Uint32Array(this.indices);
}
return index;
}
}
Mesh.Features = Features;
})(Mesh || (Mesh = {}));
export class MeshList extends Array {
features;
range;
constructor(features, range) {
super();
this.features = features;
this.range = range;
}
}
//# sourceMappingURL=MeshPrimitives.js.map