@luma.gl/gltf 9.1.9 → 9.2.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/dist/dist.dev.js +659 -585
  2. package/dist/dist.min.js +4 -4
  3. package/dist/gltf/animations/animations.d.ts +16 -0
  4. package/dist/gltf/animations/animations.d.ts.map +1 -0
  5. package/dist/gltf/animations/animations.js +5 -0
  6. package/dist/gltf/animations/animations.js.map +1 -0
  7. package/dist/gltf/animations/interpolate.d.ts +4 -0
  8. package/dist/gltf/animations/interpolate.d.ts.map +1 -0
  9. package/dist/gltf/animations/interpolate.js +94 -0
  10. package/dist/gltf/animations/interpolate.js.map +1 -0
  11. package/dist/gltf/create-gltf-model.d.ts +4 -4
  12. package/dist/gltf/create-gltf-model.d.ts.map +1 -1
  13. package/dist/gltf/create-gltf-model.js +7 -8
  14. package/dist/gltf/create-gltf-model.js.map +1 -1
  15. package/dist/gltf/{create-gltf-objects.d.ts → create-scenegraph-from-gltf.d.ts} +4 -3
  16. package/dist/gltf/create-scenegraph-from-gltf.d.ts.map +1 -0
  17. package/dist/gltf/create-scenegraph-from-gltf.js +16 -0
  18. package/dist/gltf/create-scenegraph-from-gltf.js.map +1 -0
  19. package/dist/gltf/gltf-animator.d.ts +13 -28
  20. package/dist/gltf/gltf-animator.d.ts.map +1 -1
  21. package/dist/gltf/gltf-animator.js +17 -144
  22. package/dist/gltf/gltf-animator.js.map +1 -1
  23. package/dist/index.cjs +483 -447
  24. package/dist/index.cjs.map +4 -4
  25. package/dist/index.d.ts +5 -5
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +2 -2
  28. package/dist/index.js.map +1 -1
  29. package/dist/parsers/parse-gltf-animations.d.ts +4 -0
  30. package/dist/parsers/parse-gltf-animations.d.ts.map +1 -0
  31. package/dist/parsers/parse-gltf-animations.js +40 -0
  32. package/dist/parsers/parse-gltf-animations.js.map +1 -0
  33. package/dist/parsers/parse-gltf.d.ts +17 -0
  34. package/dist/parsers/parse-gltf.d.ts.map +1 -0
  35. package/dist/parsers/parse-gltf.js +118 -0
  36. package/dist/parsers/parse-gltf.js.map +1 -0
  37. package/dist/parsers/parse-pbr-material.d.ts +50 -0
  38. package/dist/parsers/parse-pbr-material.d.ts.map +1 -0
  39. package/dist/{pbr → parsers}/parse-pbr-material.js +42 -56
  40. package/dist/parsers/parse-pbr-material.js.map +1 -0
  41. package/dist/pbr/pbr-environment.d.ts.map +1 -1
  42. package/dist/pbr/pbr-environment.js +14 -10
  43. package/dist/pbr/pbr-environment.js.map +1 -1
  44. package/dist/pbr/pbr-material.d.ts +13 -0
  45. package/dist/pbr/pbr-material.d.ts.map +1 -0
  46. package/dist/pbr/pbr-material.js +2 -0
  47. package/dist/pbr/pbr-material.js.map +1 -0
  48. package/dist/utils/deep-copy.d.ts +3 -0
  49. package/dist/utils/deep-copy.d.ts.map +1 -0
  50. package/dist/utils/deep-copy.js +21 -0
  51. package/dist/utils/deep-copy.js.map +1 -0
  52. package/dist/webgl-to-webgpu/convert-webgl-attribute.d.ts +21 -0
  53. package/dist/webgl-to-webgpu/convert-webgl-attribute.d.ts.map +1 -0
  54. package/dist/webgl-to-webgpu/convert-webgl-attribute.js +29 -0
  55. package/dist/webgl-to-webgpu/convert-webgl-attribute.js.map +1 -0
  56. package/dist/webgl-to-webgpu/convert-webgl-sampler.d.ts +11 -0
  57. package/dist/webgl-to-webgpu/convert-webgl-sampler.d.ts.map +1 -0
  58. package/dist/webgl-to-webgpu/convert-webgl-sampler.js +53 -0
  59. package/dist/webgl-to-webgpu/convert-webgl-sampler.js.map +1 -0
  60. package/dist/{gltf/gl-utils.d.ts → webgl-to-webgpu/convert-webgl-topology.d.ts} +1 -1
  61. package/dist/webgl-to-webgpu/convert-webgl-topology.d.ts.map +1 -0
  62. package/dist/{gltf/gl-utils.js → webgl-to-webgpu/convert-webgl-topology.js} +1 -1
  63. package/dist/webgl-to-webgpu/convert-webgl-topology.js.map +1 -0
  64. package/package.json +7 -5
  65. package/src/gltf/animations/animations.ts +22 -0
  66. package/src/gltf/animations/interpolate.ts +153 -0
  67. package/src/gltf/create-gltf-model.ts +12 -13
  68. package/src/gltf/create-scenegraph-from-gltf.ts +27 -0
  69. package/src/gltf/gltf-animator.ts +31 -172
  70. package/src/index.ts +5 -5
  71. package/src/parsers/parse-gltf-animations.ts +55 -0
  72. package/src/parsers/parse-gltf.ts +192 -0
  73. package/src/{pbr → parsers}/parse-pbr-material.ts +90 -73
  74. package/src/pbr/pbr-environment.ts +18 -14
  75. package/src/pbr/pbr-material.ts +13 -0
  76. package/src/utils/deep-copy.ts +22 -0
  77. package/src/webgl-to-webgpu/convert-webgl-attribute.ts +47 -0
  78. package/src/webgl-to-webgpu/convert-webgl-sampler.ts +86 -0
  79. package/dist/gltf/create-gltf-objects.d.ts.map +0 -1
  80. package/dist/gltf/create-gltf-objects.js +0 -11
  81. package/dist/gltf/create-gltf-objects.js.map +0 -1
  82. package/dist/gltf/gl-utils.d.ts.map +0 -1
  83. package/dist/gltf/gl-utils.js.map +0 -1
  84. package/dist/gltf/gltf-instantiator.d.ts +0 -33
  85. package/dist/gltf/gltf-instantiator.d.ts.map +0 -1
  86. package/dist/gltf/gltf-instantiator.js +0 -185
  87. package/dist/gltf/gltf-instantiator.js.map +0 -1
  88. package/dist/pbr/parse-pbr-material.d.ts +0 -27
  89. package/dist/pbr/parse-pbr-material.d.ts.map +0 -1
  90. package/dist/pbr/parse-pbr-material.js.map +0 -1
  91. package/src/gltf/create-gltf-objects.ts +0 -22
  92. package/src/gltf/gltf-instantiator.ts +0 -233
  93. /package/src/{gltf/gl-utils.ts → webgl-to-webgpu/convert-webgl-topology.ts} +0 -0
@@ -0,0 +1,153 @@
1
+ import {GLTFNodePostprocessed} from '@loaders.gl/gltf';
2
+ import {log} from '@luma.gl/core';
3
+ import {Quaternion} from '@math.gl/core';
4
+ import {GLTFAnimationChannel, GLTFAnimationSampler} from './animations';
5
+
6
+ const scratchQuaternion = new Quaternion();
7
+
8
+ export function interpolate(
9
+ time: number,
10
+ {input, interpolation, output}: GLTFAnimationSampler,
11
+ target: GLTFNodePostprocessed,
12
+ path: GLTFAnimationChannel['path']
13
+ ) {
14
+ const maxTime = input[input.length - 1];
15
+ const animationTime = time % maxTime;
16
+
17
+ const nextIndex = input.findIndex(t => t >= animationTime);
18
+ const previousIndex = Math.max(0, nextIndex - 1);
19
+
20
+ if (!Array.isArray(target[path])) {
21
+ switch (path) {
22
+ case 'translation':
23
+ target[path] = [0, 0, 0];
24
+ break;
25
+
26
+ case 'rotation':
27
+ target[path] = [0, 0, 0, 1];
28
+ break;
29
+
30
+ case 'scale':
31
+ target[path] = [1, 1, 1];
32
+ break;
33
+
34
+ default:
35
+ log.warn(`Bad animation path ${path}`)();
36
+ }
37
+ }
38
+
39
+ // assert(target[path].length === output[previousIndex].length);
40
+ const previousTime = input[previousIndex];
41
+ const nextTime = input[nextIndex];
42
+
43
+ switch (interpolation) {
44
+ case 'STEP':
45
+ stepInterpolate(target, path, output[previousIndex] as number[]);
46
+ break;
47
+
48
+ case 'LINEAR':
49
+ if (nextTime > previousTime) {
50
+ const ratio = (animationTime - previousTime) / (nextTime - previousTime);
51
+ linearInterpolate(
52
+ target,
53
+ path,
54
+ output[previousIndex] as number[],
55
+ output[nextIndex] as number[],
56
+ ratio
57
+ );
58
+ }
59
+ break;
60
+
61
+ case 'CUBICSPLINE':
62
+ if (nextTime > previousTime) {
63
+ const ratio = (animationTime - previousTime) / (nextTime - previousTime);
64
+ const tDiff = nextTime - previousTime;
65
+
66
+ const p0 = output[3 * previousIndex + 1] as number[];
67
+ const outTangent0 = output[3 * previousIndex + 2] as number[];
68
+ const inTangent1 = output[3 * nextIndex + 0] as number[];
69
+ const p1 = output[3 * nextIndex + 1] as number[];
70
+
71
+ cubicsplineInterpolate(target, path, {p0, outTangent0, inTangent1, p1, tDiff, ratio});
72
+ }
73
+ break;
74
+
75
+ default:
76
+ log.warn(`Interpolation ${interpolation} not supported`)();
77
+ break;
78
+ }
79
+ }
80
+
81
+ function linearInterpolate(
82
+ target: GLTFNodePostprocessed,
83
+ path: GLTFAnimationChannel['path'],
84
+ start: number[],
85
+ stop: number[],
86
+ ratio: number
87
+ ) {
88
+ if (!target[path]) {
89
+ throw new Error();
90
+ }
91
+
92
+ if (path === 'rotation') {
93
+ // SLERP when path is rotation
94
+ scratchQuaternion.slerp({start, target: stop, ratio});
95
+ for (let i = 0; i < scratchQuaternion.length; i++) {
96
+ target[path][i] = scratchQuaternion[i];
97
+ }
98
+ } else {
99
+ // regular interpolation
100
+ for (let i = 0; i < start.length; i++) {
101
+ target[path][i] = ratio * stop[i] + (1 - ratio) * start[i];
102
+ }
103
+ }
104
+ }
105
+
106
+ function cubicsplineInterpolate(
107
+ target: GLTFNodePostprocessed,
108
+ path: GLTFAnimationChannel['path'],
109
+ {
110
+ p0,
111
+ outTangent0,
112
+ inTangent1,
113
+ p1,
114
+ tDiff,
115
+ ratio: t
116
+ }: {
117
+ p0: number[];
118
+ outTangent0: number[];
119
+ inTangent1: number[];
120
+ p1: number[];
121
+ tDiff: number;
122
+ ratio: number;
123
+ }
124
+ ) {
125
+ if (!target[path]) {
126
+ throw new Error();
127
+ }
128
+
129
+ // TODO: Quaternion might need normalization
130
+ for (let i = 0; i < target[path].length; i++) {
131
+ const m0 = outTangent0[i] * tDiff;
132
+ const m1 = inTangent1[i] * tDiff;
133
+ target[path][i] =
134
+ (2 * Math.pow(t, 3) - 3 * Math.pow(t, 2) + 1) * p0[i] +
135
+ (Math.pow(t, 3) - 2 * Math.pow(t, 2) + t) * m0 +
136
+ (-2 * Math.pow(t, 3) + 3 * Math.pow(t, 2)) * p1[i] +
137
+ (Math.pow(t, 3) - Math.pow(t, 2)) * m1;
138
+ }
139
+ }
140
+
141
+ function stepInterpolate(
142
+ target: GLTFNodePostprocessed,
143
+ path: GLTFAnimationChannel['path'],
144
+ value: number[]
145
+ ) {
146
+ if (!target[path]) {
147
+ throw new Error();
148
+ }
149
+
150
+ for (let i = 0; i < value.length; i++) {
151
+ target[path][i] = value[i];
152
+ }
153
+ }
@@ -2,10 +2,10 @@
2
2
  // SPDX-License-Identifier: MIT
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
- import {Device, RenderPipelineParameters, log} from '@luma.gl/core';
5
+ import {Device, type RenderPipelineParameters, log} from '@luma.gl/core';
6
6
  import {pbrMaterial, ShaderModule} from '@luma.gl/shadertools';
7
- import {Geometry, Model, ModelNode, ModelProps} from '@luma.gl/engine';
8
- import {ParsePBRMaterialOptions, parsePBRMaterial} from '../pbr/parse-pbr-material';
7
+ import {Geometry, Model, ModelNode, type ModelProps} from '@luma.gl/engine';
8
+ import {type ParsedPBRMaterial} from '../pbr/pbr-material';
9
9
 
10
10
  const SHADER = /* WGSL */ `
11
11
  layout(0) positions: vec4; // in vec4 POSITION;
@@ -112,21 +112,20 @@ export type CreateGLTFModelOptions = {
112
112
  id?: string;
113
113
  vertexCount?: number;
114
114
  geometry: Geometry;
115
- material: any;
116
- materialOptions: ParsePBRMaterialOptions;
115
+ parsedPPBRMaterial: ParsedPBRMaterial;
117
116
  modelOptions?: Partial<ModelProps>;
118
117
  };
119
118
 
119
+ /** Creates a luma.gl Model from GLTF data*/
120
120
  export function createGLTFModel(device: Device, options: CreateGLTFModelOptions): ModelNode {
121
- const {id, geometry, material, vertexCount, materialOptions, modelOptions} = options;
121
+ const {id, geometry, parsedPPBRMaterial, vertexCount, modelOptions = {}} = options;
122
122
 
123
- const parsedMaterial = parsePBRMaterial(device, material, geometry.attributes, materialOptions);
124
- log.info(4, 'createGLTFModel defines: ', parsedMaterial.defines)();
123
+ log.info(4, 'createGLTFModel defines: ', parsedPPBRMaterial.defines)();
125
124
 
126
125
  // Calculate managedResources
127
126
  // TODO: Implement resource management logic that will
128
127
  // not deallocate resources/textures/buffers that are shared
129
- const managedResources = [];
128
+ const managedResources: any[] = [];
130
129
  // managedResources.push(...parsedMaterial.generatedTextures);
131
130
  // managedResources.push(...Object.values(attributes).map((attribute) => attribute.buffer));
132
131
 
@@ -148,16 +147,16 @@ export function createGLTFModel(device: Device, options: CreateGLTFModelOptions)
148
147
  modules: [pbrMaterial as unknown as ShaderModule],
149
148
  ...modelOptions,
150
149
 
151
- defines: {...parsedMaterial.defines, ...modelOptions.defines},
152
- parameters: {...parameters, ...parsedMaterial.parameters, ...modelOptions.parameters}
150
+ defines: {...parsedPPBRMaterial.defines, ...modelOptions.defines},
151
+ parameters: {...parameters, ...parsedPPBRMaterial.parameters, ...modelOptions.parameters}
153
152
  };
154
153
 
155
154
  const model = new Model(device, modelProps);
156
155
 
157
156
  const {camera, ...pbrMaterialProps} = {
158
- ...parsedMaterial.uniforms,
157
+ ...parsedPPBRMaterial.uniforms,
159
158
  ...modelOptions.uniforms,
160
- ...parsedMaterial.bindings,
159
+ ...parsedPPBRMaterial.bindings,
161
160
  ...modelOptions.bindings
162
161
  };
163
162
 
@@ -0,0 +1,27 @@
1
+ // luma.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ import {Device} from '@luma.gl/core';
6
+ import {GroupNode} from '@luma.gl/engine';
7
+ import {GLTFPostprocessed} from '@loaders.gl/gltf';
8
+ import {parseGLTF, type ParseGLTFOptions} from '../parsers/parse-gltf';
9
+ import {GLTFAnimator} from './gltf-animator';
10
+ import {parseGLTFAnimations} from '../parsers/parse-gltf-animations';
11
+ import {deepCopy} from '../utils/deep-copy';
12
+
13
+ export function createScenegraphsFromGLTF(
14
+ device: Device,
15
+ gltf: GLTFPostprocessed,
16
+ options?: ParseGLTFOptions
17
+ ): {
18
+ scenes: GroupNode[];
19
+ animator: GLTFAnimator;
20
+ } {
21
+ gltf = deepCopy(gltf);
22
+ const scenes = parseGLTF(device, gltf, options);
23
+ // Note: There is a nasty dependency on injected nodes in the glTF
24
+ const animations = parseGLTFAnimations(gltf);
25
+ const animator = new GLTFAnimator({animations});
26
+ return {scenes, animator};
27
+ }
@@ -2,49 +2,33 @@
2
2
  // SPDX-License-Identifier: MIT
3
3
  // Copyright (c) vis.gl contributors
4
4
 
5
+ import {GLTFNodePostprocessed} from '@loaders.gl/gltf';
5
6
  import {log} from '@luma.gl/core';
6
- import {Matrix4, Quaternion} from '@math.gl/core';
7
-
8
- // TODO: import from loaders.gl?
9
- export const ATTRIBUTE_TYPE_TO_COMPONENTS = {
10
- SCALAR: 1,
11
- VEC2: 2,
12
- VEC3: 3,
13
- VEC4: 4,
14
- MAT2: 4,
15
- MAT3: 9,
16
- MAT4: 16
17
- };
18
-
19
- export const ATTRIBUTE_COMPONENT_TYPE_TO_ARRAY = {
20
- 5120: Int8Array,
21
- 5121: Uint8Array,
22
- 5122: Int16Array,
23
- 5123: Uint16Array,
24
- 5125: Uint32Array,
25
- 5126: Float32Array
26
- };
7
+ import {GroupNode} from '@luma.gl/engine';
8
+ import {Matrix4} from '@math.gl/core';
9
+ import {GLTFAnimation} from './animations/animations';
10
+ import {interpolate} from './animations/interpolate';
27
11
 
28
- type GLTFAnimationProps = {
29
- name: string;
12
+ type GLTFSingleAnimatorProps = {
13
+ animation: GLTFAnimation;
30
14
  startTime?: number;
31
15
  playing?: boolean;
32
16
  speed?: number;
33
- channels?: any;
34
17
  };
35
18
 
36
- class GLTFAnimation {
37
- name: string;
19
+ class GLTFSingleAnimator {
20
+ animation: GLTFAnimation;
38
21
  startTime: number = 0;
39
22
  playing: boolean = true;
40
23
  speed: number = 1;
41
- channels: any = [];
42
24
 
43
- constructor(props: GLTFAnimationProps) {
25
+ constructor(props: GLTFSingleAnimatorProps) {
26
+ this.animation = props.animation;
27
+ this.animation.name ||= 'unnamed';
44
28
  Object.assign(this, props);
45
29
  }
46
30
 
47
- animate(timeMs: number) {
31
+ setTime(timeMs: number) {
48
32
  if (!this.playing) {
49
33
  return;
50
34
  }
@@ -52,40 +36,37 @@ class GLTFAnimation {
52
36
  const absTime = timeMs / 1000;
53
37
  const time = (absTime - this.startTime) * this.speed;
54
38
 
55
- this.channels.forEach(({sampler, target, path}) => {
39
+ this.animation.channels.forEach(({sampler, target, path}) => {
56
40
  interpolate(time, sampler, target, path);
57
- applyTranslationRotationScale(target, target._node);
41
+ applyTranslationRotationScale(target, (target as any)._node as GroupNode);
58
42
  });
59
43
  }
60
44
  }
61
45
 
62
- export class GLTFAnimator {
46
+ export type GLTFAnimatorProps = {
63
47
  animations: GLTFAnimation[];
48
+ };
64
49
 
65
- constructor(gltf: any) {
66
- this.animations = gltf.animations.map((animation, index) => {
50
+ export class GLTFAnimator {
51
+ animations: GLTFSingleAnimator[];
52
+
53
+ constructor(props: GLTFAnimatorProps) {
54
+ this.animations = props.animations.map((animation, index) => {
67
55
  const name = animation.name || `Animation-${index}`;
68
- const samplers = animation.samplers.map(({input, interpolation = 'LINEAR', output}) => ({
69
- input: accessorToJsArray(gltf.accessors[input]),
70
- interpolation,
71
- output: accessorToJsArray(gltf.accessors[output])
72
- }));
73
- const channels = animation.channels.map(({sampler, target}) => ({
74
- sampler: samplers[sampler],
75
- target: gltf.nodes[target.node],
76
- path: target.path
77
- }));
78
- return new GLTFAnimation({name, channels});
56
+ return new GLTFSingleAnimator({
57
+ animation: {name, channels: animation.channels}
58
+ });
79
59
  });
80
60
  }
81
61
 
82
62
  /** @deprecated Use .setTime(). Will be removed (deck.gl is using this) */
83
63
  animate(time: number): void {
64
+ log.warn('GLTFAnimator#animate is deprecated. Use GLTFAnimator#setTime instead')();
84
65
  this.setTime(time);
85
66
  }
86
67
 
87
68
  setTime(time: number): void {
88
- this.animations.forEach(animation => animation.animate(time));
69
+ this.animations.forEach(animation => animation.setTime(time));
89
70
  }
90
71
 
91
72
  getAnimations() {
@@ -93,35 +74,10 @@ export class GLTFAnimator {
93
74
  }
94
75
  }
95
76
 
96
- //
97
-
98
- function accessorToJsArray(accessor) {
99
- if (!accessor._animation) {
100
- const ArrayType = ATTRIBUTE_COMPONENT_TYPE_TO_ARRAY[accessor.componentType];
101
- const components = ATTRIBUTE_TYPE_TO_COMPONENTS[accessor.type];
102
- const length = components * accessor.count;
103
- const {buffer, byteOffset} = accessor.bufferView.data;
104
-
105
- const array = new ArrayType(buffer, byteOffset + (accessor.byteOffset || 0), length);
106
-
107
- if (components === 1) {
108
- accessor._animation = Array.from(array);
109
- } else {
110
- // Slice array
111
- const slicedArray = [];
112
- for (let i = 0; i < array.length; i += components) {
113
- slicedArray.push(Array.from(array.slice(i, i + components)));
114
- }
115
- accessor._animation = slicedArray;
116
- }
117
- }
118
-
119
- return accessor._animation;
120
- }
121
-
122
77
  // TODO: share with GLTFInstantiator
123
- const helperMatrix = new Matrix4();
124
- function applyTranslationRotationScale(gltfNode, node) {
78
+ const scratchMatrix = new Matrix4();
79
+
80
+ function applyTranslationRotationScale(gltfNode: GLTFNodePostprocessed, node: GroupNode) {
125
81
  node.matrix.identity();
126
82
 
127
83
  if (gltfNode.translation) {
@@ -129,7 +85,7 @@ function applyTranslationRotationScale(gltfNode, node) {
129
85
  }
130
86
 
131
87
  if (gltfNode.rotation) {
132
- const rotationMatrix = helperMatrix.fromQuaternion(gltfNode.rotation);
88
+ const rotationMatrix = scratchMatrix.fromQuaternion(gltfNode.rotation);
133
89
  node.matrix.multiplyRight(rotationMatrix);
134
90
  }
135
91
 
@@ -137,100 +93,3 @@ function applyTranslationRotationScale(gltfNode, node) {
137
93
  node.matrix.scale(gltfNode.scale);
138
94
  }
139
95
  }
140
-
141
- const quaternion = new Quaternion();
142
- function linearInterpolate(target, path, start, stop, ratio) {
143
- if (path === 'rotation') {
144
- // SLERP when path is rotation
145
- quaternion.slerp({start, target: stop, ratio});
146
- for (let i = 0; i < quaternion.length; i++) {
147
- target[path][i] = quaternion[i];
148
- }
149
- } else {
150
- // regular interpolation
151
- for (let i = 0; i < start.length; i++) {
152
- target[path][i] = ratio * stop[i] + (1 - ratio) * start[i];
153
- }
154
- }
155
- }
156
-
157
- function cubicsplineInterpolate(target, path, {p0, outTangent0, inTangent1, p1, tDiff, ratio: t}) {
158
- // TODO: Quaternion might need normalization
159
- for (let i = 0; i < target[path].length; i++) {
160
- const m0 = outTangent0[i] * tDiff;
161
- const m1 = inTangent1[i] * tDiff;
162
- target[path][i] =
163
- (2 * Math.pow(t, 3) - 3 * Math.pow(t, 2) + 1) * p0[i] +
164
- (Math.pow(t, 3) - 2 * Math.pow(t, 2) + t) * m0 +
165
- (-2 * Math.pow(t, 3) + 3 * Math.pow(t, 2)) * p1[i] +
166
- (Math.pow(t, 3) - Math.pow(t, 2)) * m1;
167
- }
168
- }
169
-
170
- function stepInterpolate(target, path, value) {
171
- for (let i = 0; i < value.length; i++) {
172
- target[path][i] = value[i];
173
- }
174
- }
175
-
176
- function interpolate(time, {input, interpolation, output}, target, path) {
177
- const maxTime = input[input.length - 1];
178
- const animationTime = time % maxTime;
179
-
180
- const nextIndex = input.findIndex(t => t >= animationTime);
181
- const previousIndex = Math.max(0, nextIndex - 1);
182
-
183
- if (!Array.isArray(target[path])) {
184
- switch (path) {
185
- case 'translation':
186
- target[path] = [0, 0, 0];
187
- break;
188
-
189
- case 'rotation':
190
- target[path] = [0, 0, 0, 1];
191
- break;
192
-
193
- case 'scale':
194
- target[path] = [1, 1, 1];
195
- break;
196
-
197
- default:
198
- log.warn(`Bad animation path ${path}`)();
199
- }
200
- }
201
-
202
- // assert(target[path].length === output[previousIndex].length);
203
- const previousTime = input[previousIndex];
204
- const nextTime = input[nextIndex];
205
-
206
- switch (interpolation) {
207
- case 'STEP':
208
- stepInterpolate(target, path, output[previousIndex]);
209
- break;
210
-
211
- case 'LINEAR':
212
- if (nextTime > previousTime) {
213
- const ratio = (animationTime - previousTime) / (nextTime - previousTime);
214
- linearInterpolate(target, path, output[previousIndex], output[nextIndex], ratio);
215
- }
216
- break;
217
-
218
- case 'CUBICSPLINE':
219
- if (nextTime > previousTime) {
220
- const ratio = (animationTime - previousTime) / (nextTime - previousTime);
221
- const tDiff = nextTime - previousTime;
222
-
223
- const p0 = output[3 * previousIndex + 1];
224
- const outTangent0 = output[3 * previousIndex + 2];
225
- const inTangent1 = output[3 * nextIndex + 0];
226
- const p1 = output[3 * nextIndex + 1];
227
-
228
- cubicsplineInterpolate(target, path, {p0, outTangent0, inTangent1, p1, tDiff, ratio});
229
- }
230
- break;
231
-
232
- default:
233
- log.warn(`Interpolation ${interpolation} not supported`)();
234
- break;
235
- }
236
- }
package/src/index.ts CHANGED
@@ -1,10 +1,10 @@
1
1
  // luma.gl, MIT license
2
2
 
3
- export type {PBREnvironment} from './pbr/pbr-environment';
4
- export type {ParsePBRMaterialOptions, ParsedPBRMaterial} from './pbr/parse-pbr-material';
5
- export {parsePBRMaterial} from './pbr/parse-pbr-material';
6
- export {loadPBREnvironment} from './pbr/pbr-environment';
3
+ export {loadPBREnvironment, type PBREnvironment} from './pbr/pbr-environment';
4
+ export {type ParsedPBRMaterial} from './pbr/pbr-material';
5
+ export {parsePBRMaterial, type ParsePBRMaterialOptions} from './parsers/parse-pbr-material';
6
+ export {} from './pbr/pbr-environment';
7
7
 
8
8
  // glTF Scenegraph Instantiator
9
- export {createScenegraphsFromGLTF} from './gltf/create-gltf-objects';
9
+ export {createScenegraphsFromGLTF} from './gltf/create-scenegraph-from-gltf';
10
10
  export {GLTFAnimator} from './gltf/gltf-animator';
@@ -0,0 +1,55 @@
1
+ // luma.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+
5
+ import {type GLTFAccessorPostprocessed, type GLTFPostprocessed} from '@loaders.gl/gltf';
6
+ import {
7
+ type GLTFAnimation,
8
+ type GLTFAnimationChannel,
9
+ type GLTFAnimationSampler
10
+ } from '../gltf/animations/animations';
11
+
12
+ import {accessorToTypedArray} from '..//webgl-to-webgpu/convert-webgl-attribute';
13
+
14
+ export function parseGLTFAnimations(gltf: GLTFPostprocessed): GLTFAnimation[] {
15
+ const gltfAnimations = gltf.animations || [];
16
+ return gltfAnimations.map((animation, index) => {
17
+ const name = animation.name || `Animation-${index}`;
18
+ const samplers: GLTFAnimationSampler[] = animation.samplers.map(
19
+ ({input, interpolation = 'LINEAR', output}) => ({
20
+ input: accessorToJsArray(gltf.accessors[input]) as number[],
21
+ interpolation,
22
+ output: accessorToJsArray(gltf.accessors[output])
23
+ })
24
+ );
25
+ const channels: GLTFAnimationChannel[] = animation.channels.map(({sampler, target}) => ({
26
+ sampler: samplers[sampler],
27
+ target: gltf.nodes[target.node ?? 0],
28
+ path: target.path as GLTFAnimationChannel['path']
29
+ }));
30
+ return {name, channels};
31
+ });
32
+ }
33
+
34
+ //
35
+
36
+ function accessorToJsArray(
37
+ accessor: GLTFAccessorPostprocessed & {_animation?: number[] | number[][]}
38
+ ): number[] | number[][] {
39
+ if (!accessor._animation) {
40
+ const {typedArray: array, components} = accessorToTypedArray(accessor);
41
+
42
+ if (components === 1) {
43
+ accessor._animation = Array.from(array);
44
+ } else {
45
+ // Slice array
46
+ const slicedArray: number[][] = [];
47
+ for (let i = 0; i < array.length; i += components) {
48
+ slicedArray.push(Array.from(array.slice(i, i + components)));
49
+ }
50
+ accessor._animation = slicedArray;
51
+ }
52
+ }
53
+
54
+ return accessor._animation;
55
+ }