Skip to content

Commit f9b69af

Browse files
authored
feat(functions): Add 'weights' option for unwrapPrimitives() (#1666)
1 parent 09230c9 commit f9b69af

File tree

1 file changed

+77
-14
lines changed

1 file changed

+77
-14
lines changed

packages/functions/src/unwrap.ts

Lines changed: 77 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Document, Transform, Primitive, TypedArrayConstructor, vec2 } from '@gltf-transform/core';
1+
import { Document, Transform, Primitive, TypedArrayConstructor, vec2, Mesh, Node, vec3 } from '@gltf-transform/core';
22
import { Accessor } from '@gltf-transform/core';
33
import { createTransform, isUsed, shallowCloneAccessor } from './utils.js';
44
import type * as watlas from 'watlas';
@@ -14,22 +14,47 @@ interface IWatlas {
1414
};
1515
}
1616

17-
/** Options for {@link unwrapPrimitives} and {@link unwrap} functions. */
18-
export interface UnwrapPrimitivesOptions {
17+
/** Options for the {@link unwrap} transform. */
18+
export interface UnwrapOptions {
1919
/** watlas instance. */
2020
watlas: unknown;
21-
/** Target texture coordinate index (0, 1, 2, ...) for generated unwrapping. Default: 0. */
21+
/**
22+
* Target texture coordinate index (0, 1, 2, ...) for generated unwrapping.
23+
* Default: 0.
24+
*/
2225
texcoord?: number;
23-
/** Whether to overwrite existing attributes at the target texCoord index, if any. Default: false. */
26+
/**
27+
* Whether to overwrite existing attributes at the target texCoord index, if
28+
* any. Default: false.
29+
*/
2430
overwrite?: boolean;
31+
/**
32+
* Methods of grouping texcoords with the {@link unwrap} function.
33+
* Default: 'mesh'.
34+
*/
35+
groupBy?: 'primitive' | 'mesh' | 'scene';
2536
}
2637

27-
/** Options for the {@link unwrap} transform. */
28-
export interface UnwrapOptions extends UnwrapPrimitivesOptions {
38+
/** Options for the {@link unwrapPrimitives} function. */
39+
export interface UnwrapPrimitivesOptions {
40+
/** watlas instance. */
41+
watlas: unknown;
2942
/**
30-
* Methods of grouping texcoords with the {@link unwrap} function. Default: 'mesh'.
43+
* Target texture coordinate index (0, 1, 2, ...) for generated unwrapping.
44+
* Default: 0.
3145
*/
32-
groupBy?: 'primitive' | 'mesh' | 'scene';
46+
texcoord?: number;
47+
/**
48+
* Whether to overwrite existing attributes at the target texCoord index, if
49+
* any. Default: false.
50+
*/
51+
overwrite?: boolean;
52+
/**
53+
* Per-primitive texel density weights. Texel space in the atlas is allocated
54+
* proportionally with geometry dimensions in local space. If specified,
55+
* weights scale the allocation. Default: [1, 1, 1, ...].
56+
*/
57+
weights?: number[];
3358
}
3459

3560
export const UNWRAP_DEFAULTS: Required<Omit<UnwrapOptions, 'watlas'>> = {
@@ -97,11 +122,16 @@ export function unwrap(_options: UnwrapOptions): Transform {
97122
break;
98123
}
99124
case 'scene': {
100-
const scenePrims = [];
125+
const prims: Primitive[] = [];
126+
const weights: number[] = [];
101127
for (const mesh of document.getRoot().listMeshes()) {
102-
scenePrims.push(...mesh.listPrimitives());
128+
const weight = getNodeScaleMax(mesh);
129+
for (const prim of mesh.listPrimitives()) {
130+
prims.push(prim);
131+
weights.push(weight);
132+
}
103133
}
104-
unwrapPrimitives(scenePrims, options);
134+
unwrapPrimitives(prims, { ...options, weights });
105135
break;
106136
}
107137
}
@@ -161,7 +191,10 @@ export function unwrapPrimitives(primitives: Primitive[], options: UnwrapPrimiti
161191
const atlas = new watlas.Atlas();
162192

163193
const unwrapPrims = [];
164-
for (const prim of primitives) {
194+
for (let i = 0; i < primitives.length; i++) {
195+
const prim = primitives[i];
196+
const primWeight = options.weights ? options.weights[i] : 1;
197+
165198
// Don't process primitives that already have the desired TEXCOORD index
166199
// if overwrite is false.
167200
if (!options.overwrite && prim.getAttribute(dstSemantic)) {
@@ -175,7 +208,7 @@ export function unwrapPrimitives(primitives: Primitive[], options: UnwrapPrimiti
175208

176209
const meshDecl: watlas.MeshDecl = {
177210
vertexCount: position.getCount(),
178-
vertexPositionData: getAttributeFloat32Array(position),
211+
vertexPositionData: getScaledAttributeFloat32Array(position, primWeight),
179212
vertexPositionStride: position.getElementSize() * Float32Array.BYTES_PER_ELEMENT,
180213
};
181214

@@ -317,3 +350,33 @@ function getAttributeFloat32Array(attribute: Accessor): Float32Array {
317350
}
318351
return dequantizeAttributeArray(attribute.getArray()!, attribute.getComponentType(), attribute.getNormalized());
319352
}
353+
354+
// Returns scaled values of the given attribute as a Float32Array.
355+
function getScaledAttributeFloat32Array(attribute: Accessor, scale: number): Float32Array {
356+
const array = dequantizeAttributeArray(
357+
attribute.getArray()!,
358+
attribute.getComponentType(),
359+
attribute.getNormalized(),
360+
);
361+
362+
for (let i = 0; i < array.length; i++) {
363+
array[i] *= scale;
364+
}
365+
366+
return array;
367+
}
368+
369+
function getNodeScaleMax(mesh: Mesh): number {
370+
let scale = -Infinity;
371+
372+
for (const parent of mesh.listParents()) {
373+
if (parent instanceof Node) {
374+
const s = parent.getWorldScale();
375+
scale = Number.isFinite(s[0]) ? Math.max(scale, Math.abs(s[0])) : scale;
376+
scale = Number.isFinite(s[1]) ? Math.max(scale, Math.abs(s[1])) : scale;
377+
scale = Number.isFinite(s[2]) ? Math.max(scale, Math.abs(s[2])) : scale;
378+
}
379+
}
380+
381+
return scale > 0 && Number.isFinite(scale) ? scale : 1;
382+
}

0 commit comments

Comments
 (0)