Reference Source

src/viewer/scene/math/rtcCoords.js

import {math} from './math.js';

const tempVec3a = math.vec3();
const tempVec4  = math.vec4();

/**
 * Given a view matrix and a relative-to-center (RTC) coordinate origin, returns a view matrix
 * to transform RTC coordinates to View-space.
 *
 * The returned view matrix is
 *
 * @private
 */
const createRTCViewMat = (function () {

    const tempMat = new Float64Array(16);
    const rtcCenterWorld = new Float64Array(4);
    const rtcCenterView = new Float64Array(4);

    return function (viewMat, rtcCenter, rtcViewMat) {
        rtcViewMat = rtcViewMat || tempMat;
        rtcCenterWorld[0] = rtcCenter[0];
        rtcCenterWorld[1] = rtcCenter[1];
        rtcCenterWorld[2] = rtcCenter[2];
        rtcCenterWorld[3] = 1;
        math.transformVec4(viewMat, rtcCenterWorld, rtcCenterView);
        math.setMat4Translation(viewMat, rtcCenterView, rtcViewMat);
        return rtcViewMat.slice ();
    }
}());

/**
 * Converts a World-space 3D position to RTC.
 *
 * Given a double-precision World-space position, returns a double-precision relative-to-center (RTC) center pos
 * and a single-precision offset fom that center.
 * @private
 * @param {Float64Array} worldPos The World-space position.
 * @param {Float64Array} rtcCenter Double-precision relative-to-center (RTC) center pos.
 * @param {Float32Array} rtcPos Single-precision offset fom that center.
 */
function worldToRTCPos(worldPos, rtcCenter, rtcPos) {

    const xHigh = Float32Array.from([worldPos[0]])[0];
    const xLow = worldPos[0] - xHigh;

    const yHigh = Float32Array.from([worldPos[1]])[0];
    const yLow = worldPos[1] - yHigh;

    const zHigh = Float32Array.from([worldPos[2]])[0];
    const zLow = worldPos[2] - zHigh;

    rtcCenter[0] = xHigh;
    rtcCenter[1] = yHigh;
    rtcCenter[2] = zHigh;

    rtcPos[0] = xLow;
    rtcPos[1] = yLow;
    rtcPos[2] = zLow;
}


/**
 * Converts a flat array of double-precision positions to RTC positions, if necessary.
 *
 * Conversion is necessary if the coordinates have values larger than can be expressed at single-precision. When
 * that's the case, then this function will compute the RTC coordinates and RTC center and return true. Otherwise
 * this function does nothing and returns false.
 *
 * When computing the RTC position, this function uses a modulus operation to ensure that, whenever possible,
 * identical RTC centers are reused for different positions arrays.
 *
 * @private
 * @param {Float64Array} worldPositions Flat array of World-space 3D positions.
 * @param {Float64Array} rtcPositions Outputs the computed flat array of 3D RTC positions.
 * @param {Float64Array} rtcCenter Outputs the computed double-precision relative-to-center (RTC) center pos.
 * @param {Number} [cellSize=10000000] The size of each coordinate cell within the RTC coordinate system.
 * @returns {Boolean} ````True```` if the positions actually needed conversion to RTC, else ````false````. When
 * ````false````, we can safely ignore the data returned in ````rtcPositions```` and ````rtcCenter````,
 * since ````rtcCenter```` will equal ````[0,0,0]````, and ````rtcPositions```` will contain identical values to ````positions````.
 */
function worldToRTCPositions(worldPositions, rtcPositions, rtcCenter, cellSize = 1000) {

    const center = math.getPositionsCenter(worldPositions, tempVec3a);

    const rtcCenterX = Math.round(center[0] / cellSize) * cellSize;
    const rtcCenterY = Math.round(center[1] / cellSize) * cellSize;
    const rtcCenterZ = Math.round(center[2] / cellSize) * cellSize;

    rtcCenter[0] = rtcCenterX;
    rtcCenter[1] = rtcCenterY;
    rtcCenter[2] = rtcCenterZ;

    const rtcNeeded = (rtcCenter[0] !== 0 || rtcCenter[1] !== 0 || rtcCenter[2] !== 0);

    if (rtcNeeded) {
        for (let i = 0, len = worldPositions.length; i < len; i += 3) {
            rtcPositions[i + 0] = worldPositions[i + 0] - rtcCenterX;
            rtcPositions[i + 1] = worldPositions[i + 1] - rtcCenterY;
            rtcPositions[i + 2] = worldPositions[i + 2] - rtcCenterZ;
        }
    }

    return rtcNeeded;
}

/**
 * Converts an RTC 3D position to World-space.
 *
 * @private
 * @param {Float64Array} rtcCenter Double-precision relative-to-center (RTC) center pos.
 * @param {Float32Array} rtcPos Single-precision offset fom that center.
 * @param {Float64Array} worldPos The World-space position.
 */
function rtcToWorldPos(rtcCenter, rtcPos, worldPos) {
    worldPos[0] = rtcCenter[0] + rtcPos[0];
    worldPos[1] = rtcCenter[1] + rtcPos[1];
    worldPos[2] = rtcCenter[2] + rtcPos[2];
    return worldPos;
}

/**
 * Given a 3D plane defined by distance from origin and direction, and an RTC center position,
 * return a plane position that is relative to the RTC center.
 *
 * @private
 * @param dist
 * @param dir
 * @param rtcCenter
 * @param rtcPlanePos
 * @returns {*}
 */
function getPlaneRTCPos(dist, dir, rtcOrigin, rtcPlanePos, originMatrix) {
    const rtcCenter = (originMatrix
                       ? (tempVec4.set(rtcOrigin, 0), tempVec4[3] = 1, math.mulMat4v4(originMatrix, tempVec4, tempVec4))
                       : rtcOrigin);
    const rtcCenterToPlaneDist = math.dotVec3(dir, rtcCenter) + dist;
    const dirNormalized = math.normalizeVec3(dir, tempVec3a);
    math.mulVec3Scalar(dirNormalized, -rtcCenterToPlaneDist, rtcPlanePos);
    return rtcPlanePos;
}

export {createRTCViewMat, worldToRTCPos, worldToRTCPositions, rtcToWorldPos, getPlaneRTCPos};