UNPKG

@itwin/core-common

Version:

iTwin.js components common to frontend and backend

249 lines • 13.1 kB
/*--------------------------------------------------------------------------------------------- * 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 { Matrix3d, Point2d, Point3d, Transform, Vector3d } from "@itwin/core-geometry"; import { compareBooleans, compareBooleansOrUndefined, compareNumbers, compareNumbersOrUndefined, comparePossiblyUndefined, expectDefined } from "@itwin/core-bentley"; function compareNormalMapParams(lhs, rhs) { return comparePossiblyUndefined((lh, rh) => lh.compare(rh), lhs.normalMap, rhs.normalMap) || compareBooleansOrUndefined(lhs.greenUp, rhs.greenUp) || compareNumbersOrUndefined(lhs.scale, rhs.scale) || compareBooleansOrUndefined(lhs.useConstantLod, rhs.useConstantLod); } /** Describes how to map a [[RenderTexture]]'s image onto a surface as part of a [[RenderMaterial]]. * @public */ export class TextureMapping { /** The texture to be mapped to the surface. If normalMapParams is present but does not contain a normal map, then texture is used as a normal map rather than a pattern map. */ texture; /** The parameters for normal mapping. * @beta */ normalMapParams; /** The parameters describing how the textures are mapped to the surface. */ params; constructor(tx, params) { this.texture = tx; this.params = params; } /** Compute texture coordinates for a polyface. * @param visitor The polyface for which to compute UV coordinates based on this texture mapping. * @param localToWorld The polyface's local-to-world transform, used for [[TextureMapping.Mode.ElevationDrape]]. * @returns the texture coordinates, or undefined if computation failed. */ computeUVParams(visitor, localToWorld = Transform.createIdentity()) { return this.params.computeUVParams(visitor, localToWorld); } /** An [OrderedComparator]($bentley) that compares this mapping against `other`. */ compare(other) { if (this === other) { return 0; } return this.texture.compare(other.texture) || this.params.compare(other.params) || comparePossiblyUndefined((lh, rh) => compareNormalMapParams(lh, rh), this.normalMapParams, other.normalMapParams); } } /** @public */ (function (TextureMapping) { /** Enumerates the possible texture mapping modes. */ let Mode; (function (Mode) { Mode[Mode["None"] = -1] = "None"; Mode[Mode["Parametric"] = 0] = "Parametric"; Mode[Mode["ElevationDrape"] = 1] = "ElevationDrape"; Mode[Mode["Planar"] = 2] = "Planar"; /** Currently unsupported. */ Mode[Mode["DirectionalDrape"] = 3] = "DirectionalDrape"; /** Currently unsupported. */ Mode[Mode["Cubic"] = 4] = "Cubic"; /** Currently unsupported. */ Mode[Mode["Spherical"] = 5] = "Spherical"; /** Currently unsupported. */ Mode[Mode["Cylindrical"] = 6] = "Cylindrical"; /** Currently unsupported. */ Mode[Mode["Solid"] = 7] = "Solid"; /** Currently unsupported. */ Mode[Mode["FrontProject"] = 8] = "FrontProject"; })(Mode = TextureMapping.Mode || (TextureMapping.Mode = {})); /** A 2x3 matrix for mapping a texture image to a surface. */ class Trans2x3 { /** The 3x4 transform produced from the 2x3 matrix. */ transform; /** Construct from the two rows of the matrix: * ``` * | m00 m01 originX | * | m10 m11 originY | * ``` * Producing the [Transform]($core-geometry): * ``` * | m00 m01 0 originX | * | m10 m11 0 originY | * | 0 0 1 0 | * ``` */ constructor(m00 = 1, m01 = 0, originX = 0, m10 = 0, m11 = 1, originY = 0) { const origin = new Point3d(originX, originY, 0); const matrix = Matrix3d.createRowValues(m00, m01, 0, m10, m11, 0, 0, 0, 1); this.transform = Transform.createRefs(origin, matrix); } /** An immutable 2x3 identity matrix. */ static identity = new Trans2x3(); /** An [OrderedComparator]($bentley) that compares this Trans2x3 against `other`. */ compare(other) { if (this === other) { return 0; } const originDiff = compareNumbers(this.transform.origin.x, other.transform.origin.x) || compareNumbers(this.transform.origin.y, other.transform.origin.y); if (originDiff !== 0) { return originDiff; } for (const i of [0, 1, 3, 4]) { const matDiff = compareNumbers(this.transform.matrix.coffs[i], other.transform.matrix.coffs[i]); if (matDiff !== 0) { return matDiff; } } return 0; } } TextureMapping.Trans2x3 = Trans2x3; function compareConstantLodParams(lhs, rhs) { return compareNumbers(lhs.repetitions, rhs.repetitions) || compareNumbers(lhs.offset.x, rhs.offset.x) || compareNumbers(lhs.offset.y, rhs.offset.y) || compareNumbers(lhs.minDistClamp, rhs.minDistClamp) || compareNumbers(lhs.maxDistClamp, rhs.maxDistClamp); } /** Parameters describing how a [[RenderTexture]]'s image is mapped to a surface. */ class Params { /** The matrix used to map the image to a surface. */ textureMatrix; /** The ratio in [0, 1] with which to mix the color sampled from the texture with the element's color. * A value of 0.0 uses only the element color. A value of 1.0 uses only the texture color. */ weight; /** The mode by which to map the image to a surface. */ mode; worldMapping; /** True if want to use constant LOD texture mapping for the surface texture. */ useConstantLod; /** Parameters for constantLod mapping mode. */ constantLodParams; constructor(props) { this.textureMatrix = props?.textureMat2x3 ?? Trans2x3.identity; this.weight = props?.textureWeight ?? 1; this.mode = props?.mapMode ?? Mode.Parametric; this.worldMapping = props?.worldMapping ?? false; this.useConstantLod = props?.useConstantLod ?? false; this.constantLodParams = { repetitions: props?.constantLodProps?.repetitions ?? 1, offset: props?.constantLodProps?.offset ?? { x: 0, y: 0 }, minDistClamp: props?.constantLodProps?.minDistClamp ?? 1, maxDistClamp: props?.constantLodProps?.maxDistClamp ?? 4096 * 1024 * 1024, }; } /** An [OrderedComparator]($bentley) that compares these Params against `other`. */ compare(other) { if (this === other) { return 0; } return compareNumbers(this.weight, other.weight) || compareNumbers(this.mode, other.mode) || compareBooleans(this.worldMapping, other.worldMapping) || compareBooleans(this.useConstantLod, other.useConstantLod) || this.textureMatrix.compare(other.textureMatrix) || compareConstantLodParams(this.constantLodParams, other.constantLodParams); } /** Compute texture coordinates for a polyface. * @param visitor The polyface for which to compute UV coordinates based on this texture mapping. * @param localToWorld The polyface's local-to-world transform, used for [[TextureMapping.Mode.ElevationDrape]]. * @returns the texture coordinates, or undefined if computation failed. */ computeUVParams(visitor, localToWorld = Transform.createIdentity()) { switch (this.mode) { default: // Fall through to parametric in default case case TextureMapping.Mode.Parametric: { return this.computeParametricUVParams(visitor, this.textureMatrix.transform, !this.worldMapping); } case TextureMapping.Mode.Planar: { const normalIndices = visitor.normalIndex; if (!normalIndices) return undefined; // Ignore planar mode unless master or sub units for scaleMode and facet is planar if (!this.worldMapping || (visitor.normalIndex !== undefined && (normalIndices[0] !== normalIndices[1] || normalIndices[0] !== normalIndices[2]))) { return this.computeParametricUVParams(visitor, this.textureMatrix.transform, !this.worldMapping); } else { return this.computePlanarUVParams(visitor, this.textureMatrix.transform); } } case TextureMapping.Mode.ElevationDrape: { return this.computeElevationDrapeUVParams(visitor, this.textureMatrix.transform, localToWorld); } } } /** Computes UV parameters given a texture mapping mode of parametric. */ computeParametricUVParams(visitor, uvTransform, isRelativeUnits) { const params = []; for (let i = 0; i < visitor.numEdgesThisFacet; i++) { let param = Point2d.create(); if (isRelativeUnits || !visitor.tryGetDistanceParameter(i, param)) { if (!visitor.tryGetNormalizedParameter(i, param)) { // If mesh does not have facetFaceData, we still want to use the texture coordinates if they are present param = expectDefined(visitor.getParam(i)); } } params.push(uvTransform.multiplyPoint2d(param)); } return params; } /** Computes UV parameters given a texture mapping mode of planar. The result is stored in the Point2d array given. */ computePlanarUVParams(visitor, uvTransform) { const params = []; const points = visitor.point; let normal; if (visitor.normal === undefined) normal = points.getPoint3dAtUncheckedPointIndex(0).crossProductToPoints(points.getPoint3dAtUncheckedPointIndex(1), points.getPoint3dAtUncheckedPointIndex(2)); else normal = expectDefined(visitor.normal.getVector3dAtCheckedVectorIndex(0)); if (!normal.normalize(normal)) return undefined; // adjust U texture coordinate to be a continuous length starting at the // origin. V coordinate stays the same. This mode assumes Z is up vector // Flipping normal puts us in a planar coordinate system consistent with MicroStation's display system normal.scale(-1.0, normal); // pick the first vertex normal const sideVector = Vector3d.create(normal.y, -normal.x, 0.0); // if the magnitude of the normal is near zero, the real normal points // almost straighten up.. In this case, use Y as the up vector to match QV const magnitude = sideVector.magnitude(); sideVector.normalize(sideVector); // won't remain undefined if failed due to following check.. if (magnitude < 1e-3) { normal.set(0, 0, -1); sideVector.set(1, 0, 0); } const upVector = sideVector.crossProduct(normal).normalize(); if (!upVector) return undefined; const numEdges = visitor.numEdgesThisFacet; for (let i = 0; i < numEdges; i++) { const vector = Vector3d.createFrom(points.getPoint3dAtUncheckedPointIndex(i)); params.push(Point2d.create(vector.dotProduct(sideVector), vector.dotProduct(upVector))); uvTransform.multiplyPoint2d(params[i], params[i]); } return params; } /** Computes UV parameters given a texture mapping mode of elevation drape. The result is stored in the Point2d array given. */ computeElevationDrapeUVParams(visitor, uvTransform, localToWorld) { const params = []; const numEdges = visitor.numEdgesThisFacet; for (let i = 0; i < numEdges; i++) { const point = visitor.point.getPoint3dAtUncheckedPointIndex(i); if (localToWorld !== undefined) localToWorld.multiplyPoint3d(point, point); params.push(Point2d.createFrom(point)); uvTransform.multiplyPoint2d(params[i], params[i]); } return params; } } TextureMapping.Params = Params; })(TextureMapping || (TextureMapping = {})); Object.freeze(TextureMapping.Trans2x3.identity); //# sourceMappingURL=TextureMapping.js.map