@itwin/core-frontend
Version:
iTwin.js frontend components
201 lines • 9.12 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 { Range1d, Range2d, Vector3d, } from "@itwin/core-geometry";
import { OctEncodedNormal, QParams2d, QPoint2d, QPoint3d, Quantization, } from "@itwin/core-common";
import { RealityMeshParamsBuilder } from "../../render/RealityMeshParams";
class UpsampleIndexMap extends Map {
_next = 0;
indices = new Array();
addTriangle(indices) {
for (const index of indices) {
let mapIndex = this.get(index);
if (undefined === mapIndex)
this.set(index, mapIndex = this._next++);
this.indices.push(mapIndex);
}
}
}
class ClipAxis {
vertical;
lessThan;
value;
constructor(vertical, lessThan, value) {
this.vertical = vertical;
this.lessThan = lessThan;
this.value = value;
}
}
export function upsampleRealityMeshParams(params, uvSampleRange) {
const indexMap = new UpsampleIndexMap();
const uvParams = QParams2d.fromZeroToOne();
const uvLow = QPoint2d.create(uvSampleRange.low, uvParams);
const uvHigh = QPoint2d.create(uvSampleRange.high, uvParams);
const uvRange = Range2d.createXYXY(uvLow.x, uvLow.y, uvHigh.x, uvHigh.y);
const clipAxes = new Array();
const addedPoints = new Array(), addedParams = new Array(), addedNormals = new Array();
if (uvLow.x > 0)
clipAxes.push(new ClipAxis(true, false, uvLow.x));
if (uvHigh.x < Quantization.rangeScale16)
clipAxes.push(new ClipAxis(true, true, uvHigh.x));
if (uvLow.y > 0)
clipAxes.push(new ClipAxis(false, false, uvLow.y));
if (uvHigh.y < Quantization.rangeScale16)
clipAxes.push(new ClipAxis(false, true, uvHigh.y));
const triangleRange = Range2d.createNull();
for (let i = 0; i < params.indices.length;) {
const triangleIndices = [params.indices[i++], params.indices[i++], params.indices[i++]];
Range2d.createNull(triangleRange);
for (const index of triangleIndices) {
const paramIndex = 2 * index;
triangleRange.extendXY(params.uvs.points[paramIndex], params.uvs.points[paramIndex + 1]);
}
if (uvRange.intersectsRange(triangleRange)) {
if (uvRange.containsRange(triangleRange)) {
indexMap.addTriangle(triangleIndices);
}
else {
addClipped(params, triangleIndices, indexMap, clipAxes, 0, addedPoints, addedParams, addedNormals);
}
}
}
const parentPoints = params.positions;
const parentParams = params.uvs;
const parentNormals = params.normals;
const parentPointCount = parentPoints.points.length / 3;
const zRange = Range1d.createNull();
const builder = new RealityMeshParamsBuilder({
positionRange: parentPoints.params.computeRange(),
initialVertexCapacity: indexMap.size,
initialIndexCapacity: indexMap.indices.length,
wantNormals: parentNormals !== undefined,
});
const pos = new QPoint3d();
const uv = new QPoint2d();
for (const entry of indexMap.entries()) {
const parentIndex = entry[0];
let normal;
if (parentIndex < parentPointCount) {
const pointIndex = 3 * parentIndex;
pos.setFromScalars(parentPoints.points[pointIndex], parentPoints.points[pointIndex + 1], parentPoints.points[pointIndex + 2]);
const paramIndex = 2 * parentIndex;
uv.setFromScalars(parentParams.points[paramIndex], parentParams.points[paramIndex + 1]);
if (parentNormals)
normal = parentNormals[parentIndex];
}
else {
const addedIndex = parentIndex - parentPointCount;
addedPoints[addedIndex].clone(pos);
addedParams[addedIndex].clone(uv);
if (addedNormals.length > 0)
normal = addedNormals[addedIndex];
}
builder.addQuantizedVertex(pos, uv, normal);
zRange.extendX(pos.z);
}
builder.addIndices(indexMap.indices);
const mesh = builder.finish();
const qParams = mesh.positions.params;
return {
mesh: builder.finish(),
heightRange: Range1d.createXX(Quantization.unquantize(zRange.low, qParams.origin.z, qParams.scale.z), Quantization.unquantize(zRange.high, qParams.origin.z, qParams.scale.z)),
};
}
function interpolate(value0, value1, fraction) {
return value0 + (value1 - value0) * fraction;
}
function interpolateInt(value0, value1, fraction) {
return Math.floor(.5 + interpolate(value0, value1, fraction));
}
function interpolateQPoint3d(qPoint, qNext, fraction) {
return QPoint3d.fromScalars(interpolateInt(qPoint.x, qNext.x, fraction), interpolateInt(qPoint.y, qNext.y, fraction), interpolateInt(qPoint.z, qNext.z, fraction));
}
function interpolateQPoint2d(qPoint, qNext, fraction) {
return QPoint2d.fromScalars(interpolateInt(qPoint.x, qNext.x, fraction), interpolateInt(qPoint.y, qNext.y, fraction));
}
function interpolateOctEncodedNormal(normal0, normal1, fraction) {
const n0 = OctEncodedNormal.decodeValue(normal0);
const n1 = OctEncodedNormal.decodeValue(normal1);
if (undefined !== n0 && undefined !== n1) {
const n = Vector3d.create(interpolate(n0.x, n1.x, fraction), interpolate(n0.y, n1.y, fraction), interpolate(n0.z, n1.z, fraction));
n.normalizeInPlace();
return OctEncodedNormal.encode(n);
}
else {
return OctEncodedNormal.encode(Vector3d.create(0, 0, 1));
}
}
function addClipped(params, triangleIndices, indexMap, clipAxes, clipIndex, addedPoints, addedParams, addedNormals) {
if (clipIndex === clipAxes.length) {
indexMap.addTriangle(triangleIndices);
return;
}
const inside = [false, false, false];
const values = [0, 0, 0];
const clipOutput = [];
const parentPoints = params.positions.points;
const parentParams = params.uvs.points;
const parentNormals = params.normals;
const clipAxis = clipAxes[clipIndex++];
const clipValue = clipAxis.value;
const parentPointCount = parentPoints.length / 3;
const scratchQPoint3d = new QPoint3d(), scratchQPoint3d1 = new QPoint3d();
const scratchQPoint2d = new QPoint2d(), scratchQPoint2d1 = new QPoint2d();
const getPoint = (index, result) => {
if (index < parentPointCount) {
const pointIndex = index * 3;
result.setFromScalars(parentPoints[pointIndex], parentPoints[pointIndex + 1], parentPoints[pointIndex + 2]);
}
else {
addedPoints[index - parentPointCount].clone(result);
}
return result;
};
const getParam = (index, result) => {
if (index < parentPointCount) {
const pointIndex = index * 2;
result.setFromScalars(parentParams[pointIndex], parentParams[pointIndex + 1]);
}
else {
addedParams[index - parentPointCount].clone(result);
}
return result;
};
const getNormal = (index) => {
if (!parentNormals)
return undefined;
return (index < parentPointCount) ? parentNormals[index] : addedNormals[index - parentPointCount];
};
for (let i = 0; i < 3; i++) {
const index = triangleIndices[i];
const thisParam = getParam(index, scratchQPoint2d);
const thisValue = clipAxis.vertical ? thisParam.x : thisParam.y;
values[i] = thisValue;
inside[i] = clipAxis.lessThan ? (thisValue < clipValue) : (thisValue > clipValue);
}
for (let i = 0; i < 3; i++) {
const index = triangleIndices[i];
const next = (i + 1) % 3;
if (inside[i])
clipOutput.push(index);
if (inside[i] !== inside[next]) {
const nextIndex = triangleIndices[next];
const fraction = (clipValue - values[i]) / (values[next] - values[i]);
clipOutput.push(parentPointCount + addedPoints.length);
addedPoints.push(interpolateQPoint3d(getPoint(index, scratchQPoint3d), getPoint(nextIndex, scratchQPoint3d1), fraction));
addedParams.push(interpolateQPoint2d(getParam(index, scratchQPoint2d), getParam(nextIndex, scratchQPoint2d1), fraction));
if (parentNormals)
addedNormals.push(interpolateOctEncodedNormal(getNormal(index), getNormal(nextIndex), fraction));
}
}
if (clipOutput.length > 2) {
addClipped(params, clipOutput.slice(0, 3), indexMap, clipAxes, clipIndex, addedPoints, addedParams, addedNormals);
if (clipOutput.length > 3)
addClipped(params, [clipOutput[0], clipOutput[2], clipOutput[3]], indexMap, clipAxes, clipIndex, addedPoints, addedParams, addedNormals);
}
}
//# sourceMappingURL=UpsampleRealityMeshParams.js.map