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' ;
2
2
import { Accessor } from '@gltf-transform/core' ;
3
3
import { createTransform , isUsed , shallowCloneAccessor } from './utils.js' ;
4
4
import type * as watlas from 'watlas' ;
@@ -14,22 +14,47 @@ interface IWatlas {
14
14
} ;
15
15
}
16
16
17
- /** Options for { @link unwrapPrimitives} and {@link unwrap} functions . */
18
- export interface UnwrapPrimitivesOptions {
17
+ /** Options for the {@link unwrap} transform . */
18
+ export interface UnwrapOptions {
19
19
/** watlas instance. */
20
20
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
+ */
22
25
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
+ */
24
30
overwrite ?: boolean ;
31
+ /**
32
+ * Methods of grouping texcoords with the {@link unwrap} function.
33
+ * Default: 'mesh'.
34
+ */
35
+ groupBy ?: 'primitive' | 'mesh' | 'scene' ;
25
36
}
26
37
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 ;
29
42
/**
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.
31
45
*/
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 [ ] ;
33
58
}
34
59
35
60
export const UNWRAP_DEFAULTS : Required < Omit < UnwrapOptions , 'watlas' > > = {
@@ -97,11 +122,16 @@ export function unwrap(_options: UnwrapOptions): Transform {
97
122
break ;
98
123
}
99
124
case 'scene' : {
100
- const scenePrims = [ ] ;
125
+ const prims : Primitive [ ] = [ ] ;
126
+ const weights : number [ ] = [ ] ;
101
127
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
+ }
103
133
}
104
- unwrapPrimitives ( scenePrims , options ) ;
134
+ unwrapPrimitives ( prims , { ... options , weights } ) ;
105
135
break ;
106
136
}
107
137
}
@@ -161,7 +191,10 @@ export function unwrapPrimitives(primitives: Primitive[], options: UnwrapPrimiti
161
191
const atlas = new watlas . Atlas ( ) ;
162
192
163
193
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
+
165
198
// Don't process primitives that already have the desired TEXCOORD index
166
199
// if overwrite is false.
167
200
if ( ! options . overwrite && prim . getAttribute ( dstSemantic ) ) {
@@ -175,7 +208,7 @@ export function unwrapPrimitives(primitives: Primitive[], options: UnwrapPrimiti
175
208
176
209
const meshDecl : watlas . MeshDecl = {
177
210
vertexCount : position . getCount ( ) ,
178
- vertexPositionData : getAttributeFloat32Array ( position ) ,
211
+ vertexPositionData : getScaledAttributeFloat32Array ( position , primWeight ) ,
179
212
vertexPositionStride : position . getElementSize ( ) * Float32Array . BYTES_PER_ELEMENT ,
180
213
} ;
181
214
@@ -317,3 +350,33 @@ function getAttributeFloat32Array(attribute: Accessor): Float32Array {
317
350
}
318
351
return dequantizeAttributeArray ( attribute . getArray ( ) ! , attribute . getComponentType ( ) , attribute . getNormalized ( ) ) ;
319
352
}
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