@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.
- package/dist/dist.dev.js +659 -585
- package/dist/dist.min.js +4 -4
- package/dist/gltf/animations/animations.d.ts +16 -0
- package/dist/gltf/animations/animations.d.ts.map +1 -0
- package/dist/gltf/animations/animations.js +5 -0
- package/dist/gltf/animations/animations.js.map +1 -0
- package/dist/gltf/animations/interpolate.d.ts +4 -0
- package/dist/gltf/animations/interpolate.d.ts.map +1 -0
- package/dist/gltf/animations/interpolate.js +94 -0
- package/dist/gltf/animations/interpolate.js.map +1 -0
- package/dist/gltf/create-gltf-model.d.ts +4 -4
- package/dist/gltf/create-gltf-model.d.ts.map +1 -1
- package/dist/gltf/create-gltf-model.js +7 -8
- package/dist/gltf/create-gltf-model.js.map +1 -1
- package/dist/gltf/{create-gltf-objects.d.ts → create-scenegraph-from-gltf.d.ts} +4 -3
- package/dist/gltf/create-scenegraph-from-gltf.d.ts.map +1 -0
- package/dist/gltf/create-scenegraph-from-gltf.js +16 -0
- package/dist/gltf/create-scenegraph-from-gltf.js.map +1 -0
- package/dist/gltf/gltf-animator.d.ts +13 -28
- package/dist/gltf/gltf-animator.d.ts.map +1 -1
- package/dist/gltf/gltf-animator.js +17 -144
- package/dist/gltf/gltf-animator.js.map +1 -1
- package/dist/index.cjs +483 -447
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +5 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/parsers/parse-gltf-animations.d.ts +4 -0
- package/dist/parsers/parse-gltf-animations.d.ts.map +1 -0
- package/dist/parsers/parse-gltf-animations.js +40 -0
- package/dist/parsers/parse-gltf-animations.js.map +1 -0
- package/dist/parsers/parse-gltf.d.ts +17 -0
- package/dist/parsers/parse-gltf.d.ts.map +1 -0
- package/dist/parsers/parse-gltf.js +118 -0
- package/dist/parsers/parse-gltf.js.map +1 -0
- package/dist/parsers/parse-pbr-material.d.ts +50 -0
- package/dist/parsers/parse-pbr-material.d.ts.map +1 -0
- package/dist/{pbr → parsers}/parse-pbr-material.js +42 -56
- package/dist/parsers/parse-pbr-material.js.map +1 -0
- package/dist/pbr/pbr-environment.d.ts.map +1 -1
- package/dist/pbr/pbr-environment.js +14 -10
- package/dist/pbr/pbr-environment.js.map +1 -1
- package/dist/pbr/pbr-material.d.ts +13 -0
- package/dist/pbr/pbr-material.d.ts.map +1 -0
- package/dist/pbr/pbr-material.js +2 -0
- package/dist/pbr/pbr-material.js.map +1 -0
- package/dist/utils/deep-copy.d.ts +3 -0
- package/dist/utils/deep-copy.d.ts.map +1 -0
- package/dist/utils/deep-copy.js +21 -0
- package/dist/utils/deep-copy.js.map +1 -0
- package/dist/webgl-to-webgpu/convert-webgl-attribute.d.ts +21 -0
- package/dist/webgl-to-webgpu/convert-webgl-attribute.d.ts.map +1 -0
- package/dist/webgl-to-webgpu/convert-webgl-attribute.js +29 -0
- package/dist/webgl-to-webgpu/convert-webgl-attribute.js.map +1 -0
- package/dist/webgl-to-webgpu/convert-webgl-sampler.d.ts +11 -0
- package/dist/webgl-to-webgpu/convert-webgl-sampler.d.ts.map +1 -0
- package/dist/webgl-to-webgpu/convert-webgl-sampler.js +53 -0
- package/dist/webgl-to-webgpu/convert-webgl-sampler.js.map +1 -0
- package/dist/{gltf/gl-utils.d.ts → webgl-to-webgpu/convert-webgl-topology.d.ts} +1 -1
- package/dist/webgl-to-webgpu/convert-webgl-topology.d.ts.map +1 -0
- package/dist/{gltf/gl-utils.js → webgl-to-webgpu/convert-webgl-topology.js} +1 -1
- package/dist/webgl-to-webgpu/convert-webgl-topology.js.map +1 -0
- package/package.json +7 -5
- package/src/gltf/animations/animations.ts +22 -0
- package/src/gltf/animations/interpolate.ts +153 -0
- package/src/gltf/create-gltf-model.ts +12 -13
- package/src/gltf/create-scenegraph-from-gltf.ts +27 -0
- package/src/gltf/gltf-animator.ts +31 -172
- package/src/index.ts +5 -5
- package/src/parsers/parse-gltf-animations.ts +55 -0
- package/src/parsers/parse-gltf.ts +192 -0
- package/src/{pbr → parsers}/parse-pbr-material.ts +90 -73
- package/src/pbr/pbr-environment.ts +18 -14
- package/src/pbr/pbr-material.ts +13 -0
- package/src/utils/deep-copy.ts +22 -0
- package/src/webgl-to-webgpu/convert-webgl-attribute.ts +47 -0
- package/src/webgl-to-webgpu/convert-webgl-sampler.ts +86 -0
- package/dist/gltf/create-gltf-objects.d.ts.map +0 -1
- package/dist/gltf/create-gltf-objects.js +0 -11
- package/dist/gltf/create-gltf-objects.js.map +0 -1
- package/dist/gltf/gl-utils.d.ts.map +0 -1
- package/dist/gltf/gl-utils.js.map +0 -1
- package/dist/gltf/gltf-instantiator.d.ts +0 -33
- package/dist/gltf/gltf-instantiator.d.ts.map +0 -1
- package/dist/gltf/gltf-instantiator.js +0 -185
- package/dist/gltf/gltf-instantiator.js.map +0 -1
- package/dist/pbr/parse-pbr-material.d.ts +0 -27
- package/dist/pbr/parse-pbr-material.d.ts.map +0 -1
- package/dist/pbr/parse-pbr-material.js.map +0 -1
- package/src/gltf/create-gltf-objects.ts +0 -22
- package/src/gltf/gltf-instantiator.ts +0 -233
- /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 {
|
|
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
|
-
|
|
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,
|
|
121
|
+
const {id, geometry, parsedPPBRMaterial, vertexCount, modelOptions = {}} = options;
|
|
122
122
|
|
|
123
|
-
|
|
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: {...
|
|
152
|
-
parameters: {...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
|
-
...
|
|
157
|
+
...parsedPPBRMaterial.uniforms,
|
|
159
158
|
...modelOptions.uniforms,
|
|
160
|
-
...
|
|
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 {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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
|
|
29
|
-
|
|
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
|
|
37
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
|
46
|
+
export type GLTFAnimatorProps = {
|
|
63
47
|
animations: GLTFAnimation[];
|
|
48
|
+
};
|
|
64
49
|
|
|
65
|
-
|
|
66
|
-
|
|
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
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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.
|
|
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
|
|
124
|
-
|
|
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 =
|
|
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
|
|
4
|
-
export type
|
|
5
|
-
export {parsePBRMaterial} from './
|
|
6
|
-
export {
|
|
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
|
|
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
|
+
}
|