UNPKG

@itwin/core-frontend

Version:
337 lines • 16.3 kB
"use strict"; /*--------------------------------------------------------------------------------------------- * 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 */ Object.defineProperty(exports, "__esModule", { value: true }); exports.MeshBuilderPolyface = exports.MeshEdgeCreationOptions = exports.MeshBuilder = void 0; const core_bentley_1 = require("@itwin/core-bentley"); const core_geometry_1 = require("@itwin/core-geometry"); const core_common_1 = require("@itwin/core-common"); const Primitives_1 = require("./Primitives"); const VertexKey_1 = require("./VertexKey"); const MeshPrimitives_1 = require("./MeshPrimitives"); /** @internal */ class MeshBuilder { vertexMap; _triangleSet; _currentPolyface; mesh; tolerance; areaTolerance; tileRange; get currentPolyface() { return this._currentPolyface; } get displayParams() { return this.mesh.displayParams; } set displayParams(params) { this.mesh.displayParams = params; } /** create reference for triangleSet on demand */ get triangleSet() { if (undefined === this._triangleSet) this._triangleSet = new Primitives_1.TriangleSet(); return this._triangleSet; } constructor(mesh, tolerance, areaTolerance, tileRange) { this.mesh = mesh; this.tolerance = tolerance; this.areaTolerance = areaTolerance; this.tileRange = tileRange; let vertexTolerance; if (mesh.points instanceof core_common_1.QPoint3dList) { const p0 = mesh.points.params.unquantize(0, 0, 0); const p1 = mesh.points.params.unquantize(1, 1, 1); vertexTolerance = p1.minus(p0, p0); } else { vertexTolerance = { x: tolerance, y: tolerance, z: tolerance }; } this.vertexMap = new VertexKey_1.VertexMap(vertexTolerance); } /** create a new MeshBuilder */ static create(props) { const mesh = MeshPrimitives_1.Mesh.create(props); const { tolerance, areaTolerance, range } = props; return new MeshBuilder(mesh, tolerance, areaTolerance, range); } /** * iterate through each point list of the strokes primitive and either load the point string or polyline into builder * @param strokes lists of stroke primitive point lists to iterate * @param isDisjoint if true add point string, else add polyline * @param fillColor */ addStrokePointLists(strokes, isDisjoint, fillColor, feature) { for (const strokePoints of strokes) { if (isDisjoint) this.addPointString(strokePoints.points, fillColor, feature); else this.addPolyline(strokePoints.points, fillColor, feature); } } /** * add data from polyface into mesh builder * @param polyface the indexed polyface to iterate the facets of to load each facet's triangles' vertices * @param props the properties required for this operation */ addFromPolyface(polyface, props, feature) { this.beginPolyface(polyface, props.edgeOptions); const visitor = polyface.createVisitor(); while (visitor.moveToNextFacet()) { this.addFromPolyfaceVisitor(visitor, props, feature); } this.endPolyface(); } /** * @param visitor the PolyfaceVisitor containing the face data to be added * @param props the properties required for this operation: */ addFromPolyfaceVisitor(visitor, options, feature) { const { pointCount, normalCount, paramCount, requireNormals } = visitor; const { includeParams, mappedTexture } = options; const isDegenerate = requireNormals && normalCount < pointCount; // TFS#790263: Degenerate triangle - no normals. // a triangle must have at least 3 points if (pointCount < 3 || isDegenerate) return; const haveParam = includeParams && paramCount > 0; const triangleCount = pointCount - 2; (0, core_bentley_1.assert)(!includeParams || paramCount > 0); (0, core_bentley_1.assert)(!haveParam || undefined !== mappedTexture); // The face represented by this visitor should be convex (we request that in facet options) - so we do a simple fan triangulation. const polyfaceVisitorOptions = { ...options, triangleCount, haveParam }; for (let triangleIndex = 0; triangleIndex < triangleCount; triangleIndex++) { const triangle = this.createTriangle(triangleIndex, visitor, polyfaceVisitorOptions, feature); if (undefined !== triangle) this.addTriangle(triangle); } } createTriangleVertices(triangleIndex, visitor, options, feature) { const { point, requireNormals } = visitor; const { fillColor, haveParam } = options; // If we do not have UVParams stored on the IndexedPolyface, compute them now let params; if (haveParam && options.mappedTexture) { (0, core_bentley_1.assert)(this.mesh.points.length === 0 || this.mesh.uvParams.length !== 0); const mappedTexture = options.mappedTexture; const transformToImodel = mappedTexture.params.textureMatrix.transform; if (transformToImodel) params = mappedTexture.computeUVParams(visitor, transformToImodel); (0, core_bentley_1.assert)(params !== undefined); } const vertices = []; for (let i = 0; i < 3; ++i) { const vertexIndex = 0 === i ? 0 : triangleIndex + i; const position = point.getPoint3dAtUncheckedPointIndex(vertexIndex); const normal = requireNormals ? core_common_1.OctEncodedNormal.fromVector((0, core_bentley_1.expectDefined)(visitor.getNormal(vertexIndex))) : undefined; const uvParam = params ? params[vertexIndex] : undefined; vertices[i] = { position, fillColor, normal, uvParam, sourceIndex: vertexIndex, feature }; } // Previously we would add all 3 vertices to our map, then detect degenerate triangles in AddTriangle(). // This led to unused vertex data, and caused mismatch in # of vertices when recreating the MeshBuilder from the data in the tile cache. // Detect beforehand instead. if (this.vertexMap.arePositionsAlmostEqual(vertices[0], vertices[1]) || this.vertexMap.arePositionsAlmostEqual(vertices[0], vertices[2]) || this.vertexMap.arePositionsAlmostEqual(vertices[1], vertices[2])) return undefined; return vertices; } createTriangle(triangleIndex, visitor, options, feature) { // generate vertex key properties for each of the three sides of the triangle const vertices = this.createTriangleVertices(triangleIndex, visitor, options, feature); // avoid creating degenerate triangles if (undefined === vertices) return undefined; const { edgeVisible } = visitor; const triangle = new Primitives_1.Triangle(); triangle.setEdgeVisibility(0 === triangleIndex ? edgeVisible[0] : false, edgeVisible[triangleIndex + 1], triangleIndex === options.triangleCount - 1 ? edgeVisible[triangleIndex + 2] : false); // set each triangle index to the index associated with the vertex key location in the vertex map vertices.forEach((vertexProps, i) => { let vertexKeyIndex; if (visitor.auxData) { // No deduplication with auxData (for now...) vertexKeyIndex = this.mesh.addVertex(vertexProps); this.mesh.addAuxChannels(visitor.auxData.channels, vertexProps.sourceIndex); } else { vertexKeyIndex = this.addVertex(vertexProps); } triangle.indices[i] = vertexKeyIndex; // if the current polyface exists, map the vertex key index to the visitor's client point index if (this.currentPolyface !== undefined) this.currentPolyface.vertexIndexMap.set(vertexKeyIndex, visitor.clientPointIndex(vertexProps.sourceIndex)); }); return triangle; } /** removed Feature for now */ addPolyline(points, fillColor, feature) { const { mesh } = this; const poly = new core_common_1.MeshPolyline(); for (const position of points) poly.addIndex(this.addVertex({ position, fillColor, feature })); mesh.addPolyline(poly); } /** removed Feature for now */ addPointString(points, fillColor, feature) { const { mesh } = this; const poly = new core_common_1.MeshPolyline(); for (const position of points) poly.addIndex(this.addVertex({ position, fillColor, feature })); mesh.addPolyline(poly); } beginPolyface(polyface, options) { if (!options.generateNoEdges) { const triangles = this.mesh.triangles; this._currentPolyface = new MeshBuilderPolyface(polyface, options, triangles === undefined ? 0 : triangles.length); } } endPolyface() { const { currentPolyface, mesh } = this; if (undefined === currentPolyface) return; this._currentPolyface = undefined; buildMeshEdges(mesh, currentPolyface); } addVertex(vertex, addToMeshOnInsert = true) { // if vertex key isn't duplicate, then also insert properties into mesh const onInsert = (vk) => this.mesh.addVertex(vk); return this.vertexMap.insertKey(vertex, addToMeshOnInsert ? onInsert : undefined); } addTriangle(triangle) { // Attempt to avoid adding vertices originating from degenerate triangles before we get here. // Removed assert and just return if degenerate at this point because uncommon cases (not worth testing for) can still occur. if (triangle.isDegenerate) return; const onInsert = (_vk) => this.mesh.addTriangle(triangle); this.triangleSet.insertKey(triangle, onInsert); } } exports.MeshBuilder = MeshBuilder; /** @internal */ class MeshEdgeCreationOptions { type; minCreaseAngle = 20.0 * core_geometry_1.Angle.radiansPerDegree; get generateAllEdges() { return this.type === MeshEdgeCreationOptions.Type.AllEdges; } get generateNoEdges() { return this.type === MeshEdgeCreationOptions.Type.NoEdges; } get generateCreaseEdges() { return 0 !== (this.type & MeshEdgeCreationOptions.Type.CreaseEdges); } /** Create edge chains for polyfaces that do not already have them. */ get createEdgeChains() { return 0 !== (this.type & MeshEdgeCreationOptions.Type.CreateChains); } constructor(type = MeshEdgeCreationOptions.Type.NoEdges) { this.type = type; } } exports.MeshEdgeCreationOptions = MeshEdgeCreationOptions; /** @internal */ (function (MeshEdgeCreationOptions) { let Type; (function (Type) { Type[Type["NoEdges"] = 0] = "NoEdges"; Type[Type["CreaseEdges"] = 2] = "CreaseEdges"; Type[Type["SmoothEdges"] = 4] = "SmoothEdges"; Type[Type["CreateChains"] = 8] = "CreateChains"; Type[Type["DefaultEdges"] = 2] = "DefaultEdges"; Type[Type["AllEdges"] = 6] = "AllEdges"; })(Type = MeshEdgeCreationOptions.Type || (MeshEdgeCreationOptions.Type = {})); })(MeshEdgeCreationOptions || (exports.MeshEdgeCreationOptions = MeshEdgeCreationOptions = {})); /** @internal */ class MeshBuilderPolyface { polyface; edgeOptions; vertexIndexMap = new Map(); baseTriangleIndex; constructor(polyface, edgeOptions, baseTriangleIndex) { this.polyface = polyface; this.edgeOptions = edgeOptions; this.baseTriangleIndex = baseTriangleIndex; } } exports.MeshBuilderPolyface = MeshBuilderPolyface; class EdgeInfo { visible; faceIndex0; edge; point0; point1; faceIndex1; constructor(visible, faceIndex0, edge, point0, point1) { this.visible = visible; this.faceIndex0 = faceIndex0; this.edge = edge; this.point0 = point0; this.point1 = point1; } addFace(visible, faceIndex) { if (undefined === this.faceIndex1) { this.visible ||= visible; this.faceIndex1 = faceIndex; } } } function buildMeshEdges(mesh, polyface) { if (!mesh.triangles) return; const edgeMap = new core_bentley_1.Dictionary((lhs, rhs) => lhs.compareTo(rhs)); const triangleNormals = []; // We need to detect the edge pairs -- Can't do that from the Mesh indices as these are not shared - so we'll // assume that the polyface indices are properly shared, this should be true as a seperate index array is used // for Polyfaces. const triangle = new Primitives_1.Triangle(); const polyfacePoints = [new core_geometry_1.Point3d(), new core_geometry_1.Point3d(), new core_geometry_1.Point3d()]; const polyfaceIndices = [0, 0, 0]; for (let triangleIndex = polyface.baseTriangleIndex; triangleIndex < mesh.triangles.length; triangleIndex++) { let indexNotFound = false; mesh.triangles.getTriangle(triangleIndex, triangle); for (let j = 0; j < 3; j++) { const foundPolyfaceIndex = polyface.vertexIndexMap.get(triangle.indices[j]); (0, core_bentley_1.assert)(undefined !== foundPolyfaceIndex); if (undefined === foundPolyfaceIndex) { indexNotFound = true; continue; } polyfaceIndices[j] = foundPolyfaceIndex; polyface.polyface.data.getPoint(foundPolyfaceIndex, polyfacePoints[j]); } if (indexNotFound) continue; for (let j = 0; j < 3; j++) { const jNext = (j + 1) % 3; const triangleNormalIndex = triangleNormals.length; const meshEdge = new core_common_1.MeshEdge(triangle.indices[j], triangle.indices[jNext]); const polyfaceEdge = new core_common_1.MeshEdge(polyfaceIndices[j], polyfaceIndices[jNext]); const edgeInfo = new EdgeInfo(triangle.isEdgeVisible(j), triangleNormalIndex, meshEdge, polyfacePoints[j], polyfacePoints[jNext]); const findOrInsert = edgeMap.findOrInsert(polyfaceEdge, edgeInfo); if (!findOrInsert.inserted) findOrInsert.value.addFace(edgeInfo.visible, triangleNormalIndex); } const normal = core_geometry_1.Vector3d.createCrossProductToPoints(polyfacePoints[0], polyfacePoints[1], polyfacePoints[2]); normal.normalizeInPlace(); triangleNormals.push(normal); } // If there is no visibility indication in the mesh, infer from the mesh geometry. if (!polyface.edgeOptions.generateAllEdges) { const minEdgeDot = Math.cos(polyface.edgeOptions.minCreaseAngle); for (const edgeInfo of edgeMap.values()) { if (undefined !== edgeInfo.faceIndex1) { const normal0 = triangleNormals[edgeInfo.faceIndex0]; const normal1 = triangleNormals[edgeInfo.faceIndex1]; if (Math.abs(normal0.dotProduct(normal1)) > minEdgeDot) edgeInfo.visible = false; } } } // Now populate the MeshEdges. // ###TODO edge chains? if (undefined === mesh.edges) mesh.edges = new core_common_1.MeshEdges(); const maxPlanarDot = 0.999999; for (const edgeInfo of edgeMap.values()) { if (edgeInfo.visible) { mesh.edges.visible.push(edgeInfo.edge); } else if (undefined !== edgeInfo.faceIndex1) { const normal0 = triangleNormals[edgeInfo.faceIndex0]; const normal1 = triangleNormals[edgeInfo.faceIndex1]; if (Math.abs(normal0.dotProduct(normal1)) < maxPlanarDot) { mesh.edges.silhouette.push(edgeInfo.edge); mesh.edges.silhouetteNormals.push(new core_common_1.OctEncodedNormalPair(core_common_1.OctEncodedNormal.fromVector(normal0), core_common_1.OctEncodedNormal.fromVector(normal1))); } } } } //# sourceMappingURL=MeshBuilder.js.map