| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493 |
- import { AlwaysDepth, EqualDepth, GreaterDepth, GreaterEqualDepth, LessDepth, LessEqualDepth, NeverDepth, NotEqualDepth } from './constants.js';
- /**
- * Finds the minimum value in an array.
- *
- * @private
- * @param {Array<number>} array - The array to search for the minimum value.
- * @return {number} The minimum value in the array, or Infinity if the array is empty.
- */
- function arrayMin( array ) {
- if ( array.length === 0 ) return Infinity;
- let min = array[ 0 ];
- for ( let i = 1, l = array.length; i < l; ++ i ) {
- if ( array[ i ] < min ) min = array[ i ];
- }
- return min;
- }
- /**
- * Finds the maximum value in an array.
- *
- * @private
- * @param {Array<number>} array - The array to search for the maximum value.
- * @return {number} The maximum value in the array, or -Infinity if the array is empty.
- */
- function arrayMax( array ) {
- if ( array.length === 0 ) return - Infinity;
- let max = array[ 0 ];
- for ( let i = 1, l = array.length; i < l; ++ i ) {
- if ( array[ i ] > max ) max = array[ i ];
- }
- return max;
- }
- /**
- * Checks if an array contains values that require Uint32 representation.
- *
- * This function determines whether the array contains any values >= 65535,
- * which would require a Uint32Array rather than a Uint16Array for proper storage.
- * The function iterates from the end of the array, assuming larger values are
- * typically located at the end.
- *
- * @private
- * @param {Array<number>} array - The array to check.
- * @return {boolean} True if the array contains values >= 65535, false otherwise.
- */
- function arrayNeedsUint32( array ) {
- // assumes larger values usually on last
- for ( let i = array.length - 1; i >= 0; -- i ) {
- if ( array[ i ] >= 65535 ) return true; // account for PRIMITIVE_RESTART_FIXED_INDEX, #24565
- }
- return false;
- }
- /**
- * Map of typed array constructor names to their constructors.
- * This mapping enables dynamic creation of typed arrays based on string type names.
- *
- * @private
- * @constant
- * @type {Object<string, TypedArrayConstructor>}
- */
- const TYPED_ARRAYS = {
- Int8Array: Int8Array,
- Uint8Array: Uint8Array,
- Uint8ClampedArray: Uint8ClampedArray,
- Int16Array: Int16Array,
- Uint16Array: Uint16Array,
- Int32Array: Int32Array,
- Uint32Array: Uint32Array,
- Float32Array: Float32Array,
- Float64Array: Float64Array
- };
- /**
- * Creates a typed array of the specified type from the given buffer.
- *
- * @private
- * @param {string} type - The name of the typed array type (e.g., 'Float32Array', 'Uint16Array').
- * @param {ArrayBuffer} buffer - The buffer to create the typed array from.
- * @return {TypedArray} A new typed array of the specified type.
- */
- function getTypedArray( type, buffer ) {
- return new TYPED_ARRAYS[ type ]( buffer );
- }
- /**
- * Returns `true` if the given object is a typed array.
- *
- * @param {any} array - The object to check.
- * @return {boolean} Whether the given object is a typed array.
- */
- function isTypedArray( array ) {
- return ArrayBuffer.isView( array ) && ! ( array instanceof DataView );
- }
- /**
- * Creates an XHTML element with the specified tag name.
- *
- * This function uses the XHTML namespace to create DOM elements,
- * ensuring proper element creation in XML-based contexts.
- *
- * @private
- * @param {string} name - The tag name of the element to create (e.g., 'canvas', 'div').
- * @return {HTMLElement} The created XHTML element.
- */
- function createElementNS( name ) {
- return document.createElementNS( 'http://www.w3.org/1999/xhtml', name );
- }
- /**
- * Creates a canvas element configured for block display.
- *
- * This is a convenience function that creates a canvas element with
- * display style set to 'block', which is commonly used in three.js
- * rendering contexts to avoid inline element spacing issues.
- *
- * @return {HTMLCanvasElement} A canvas element with display set to 'block'.
- */
- function createCanvasElement() {
- const canvas = createElementNS( 'canvas' );
- canvas.style.display = 'block';
- return canvas;
- }
- /**
- * Internal cache for tracking warning messages to prevent duplicate warnings.
- *
- * @private
- * @type {Object<string, boolean>}
- */
- const _cache = {};
- /**
- * Custom console function handler for intercepting log, warn, and error calls.
- *
- * @private
- * @type {Function|null}
- */
- let _setConsoleFunction = null;
- /**
- * Sets a custom function to handle console output.
- *
- * This allows external code to intercept and handle console.log, console.warn,
- * and console.error calls made by three.js, which is useful for custom logging,
- * testing, or debugging workflows.
- *
- * @param {Function} fn - The function to handle console output. Should accept
- * (type, message, ...params) where type is 'log', 'warn', or 'error'.
- */
- function setConsoleFunction( fn ) {
- _setConsoleFunction = fn;
- }
- /**
- * Gets the currently set custom console function.
- *
- * @return {Function|null} The custom console function, or null if not set.
- */
- function getConsoleFunction() {
- return _setConsoleFunction;
- }
- /**
- * Logs an informational message with the 'THREE.' prefix.
- *
- * If a custom console function is set via setConsoleFunction(), it will be used
- * instead of the native console.log. The first parameter is treated as the
- * method name and is automatically prefixed with 'THREE.'.
- *
- * @param {...any} params - The message components. The first param is used as
- * the method name and prefixed with 'THREE.'.
- */
- function log( ...params ) {
- const message = 'THREE.' + params.shift();
- if ( _setConsoleFunction ) {
- _setConsoleFunction( 'log', message, ...params );
- } else {
- console.log( message, ...params );
- }
- }
- /**
- * Enhances log/warn/error messages related to TSL.
- *
- * @param {Array<any>} params - The original message parameters.
- * @returns {Array<any>} The filtered and enhanced message parameters.
- */
- function enhanceLogMessage( params ) {
- const message = params[ 0 ];
- if ( typeof message === 'string' && message.startsWith( 'TSL:' ) ) {
- const stackTrace = params[ 1 ];
- if ( stackTrace && stackTrace.isStackTrace ) {
- params[ 0 ] += ' ' + stackTrace.getLocation();
- } else {
- params[ 1 ] = 'Stack trace not available. Enable "THREE.Node.captureStackTrace" to capture stack traces.';
- }
- }
- return params;
- }
- /**
- * Logs a warning message with the 'THREE.' prefix.
- *
- * If a custom console function is set via setConsoleFunction(), it will be used
- * instead of the native console.warn. The first parameter is treated as the
- * method name and is automatically prefixed with 'THREE.'.
- *
- * @param {...any} params - The message components. The first param is used as
- * the method name and prefixed with 'THREE.'.
- */
- function warn( ...params ) {
- params = enhanceLogMessage( params );
- const message = 'THREE.' + params.shift();
- if ( _setConsoleFunction ) {
- _setConsoleFunction( 'warn', message, ...params );
- } else {
- const stackTrace = params[ 0 ];
- if ( stackTrace && stackTrace.isStackTrace ) {
- console.warn( stackTrace.getError( message ) );
- } else {
- console.warn( message, ...params );
- }
- }
- }
- /**
- * Logs an error message with the 'THREE.' prefix.
- *
- * If a custom console function is set via setConsoleFunction(), it will be used
- * instead of the native console.error. The first parameter is treated as the
- * method name and is automatically prefixed with 'THREE.'.
- *
- * @param {...any} params - The message components. The first param is used as
- * the method name and prefixed with 'THREE.'.
- */
- function error( ...params ) {
- params = enhanceLogMessage( params );
- const message = 'THREE.' + params.shift();
- if ( _setConsoleFunction ) {
- _setConsoleFunction( 'error', message, ...params );
- } else {
- const stackTrace = params[ 0 ];
- if ( stackTrace && stackTrace.isStackTrace ) {
- console.error( stackTrace.getError( message ) );
- } else {
- console.error( message, ...params );
- }
- }
- }
- /**
- * Logs a warning message only once, preventing duplicate warnings.
- *
- * This function maintains an internal cache of warning messages and will only
- * output each unique warning message once. Useful for warnings that may be
- * triggered repeatedly but should only be shown to the user once.
- *
- * @param {...any} params - The warning message components.
- */
- function warnOnce( ...params ) {
- const message = params.join( ' ' );
- if ( message in _cache ) return;
- _cache[ message ] = true;
- warn( ...params );
- }
- /**
- * Yields execution to the main thread to allow rendering and other tasks.
- * Uses scheduler.yield() when available (Chrome 115+), falls back to requestAnimationFrame.
- *
- * @return {Promise<void>}
- */
- function yieldToMain() {
- if ( typeof self !== 'undefined' && typeof self.scheduler !== 'undefined' && typeof self.scheduler.yield !== 'undefined' ) {
- return self.scheduler.yield();
- }
- return new Promise( resolve => {
- requestAnimationFrame( resolve );
- } );
- }
- /**
- * Asynchronously probes for WebGL sync object completion.
- *
- * This function creates a promise that resolves when the WebGL sync object
- * signals completion or rejects if the sync operation fails. It uses polling
- * at the specified interval to check the sync status without blocking the
- * main thread. This is useful for GPU-CPU synchronization in WebGL contexts.
- *
- * @private
- * @param {WebGL2RenderingContext} gl - The WebGL rendering context.
- * @param {WebGLSync} sync - The WebGL sync object to wait for.
- * @param {number} interval - The polling interval in milliseconds.
- * @return {Promise<void>} A promise that resolves when the sync completes or rejects if it fails.
- */
- function probeAsync( gl, sync, interval ) {
- return new Promise( function ( resolve, reject ) {
- function probe() {
- switch ( gl.clientWaitSync( sync, gl.SYNC_FLUSH_COMMANDS_BIT, 0 ) ) {
- case gl.WAIT_FAILED:
- reject();
- break;
- case gl.TIMEOUT_EXPIRED:
- setTimeout( probe, interval );
- break;
- default:
- resolve();
- }
- }
- setTimeout( probe, interval );
- } );
- }
- /**
- * Converts a projection matrix from normalized device coordinates (NDC)
- * range [-1, 1] to [0, 1].
- *
- * This conversion is commonly needed when working with depth textures or
- * render targets that expect depth values in the [0, 1] range rather than
- * the standard OpenGL NDC range of [-1, 1]. The function modifies the
- * projection matrix in place.
- *
- * @private
- * @param {Matrix4} projectionMatrix - The projection matrix to convert (modified in place).
- */
- function toNormalizedProjectionMatrix( projectionMatrix ) {
- const m = projectionMatrix.elements;
- // Convert [-1, 1] to [0, 1] projection matrix
- m[ 2 ] = 0.5 * m[ 2 ] + 0.5 * m[ 3 ];
- m[ 6 ] = 0.5 * m[ 6 ] + 0.5 * m[ 7 ];
- m[ 10 ] = 0.5 * m[ 10 ] + 0.5 * m[ 11 ];
- m[ 14 ] = 0.5 * m[ 14 ] + 0.5 * m[ 15 ];
- }
- /**
- * Reverses the depth range of a projection matrix.
- *
- * This function inverts the depth mapping of a projection matrix, which is
- * useful for reversed-Z depth buffer techniques that can improve depth
- * precision. The function handles both perspective and orthographic projection
- * matrices differently and modifies the matrix in place.
- *
- * For perspective matrices (where m[11] === -1), the depth mapping is
- * reversed with an offset. For orthographic matrices, a simpler reversal
- * is applied.
- *
- * @private
- * @param {Matrix4} projectionMatrix - The projection matrix to reverse (modified in place).
- */
- function toReversedProjectionMatrix( projectionMatrix ) {
- const m = projectionMatrix.elements;
- const isPerspectiveMatrix = m[ 11 ] === - 1;
- // Reverse [0, 1] projection matrix
- if ( isPerspectiveMatrix ) {
- m[ 10 ] = - m[ 10 ] - 1;
- m[ 14 ] = - m[ 14 ];
- } else {
- m[ 10 ] = - m[ 10 ];
- m[ 14 ] = - m[ 14 ] + 1;
- }
- }
- /**
- * Used to select the correct depth functions
- * when reversed depth buffer is used.
- *
- * @private
- * @type {Object}
- */
- const ReversedDepthFuncs = {
- [ NeverDepth ]: AlwaysDepth,
- [ LessDepth ]: GreaterDepth,
- [ EqualDepth ]: NotEqualDepth,
- [ LessEqualDepth ]: GreaterEqualDepth,
- [ AlwaysDepth ]: NeverDepth,
- [ GreaterDepth ]: LessDepth,
- [ NotEqualDepth ]: EqualDepth,
- [ GreaterEqualDepth ]: LessEqualDepth,
- };
- export { arrayMin, arrayMax, arrayNeedsUint32, getTypedArray, createElementNS, createCanvasElement, setConsoleFunction, getConsoleFunction, log, warn, error, warnOnce, probeAsync, yieldToMain, toNormalizedProjectionMatrix, toReversedProjectionMatrix, isTypedArray, ReversedDepthFuncs };
|