@babylonjs/loaders 5.25.0 → 5.26.1

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.
@@ -4,6 +4,7 @@ import { Color3 } from "@babylonjs/core/Maths/math.color.js";
4
4
  import { Tools } from "@babylonjs/core/Misc/tools.js";
5
5
  import { Camera } from "@babylonjs/core/Cameras/camera.js";
6
6
  import { FreeCamera } from "@babylonjs/core/Cameras/freeCamera.js";
7
+ import { AnimationKeyInterpolation } from "@babylonjs/core/Animations/animationKey.js";
7
8
  import { AnimationGroup } from "@babylonjs/core/Animations/animationGroup.js";
8
9
  import { Bone } from "@babylonjs/core/Bones/bone.js";
9
10
  import { Skeleton } from "@babylonjs/core/Bones/skeleton.js";
@@ -20,6 +21,27 @@ import { GLTFFileLoader, GLTFLoaderState, GLTFLoaderCoordinateSystemMode, GLTFLo
20
21
  import { DecodeBase64UrlToBinary, IsBase64DataUrl, LoadFileError } from "@babylonjs/core/Misc/fileTools.js";
21
22
  import { Logger } from "@babylonjs/core/Misc/logger.js";
22
23
  import { BoundingInfo } from "@babylonjs/core/Culling/boundingInfo.js";
24
+ import { nodeAnimationData } from "./glTFLoaderAnimation.js";
25
+ // https://stackoverflow.com/a/48218209
26
+ function mergeDeep(...objects) {
27
+ const isObject = (obj) => obj && typeof obj === "object";
28
+ return objects.reduce((prev, obj) => {
29
+ Object.keys(obj).forEach((key) => {
30
+ const pVal = prev[key];
31
+ const oVal = obj[key];
32
+ if (Array.isArray(pVal) && Array.isArray(oVal)) {
33
+ prev[key] = pVal.concat(...oVal);
34
+ }
35
+ else if (isObject(pVal) && isObject(oVal)) {
36
+ prev[key] = mergeDeep(pVal, oVal);
37
+ }
38
+ else {
39
+ prev[key] = oVal;
40
+ }
41
+ });
42
+ return prev;
43
+ }, {});
44
+ }
23
45
  /**
24
46
  * Helper class for working with arrays when loading the glTF asset
25
47
  */
@@ -49,26 +71,6 @@ export class ArrayItem {
49
71
  }
50
72
  }
51
73
  }
52
- // https://stackoverflow.com/a/48218209
53
- function mergeDeep(...objects) {
54
- const isObject = (obj) => obj && typeof obj === "object";
55
- return objects.reduce((prev, obj) => {
56
- Object.keys(obj).forEach((key) => {
57
- const pVal = prev[key];
58
- const oVal = obj[key];
59
- if (Array.isArray(pVal) && Array.isArray(oVal)) {
60
- prev[key] = pVal.concat(...oVal);
61
- }
62
- else if (isObject(pVal) && isObject(oVal)) {
63
- prev[key] = mergeDeep(pVal, oVal);
64
- }
65
- else {
66
- prev[key] = oVal;
67
- }
68
- });
69
- return prev;
70
- }, {});
71
- }
72
74
  /**
73
75
  * The glTF 2.0 loader
74
76
  */
@@ -1114,6 +1116,7 @@ export class GLTFLoader {
1114
1116
  babylonCamera._parentContainer = this._assetContainer;
1115
1117
  this._babylonScene._blockEntityCollection = false;
1116
1118
  babylonCamera.ignoreParentScaling = true;
1119
+ camera._babylonCamera = babylonCamera;
1117
1120
  babylonCamera.rotation = new Vector3(0, Math.PI, 0);
1118
1121
  switch (camera.type) {
1119
1122
  case "perspective" /* PERSPECTIVE */: {
@@ -1146,8 +1149,6 @@ export class GLTFLoader {
1146
1149
  GLTFLoader.AddPointerMetadata(babylonCamera, context);
1147
1150
  this._parent.onCameraLoadedObservable.notifyObservers(babylonCamera);
1148
1151
  assign(babylonCamera);
1149
- // register the camera to be used later.
1150
- camera._babylonCamera = babylonCamera;
1151
1152
  this.logClose();
1152
1153
  return Promise.all(promises).then(() => {
1153
1154
  return babylonCamera;
@@ -1177,32 +1178,190 @@ export class GLTFLoader {
1177
1178
  * @returns A promise that resolves with the loaded Babylon animation group when the load is complete
1178
1179
  */
1179
1180
  loadAnimationAsync(context, animation) {
1180
- // turn everything into pointer
1181
- for (const channel of animation.channels) {
1182
- if (channel.target.path == "pointer" /* POINTER */) {
1183
- continue;
1184
- }
1185
- // decorate the channel with a KHR_animation_pointer extension.
1186
- channel.target.extensions = channel.target.extensions || {};
1187
- channel.target.extensions.KHR_animation_pointer = {
1188
- pointer: `/nodes/${channel.target.node}/${channel.target.path}`,
1189
- };
1190
- channel.target.path = "pointer" /* POINTER */;
1191
- delete channel.target.node;
1192
- // ensure to declare extension used.
1193
- this._gltf.extensionsUsed = this._gltf.extensionsUsed || [];
1194
- if (this._gltf.extensionsUsed.indexOf(GLTFLoader._KHRAnimationPointerName) === -1) {
1195
- this._gltf.extensionsUsed.push(GLTFLoader._KHRAnimationPointerName);
1196
- }
1181
+ const promise = this._extensionsLoadAnimationAsync(context, animation);
1182
+ if (promise) {
1183
+ return promise;
1197
1184
  }
1198
- // create the animation group to be passed to extension.
1199
1185
  this._babylonScene._blockEntityCollection = !!this._assetContainer;
1200
1186
  const babylonAnimationGroup = new AnimationGroup(animation.name || `animation${animation.index}`, this._babylonScene);
1201
1187
  babylonAnimationGroup._parentContainer = this._assetContainer;
1202
1188
  this._babylonScene._blockEntityCollection = false;
1203
1189
  animation._babylonAnimationGroup = babylonAnimationGroup;
1204
- const promise = this._extensionsLoadAnimationAsync(context, animation);
1205
- return promise ?? Promise.resolve(animation._babylonAnimationGroup);
1190
+ const promises = new Array();
1191
+ ArrayItem.Assign(animation.channels);
1192
+ ArrayItem.Assign(animation.samplers);
1193
+ for (const channel of animation.channels) {
1194
+ promises.push(this._loadAnimationChannelAsync(`${context}/channels/${channel.index}`, context, animation, channel, (babylonTarget, babylonAnimation) => {
1195
+ babylonTarget.animations = babylonTarget.animations || [];
1196
+ babylonTarget.animations.push(babylonAnimation);
1197
+ babylonAnimationGroup.addTargetedAnimation(babylonAnimation, babylonTarget);
1198
+ }));
1199
+ }
1200
+ return Promise.all(promises).then(() => {
1201
+ babylonAnimationGroup.normalize(0);
1202
+ return babylonAnimationGroup;
1203
+ });
1204
+ }
1205
+ /**
1206
+ * @hidden
1207
+ * Loads a glTF animation channel.
1208
+ * @param context The context when loading the asset
1209
+ * @param animationContext The context of the animation when loading the asset
1210
+ * @param animation The glTF animation property
1211
+ * @param channel The glTF animation channel property
1212
+ * @param onLoad Called for each animation loaded
1213
+ * @returns A void promise that resolves when the load is complete
1214
+ */
1215
+ _loadAnimationChannelAsync(context, animationContext, animation, channel, onLoad) {
1216
+ const promise = this._extensionsLoadAnimationChannelAsync(context, animationContext, animation, channel, onLoad);
1217
+ if (promise) {
1218
+ return promise;
1219
+ }
1220
+ if (channel.target.node == undefined) {
1221
+ return Promise.resolve();
1222
+ }
1223
+ const targetNode = ArrayItem.Get(`${context}/target/node`, this._gltf.nodes, channel.target.node);
1224
+ // Ignore animations that have no animation targets.
1225
+ if ((channel.target.path === "weights" /* WEIGHTS */ && !targetNode._numMorphTargets) ||
1226
+ (channel.target.path !== "weights" /* WEIGHTS */ && !targetNode._babylonTransformNode)) {
1227
+ return Promise.resolve();
1228
+ }
1229
+ let properties;
1230
+ switch (channel.target.path) {
1231
+ case "translation" /* TRANSLATION */: {
1232
+ properties = nodeAnimationData.translation;
1233
+ break;
1234
+ }
1235
+ case "rotation" /* ROTATION */: {
1236
+ properties = nodeAnimationData.rotation;
1237
+ break;
1238
+ }
1239
+ case "scale" /* SCALE */: {
1240
+ properties = nodeAnimationData.scale;
1241
+ break;
1242
+ }
1243
+ case "weights" /* WEIGHTS */: {
1244
+ properties = nodeAnimationData.weights;
1245
+ break;
1246
+ }
1247
+ default: {
1248
+ throw new Error(`${context}/target/path: Invalid value (${channel.target.path})`);
1249
+ }
1250
+ }
1251
+ const targetInfo = {
1252
+ target: targetNode,
1253
+ properties: properties,
1254
+ };
1255
+ return this._loadAnimationChannelFromTargetInfoAsync(context, animationContext, animation, channel, targetInfo, onLoad);
1256
+ }
1257
+ /**
1258
+ * @hidden
1259
+ * Loads a glTF animation channel.
1260
+ * @param context The context when loading the asset
1261
+ * @param animationContext The context of the animation when loading the asset
1262
+ * @param animation The glTF animation property
1263
+ * @param channel The glTF animation channel property
1264
+ * @param targetInfo The glTF target and properties
1265
+ * @param onLoad Called for each animation loaded
1266
+ * @returns A void promise that resolves when the load is complete
1267
+ */
1268
+ _loadAnimationChannelFromTargetInfoAsync(context, animationContext, animation, channel, targetInfo, onLoad) {
1269
+ const fps = this.parent.targetFps;
1270
+ const invfps = 1 / fps;
1271
+ const sampler = ArrayItem.Get(`${context}/sampler`, animation.samplers, channel.sampler);
1272
+ return this._loadAnimationSamplerAsync(`${animationContext}/samplers/${channel.sampler}`, sampler).then((data) => {
1273
+ let numAnimations = 0;
1274
+ // Extract the corresponding values from the read value.
1275
+ // GLTF values may be dispatched to several Babylon properties.
1276
+ // For example, baseColorFactor [`r`, `g`, `b`, `a`] is dispatched to
1277
+ // - albedoColor as Color3(`r`, `g`, `b`)
1278
+ // - alpha as `a`
1279
+ for (const property of targetInfo.properties) {
1280
+ const stride = property.getStride(targetInfo.target);
1281
+ const input = data.input;
1282
+ const output = data.output;
1283
+ const keys = new Array(input.length);
1284
+ let outputOffset = 0;
1285
+ switch (data.interpolation) {
1286
+ case "STEP" /* STEP */: {
1287
+ for (let index = 0; index < input.length; index++) {
1288
+ const value = property.getValue(targetInfo.target, output, outputOffset, 1);
1289
+ outputOffset += stride;
1290
+ keys[index] = {
1291
+ frame: input[index] * fps,
1292
+ value: value,
1293
+ interpolation: AnimationKeyInterpolation.STEP,
1294
+ };
1295
+ }
1296
+ break;
1297
+ }
1298
+ case "CUBICSPLINE" /* CUBICSPLINE */: {
1299
+ for (let index = 0; index < input.length; index++) {
1300
+ const inTangent = property.getValue(targetInfo.target, output, outputOffset, invfps);
1301
+ outputOffset += stride;
1302
+ const value = property.getValue(targetInfo.target, output, outputOffset, 1);
1303
+ outputOffset += stride;
1304
+ const outTangent = property.getValue(targetInfo.target, output, outputOffset, invfps);
1305
+ outputOffset += stride;
1306
+ keys[index] = {
1307
+ frame: input[index] * fps,
1308
+ inTangent: inTangent,
1309
+ value: value,
1310
+ outTangent: outTangent,
1311
+ };
1312
+ }
1313
+ break;
1314
+ }
1315
+ case "LINEAR" /* LINEAR */: {
1316
+ for (let index = 0; index < input.length; index++) {
1317
+ const value = property.getValue(targetInfo.target, output, outputOffset, 1);
1318
+ outputOffset += stride;
1319
+ keys[index] = {
1320
+ frame: input[index] * fps,
1321
+ value: value,
1322
+ };
1323
+ }
1324
+ break;
1325
+ }
1326
+ }
1327
+ if (outputOffset > 0) {
1328
+ const name = `${animation.name || `animation${animation.index}`}_channel${channel.index}_${numAnimations}`;
1329
+ property.buildAnimations(targetInfo.target, name, fps, keys, (babylonAnimatable, babylonAnimation) => {
1330
+ ++numAnimations;
1331
+ onLoad(babylonAnimatable, babylonAnimation);
1332
+ });
1333
+ }
1334
+ }
1335
+ });
1336
+ }
1337
+ _loadAnimationSamplerAsync(context, sampler) {
1338
+ if (sampler._data) {
1339
+ return sampler._data;
1340
+ }
1341
+ const interpolation = sampler.interpolation || "LINEAR" /* LINEAR */;
1342
+ switch (interpolation) {
1343
+ case "STEP" /* STEP */:
1344
+ case "LINEAR" /* LINEAR */:
1345
+ case "CUBICSPLINE" /* CUBICSPLINE */: {
1346
+ break;
1347
+ }
1348
+ default: {
1349
+ throw new Error(`${context}/interpolation: Invalid value (${sampler.interpolation})`);
1350
+ }
1351
+ }
1352
+ const inputAccessor = ArrayItem.Get(`${context}/input`, this._gltf.accessors, sampler.input);
1353
+ const outputAccessor = ArrayItem.Get(`${context}/output`, this._gltf.accessors, sampler.output);
1354
+ sampler._data = Promise.all([
1355
+ this._loadFloatAccessorAsync(`/accessors/${inputAccessor.index}`, inputAccessor),
1356
+ this._loadFloatAccessorAsync(`/accessors/${outputAccessor.index}`, outputAccessor),
1357
+ ]).then(([inputData, outputData]) => {
1358
+ return {
1359
+ input: inputData,
1360
+ interpolation: interpolation,
1361
+ output: outputData,
1362
+ };
1363
+ });
1364
+ return sampler._data;
1206
1365
  }
1207
1366
  /**
1208
1367
  * Loads a glTF buffer.
@@ -1746,7 +1905,6 @@ export class GLTFLoader {
1746
1905
  /**
1747
1906
  * Adds a JSON pointer to the metadata of the Babylon object at `<object>.metadata.gltf.pointers`.
1748
1907
  * @param babylonObject the Babylon object with metadata
1749
- * @param babylonObject.metadata
1750
1908
  * @param pointer the JSON pointer
1751
1909
  */
1752
1910
  static AddPointerMetadata(babylonObject, pointer) {
@@ -2003,6 +2161,9 @@ export class GLTFLoader {
2003
2161
  _extensionsLoadAnimationAsync(context, animation) {
2004
2162
  return this._applyExtensions(animation, "loadAnimation", (extension) => extension.loadAnimationAsync && extension.loadAnimationAsync(context, animation));
2005
2163
  }
2164
+ _extensionsLoadAnimationChannelAsync(context, animationContext, animation, channel, onLoad) {
2165
+ return this._applyExtensions(animation, "loadAnimationChannel", (extension) => extension._loadAnimationChannelAsync && extension._loadAnimationChannelAsync(context, animationContext, animation, channel, onLoad));
2166
+ }
2006
2167
  _extensionsLoadSkinAsync(context, node, skin) {
2007
2168
  return this._applyExtensions(skin, "loadSkin", (extension) => extension._loadSkinAsync && extension._loadSkinAsync(context, node, skin));
2008
2169
  }
@@ -2096,10 +2257,6 @@ export class GLTFLoader {
2096
2257
  this._parent._endPerformanceCounter(counterName);
2097
2258
  }
2098
2259
  }
2099
- /** @internal */
2100
- // note : KHR_animation_pointer is used to load animation in ALL case, turning everything
2101
- // into pointer. This is the reason why this value is located here.
2102
- GLTFLoader._KHRAnimationPointerName = "KHR_animation_pointer";
2103
2260
  GLTFLoader._RegisteredExtensions = {};
2104
2261
  /**
2105
2262
  * The default glTF sampler.