@gltf-transform/functions
Version:
Functions for common glTF modifications, written using the core API
80 lines (66 loc) • 2.35 kB
text/typescript
import type { Accessor, Document, Primitive, Transform, vec3 } from '@gltf-transform/core';
import { createTransform } from './utils.js';
const NAME = 'vertexColorSpace';
/** Options for the {@link vertexColorSpace} function. */
export interface ColorSpaceOptions {
/** Input color space of vertex colors, to be converted to "srgb-linear". Required. */
inputColorSpace: 'srgb' | 'srgb-linear';
}
/**
* Vertex color color space correction. The glTF format requires vertex colors to be stored
* in Linear Rec. 709 D65 color space, and this function provides a way to correct vertex
* colors that are (incorrectly) stored in sRGB.
*
* Example:
*
* ```typescript
* import { vertexColorSpace } from '@gltf-transform/functions';
*
* await document.transform(
* vertexColorSpace({ inputColorSpace: 'srgb' })
* );
* ```
*
* @category Transforms
*/
export function vertexColorSpace(options: ColorSpaceOptions): Transform {
return createTransform(NAME, (doc: Document): void => {
const logger = doc.getLogger();
const inputColorSpace = (options.inputColorSpace || '').toLowerCase();
if (inputColorSpace === 'srgb-linear') {
logger.info(`${NAME}: Vertex colors already linear. Skipping conversion.`);
return;
}
if (inputColorSpace !== 'srgb') {
logger.error(
`${NAME}: Unknown input color space "${inputColorSpace}" – should be "srgb" or ` +
'"srgb-linear". Skipping conversion.',
);
return;
}
const converted = new Set<Accessor>();
// Source: THREE.Color
function sRGBToLinear(c: number): number {
return c < 0.04045 ? c * 0.0773993808 : Math.pow(c * 0.9478672986 + 0.0521327014, 2.4);
}
function updatePrimitive(primitive: Primitive): void {
const color = [0, 0, 0] as vec3;
let attribute: Accessor | null;
for (let i = 0; (attribute = primitive.getAttribute(`COLOR_${i}`)); i++) {
if (converted.has(attribute)) continue;
for (let j = 0; j < attribute.getCount(); j++) {
attribute.getElement(j, color);
color[0] = sRGBToLinear(color[0]);
color[1] = sRGBToLinear(color[1]);
color[2] = sRGBToLinear(color[2]);
attribute.setElement(j, color);
}
converted.add(attribute);
}
}
doc.getRoot()
.listMeshes()
.forEach((mesh) => mesh.listPrimitives().forEach(updatePrimitive));
logger.debug(`${NAME}: Complete.`);
});
}