@itwin/core-common
Version:
iTwin.js components common to frontend and backend
249 lines • 13.1 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 { 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