@gltf-transform/functions
Advanced tools
Comparing version 3.7.4 to 3.7.5
@@ -9,4 +9,2 @@ import { Transform } from '@gltf-transform/core'; | ||
keepAttributes?: boolean; | ||
/** Whether to keep single-color textures that can be converted to material factors. */ | ||
keepSolidTextures?: boolean; | ||
} | ||
@@ -13,0 +11,0 @@ /** |
{ | ||
"name": "@gltf-transform/functions", | ||
"version": "3.7.4", | ||
"version": "3.7.5", | ||
"repository": "github:donmccurdy/glTF-Transform", | ||
@@ -39,4 +39,4 @@ "homepage": "https://gltf-transform.dev/functions.html", | ||
"dependencies": { | ||
"@gltf-transform/core": "^3.7.4", | ||
"@gltf-transform/extensions": "^3.7.4", | ||
"@gltf-transform/core": "^3.7.5", | ||
"@gltf-transform/extensions": "^3.7.5", | ||
"ktx-parse": "^0.6.0", | ||
@@ -57,3 +57,3 @@ "ndarray": "^1.0.19", | ||
}, | ||
"gitHead": "22d81ccfcc651f14ab7a6e4cb3dd73ff5c4ec833" | ||
"gitHead": "bf05150bcaa3df7add9c86ba8ffaffae3fdd94a7" | ||
} |
153
src/prune.ts
import { | ||
AnimationChannel, | ||
ColorUtils, | ||
Document, | ||
ExtensionProperty, | ||
Graph, | ||
ILogger, | ||
Material, | ||
Node, | ||
Primitive, | ||
PrimitiveTarget, | ||
Property, | ||
PropertyType, | ||
Root, | ||
Transform, | ||
Node, | ||
Scene, | ||
ExtensionProperty, | ||
Material, | ||
Primitive, | ||
PrimitiveTarget, | ||
Texture, | ||
TextureInfo, | ||
Transform, | ||
vec3, | ||
vec4, | ||
} from '@gltf-transform/core'; | ||
import { mul as mulVec3 } from 'gl-matrix/vec3'; | ||
import { add, create, len, mul, scale, sub } from 'gl-matrix/vec4'; | ||
import { NdArray } from 'ndarray'; | ||
import { getPixels } from 'ndarray-pixels'; | ||
import { getTextureColorSpace } from './get-texture-color-space.js'; | ||
import { createTransform } from './utils.js'; | ||
import { listTextureInfoByMaterial } from './list-texture-info.js'; | ||
import { listTextureSlots } from './list-texture-slots.js'; | ||
import { createTransform } from './utils.js'; | ||
const NAME = 'prune'; | ||
const EPS = 3 / 255; | ||
export interface PruneOptions { | ||
@@ -42,4 +30,2 @@ /** List of {@link PropertyType} identifiers to be de-duplicated.*/ | ||
keepAttributes?: boolean; | ||
/** Whether to keep single-color textures that can be converted to material factors. */ | ||
keepSolidTextures?: boolean; | ||
} | ||
@@ -62,3 +48,2 @@ const PRUNE_DEFAULTS: Required<PruneOptions> = { | ||
keepAttributes: true, | ||
keepSolidTextures: true, | ||
}; | ||
@@ -90,6 +75,6 @@ | ||
return createTransform(NAME, async (document: Document): Promise<void> => { | ||
const logger = document.getLogger(); | ||
const root = document.getRoot(); | ||
const graph = document.getGraph(); | ||
return createTransform(NAME, (doc: Document): void => { | ||
const logger = doc.getLogger(); | ||
const root = doc.getRoot(); | ||
const graph = doc.getGraph(); | ||
@@ -129,3 +114,3 @@ const disposed: Record<string, number> = {}; | ||
const material = prim.getMaterial(); | ||
const required = listRequiredSemantics(document, material); | ||
const required = listRequiredSemantics(doc, material); | ||
const unused = listUnusedSemantics(prim, required); | ||
@@ -169,10 +154,3 @@ pruneAttributes(prim, unused); | ||
if (propertyTypes.has(PropertyType.MATERIAL)) root.listMaterials().forEach(treeShake); | ||
if (propertyTypes.has(PropertyType.TEXTURE)) { | ||
root.listTextures().forEach(treeShake); | ||
if (!options.keepSolidTextures) { | ||
await pruneSolidTextures(document); | ||
} | ||
} | ||
if (propertyTypes.has(PropertyType.TEXTURE)) root.listTextures().forEach(treeShake); | ||
if (propertyTypes.has(PropertyType.ACCESSOR)) root.listAccessors().forEach(treeShake); | ||
@@ -278,3 +256,3 @@ if (propertyTypes.has(PropertyType.BUFFER)) root.listBuffers().forEach(treeShake); | ||
material: Material | ExtensionProperty | null, | ||
semantics = new Set<string>(), | ||
semantics = new Set<string>() | ||
): Set<string> { | ||
@@ -365,104 +343,1 @@ if (!material) return semantics; | ||
} | ||
/********************************************************************************************** | ||
* Prune solid (single-color) textures. | ||
*/ | ||
async function pruneSolidTextures(document: Document): Promise<void> { | ||
const root = document.getRoot(); | ||
const graph = document.getGraph(); | ||
const logger = document.getLogger(); | ||
const textures = root.listTextures(); | ||
const pending = textures.map(async (texture) => { | ||
const factor = await getTextureFactor(texture); | ||
if (!factor) return; | ||
if (getTextureColorSpace(texture) === 'srgb') { | ||
ColorUtils.convertSRGBToLinear(factor, factor); | ||
} | ||
const name = texture.getName() || texture.getURI(); | ||
const size = texture.getSize()?.join('x'); | ||
const slots = listTextureSlots(texture); | ||
for (const edge of graph.listParentEdges(texture)) { | ||
const parent = edge.getParent(); | ||
if (parent !== root && applyMaterialFactor(parent as Material, factor, edge.getName(), logger)) { | ||
edge.dispose(); | ||
} | ||
} | ||
if (texture.listParents().length === 1) { | ||
texture.dispose(); | ||
logger.debug(`${NAME}: Removed single-color texture "${name}" (${size}px ${slots.join(', ')})`); | ||
} | ||
}); | ||
await Promise.all(pending); | ||
} | ||
function applyMaterialFactor( | ||
material: Material | ExtensionProperty, | ||
factor: vec4, | ||
slot: string, | ||
logger: ILogger, | ||
): boolean { | ||
if (material instanceof Material) { | ||
switch (slot) { | ||
case 'baseColorTexture': | ||
material.setBaseColorFactor(mul(factor, factor, material.getBaseColorFactor()) as vec4); | ||
return true; | ||
case 'emissiveTexture': | ||
material.setEmissiveFactor( | ||
mulVec3([0, 0, 0], factor.slice(0, 3) as vec3, material.getEmissiveFactor()) as vec3, | ||
); | ||
return true; | ||
case 'occlusionTexture': | ||
return Math.abs(factor[0] - 1) <= EPS; | ||
case 'metallicRoughnessTexture': | ||
material.setRoughnessFactor(factor[1] * material.getRoughnessFactor()); | ||
material.setMetallicFactor(factor[2] * material.getMetallicFactor()); | ||
return true; | ||
case 'normalTexture': | ||
return len(sub(create(), factor, [0.5, 0.5, 1, 1])) <= EPS; | ||
} | ||
} | ||
logger.warn(`${NAME}: Detected single-color ${slot} texture. Pruning ${slot} not yet supported.`); | ||
return false; | ||
} | ||
async function getTextureFactor(texture: Texture): Promise<vec4 | null> { | ||
const pixels = await maybeGetPixels(texture); | ||
if (!pixels) return null; | ||
const min: vec4 = [Infinity, Infinity, Infinity, Infinity]; | ||
const max: vec4 = [-Infinity, -Infinity, -Infinity, -Infinity]; | ||
const target: vec4 = [0, 0, 0, 0]; | ||
const [width, height] = pixels.shape; | ||
for (let i = 0; i < width; i++) { | ||
for (let j = 0; j < height; j++) { | ||
for (let k = 0; k < 4; k++) { | ||
min[k] = Math.min(min[k], pixels.get(i, j, k)); | ||
max[k] = Math.max(max[k], pixels.get(i, j, k)); | ||
} | ||
} | ||
if (len(sub(target, max, min)) / 255 > EPS) { | ||
return null; | ||
} | ||
} | ||
return scale(target, add(target, max, min), 0.5 / 255) as vec4; | ||
} | ||
async function maybeGetPixels(texture: Texture): Promise<NdArray<Uint8Array> | null> { | ||
try { | ||
return await getPixels(texture.getImage()!, texture.getMimeType()); | ||
} catch (e) { | ||
return null; | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1392401
7670
+ Addedbare-stream@2.4.0(transitive)
- Removedbare-stream@2.4.2(transitive)
Updated@gltf-transform/core@^3.7.5