glTFLoader.js 108 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315
  1. import { Deferred } from "@babylonjs/core/Misc/deferred.js";
  2. import { Quaternion, Vector3, Matrix, TmpVectors } from "@babylonjs/core/Maths/math.vector.js";
  3. import { Color3 } from "@babylonjs/core/Maths/math.color.js";
  4. import { Tools } from "@babylonjs/core/Misc/tools.js";
  5. import { Camera } from "@babylonjs/core/Cameras/camera.js";
  6. import { FreeCamera } from "@babylonjs/core/Cameras/freeCamera.js";
  7. import { AnimationKeyInterpolation } from "@babylonjs/core/Animations/animationKey.js";
  8. import { AnimationGroup } from "@babylonjs/core/Animations/animationGroup.js";
  9. import { Bone } from "@babylonjs/core/Bones/bone.js";
  10. import { Skeleton } from "@babylonjs/core/Bones/skeleton.js";
  11. import { Material } from "@babylonjs/core/Materials/material.js";
  12. import { PBRMaterial } from "@babylonjs/core/Materials/PBR/pbrMaterial.js";
  13. import { Texture } from "@babylonjs/core/Materials/Textures/texture.js";
  14. import { TransformNode } from "@babylonjs/core/Meshes/transformNode.js";
  15. import { Buffer, VertexBuffer } from "@babylonjs/core/Buffers/buffer.js";
  16. import { Geometry } from "@babylonjs/core/Meshes/geometry.js";
  17. import { AbstractMesh } from "@babylonjs/core/Meshes/abstractMesh.js";
  18. import { Mesh } from "@babylonjs/core/Meshes/mesh.js";
  19. import { MorphTarget } from "@babylonjs/core/Morph/morphTarget.js";
  20. import { MorphTargetManager } from "@babylonjs/core/Morph/morphTargetManager.js";
  21. import { GLTFFileLoader, GLTFLoaderState, GLTFLoaderCoordinateSystemMode, GLTFLoaderAnimationStartMode } from "../glTFFileLoader.js";
  22. import { DecodeBase64UrlToBinary, IsBase64DataUrl, LoadFileError } from "@babylonjs/core/Misc/fileTools.js";
  23. import { Logger } from "@babylonjs/core/Misc/logger.js";
  24. import { BoundingInfo } from "@babylonjs/core/Culling/boundingInfo.js";
  25. import { nodeAnimationData } from "./glTFLoaderAnimation.js";
  26. // https://stackoverflow.com/a/48218209
  27. function mergeDeep(...objects) {
  28. const isObject = (obj) => obj && typeof obj === "object";
  29. return objects.reduce((prev, obj) => {
  30. Object.keys(obj).forEach((key) => {
  31. const pVal = prev[key];
  32. const oVal = obj[key];
  33. if (Array.isArray(pVal) && Array.isArray(oVal)) {
  34. prev[key] = pVal.concat(...oVal);
  35. }
  36. else if (isObject(pVal) && isObject(oVal)) {
  37. prev[key] = mergeDeep(pVal, oVal);
  38. }
  39. else {
  40. prev[key] = oVal;
  41. }
  42. });
  43. return prev;
  44. }, {});
  45. }
  46. /**
  47. * Helper class for working with arrays when loading the glTF asset
  48. */
  49. export class ArrayItem {
  50. /**
  51. * Gets an item from the given array.
  52. * @param context The context when loading the asset
  53. * @param array The array to get the item from
  54. * @param index The index to the array
  55. * @returns The array item
  56. */
  57. static Get(context, array, index) {
  58. if (!array || index == undefined || !array[index]) {
  59. throw new Error(`${context}: Failed to find index (${index})`);
  60. }
  61. return array[index];
  62. }
  63. /**
  64. * Gets an item from the given array or returns null if not available.
  65. * @param array The array to get the item from
  66. * @param index The index to the array
  67. * @returns The array item or null
  68. */
  69. static TryGet(array, index) {
  70. if (!array || index == undefined || !array[index]) {
  71. return null;
  72. }
  73. return array[index];
  74. }
  75. /**
  76. * Assign an `index` field to each item of the given array.
  77. * @param array The array of items
  78. */
  79. static Assign(array) {
  80. if (array) {
  81. for (let index = 0; index < array.length; index++) {
  82. array[index].index = index;
  83. }
  84. }
  85. }
  86. }
  87. /**
  88. * The glTF 2.0 loader
  89. */
  90. export class GLTFLoader {
  91. /**
  92. * Registers a loader extension.
  93. * @param name The name of the loader extension.
  94. * @param factory The factory function that creates the loader extension.
  95. */
  96. static RegisterExtension(name, factory) {
  97. if (GLTFLoader.UnregisterExtension(name)) {
  98. Logger.Warn(`Extension with the name '${name}' already exists`);
  99. }
  100. GLTFLoader._RegisteredExtensions[name] = {
  101. factory: factory,
  102. };
  103. }
  104. /**
  105. * Unregisters a loader extension.
  106. * @param name The name of the loader extension.
  107. * @returns A boolean indicating whether the extension has been unregistered
  108. */
  109. static UnregisterExtension(name) {
  110. if (!GLTFLoader._RegisteredExtensions[name]) {
  111. return false;
  112. }
  113. delete GLTFLoader._RegisteredExtensions[name];
  114. return true;
  115. }
  116. /**
  117. * The object that represents the glTF JSON.
  118. */
  119. get gltf() {
  120. if (!this._gltf) {
  121. throw new Error("glTF JSON is not available");
  122. }
  123. return this._gltf;
  124. }
  125. /**
  126. * The BIN chunk of a binary glTF.
  127. */
  128. get bin() {
  129. return this._bin;
  130. }
  131. /**
  132. * The parent file loader.
  133. */
  134. get parent() {
  135. return this._parent;
  136. }
  137. /**
  138. * The Babylon scene when loading the asset.
  139. */
  140. get babylonScene() {
  141. if (!this._babylonScene) {
  142. throw new Error("Scene is not available");
  143. }
  144. return this._babylonScene;
  145. }
  146. /**
  147. * The root Babylon node when loading the asset.
  148. */
  149. get rootBabylonMesh() {
  150. return this._rootBabylonMesh;
  151. }
  152. /**
  153. * The root url when loading the asset.
  154. */
  155. get rootUrl() {
  156. return this._rootUrl;
  157. }
  158. /**
  159. * @internal
  160. */
  161. constructor(parent) {
  162. /** @internal */
  163. this._completePromises = new Array();
  164. /** @internal */
  165. this._assetContainer = null;
  166. /** Storage */
  167. this._babylonLights = [];
  168. /** @internal */
  169. this._disableInstancedMesh = 0;
  170. /** @internal */
  171. this._allMaterialsDirtyRequired = false;
  172. this._extensions = new Array();
  173. this._disposed = false;
  174. this._rootUrl = null;
  175. this._fileName = null;
  176. this._uniqueRootUrl = null;
  177. this._bin = null;
  178. this._rootBabylonMesh = null;
  179. this._defaultBabylonMaterialData = {};
  180. this._postSceneLoadActions = new Array();
  181. this._parent = parent;
  182. }
  183. /** @internal */
  184. dispose() {
  185. if (this._disposed) {
  186. return;
  187. }
  188. this._disposed = true;
  189. this._completePromises.length = 0;
  190. this._extensions.forEach((extension) => extension.dispose && extension.dispose());
  191. this._extensions.length = 0;
  192. this._gltf = null; // TODO
  193. this._bin = null;
  194. this._babylonScene = null; // TODO
  195. this._rootBabylonMesh = null;
  196. this._defaultBabylonMaterialData = {};
  197. this._postSceneLoadActions.length = 0;
  198. this._parent.dispose();
  199. }
  200. /**
  201. * @internal
  202. */
  203. importMeshAsync(meshesNames, scene, container, data, rootUrl, onProgress, fileName = "") {
  204. return Promise.resolve().then(() => {
  205. this._babylonScene = scene;
  206. this._assetContainer = container;
  207. this._loadData(data);
  208. let nodes = null;
  209. if (meshesNames) {
  210. const nodeMap = {};
  211. if (this._gltf.nodes) {
  212. for (const node of this._gltf.nodes) {
  213. if (node.name) {
  214. nodeMap[node.name] = node.index;
  215. }
  216. }
  217. }
  218. const names = meshesNames instanceof Array ? meshesNames : [meshesNames];
  219. nodes = names.map((name) => {
  220. const node = nodeMap[name];
  221. if (node === undefined) {
  222. throw new Error(`Failed to find node '${name}'`);
  223. }
  224. return node;
  225. });
  226. }
  227. return this._loadAsync(rootUrl, fileName, nodes, () => {
  228. return {
  229. meshes: this._getMeshes(),
  230. particleSystems: [],
  231. skeletons: this._getSkeletons(),
  232. animationGroups: this._getAnimationGroups(),
  233. lights: this._babylonLights,
  234. transformNodes: this._getTransformNodes(),
  235. geometries: this._getGeometries(),
  236. spriteManagers: [],
  237. };
  238. });
  239. });
  240. }
  241. /**
  242. * @internal
  243. */
  244. loadAsync(scene, data, rootUrl, onProgress, fileName = "") {
  245. return Promise.resolve().then(() => {
  246. this._babylonScene = scene;
  247. this._loadData(data);
  248. return this._loadAsync(rootUrl, fileName, null, () => undefined);
  249. });
  250. }
  251. _loadAsync(rootUrl, fileName, nodes, resultFunc) {
  252. return Promise.resolve()
  253. .then(() => {
  254. this._rootUrl = rootUrl;
  255. this._uniqueRootUrl = !rootUrl.startsWith("file:") && fileName ? rootUrl : `${rootUrl}${Date.now()}/`;
  256. this._fileName = fileName;
  257. this._allMaterialsDirtyRequired = false;
  258. this._loadExtensions();
  259. this._checkExtensions();
  260. const loadingToReadyCounterName = `${GLTFLoaderState[GLTFLoaderState.LOADING]} => ${GLTFLoaderState[GLTFLoaderState.READY]}`;
  261. const loadingToCompleteCounterName = `${GLTFLoaderState[GLTFLoaderState.LOADING]} => ${GLTFLoaderState[GLTFLoaderState.COMPLETE]}`;
  262. this._parent._startPerformanceCounter(loadingToReadyCounterName);
  263. this._parent._startPerformanceCounter(loadingToCompleteCounterName);
  264. this._parent._setState(GLTFLoaderState.LOADING);
  265. this._extensionsOnLoading();
  266. const promises = new Array();
  267. // Block the marking of materials dirty until the scene is loaded.
  268. const oldBlockMaterialDirtyMechanism = this._babylonScene.blockMaterialDirtyMechanism;
  269. this._babylonScene.blockMaterialDirtyMechanism = true;
  270. if (!this.parent.loadOnlyMaterials) {
  271. if (nodes) {
  272. promises.push(this.loadSceneAsync("/nodes", { nodes: nodes, index: -1 }));
  273. }
  274. else if (this._gltf.scene != undefined || (this._gltf.scenes && this._gltf.scenes[0])) {
  275. const scene = ArrayItem.Get(`/scene`, this._gltf.scenes, this._gltf.scene || 0);
  276. promises.push(this.loadSceneAsync(`/scenes/${scene.index}`, scene));
  277. }
  278. }
  279. if (!this.parent.skipMaterials && this.parent.loadAllMaterials && this._gltf.materials) {
  280. for (let m = 0; m < this._gltf.materials.length; ++m) {
  281. const material = this._gltf.materials[m];
  282. const context = "/materials/" + m;
  283. const babylonDrawMode = Material.TriangleFillMode;
  284. promises.push(this._loadMaterialAsync(context, material, null, babylonDrawMode, () => { }));
  285. }
  286. }
  287. // Restore the blocking of material dirty.
  288. if (this._allMaterialsDirtyRequired) {
  289. // This can happen if we add a light for instance as it will impact the whole scene.
  290. // This automatically resets everything if needed.
  291. this._babylonScene.blockMaterialDirtyMechanism = oldBlockMaterialDirtyMechanism;
  292. }
  293. else {
  294. // By default a newly created material is dirty so there is no need to flag the full scene as dirty.
  295. // For perf reasons, we then bypass blockMaterialDirtyMechanism as this would "dirty" the entire scene.
  296. this._babylonScene._forceBlockMaterialDirtyMechanism(oldBlockMaterialDirtyMechanism);
  297. }
  298. if (this._parent.compileMaterials) {
  299. promises.push(this._compileMaterialsAsync());
  300. }
  301. if (this._parent.compileShadowGenerators) {
  302. promises.push(this._compileShadowGeneratorsAsync());
  303. }
  304. const resultPromise = Promise.all(promises).then(() => {
  305. if (this._rootBabylonMesh && this._rootBabylonMesh !== this._parent.customRootNode) {
  306. this._rootBabylonMesh.setEnabled(true);
  307. }
  308. this._extensionsOnReady();
  309. this._parent._setState(GLTFLoaderState.READY);
  310. this._startAnimations();
  311. return resultFunc();
  312. });
  313. return resultPromise.then((result) => {
  314. this._parent._endPerformanceCounter(loadingToReadyCounterName);
  315. Tools.SetImmediate(() => {
  316. if (!this._disposed) {
  317. Promise.all(this._completePromises).then(() => {
  318. this._parent._endPerformanceCounter(loadingToCompleteCounterName);
  319. this._parent._setState(GLTFLoaderState.COMPLETE);
  320. this._parent.onCompleteObservable.notifyObservers(undefined);
  321. this._parent.onCompleteObservable.clear();
  322. this.dispose();
  323. }, (error) => {
  324. this._parent.onErrorObservable.notifyObservers(error);
  325. this._parent.onErrorObservable.clear();
  326. this.dispose();
  327. });
  328. }
  329. });
  330. return result;
  331. });
  332. })
  333. .catch((error) => {
  334. if (!this._disposed) {
  335. this._parent.onErrorObservable.notifyObservers(error);
  336. this._parent.onErrorObservable.clear();
  337. this.dispose();
  338. }
  339. throw error;
  340. });
  341. }
  342. _loadData(data) {
  343. this._gltf = data.json;
  344. this._setupData();
  345. if (data.bin) {
  346. const buffers = this._gltf.buffers;
  347. if (buffers && buffers[0] && !buffers[0].uri) {
  348. const binaryBuffer = buffers[0];
  349. if (binaryBuffer.byteLength < data.bin.byteLength - 3 || binaryBuffer.byteLength > data.bin.byteLength) {
  350. Logger.Warn(`Binary buffer length (${binaryBuffer.byteLength}) from JSON does not match chunk length (${data.bin.byteLength})`);
  351. }
  352. this._bin = data.bin;
  353. }
  354. else {
  355. Logger.Warn("Unexpected BIN chunk");
  356. }
  357. }
  358. }
  359. _setupData() {
  360. ArrayItem.Assign(this._gltf.accessors);
  361. ArrayItem.Assign(this._gltf.animations);
  362. ArrayItem.Assign(this._gltf.buffers);
  363. ArrayItem.Assign(this._gltf.bufferViews);
  364. ArrayItem.Assign(this._gltf.cameras);
  365. ArrayItem.Assign(this._gltf.images);
  366. ArrayItem.Assign(this._gltf.materials);
  367. ArrayItem.Assign(this._gltf.meshes);
  368. ArrayItem.Assign(this._gltf.nodes);
  369. ArrayItem.Assign(this._gltf.samplers);
  370. ArrayItem.Assign(this._gltf.scenes);
  371. ArrayItem.Assign(this._gltf.skins);
  372. ArrayItem.Assign(this._gltf.textures);
  373. if (this._gltf.nodes) {
  374. const nodeParents = {};
  375. for (const node of this._gltf.nodes) {
  376. if (node.children) {
  377. for (const index of node.children) {
  378. nodeParents[index] = node.index;
  379. }
  380. }
  381. }
  382. const rootNode = this._createRootNode();
  383. for (const node of this._gltf.nodes) {
  384. const parentIndex = nodeParents[node.index];
  385. node.parent = parentIndex === undefined ? rootNode : this._gltf.nodes[parentIndex];
  386. }
  387. }
  388. }
  389. _loadExtensions() {
  390. for (const name in GLTFLoader._RegisteredExtensions) {
  391. const extension = GLTFLoader._RegisteredExtensions[name].factory(this);
  392. if (extension.name !== name) {
  393. Logger.Warn(`The name of the glTF loader extension instance does not match the registered name: ${extension.name} !== ${name}`);
  394. }
  395. this._extensions.push(extension);
  396. this._parent.onExtensionLoadedObservable.notifyObservers(extension);
  397. }
  398. this._extensions.sort((a, b) => (a.order || Number.MAX_VALUE) - (b.order || Number.MAX_VALUE));
  399. this._parent.onExtensionLoadedObservable.clear();
  400. }
  401. _checkExtensions() {
  402. if (this._gltf.extensionsRequired) {
  403. for (const name of this._gltf.extensionsRequired) {
  404. const available = this._extensions.some((extension) => extension.name === name && extension.enabled);
  405. if (!available) {
  406. throw new Error(`Required extension ${name} is not available`);
  407. }
  408. }
  409. }
  410. }
  411. _createRootNode() {
  412. if (this._parent.customRootNode !== undefined) {
  413. this._rootBabylonMesh = this._parent.customRootNode;
  414. return {
  415. // eslint-disable-next-line @typescript-eslint/naming-convention
  416. _babylonTransformNode: this._rootBabylonMesh === null ? undefined : this._rootBabylonMesh,
  417. index: -1,
  418. };
  419. }
  420. this._babylonScene._blockEntityCollection = !!this._assetContainer;
  421. const rootMesh = new Mesh("__root__", this._babylonScene);
  422. this._rootBabylonMesh = rootMesh;
  423. this._rootBabylonMesh._parentContainer = this._assetContainer;
  424. this._babylonScene._blockEntityCollection = false;
  425. this._rootBabylonMesh.setEnabled(false);
  426. const rootNode = {
  427. // eslint-disable-next-line @typescript-eslint/naming-convention
  428. _babylonTransformNode: this._rootBabylonMesh,
  429. index: -1,
  430. };
  431. switch (this._parent.coordinateSystemMode) {
  432. case GLTFLoaderCoordinateSystemMode.AUTO: {
  433. if (!this._babylonScene.useRightHandedSystem) {
  434. rootNode.rotation = [0, 1, 0, 0];
  435. rootNode.scale = [1, 1, -1];
  436. GLTFLoader._LoadTransform(rootNode, this._rootBabylonMesh);
  437. }
  438. break;
  439. }
  440. case GLTFLoaderCoordinateSystemMode.FORCE_RIGHT_HANDED: {
  441. this._babylonScene.useRightHandedSystem = true;
  442. break;
  443. }
  444. default: {
  445. throw new Error(`Invalid coordinate system mode (${this._parent.coordinateSystemMode})`);
  446. }
  447. }
  448. this._parent.onMeshLoadedObservable.notifyObservers(rootMesh);
  449. return rootNode;
  450. }
  451. /**
  452. * Loads a glTF scene.
  453. * @param context The context when loading the asset
  454. * @param scene The glTF scene property
  455. * @returns A promise that resolves when the load is complete
  456. */
  457. loadSceneAsync(context, scene) {
  458. const extensionPromise = this._extensionsLoadSceneAsync(context, scene);
  459. if (extensionPromise) {
  460. return extensionPromise;
  461. }
  462. const promises = new Array();
  463. this.logOpen(`${context} ${scene.name || ""}`);
  464. if (scene.nodes) {
  465. for (const index of scene.nodes) {
  466. const node = ArrayItem.Get(`${context}/nodes/${index}`, this._gltf.nodes, index);
  467. promises.push(this.loadNodeAsync(`/nodes/${node.index}`, node, (babylonMesh) => {
  468. babylonMesh.parent = this._rootBabylonMesh;
  469. }));
  470. }
  471. }
  472. for (const action of this._postSceneLoadActions) {
  473. action();
  474. }
  475. promises.push(this._loadAnimationsAsync());
  476. this.logClose();
  477. return Promise.all(promises).then(() => { });
  478. }
  479. _forEachPrimitive(node, callback) {
  480. if (node._primitiveBabylonMeshes) {
  481. for (const babylonMesh of node._primitiveBabylonMeshes) {
  482. callback(babylonMesh);
  483. }
  484. }
  485. }
  486. _getGeometries() {
  487. const geometries = [];
  488. const nodes = this._gltf.nodes;
  489. if (nodes) {
  490. for (const node of nodes) {
  491. this._forEachPrimitive(node, (babylonMesh) => {
  492. const geometry = babylonMesh.geometry;
  493. if (geometry && geometries.indexOf(geometry) === -1) {
  494. geometries.push(geometry);
  495. }
  496. });
  497. }
  498. }
  499. return geometries;
  500. }
  501. _getMeshes() {
  502. const meshes = [];
  503. // Root mesh is always first, if available.
  504. if (this._rootBabylonMesh instanceof AbstractMesh) {
  505. meshes.push(this._rootBabylonMesh);
  506. }
  507. const nodes = this._gltf.nodes;
  508. if (nodes) {
  509. for (const node of nodes) {
  510. this._forEachPrimitive(node, (babylonMesh) => {
  511. meshes.push(babylonMesh);
  512. });
  513. }
  514. }
  515. return meshes;
  516. }
  517. _getTransformNodes() {
  518. const transformNodes = [];
  519. const nodes = this._gltf.nodes;
  520. if (nodes) {
  521. for (const node of nodes) {
  522. if (node._babylonTransformNode && node._babylonTransformNode.getClassName() === "TransformNode") {
  523. transformNodes.push(node._babylonTransformNode);
  524. }
  525. if (node._babylonTransformNodeForSkin) {
  526. transformNodes.push(node._babylonTransformNodeForSkin);
  527. }
  528. }
  529. }
  530. return transformNodes;
  531. }
  532. _getSkeletons() {
  533. const skeletons = [];
  534. const skins = this._gltf.skins;
  535. if (skins) {
  536. for (const skin of skins) {
  537. if (skin._data) {
  538. skeletons.push(skin._data.babylonSkeleton);
  539. }
  540. }
  541. }
  542. return skeletons;
  543. }
  544. _getAnimationGroups() {
  545. const animationGroups = [];
  546. const animations = this._gltf.animations;
  547. if (animations) {
  548. for (const animation of animations) {
  549. if (animation._babylonAnimationGroup) {
  550. animationGroups.push(animation._babylonAnimationGroup);
  551. }
  552. }
  553. }
  554. return animationGroups;
  555. }
  556. _startAnimations() {
  557. switch (this._parent.animationStartMode) {
  558. case GLTFLoaderAnimationStartMode.NONE: {
  559. // do nothing
  560. break;
  561. }
  562. case GLTFLoaderAnimationStartMode.FIRST: {
  563. const babylonAnimationGroups = this._getAnimationGroups();
  564. if (babylonAnimationGroups.length !== 0) {
  565. babylonAnimationGroups[0].start(true);
  566. }
  567. break;
  568. }
  569. case GLTFLoaderAnimationStartMode.ALL: {
  570. const babylonAnimationGroups = this._getAnimationGroups();
  571. for (const babylonAnimationGroup of babylonAnimationGroups) {
  572. babylonAnimationGroup.start(true);
  573. }
  574. break;
  575. }
  576. default: {
  577. Logger.Error(`Invalid animation start mode (${this._parent.animationStartMode})`);
  578. return;
  579. }
  580. }
  581. }
  582. /**
  583. * Loads a glTF node.
  584. * @param context The context when loading the asset
  585. * @param node The glTF node property
  586. * @param assign A function called synchronously after parsing the glTF properties
  587. * @returns A promise that resolves with the loaded Babylon mesh when the load is complete
  588. */
  589. loadNodeAsync(context, node, assign = () => { }) {
  590. const extensionPromise = this._extensionsLoadNodeAsync(context, node, assign);
  591. if (extensionPromise) {
  592. return extensionPromise;
  593. }
  594. if (node._babylonTransformNode) {
  595. throw new Error(`${context}: Invalid recursive node hierarchy`);
  596. }
  597. const promises = new Array();
  598. this.logOpen(`${context} ${node.name || ""}`);
  599. const loadNode = (babylonTransformNode) => {
  600. GLTFLoader.AddPointerMetadata(babylonTransformNode, context);
  601. GLTFLoader._LoadTransform(node, babylonTransformNode);
  602. if (node.camera != undefined) {
  603. const camera = ArrayItem.Get(`${context}/camera`, this._gltf.cameras, node.camera);
  604. promises.push(this.loadCameraAsync(`/cameras/${camera.index}`, camera, (babylonCamera) => {
  605. babylonCamera.parent = babylonTransformNode;
  606. }));
  607. }
  608. if (node.children) {
  609. for (const index of node.children) {
  610. const childNode = ArrayItem.Get(`${context}/children/${index}`, this._gltf.nodes, index);
  611. promises.push(this.loadNodeAsync(`/nodes/${childNode.index}`, childNode, (childBabylonMesh) => {
  612. childBabylonMesh.parent = babylonTransformNode;
  613. }));
  614. }
  615. }
  616. assign(babylonTransformNode);
  617. };
  618. if (node.mesh == undefined || node.skin != undefined) {
  619. const nodeName = node.name || `node${node.index}`;
  620. this._babylonScene._blockEntityCollection = !!this._assetContainer;
  621. const transformNode = new TransformNode(nodeName, this._babylonScene);
  622. transformNode._parentContainer = this._assetContainer;
  623. this._babylonScene._blockEntityCollection = false;
  624. if (node.mesh == undefined) {
  625. node._babylonTransformNode = transformNode;
  626. }
  627. else {
  628. node._babylonTransformNodeForSkin = transformNode;
  629. }
  630. loadNode(transformNode);
  631. }
  632. if (node.mesh != undefined) {
  633. if (node.skin == undefined) {
  634. const mesh = ArrayItem.Get(`${context}/mesh`, this._gltf.meshes, node.mesh);
  635. promises.push(this._loadMeshAsync(`/meshes/${mesh.index}`, node, mesh, loadNode));
  636. }
  637. else {
  638. // See https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins (second implementation note)
  639. // This code path will place the skinned mesh as a sibling of the skeleton root node without loading the
  640. // transform, which effectively ignores the transform of the skinned mesh, as per spec.
  641. const mesh = ArrayItem.Get(`${context}/mesh`, this._gltf.meshes, node.mesh);
  642. promises.push(this._loadMeshAsync(`/meshes/${mesh.index}`, node, mesh, (babylonTransformNode) => {
  643. const babylonTransformNodeForSkin = node._babylonTransformNodeForSkin;
  644. // Merge the metadata from the skin node to the skinned mesh in case a loader extension added metadata.
  645. babylonTransformNode.metadata = mergeDeep(babylonTransformNodeForSkin.metadata, babylonTransformNode.metadata || {});
  646. const skin = ArrayItem.Get(`${context}/skin`, this._gltf.skins, node.skin);
  647. promises.push(this._loadSkinAsync(`/skins/${skin.index}`, node, skin, (babylonSkeleton) => {
  648. this._forEachPrimitive(node, (babylonMesh) => {
  649. babylonMesh.skeleton = babylonSkeleton;
  650. });
  651. // Wait until all the nodes are parented before parenting the skinned mesh.
  652. this._postSceneLoadActions.push(() => {
  653. if (skin.skeleton != undefined) {
  654. // Place the skinned mesh node as a sibling of the skeleton root node.
  655. // Handle special case when the parent of the skeleton root is the skinned mesh.
  656. const parentNode = ArrayItem.Get(`/skins/${skin.index}/skeleton`, this._gltf.nodes, skin.skeleton).parent;
  657. if (node.index === parentNode.index) {
  658. babylonTransformNode.parent = babylonTransformNodeForSkin.parent;
  659. }
  660. else {
  661. babylonTransformNode.parent = parentNode._babylonTransformNode;
  662. }
  663. }
  664. else {
  665. babylonTransformNode.parent = this._rootBabylonMesh;
  666. }
  667. this._parent.onSkinLoadedObservable.notifyObservers({ node: babylonTransformNodeForSkin, skinnedNode: babylonTransformNode });
  668. });
  669. }));
  670. }));
  671. }
  672. }
  673. this.logClose();
  674. return Promise.all(promises).then(() => {
  675. this._forEachPrimitive(node, (babylonMesh) => {
  676. if (babylonMesh.geometry && babylonMesh.geometry.useBoundingInfoFromGeometry) {
  677. // simply apply the world matrices to the bounding info - the extends are already ok
  678. babylonMesh._updateBoundingInfo();
  679. }
  680. else {
  681. babylonMesh.refreshBoundingInfo(true, true);
  682. }
  683. });
  684. return node._babylonTransformNode;
  685. });
  686. }
  687. _loadMeshAsync(context, node, mesh, assign) {
  688. const primitives = mesh.primitives;
  689. if (!primitives || !primitives.length) {
  690. throw new Error(`${context}: Primitives are missing`);
  691. }
  692. if (primitives[0].index == undefined) {
  693. ArrayItem.Assign(primitives);
  694. }
  695. const promises = new Array();
  696. this.logOpen(`${context} ${mesh.name || ""}`);
  697. const name = node.name || `node${node.index}`;
  698. if (primitives.length === 1) {
  699. const primitive = mesh.primitives[0];
  700. promises.push(this._loadMeshPrimitiveAsync(`${context}/primitives/${primitive.index}`, name, node, mesh, primitive, (babylonMesh) => {
  701. node._babylonTransformNode = babylonMesh;
  702. node._primitiveBabylonMeshes = [babylonMesh];
  703. }));
  704. }
  705. else {
  706. this._babylonScene._blockEntityCollection = !!this._assetContainer;
  707. node._babylonTransformNode = new TransformNode(name, this._babylonScene);
  708. node._babylonTransformNode._parentContainer = this._assetContainer;
  709. this._babylonScene._blockEntityCollection = false;
  710. node._primitiveBabylonMeshes = [];
  711. for (const primitive of primitives) {
  712. promises.push(this._loadMeshPrimitiveAsync(`${context}/primitives/${primitive.index}`, `${name}_primitive${primitive.index}`, node, mesh, primitive, (babylonMesh) => {
  713. babylonMesh.parent = node._babylonTransformNode;
  714. node._primitiveBabylonMeshes.push(babylonMesh);
  715. }));
  716. }
  717. }
  718. assign(node._babylonTransformNode);
  719. this.logClose();
  720. return Promise.all(promises).then(() => {
  721. return node._babylonTransformNode;
  722. });
  723. }
  724. /**
  725. * @internal Define this method to modify the default behavior when loading data for mesh primitives.
  726. * @param context The context when loading the asset
  727. * @param name The mesh name when loading the asset
  728. * @param node The glTF node when loading the asset
  729. * @param mesh The glTF mesh when loading the asset
  730. * @param primitive The glTF mesh primitive property
  731. * @param assign A function called synchronously after parsing the glTF properties
  732. * @returns A promise that resolves with the loaded mesh when the load is complete or null if not handled
  733. */
  734. _loadMeshPrimitiveAsync(context, name, node, mesh, primitive, assign) {
  735. const extensionPromise = this._extensionsLoadMeshPrimitiveAsync(context, name, node, mesh, primitive, assign);
  736. if (extensionPromise) {
  737. return extensionPromise;
  738. }
  739. this.logOpen(`${context}`);
  740. const shouldInstance = this._disableInstancedMesh === 0 && this._parent.createInstances && node.skin == undefined && !mesh.primitives[0].targets;
  741. let babylonAbstractMesh;
  742. let promise;
  743. if (shouldInstance && primitive._instanceData) {
  744. this._babylonScene._blockEntityCollection = !!this._assetContainer;
  745. babylonAbstractMesh = primitive._instanceData.babylonSourceMesh.createInstance(name);
  746. babylonAbstractMesh._parentContainer = this._assetContainer;
  747. this._babylonScene._blockEntityCollection = false;
  748. promise = primitive._instanceData.promise;
  749. }
  750. else {
  751. const promises = new Array();
  752. this._babylonScene._blockEntityCollection = !!this._assetContainer;
  753. const babylonMesh = new Mesh(name, this._babylonScene);
  754. babylonMesh._parentContainer = this._assetContainer;
  755. this._babylonScene._blockEntityCollection = false;
  756. babylonMesh.overrideMaterialSideOrientation = this._babylonScene.useRightHandedSystem ? Material.CounterClockWiseSideOrientation : Material.ClockWiseSideOrientation;
  757. this._createMorphTargets(context, node, mesh, primitive, babylonMesh);
  758. promises.push(this._loadVertexDataAsync(context, primitive, babylonMesh).then((babylonGeometry) => {
  759. return this._loadMorphTargetsAsync(context, primitive, babylonMesh, babylonGeometry).then(() => {
  760. if (this._disposed) {
  761. return;
  762. }
  763. this._babylonScene._blockEntityCollection = !!this._assetContainer;
  764. babylonGeometry.applyToMesh(babylonMesh);
  765. babylonGeometry._parentContainer = this._assetContainer;
  766. this._babylonScene._blockEntityCollection = false;
  767. });
  768. }));
  769. const babylonDrawMode = GLTFLoader._GetDrawMode(context, primitive.mode);
  770. if (primitive.material == undefined) {
  771. let babylonMaterial = this._defaultBabylonMaterialData[babylonDrawMode];
  772. if (!babylonMaterial) {
  773. babylonMaterial = this._createDefaultMaterial("__GLTFLoader._default", babylonDrawMode);
  774. this._parent.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
  775. this._defaultBabylonMaterialData[babylonDrawMode] = babylonMaterial;
  776. }
  777. babylonMesh.material = babylonMaterial;
  778. }
  779. else if (!this.parent.skipMaterials) {
  780. const material = ArrayItem.Get(`${context}/material`, this._gltf.materials, primitive.material);
  781. promises.push(this._loadMaterialAsync(`/materials/${material.index}`, material, babylonMesh, babylonDrawMode, (babylonMaterial) => {
  782. babylonMesh.material = babylonMaterial;
  783. }));
  784. }
  785. promise = Promise.all(promises);
  786. if (shouldInstance) {
  787. primitive._instanceData = {
  788. babylonSourceMesh: babylonMesh,
  789. promise: promise,
  790. };
  791. }
  792. babylonAbstractMesh = babylonMesh;
  793. }
  794. GLTFLoader.AddPointerMetadata(babylonAbstractMesh, context);
  795. this._parent.onMeshLoadedObservable.notifyObservers(babylonAbstractMesh);
  796. assign(babylonAbstractMesh);
  797. this.logClose();
  798. return promise.then(() => {
  799. return babylonAbstractMesh;
  800. });
  801. }
  802. _loadVertexDataAsync(context, primitive, babylonMesh) {
  803. const extensionPromise = this._extensionsLoadVertexDataAsync(context, primitive, babylonMesh);
  804. if (extensionPromise) {
  805. return extensionPromise;
  806. }
  807. const attributes = primitive.attributes;
  808. if (!attributes) {
  809. throw new Error(`${context}: Attributes are missing`);
  810. }
  811. const promises = new Array();
  812. const babylonGeometry = new Geometry(babylonMesh.name, this._babylonScene);
  813. if (primitive.indices == undefined) {
  814. babylonMesh.isUnIndexed = true;
  815. }
  816. else {
  817. const accessor = ArrayItem.Get(`${context}/indices`, this._gltf.accessors, primitive.indices);
  818. promises.push(this._loadIndicesAccessorAsync(`/accessors/${accessor.index}`, accessor).then((data) => {
  819. babylonGeometry.setIndices(data);
  820. }));
  821. }
  822. const loadAttribute = (name, kind, callback) => {
  823. if (attributes[name] == undefined) {
  824. return;
  825. }
  826. babylonMesh._delayInfo = babylonMesh._delayInfo || [];
  827. if (babylonMesh._delayInfo.indexOf(kind) === -1) {
  828. babylonMesh._delayInfo.push(kind);
  829. }
  830. const accessor = ArrayItem.Get(`${context}/attributes/${name}`, this._gltf.accessors, attributes[name]);
  831. promises.push(this._loadVertexAccessorAsync(`/accessors/${accessor.index}`, accessor, kind).then((babylonVertexBuffer) => {
  832. if (babylonVertexBuffer.getKind() === VertexBuffer.PositionKind && !this.parent.alwaysComputeBoundingBox && !babylonMesh.skeleton) {
  833. if (accessor.min && accessor.max) {
  834. const min = TmpVectors.Vector3[0].copyFromFloats(...accessor.min);
  835. const max = TmpVectors.Vector3[1].copyFromFloats(...accessor.max);
  836. if (accessor.normalized && accessor.componentType !== 5126 /* AccessorComponentType.FLOAT */) {
  837. let divider = 1;
  838. switch (accessor.componentType) {
  839. case 5120 /* AccessorComponentType.BYTE */:
  840. divider = 127.0;
  841. break;
  842. case 5121 /* AccessorComponentType.UNSIGNED_BYTE */:
  843. divider = 255.0;
  844. break;
  845. case 5122 /* AccessorComponentType.SHORT */:
  846. divider = 32767.0;
  847. break;
  848. case 5123 /* AccessorComponentType.UNSIGNED_SHORT */:
  849. divider = 65535.0;
  850. break;
  851. }
  852. const oneOverDivider = 1 / divider;
  853. min.scaleInPlace(oneOverDivider);
  854. max.scaleInPlace(oneOverDivider);
  855. }
  856. babylonGeometry._boundingInfo = new BoundingInfo(min, max);
  857. babylonGeometry.useBoundingInfoFromGeometry = true;
  858. }
  859. }
  860. babylonGeometry.setVerticesBuffer(babylonVertexBuffer, accessor.count);
  861. }));
  862. if (kind == VertexBuffer.MatricesIndicesExtraKind) {
  863. babylonMesh.numBoneInfluencers = 8;
  864. }
  865. if (callback) {
  866. callback(accessor);
  867. }
  868. };
  869. loadAttribute("POSITION", VertexBuffer.PositionKind);
  870. loadAttribute("NORMAL", VertexBuffer.NormalKind);
  871. loadAttribute("TANGENT", VertexBuffer.TangentKind);
  872. loadAttribute("TEXCOORD_0", VertexBuffer.UVKind);
  873. loadAttribute("TEXCOORD_1", VertexBuffer.UV2Kind);
  874. loadAttribute("TEXCOORD_2", VertexBuffer.UV3Kind);
  875. loadAttribute("TEXCOORD_3", VertexBuffer.UV4Kind);
  876. loadAttribute("TEXCOORD_4", VertexBuffer.UV5Kind);
  877. loadAttribute("TEXCOORD_5", VertexBuffer.UV6Kind);
  878. loadAttribute("JOINTS_0", VertexBuffer.MatricesIndicesKind);
  879. loadAttribute("WEIGHTS_0", VertexBuffer.MatricesWeightsKind);
  880. loadAttribute("JOINTS_1", VertexBuffer.MatricesIndicesExtraKind);
  881. loadAttribute("WEIGHTS_1", VertexBuffer.MatricesWeightsExtraKind);
  882. loadAttribute("COLOR_0", VertexBuffer.ColorKind, (accessor) => {
  883. if (accessor.type === "VEC4" /* AccessorType.VEC4 */) {
  884. babylonMesh.hasVertexAlpha = true;
  885. }
  886. });
  887. return Promise.all(promises).then(() => {
  888. return babylonGeometry;
  889. });
  890. }
  891. _createMorphTargets(context, node, mesh, primitive, babylonMesh) {
  892. if (!primitive.targets) {
  893. return;
  894. }
  895. if (node._numMorphTargets == undefined) {
  896. node._numMorphTargets = primitive.targets.length;
  897. }
  898. else if (primitive.targets.length !== node._numMorphTargets) {
  899. throw new Error(`${context}: Primitives do not have the same number of targets`);
  900. }
  901. const targetNames = mesh.extras ? mesh.extras.targetNames : null;
  902. this._babylonScene._blockEntityCollection = !!this._assetContainer;
  903. babylonMesh.morphTargetManager = new MorphTargetManager(this._babylonScene);
  904. babylonMesh.morphTargetManager._parentContainer = this._assetContainer;
  905. this._babylonScene._blockEntityCollection = false;
  906. babylonMesh.morphTargetManager.areUpdatesFrozen = true;
  907. for (let index = 0; index < primitive.targets.length; index++) {
  908. const weight = node.weights ? node.weights[index] : mesh.weights ? mesh.weights[index] : 0;
  909. const name = targetNames ? targetNames[index] : `morphTarget${index}`;
  910. babylonMesh.morphTargetManager.addTarget(new MorphTarget(name, weight, babylonMesh.getScene()));
  911. // TODO: tell the target whether it has positions, normals, tangents
  912. }
  913. }
  914. _loadMorphTargetsAsync(context, primitive, babylonMesh, babylonGeometry) {
  915. if (!primitive.targets) {
  916. return Promise.resolve();
  917. }
  918. const promises = new Array();
  919. const morphTargetManager = babylonMesh.morphTargetManager;
  920. for (let index = 0; index < morphTargetManager.numTargets; index++) {
  921. const babylonMorphTarget = morphTargetManager.getTarget(index);
  922. promises.push(this._loadMorphTargetVertexDataAsync(`${context}/targets/${index}`, babylonGeometry, primitive.targets[index], babylonMorphTarget));
  923. }
  924. return Promise.all(promises).then(() => {
  925. morphTargetManager.areUpdatesFrozen = false;
  926. });
  927. }
  928. _loadMorphTargetVertexDataAsync(context, babylonGeometry, attributes, babylonMorphTarget) {
  929. const promises = new Array();
  930. const loadAttribute = (attribute, kind, setData) => {
  931. if (attributes[attribute] == undefined) {
  932. return;
  933. }
  934. const babylonVertexBuffer = babylonGeometry.getVertexBuffer(kind);
  935. if (!babylonVertexBuffer) {
  936. return;
  937. }
  938. const accessor = ArrayItem.Get(`${context}/${attribute}`, this._gltf.accessors, attributes[attribute]);
  939. promises.push(this._loadFloatAccessorAsync(`/accessors/${accessor.index}`, accessor).then((data) => {
  940. setData(babylonVertexBuffer, data);
  941. }));
  942. };
  943. loadAttribute("POSITION", VertexBuffer.PositionKind, (babylonVertexBuffer, data) => {
  944. const positions = new Float32Array(data.length);
  945. babylonVertexBuffer.forEach(data.length, (value, index) => {
  946. positions[index] = data[index] + value;
  947. });
  948. babylonMorphTarget.setPositions(positions);
  949. });
  950. loadAttribute("NORMAL", VertexBuffer.NormalKind, (babylonVertexBuffer, data) => {
  951. const normals = new Float32Array(data.length);
  952. babylonVertexBuffer.forEach(normals.length, (value, index) => {
  953. normals[index] = data[index] + value;
  954. });
  955. babylonMorphTarget.setNormals(normals);
  956. });
  957. loadAttribute("TANGENT", VertexBuffer.TangentKind, (babylonVertexBuffer, data) => {
  958. const tangents = new Float32Array((data.length / 3) * 4);
  959. let dataIndex = 0;
  960. babylonVertexBuffer.forEach((data.length / 3) * 4, (value, index) => {
  961. // Tangent data for morph targets is stored as xyz delta.
  962. // The vertexData.tangent is stored as xyzw.
  963. // So we need to skip every fourth vertexData.tangent.
  964. if ((index + 1) % 4 !== 0) {
  965. tangents[dataIndex] = data[dataIndex] + value;
  966. dataIndex++;
  967. }
  968. });
  969. babylonMorphTarget.setTangents(tangents);
  970. });
  971. return Promise.all(promises).then(() => { });
  972. }
  973. static _LoadTransform(node, babylonNode) {
  974. // Ignore the TRS of skinned nodes.
  975. // See https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins (second implementation note)
  976. if (node.skin != undefined) {
  977. return;
  978. }
  979. let position = Vector3.Zero();
  980. let rotation = Quaternion.Identity();
  981. let scaling = Vector3.One();
  982. if (node.matrix) {
  983. const matrix = Matrix.FromArray(node.matrix);
  984. matrix.decompose(scaling, rotation, position);
  985. }
  986. else {
  987. if (node.translation) {
  988. position = Vector3.FromArray(node.translation);
  989. }
  990. if (node.rotation) {
  991. rotation = Quaternion.FromArray(node.rotation);
  992. }
  993. if (node.scale) {
  994. scaling = Vector3.FromArray(node.scale);
  995. }
  996. }
  997. babylonNode.position = position;
  998. babylonNode.rotationQuaternion = rotation;
  999. babylonNode.scaling = scaling;
  1000. }
  1001. _loadSkinAsync(context, node, skin, assign) {
  1002. const extensionPromise = this._extensionsLoadSkinAsync(context, node, skin);
  1003. if (extensionPromise) {
  1004. return extensionPromise;
  1005. }
  1006. if (skin._data) {
  1007. assign(skin._data.babylonSkeleton);
  1008. return skin._data.promise;
  1009. }
  1010. const skeletonId = `skeleton${skin.index}`;
  1011. this._babylonScene._blockEntityCollection = !!this._assetContainer;
  1012. const babylonSkeleton = new Skeleton(skin.name || skeletonId, skeletonId, this._babylonScene);
  1013. babylonSkeleton._parentContainer = this._assetContainer;
  1014. this._babylonScene._blockEntityCollection = false;
  1015. this._loadBones(context, skin, babylonSkeleton);
  1016. const promise = this._loadSkinInverseBindMatricesDataAsync(context, skin).then((inverseBindMatricesData) => {
  1017. this._updateBoneMatrices(babylonSkeleton, inverseBindMatricesData);
  1018. });
  1019. skin._data = {
  1020. babylonSkeleton: babylonSkeleton,
  1021. promise: promise,
  1022. };
  1023. assign(babylonSkeleton);
  1024. return promise;
  1025. }
  1026. _loadBones(context, skin, babylonSkeleton) {
  1027. if (skin.skeleton == undefined || this._parent.alwaysComputeSkeletonRootNode) {
  1028. const rootNode = this._findSkeletonRootNode(`${context}/joints`, skin.joints);
  1029. if (rootNode) {
  1030. if (skin.skeleton === undefined) {
  1031. skin.skeleton = rootNode.index;
  1032. }
  1033. else {
  1034. const isParent = (a, b) => {
  1035. for (; b.parent; b = b.parent) {
  1036. if (b.parent === a) {
  1037. return true;
  1038. }
  1039. }
  1040. return false;
  1041. };
  1042. const skeletonNode = ArrayItem.Get(`${context}/skeleton`, this._gltf.nodes, skin.skeleton);
  1043. if (skeletonNode !== rootNode && !isParent(skeletonNode, rootNode)) {
  1044. Logger.Warn(`${context}/skeleton: Overriding with nearest common ancestor as skeleton node is not a common root`);
  1045. skin.skeleton = rootNode.index;
  1046. }
  1047. }
  1048. }
  1049. else {
  1050. Logger.Warn(`${context}: Failed to find common root`);
  1051. }
  1052. }
  1053. const babylonBones = {};
  1054. for (const index of skin.joints) {
  1055. const node = ArrayItem.Get(`${context}/joints/${index}`, this._gltf.nodes, index);
  1056. this._loadBone(node, skin, babylonSkeleton, babylonBones);
  1057. }
  1058. }
  1059. _findSkeletonRootNode(context, joints) {
  1060. if (joints.length === 0) {
  1061. return null;
  1062. }
  1063. const paths = {};
  1064. for (const index of joints) {
  1065. const path = [];
  1066. let node = ArrayItem.Get(`${context}/${index}`, this._gltf.nodes, index);
  1067. while (node.index !== -1) {
  1068. path.unshift(node);
  1069. node = node.parent;
  1070. }
  1071. paths[index] = path;
  1072. }
  1073. let rootNode = null;
  1074. for (let i = 0;; ++i) {
  1075. let path = paths[joints[0]];
  1076. if (i >= path.length) {
  1077. return rootNode;
  1078. }
  1079. const node = path[i];
  1080. for (let j = 1; j < joints.length; ++j) {
  1081. path = paths[joints[j]];
  1082. if (i >= path.length || node !== path[i]) {
  1083. return rootNode;
  1084. }
  1085. }
  1086. rootNode = node;
  1087. }
  1088. }
  1089. _loadBone(node, skin, babylonSkeleton, babylonBones) {
  1090. let babylonBone = babylonBones[node.index];
  1091. if (babylonBone) {
  1092. return babylonBone;
  1093. }
  1094. let parentBabylonBone = null;
  1095. if (node.index !== skin.skeleton) {
  1096. if (node.parent && node.parent.index !== -1) {
  1097. parentBabylonBone = this._loadBone(node.parent, skin, babylonSkeleton, babylonBones);
  1098. }
  1099. else if (skin.skeleton !== undefined) {
  1100. Logger.Warn(`/skins/${skin.index}/skeleton: Skeleton node is not a common root`);
  1101. }
  1102. }
  1103. const boneIndex = skin.joints.indexOf(node.index);
  1104. babylonBone = new Bone(node.name || `joint${node.index}`, babylonSkeleton, parentBabylonBone, this._getNodeMatrix(node), null, null, boneIndex);
  1105. babylonBones[node.index] = babylonBone;
  1106. // Wait until the scene is loaded to ensure the transform nodes are loaded.
  1107. this._postSceneLoadActions.push(() => {
  1108. // Link the Babylon bone with the corresponding Babylon transform node.
  1109. // A glTF joint is a pointer to a glTF node in the glTF node hierarchy similar to Unity3D.
  1110. babylonBone.linkTransformNode(node._babylonTransformNode);
  1111. });
  1112. return babylonBone;
  1113. }
  1114. _loadSkinInverseBindMatricesDataAsync(context, skin) {
  1115. if (skin.inverseBindMatrices == undefined) {
  1116. return Promise.resolve(null);
  1117. }
  1118. const accessor = ArrayItem.Get(`${context}/inverseBindMatrices`, this._gltf.accessors, skin.inverseBindMatrices);
  1119. return this._loadFloatAccessorAsync(`/accessors/${accessor.index}`, accessor);
  1120. }
  1121. _updateBoneMatrices(babylonSkeleton, inverseBindMatricesData) {
  1122. for (const babylonBone of babylonSkeleton.bones) {
  1123. const baseMatrix = Matrix.Identity();
  1124. const boneIndex = babylonBone._index;
  1125. if (inverseBindMatricesData && boneIndex !== -1) {
  1126. Matrix.FromArrayToRef(inverseBindMatricesData, boneIndex * 16, baseMatrix);
  1127. baseMatrix.invertToRef(baseMatrix);
  1128. }
  1129. const babylonParentBone = babylonBone.getParent();
  1130. if (babylonParentBone) {
  1131. baseMatrix.multiplyToRef(babylonParentBone.getAbsoluteInverseBindMatrix(), baseMatrix);
  1132. }
  1133. babylonBone.updateMatrix(baseMatrix, false, false);
  1134. babylonBone._updateAbsoluteBindMatrices(undefined, false);
  1135. }
  1136. }
  1137. _getNodeMatrix(node) {
  1138. return node.matrix
  1139. ? Matrix.FromArray(node.matrix)
  1140. : Matrix.Compose(node.scale ? Vector3.FromArray(node.scale) : Vector3.One(), node.rotation ? Quaternion.FromArray(node.rotation) : Quaternion.Identity(), node.translation ? Vector3.FromArray(node.translation) : Vector3.Zero());
  1141. }
  1142. /**
  1143. * Loads a glTF camera.
  1144. * @param context The context when loading the asset
  1145. * @param camera The glTF camera property
  1146. * @param assign A function called synchronously after parsing the glTF properties
  1147. * @returns A promise that resolves with the loaded Babylon camera when the load is complete
  1148. */
  1149. loadCameraAsync(context, camera, assign = () => { }) {
  1150. const extensionPromise = this._extensionsLoadCameraAsync(context, camera, assign);
  1151. if (extensionPromise) {
  1152. return extensionPromise;
  1153. }
  1154. const promises = new Array();
  1155. this.logOpen(`${context} ${camera.name || ""}`);
  1156. this._babylonScene._blockEntityCollection = !!this._assetContainer;
  1157. const babylonCamera = new FreeCamera(camera.name || `camera${camera.index}`, Vector3.Zero(), this._babylonScene, false);
  1158. babylonCamera._parentContainer = this._assetContainer;
  1159. this._babylonScene._blockEntityCollection = false;
  1160. babylonCamera.ignoreParentScaling = true;
  1161. camera._babylonCamera = babylonCamera;
  1162. // Rotation by 180 as glTF has a different convention than Babylon.
  1163. babylonCamera.rotation.set(0, Math.PI, 0);
  1164. switch (camera.type) {
  1165. case "perspective" /* CameraType.PERSPECTIVE */: {
  1166. const perspective = camera.perspective;
  1167. if (!perspective) {
  1168. throw new Error(`${context}: Camera perspective properties are missing`);
  1169. }
  1170. babylonCamera.fov = perspective.yfov;
  1171. babylonCamera.minZ = perspective.znear;
  1172. babylonCamera.maxZ = perspective.zfar || 0;
  1173. break;
  1174. }
  1175. case "orthographic" /* CameraType.ORTHOGRAPHIC */: {
  1176. if (!camera.orthographic) {
  1177. throw new Error(`${context}: Camera orthographic properties are missing`);
  1178. }
  1179. babylonCamera.mode = Camera.ORTHOGRAPHIC_CAMERA;
  1180. babylonCamera.orthoLeft = -camera.orthographic.xmag;
  1181. babylonCamera.orthoRight = camera.orthographic.xmag;
  1182. babylonCamera.orthoBottom = -camera.orthographic.ymag;
  1183. babylonCamera.orthoTop = camera.orthographic.ymag;
  1184. babylonCamera.minZ = camera.orthographic.znear;
  1185. babylonCamera.maxZ = camera.orthographic.zfar;
  1186. break;
  1187. }
  1188. default: {
  1189. throw new Error(`${context}: Invalid camera type (${camera.type})`);
  1190. }
  1191. }
  1192. GLTFLoader.AddPointerMetadata(babylonCamera, context);
  1193. this._parent.onCameraLoadedObservable.notifyObservers(babylonCamera);
  1194. assign(babylonCamera);
  1195. this.logClose();
  1196. return Promise.all(promises).then(() => {
  1197. return babylonCamera;
  1198. });
  1199. }
  1200. _loadAnimationsAsync() {
  1201. const animations = this._gltf.animations;
  1202. if (!animations) {
  1203. return Promise.resolve();
  1204. }
  1205. const promises = new Array();
  1206. for (let index = 0; index < animations.length; index++) {
  1207. const animation = animations[index];
  1208. promises.push(this.loadAnimationAsync(`/animations/${animation.index}`, animation).then((animationGroup) => {
  1209. // Delete the animation group if it ended up not having any animations in it.
  1210. if (animationGroup.targetedAnimations.length === 0) {
  1211. animationGroup.dispose();
  1212. }
  1213. }));
  1214. }
  1215. return Promise.all(promises).then(() => { });
  1216. }
  1217. /**
  1218. * Loads a glTF animation.
  1219. * @param context The context when loading the asset
  1220. * @param animation The glTF animation property
  1221. * @returns A promise that resolves with the loaded Babylon animation group when the load is complete
  1222. */
  1223. loadAnimationAsync(context, animation) {
  1224. const promise = this._extensionsLoadAnimationAsync(context, animation);
  1225. if (promise) {
  1226. return promise;
  1227. }
  1228. this._babylonScene._blockEntityCollection = !!this._assetContainer;
  1229. const babylonAnimationGroup = new AnimationGroup(animation.name || `animation${animation.index}`, this._babylonScene);
  1230. babylonAnimationGroup._parentContainer = this._assetContainer;
  1231. this._babylonScene._blockEntityCollection = false;
  1232. animation._babylonAnimationGroup = babylonAnimationGroup;
  1233. const promises = new Array();
  1234. ArrayItem.Assign(animation.channels);
  1235. ArrayItem.Assign(animation.samplers);
  1236. for (const channel of animation.channels) {
  1237. promises.push(this._loadAnimationChannelAsync(`${context}/channels/${channel.index}`, context, animation, channel, (babylonTarget, babylonAnimation) => {
  1238. babylonTarget.animations = babylonTarget.animations || [];
  1239. babylonTarget.animations.push(babylonAnimation);
  1240. babylonAnimationGroup.addTargetedAnimation(babylonAnimation, babylonTarget);
  1241. }));
  1242. }
  1243. return Promise.all(promises).then(() => {
  1244. babylonAnimationGroup.normalize(0);
  1245. return babylonAnimationGroup;
  1246. });
  1247. }
  1248. /**
  1249. * @hidden
  1250. * Loads a glTF animation channel.
  1251. * @param context The context when loading the asset
  1252. * @param animationContext The context of the animation when loading the asset
  1253. * @param animation The glTF animation property
  1254. * @param channel The glTF animation channel property
  1255. * @param onLoad Called for each animation loaded
  1256. * @returns A void promise that resolves when the load is complete
  1257. */
  1258. _loadAnimationChannelAsync(context, animationContext, animation, channel, onLoad) {
  1259. const promise = this._extensionsLoadAnimationChannelAsync(context, animationContext, animation, channel, onLoad);
  1260. if (promise) {
  1261. return promise;
  1262. }
  1263. if (channel.target.node == undefined) {
  1264. return Promise.resolve();
  1265. }
  1266. const targetNode = ArrayItem.Get(`${context}/target/node`, this._gltf.nodes, channel.target.node);
  1267. // Ignore animations that have no animation targets.
  1268. if ((channel.target.path === "weights" /* AnimationChannelTargetPath.WEIGHTS */ && !targetNode._numMorphTargets) ||
  1269. (channel.target.path !== "weights" /* AnimationChannelTargetPath.WEIGHTS */ && !targetNode._babylonTransformNode)) {
  1270. return Promise.resolve();
  1271. }
  1272. let properties;
  1273. switch (channel.target.path) {
  1274. case "translation" /* AnimationChannelTargetPath.TRANSLATION */: {
  1275. properties = nodeAnimationData.translation;
  1276. break;
  1277. }
  1278. case "rotation" /* AnimationChannelTargetPath.ROTATION */: {
  1279. properties = nodeAnimationData.rotation;
  1280. break;
  1281. }
  1282. case "scale" /* AnimationChannelTargetPath.SCALE */: {
  1283. properties = nodeAnimationData.scale;
  1284. break;
  1285. }
  1286. case "weights" /* AnimationChannelTargetPath.WEIGHTS */: {
  1287. properties = nodeAnimationData.weights;
  1288. break;
  1289. }
  1290. default: {
  1291. throw new Error(`${context}/target/path: Invalid value (${channel.target.path})`);
  1292. }
  1293. }
  1294. const targetInfo = {
  1295. object: targetNode,
  1296. info: properties,
  1297. };
  1298. return this._loadAnimationChannelFromTargetInfoAsync(context, animationContext, animation, channel, targetInfo, onLoad);
  1299. }
  1300. /**
  1301. * @hidden
  1302. * Loads a glTF animation channel.
  1303. * @param context The context when loading the asset
  1304. * @param animationContext The context of the animation when loading the asset
  1305. * @param animation The glTF animation property
  1306. * @param channel The glTF animation channel property
  1307. * @param targetInfo The glTF target and properties
  1308. * @param onLoad Called for each animation loaded
  1309. * @returns A void promise that resolves when the load is complete
  1310. */
  1311. _loadAnimationChannelFromTargetInfoAsync(context, animationContext, animation, channel, targetInfo, onLoad) {
  1312. const fps = this.parent.targetFps;
  1313. const invfps = 1 / fps;
  1314. const sampler = ArrayItem.Get(`${context}/sampler`, animation.samplers, channel.sampler);
  1315. return this._loadAnimationSamplerAsync(`${animationContext}/samplers/${channel.sampler}`, sampler).then((data) => {
  1316. let numAnimations = 0;
  1317. const target = targetInfo.object;
  1318. const propertyInfos = targetInfo.info;
  1319. // Extract the corresponding values from the read value.
  1320. // GLTF values may be dispatched to several Babylon properties.
  1321. // For example, baseColorFactor [`r`, `g`, `b`, `a`] is dispatched to
  1322. // - albedoColor as Color3(`r`, `g`, `b`)
  1323. // - alpha as `a`
  1324. for (const propertyInfo of propertyInfos) {
  1325. const stride = propertyInfo.getStride(target);
  1326. const input = data.input;
  1327. const output = data.output;
  1328. const keys = new Array(input.length);
  1329. let outputOffset = 0;
  1330. switch (data.interpolation) {
  1331. case "STEP" /* AnimationSamplerInterpolation.STEP */: {
  1332. for (let index = 0; index < input.length; index++) {
  1333. const value = propertyInfo.getValue(target, output, outputOffset, 1);
  1334. outputOffset += stride;
  1335. keys[index] = {
  1336. frame: input[index] * fps,
  1337. value: value,
  1338. interpolation: AnimationKeyInterpolation.STEP,
  1339. };
  1340. }
  1341. break;
  1342. }
  1343. case "CUBICSPLINE" /* AnimationSamplerInterpolation.CUBICSPLINE */: {
  1344. for (let index = 0; index < input.length; index++) {
  1345. const inTangent = propertyInfo.getValue(target, output, outputOffset, invfps);
  1346. outputOffset += stride;
  1347. const value = propertyInfo.getValue(target, output, outputOffset, 1);
  1348. outputOffset += stride;
  1349. const outTangent = propertyInfo.getValue(target, output, outputOffset, invfps);
  1350. outputOffset += stride;
  1351. keys[index] = {
  1352. frame: input[index] * fps,
  1353. inTangent: inTangent,
  1354. value: value,
  1355. outTangent: outTangent,
  1356. };
  1357. }
  1358. break;
  1359. }
  1360. case "LINEAR" /* AnimationSamplerInterpolation.LINEAR */: {
  1361. for (let index = 0; index < input.length; index++) {
  1362. const value = propertyInfo.getValue(target, output, outputOffset, 1);
  1363. outputOffset += stride;
  1364. keys[index] = {
  1365. frame: input[index] * fps,
  1366. value: value,
  1367. };
  1368. }
  1369. break;
  1370. }
  1371. }
  1372. if (outputOffset > 0) {
  1373. const name = `${animation.name || `animation${animation.index}`}_channel${channel.index}_${numAnimations}`;
  1374. propertyInfo.buildAnimations(target, name, fps, keys, (babylonAnimatable, babylonAnimation) => {
  1375. ++numAnimations;
  1376. onLoad(babylonAnimatable, babylonAnimation);
  1377. });
  1378. }
  1379. }
  1380. });
  1381. }
  1382. _loadAnimationSamplerAsync(context, sampler) {
  1383. if (sampler._data) {
  1384. return sampler._data;
  1385. }
  1386. const interpolation = sampler.interpolation || "LINEAR" /* AnimationSamplerInterpolation.LINEAR */;
  1387. switch (interpolation) {
  1388. case "STEP" /* AnimationSamplerInterpolation.STEP */:
  1389. case "LINEAR" /* AnimationSamplerInterpolation.LINEAR */:
  1390. case "CUBICSPLINE" /* AnimationSamplerInterpolation.CUBICSPLINE */: {
  1391. break;
  1392. }
  1393. default: {
  1394. throw new Error(`${context}/interpolation: Invalid value (${sampler.interpolation})`);
  1395. }
  1396. }
  1397. const inputAccessor = ArrayItem.Get(`${context}/input`, this._gltf.accessors, sampler.input);
  1398. const outputAccessor = ArrayItem.Get(`${context}/output`, this._gltf.accessors, sampler.output);
  1399. sampler._data = Promise.all([
  1400. this._loadFloatAccessorAsync(`/accessors/${inputAccessor.index}`, inputAccessor),
  1401. this._loadFloatAccessorAsync(`/accessors/${outputAccessor.index}`, outputAccessor),
  1402. ]).then(([inputData, outputData]) => {
  1403. return {
  1404. input: inputData,
  1405. interpolation: interpolation,
  1406. output: outputData,
  1407. };
  1408. });
  1409. return sampler._data;
  1410. }
  1411. /**
  1412. * Loads a glTF buffer.
  1413. * @param context The context when loading the asset
  1414. * @param buffer The glTF buffer property
  1415. * @param byteOffset The byte offset to use
  1416. * @param byteLength The byte length to use
  1417. * @returns A promise that resolves with the loaded data when the load is complete
  1418. */
  1419. loadBufferAsync(context, buffer, byteOffset, byteLength) {
  1420. const extensionPromise = this._extensionsLoadBufferAsync(context, buffer, byteOffset, byteLength);
  1421. if (extensionPromise) {
  1422. return extensionPromise;
  1423. }
  1424. if (!buffer._data) {
  1425. if (buffer.uri) {
  1426. buffer._data = this.loadUriAsync(`${context}/uri`, buffer, buffer.uri);
  1427. }
  1428. else {
  1429. if (!this._bin) {
  1430. throw new Error(`${context}: Uri is missing or the binary glTF is missing its binary chunk`);
  1431. }
  1432. buffer._data = this._bin.readAsync(0, buffer.byteLength);
  1433. }
  1434. }
  1435. return buffer._data.then((data) => {
  1436. try {
  1437. return new Uint8Array(data.buffer, data.byteOffset + byteOffset, byteLength);
  1438. }
  1439. catch (e) {
  1440. throw new Error(`${context}: ${e.message}`);
  1441. }
  1442. });
  1443. }
  1444. /**
  1445. * Loads a glTF buffer view.
  1446. * @param context The context when loading the asset
  1447. * @param bufferView The glTF buffer view property
  1448. * @returns A promise that resolves with the loaded data when the load is complete
  1449. */
  1450. loadBufferViewAsync(context, bufferView) {
  1451. const extensionPromise = this._extensionsLoadBufferViewAsync(context, bufferView);
  1452. if (extensionPromise) {
  1453. return extensionPromise;
  1454. }
  1455. if (bufferView._data) {
  1456. return bufferView._data;
  1457. }
  1458. const buffer = ArrayItem.Get(`${context}/buffer`, this._gltf.buffers, bufferView.buffer);
  1459. bufferView._data = this.loadBufferAsync(`/buffers/${buffer.index}`, buffer, bufferView.byteOffset || 0, bufferView.byteLength);
  1460. return bufferView._data;
  1461. }
  1462. _loadAccessorAsync(context, accessor, constructor) {
  1463. if (accessor._data) {
  1464. return accessor._data;
  1465. }
  1466. const numComponents = GLTFLoader._GetNumComponents(context, accessor.type);
  1467. const byteStride = numComponents * VertexBuffer.GetTypeByteLength(accessor.componentType);
  1468. const length = numComponents * accessor.count;
  1469. if (accessor.bufferView == undefined) {
  1470. accessor._data = Promise.resolve(new constructor(length));
  1471. }
  1472. else {
  1473. const bufferView = ArrayItem.Get(`${context}/bufferView`, this._gltf.bufferViews, accessor.bufferView);
  1474. accessor._data = this.loadBufferViewAsync(`/bufferViews/${bufferView.index}`, bufferView).then((data) => {
  1475. if (accessor.componentType === 5126 /* AccessorComponentType.FLOAT */ && !accessor.normalized && (!bufferView.byteStride || bufferView.byteStride === byteStride)) {
  1476. return GLTFLoader._GetTypedArray(context, accessor.componentType, data, accessor.byteOffset, length);
  1477. }
  1478. else {
  1479. const typedArray = new constructor(length);
  1480. VertexBuffer.ForEach(data, accessor.byteOffset || 0, bufferView.byteStride || byteStride, numComponents, accessor.componentType, typedArray.length, accessor.normalized || false, (value, index) => {
  1481. typedArray[index] = value;
  1482. });
  1483. return typedArray;
  1484. }
  1485. });
  1486. }
  1487. if (accessor.sparse) {
  1488. const sparse = accessor.sparse;
  1489. accessor._data = accessor._data.then((data) => {
  1490. const typedArray = data;
  1491. const indicesBufferView = ArrayItem.Get(`${context}/sparse/indices/bufferView`, this._gltf.bufferViews, sparse.indices.bufferView);
  1492. const valuesBufferView = ArrayItem.Get(`${context}/sparse/values/bufferView`, this._gltf.bufferViews, sparse.values.bufferView);
  1493. return Promise.all([
  1494. this.loadBufferViewAsync(`/bufferViews/${indicesBufferView.index}`, indicesBufferView),
  1495. this.loadBufferViewAsync(`/bufferViews/${valuesBufferView.index}`, valuesBufferView),
  1496. ]).then(([indicesData, valuesData]) => {
  1497. const indices = GLTFLoader._GetTypedArray(`${context}/sparse/indices`, sparse.indices.componentType, indicesData, sparse.indices.byteOffset, sparse.count);
  1498. const sparseLength = numComponents * sparse.count;
  1499. let values;
  1500. if (accessor.componentType === 5126 /* AccessorComponentType.FLOAT */ && !accessor.normalized) {
  1501. values = GLTFLoader._GetTypedArray(`${context}/sparse/values`, accessor.componentType, valuesData, sparse.values.byteOffset, sparseLength);
  1502. }
  1503. else {
  1504. const sparseData = GLTFLoader._GetTypedArray(`${context}/sparse/values`, accessor.componentType, valuesData, sparse.values.byteOffset, sparseLength);
  1505. values = new constructor(sparseLength);
  1506. VertexBuffer.ForEach(sparseData, 0, byteStride, numComponents, accessor.componentType, values.length, accessor.normalized || false, (value, index) => {
  1507. values[index] = value;
  1508. });
  1509. }
  1510. let valuesIndex = 0;
  1511. for (let indicesIndex = 0; indicesIndex < indices.length; indicesIndex++) {
  1512. let dataIndex = indices[indicesIndex] * numComponents;
  1513. for (let componentIndex = 0; componentIndex < numComponents; componentIndex++) {
  1514. typedArray[dataIndex++] = values[valuesIndex++];
  1515. }
  1516. }
  1517. return typedArray;
  1518. });
  1519. });
  1520. }
  1521. return accessor._data;
  1522. }
  1523. /**
  1524. * @internal
  1525. */
  1526. _loadFloatAccessorAsync(context, accessor) {
  1527. return this._loadAccessorAsync(context, accessor, Float32Array);
  1528. }
  1529. /**
  1530. * @internal
  1531. */
  1532. _loadIndicesAccessorAsync(context, accessor) {
  1533. if (accessor.type !== "SCALAR" /* AccessorType.SCALAR */) {
  1534. throw new Error(`${context}/type: Invalid value ${accessor.type}`);
  1535. }
  1536. if (accessor.componentType !== 5121 /* AccessorComponentType.UNSIGNED_BYTE */ &&
  1537. accessor.componentType !== 5123 /* AccessorComponentType.UNSIGNED_SHORT */ &&
  1538. accessor.componentType !== 5125 /* AccessorComponentType.UNSIGNED_INT */) {
  1539. throw new Error(`${context}/componentType: Invalid value ${accessor.componentType}`);
  1540. }
  1541. if (accessor._data) {
  1542. return accessor._data;
  1543. }
  1544. if (accessor.sparse) {
  1545. const constructor = GLTFLoader._GetTypedArrayConstructor(`${context}/componentType`, accessor.componentType);
  1546. accessor._data = this._loadAccessorAsync(context, accessor, constructor);
  1547. }
  1548. else {
  1549. const bufferView = ArrayItem.Get(`${context}/bufferView`, this._gltf.bufferViews, accessor.bufferView);
  1550. accessor._data = this.loadBufferViewAsync(`/bufferViews/${bufferView.index}`, bufferView).then((data) => {
  1551. return GLTFLoader._GetTypedArray(context, accessor.componentType, data, accessor.byteOffset, accessor.count);
  1552. });
  1553. }
  1554. return accessor._data;
  1555. }
  1556. /**
  1557. * @internal
  1558. */
  1559. _loadVertexBufferViewAsync(bufferView) {
  1560. if (bufferView._babylonBuffer) {
  1561. return bufferView._babylonBuffer;
  1562. }
  1563. const engine = this._babylonScene.getEngine();
  1564. bufferView._babylonBuffer = this.loadBufferViewAsync(`/bufferViews/${bufferView.index}`, bufferView).then((data) => {
  1565. return new Buffer(engine, data, false);
  1566. });
  1567. return bufferView._babylonBuffer;
  1568. }
  1569. /**
  1570. * @internal
  1571. */
  1572. _loadVertexAccessorAsync(context, accessor, kind) {
  1573. if (accessor._babylonVertexBuffer?.[kind]) {
  1574. return accessor._babylonVertexBuffer[kind];
  1575. }
  1576. if (!accessor._babylonVertexBuffer) {
  1577. accessor._babylonVertexBuffer = {};
  1578. }
  1579. const engine = this._babylonScene.getEngine();
  1580. if (accessor.sparse || accessor.bufferView == undefined) {
  1581. accessor._babylonVertexBuffer[kind] = this._loadFloatAccessorAsync(context, accessor).then((data) => {
  1582. return new VertexBuffer(engine, data, kind, false);
  1583. });
  1584. }
  1585. else {
  1586. const bufferView = ArrayItem.Get(`${context}/bufferView`, this._gltf.bufferViews, accessor.bufferView);
  1587. accessor._babylonVertexBuffer[kind] = this._loadVertexBufferViewAsync(bufferView).then((babylonBuffer) => {
  1588. const numComponents = GLTFLoader._GetNumComponents(context, accessor.type);
  1589. return new VertexBuffer(engine, babylonBuffer, kind, false, undefined, bufferView.byteStride, undefined, accessor.byteOffset, numComponents, accessor.componentType, accessor.normalized, true, undefined, true);
  1590. });
  1591. }
  1592. return accessor._babylonVertexBuffer[kind];
  1593. }
  1594. _loadMaterialMetallicRoughnessPropertiesAsync(context, properties, babylonMaterial) {
  1595. if (!(babylonMaterial instanceof PBRMaterial)) {
  1596. throw new Error(`${context}: Material type not supported`);
  1597. }
  1598. const promises = new Array();
  1599. if (properties) {
  1600. if (properties.baseColorFactor) {
  1601. babylonMaterial.albedoColor = Color3.FromArray(properties.baseColorFactor);
  1602. babylonMaterial.alpha = properties.baseColorFactor[3];
  1603. }
  1604. else {
  1605. babylonMaterial.albedoColor = Color3.White();
  1606. }
  1607. babylonMaterial.metallic = properties.metallicFactor == undefined ? 1 : properties.metallicFactor;
  1608. babylonMaterial.roughness = properties.roughnessFactor == undefined ? 1 : properties.roughnessFactor;
  1609. if (properties.baseColorTexture) {
  1610. promises.push(this.loadTextureInfoAsync(`${context}/baseColorTexture`, properties.baseColorTexture, (texture) => {
  1611. texture.name = `${babylonMaterial.name} (Base Color)`;
  1612. babylonMaterial.albedoTexture = texture;
  1613. }));
  1614. }
  1615. if (properties.metallicRoughnessTexture) {
  1616. properties.metallicRoughnessTexture.nonColorData = true;
  1617. promises.push(this.loadTextureInfoAsync(`${context}/metallicRoughnessTexture`, properties.metallicRoughnessTexture, (texture) => {
  1618. texture.name = `${babylonMaterial.name} (Metallic Roughness)`;
  1619. babylonMaterial.metallicTexture = texture;
  1620. }));
  1621. babylonMaterial.useMetallnessFromMetallicTextureBlue = true;
  1622. babylonMaterial.useRoughnessFromMetallicTextureGreen = true;
  1623. babylonMaterial.useRoughnessFromMetallicTextureAlpha = false;
  1624. }
  1625. }
  1626. return Promise.all(promises).then(() => { });
  1627. }
  1628. /**
  1629. * @internal
  1630. */
  1631. _loadMaterialAsync(context, material, babylonMesh, babylonDrawMode, assign = () => { }) {
  1632. const extensionPromise = this._extensionsLoadMaterialAsync(context, material, babylonMesh, babylonDrawMode, assign);
  1633. if (extensionPromise) {
  1634. return extensionPromise;
  1635. }
  1636. material._data = material._data || {};
  1637. let babylonData = material._data[babylonDrawMode];
  1638. if (!babylonData) {
  1639. this.logOpen(`${context} ${material.name || ""}`);
  1640. const babylonMaterial = this.createMaterial(context, material, babylonDrawMode);
  1641. babylonData = {
  1642. babylonMaterial: babylonMaterial,
  1643. babylonMeshes: [],
  1644. promise: this.loadMaterialPropertiesAsync(context, material, babylonMaterial),
  1645. };
  1646. material._data[babylonDrawMode] = babylonData;
  1647. GLTFLoader.AddPointerMetadata(babylonMaterial, context);
  1648. this._parent.onMaterialLoadedObservable.notifyObservers(babylonMaterial);
  1649. this.logClose();
  1650. }
  1651. if (babylonMesh) {
  1652. babylonData.babylonMeshes.push(babylonMesh);
  1653. babylonMesh.onDisposeObservable.addOnce(() => {
  1654. const index = babylonData.babylonMeshes.indexOf(babylonMesh);
  1655. if (index !== -1) {
  1656. babylonData.babylonMeshes.splice(index, 1);
  1657. }
  1658. });
  1659. }
  1660. assign(babylonData.babylonMaterial);
  1661. return babylonData.promise.then(() => {
  1662. return babylonData.babylonMaterial;
  1663. });
  1664. }
  1665. _createDefaultMaterial(name, babylonDrawMode) {
  1666. this._babylonScene._blockEntityCollection = !!this._assetContainer;
  1667. const babylonMaterial = new PBRMaterial(name, this._babylonScene);
  1668. babylonMaterial._parentContainer = this._assetContainer;
  1669. this._babylonScene._blockEntityCollection = false;
  1670. // Moved to mesh so user can change materials on gltf meshes: babylonMaterial.sideOrientation = this._babylonScene.useRightHandedSystem ? Material.CounterClockWiseSideOrientation : Material.ClockWiseSideOrientation;
  1671. babylonMaterial.fillMode = babylonDrawMode;
  1672. babylonMaterial.enableSpecularAntiAliasing = true;
  1673. babylonMaterial.useRadianceOverAlpha = !this._parent.transparencyAsCoverage;
  1674. babylonMaterial.useSpecularOverAlpha = !this._parent.transparencyAsCoverage;
  1675. babylonMaterial.transparencyMode = PBRMaterial.PBRMATERIAL_OPAQUE;
  1676. babylonMaterial.metallic = 1;
  1677. babylonMaterial.roughness = 1;
  1678. return babylonMaterial;
  1679. }
  1680. /**
  1681. * Creates a Babylon material from a glTF material.
  1682. * @param context The context when loading the asset
  1683. * @param material The glTF material property
  1684. * @param babylonDrawMode The draw mode for the Babylon material
  1685. * @returns The Babylon material
  1686. */
  1687. createMaterial(context, material, babylonDrawMode) {
  1688. const extensionPromise = this._extensionsCreateMaterial(context, material, babylonDrawMode);
  1689. if (extensionPromise) {
  1690. return extensionPromise;
  1691. }
  1692. const name = material.name || `material${material.index}`;
  1693. const babylonMaterial = this._createDefaultMaterial(name, babylonDrawMode);
  1694. return babylonMaterial;
  1695. }
  1696. /**
  1697. * Loads properties from a glTF material into a Babylon material.
  1698. * @param context The context when loading the asset
  1699. * @param material The glTF material property
  1700. * @param babylonMaterial The Babylon material
  1701. * @returns A promise that resolves when the load is complete
  1702. */
  1703. loadMaterialPropertiesAsync(context, material, babylonMaterial) {
  1704. const extensionPromise = this._extensionsLoadMaterialPropertiesAsync(context, material, babylonMaterial);
  1705. if (extensionPromise) {
  1706. return extensionPromise;
  1707. }
  1708. const promises = new Array();
  1709. promises.push(this.loadMaterialBasePropertiesAsync(context, material, babylonMaterial));
  1710. if (material.pbrMetallicRoughness) {
  1711. promises.push(this._loadMaterialMetallicRoughnessPropertiesAsync(`${context}/pbrMetallicRoughness`, material.pbrMetallicRoughness, babylonMaterial));
  1712. }
  1713. this.loadMaterialAlphaProperties(context, material, babylonMaterial);
  1714. return Promise.all(promises).then(() => { });
  1715. }
  1716. /**
  1717. * Loads the normal, occlusion, and emissive properties from a glTF material into a Babylon material.
  1718. * @param context The context when loading the asset
  1719. * @param material The glTF material property
  1720. * @param babylonMaterial The Babylon material
  1721. * @returns A promise that resolves when the load is complete
  1722. */
  1723. loadMaterialBasePropertiesAsync(context, material, babylonMaterial) {
  1724. if (!(babylonMaterial instanceof PBRMaterial)) {
  1725. throw new Error(`${context}: Material type not supported`);
  1726. }
  1727. const promises = new Array();
  1728. babylonMaterial.emissiveColor = material.emissiveFactor ? Color3.FromArray(material.emissiveFactor) : new Color3(0, 0, 0);
  1729. if (material.doubleSided) {
  1730. babylonMaterial.backFaceCulling = false;
  1731. babylonMaterial.twoSidedLighting = true;
  1732. }
  1733. if (material.normalTexture) {
  1734. material.normalTexture.nonColorData = true;
  1735. promises.push(this.loadTextureInfoAsync(`${context}/normalTexture`, material.normalTexture, (texture) => {
  1736. texture.name = `${babylonMaterial.name} (Normal)`;
  1737. babylonMaterial.bumpTexture = texture;
  1738. }));
  1739. babylonMaterial.invertNormalMapX = !this._babylonScene.useRightHandedSystem;
  1740. babylonMaterial.invertNormalMapY = this._babylonScene.useRightHandedSystem;
  1741. if (material.normalTexture.scale != undefined && babylonMaterial.bumpTexture) {
  1742. babylonMaterial.bumpTexture.level = material.normalTexture.scale;
  1743. }
  1744. babylonMaterial.forceIrradianceInFragment = true;
  1745. }
  1746. if (material.occlusionTexture) {
  1747. material.occlusionTexture.nonColorData = true;
  1748. promises.push(this.loadTextureInfoAsync(`${context}/occlusionTexture`, material.occlusionTexture, (texture) => {
  1749. texture.name = `${babylonMaterial.name} (Occlusion)`;
  1750. babylonMaterial.ambientTexture = texture;
  1751. }));
  1752. babylonMaterial.useAmbientInGrayScale = true;
  1753. if (material.occlusionTexture.strength != undefined) {
  1754. babylonMaterial.ambientTextureStrength = material.occlusionTexture.strength;
  1755. }
  1756. }
  1757. if (material.emissiveTexture) {
  1758. promises.push(this.loadTextureInfoAsync(`${context}/emissiveTexture`, material.emissiveTexture, (texture) => {
  1759. texture.name = `${babylonMaterial.name} (Emissive)`;
  1760. babylonMaterial.emissiveTexture = texture;
  1761. }));
  1762. }
  1763. return Promise.all(promises).then(() => { });
  1764. }
  1765. /**
  1766. * Loads the alpha properties from a glTF material into a Babylon material.
  1767. * Must be called after the setting the albedo texture of the Babylon material when the material has an albedo texture.
  1768. * @param context The context when loading the asset
  1769. * @param material The glTF material property
  1770. * @param babylonMaterial The Babylon material
  1771. */
  1772. loadMaterialAlphaProperties(context, material, babylonMaterial) {
  1773. if (!(babylonMaterial instanceof PBRMaterial)) {
  1774. throw new Error(`${context}: Material type not supported`);
  1775. }
  1776. const alphaMode = material.alphaMode || "OPAQUE" /* MaterialAlphaMode.OPAQUE */;
  1777. switch (alphaMode) {
  1778. case "OPAQUE" /* MaterialAlphaMode.OPAQUE */: {
  1779. babylonMaterial.transparencyMode = PBRMaterial.PBRMATERIAL_OPAQUE;
  1780. babylonMaterial.alpha = 1.0; // Force alpha to 1.0 for opaque mode.
  1781. break;
  1782. }
  1783. case "MASK" /* MaterialAlphaMode.MASK */: {
  1784. babylonMaterial.transparencyMode = PBRMaterial.PBRMATERIAL_ALPHATEST;
  1785. babylonMaterial.alphaCutOff = material.alphaCutoff == undefined ? 0.5 : material.alphaCutoff;
  1786. if (babylonMaterial.albedoTexture) {
  1787. babylonMaterial.albedoTexture.hasAlpha = true;
  1788. }
  1789. break;
  1790. }
  1791. case "BLEND" /* MaterialAlphaMode.BLEND */: {
  1792. babylonMaterial.transparencyMode = PBRMaterial.PBRMATERIAL_ALPHABLEND;
  1793. if (babylonMaterial.albedoTexture) {
  1794. babylonMaterial.albedoTexture.hasAlpha = true;
  1795. babylonMaterial.useAlphaFromAlbedoTexture = true;
  1796. }
  1797. break;
  1798. }
  1799. default: {
  1800. throw new Error(`${context}/alphaMode: Invalid value (${material.alphaMode})`);
  1801. }
  1802. }
  1803. }
  1804. /**
  1805. * Loads a glTF texture info.
  1806. * @param context The context when loading the asset
  1807. * @param textureInfo The glTF texture info property
  1808. * @param assign A function called synchronously after parsing the glTF properties
  1809. * @returns A promise that resolves with the loaded Babylon texture when the load is complete
  1810. */
  1811. loadTextureInfoAsync(context, textureInfo, assign = () => { }) {
  1812. const extensionPromise = this._extensionsLoadTextureInfoAsync(context, textureInfo, assign);
  1813. if (extensionPromise) {
  1814. return extensionPromise;
  1815. }
  1816. this.logOpen(`${context}`);
  1817. if (textureInfo.texCoord >= 6) {
  1818. throw new Error(`${context}/texCoord: Invalid value (${textureInfo.texCoord})`);
  1819. }
  1820. const texture = ArrayItem.Get(`${context}/index`, this._gltf.textures, textureInfo.index);
  1821. texture._textureInfo = textureInfo;
  1822. const promise = this._loadTextureAsync(`/textures/${textureInfo.index}`, texture, (babylonTexture) => {
  1823. babylonTexture.coordinatesIndex = textureInfo.texCoord || 0;
  1824. GLTFLoader.AddPointerMetadata(babylonTexture, context);
  1825. this._parent.onTextureLoadedObservable.notifyObservers(babylonTexture);
  1826. assign(babylonTexture);
  1827. });
  1828. this.logClose();
  1829. return promise;
  1830. }
  1831. /**
  1832. * @internal
  1833. */
  1834. _loadTextureAsync(context, texture, assign = () => { }) {
  1835. const extensionPromise = this._extensionsLoadTextureAsync(context, texture, assign);
  1836. if (extensionPromise) {
  1837. return extensionPromise;
  1838. }
  1839. this.logOpen(`${context} ${texture.name || ""}`);
  1840. const sampler = texture.sampler == undefined ? GLTFLoader.DefaultSampler : ArrayItem.Get(`${context}/sampler`, this._gltf.samplers, texture.sampler);
  1841. const image = ArrayItem.Get(`${context}/source`, this._gltf.images, texture.source);
  1842. const promise = this._createTextureAsync(context, sampler, image, assign, undefined, !texture._textureInfo.nonColorData);
  1843. this.logClose();
  1844. return promise;
  1845. }
  1846. /**
  1847. * @internal
  1848. */
  1849. _createTextureAsync(context, sampler, image, assign = () => { }, textureLoaderOptions, useSRGBBuffer) {
  1850. const samplerData = this._loadSampler(`/samplers/${sampler.index}`, sampler);
  1851. const promises = new Array();
  1852. const deferred = new Deferred();
  1853. this._babylonScene._blockEntityCollection = !!this._assetContainer;
  1854. const textureCreationOptions = {
  1855. noMipmap: samplerData.noMipMaps,
  1856. invertY: false,
  1857. samplingMode: samplerData.samplingMode,
  1858. onLoad: () => {
  1859. if (!this._disposed) {
  1860. deferred.resolve();
  1861. }
  1862. },
  1863. onError: (message, exception) => {
  1864. if (!this._disposed) {
  1865. deferred.reject(new Error(`${context}: ${exception && exception.message ? exception.message : message || "Failed to load texture"}`));
  1866. }
  1867. },
  1868. mimeType: image.mimeType,
  1869. loaderOptions: textureLoaderOptions,
  1870. useSRGBBuffer: !!useSRGBBuffer && this._parent.useSRGBBuffers,
  1871. };
  1872. const babylonTexture = new Texture(null, this._babylonScene, textureCreationOptions);
  1873. babylonTexture._parentContainer = this._assetContainer;
  1874. this._babylonScene._blockEntityCollection = false;
  1875. promises.push(deferred.promise);
  1876. promises.push(this.loadImageAsync(`/images/${image.index}`, image).then((data) => {
  1877. const name = image.uri || `${this._fileName}#image${image.index}`;
  1878. const dataUrl = `data:${this._uniqueRootUrl}${name}`;
  1879. babylonTexture.updateURL(dataUrl, data);
  1880. }));
  1881. babylonTexture.wrapU = samplerData.wrapU;
  1882. babylonTexture.wrapV = samplerData.wrapV;
  1883. assign(babylonTexture);
  1884. return Promise.all(promises).then(() => {
  1885. return babylonTexture;
  1886. });
  1887. }
  1888. _loadSampler(context, sampler) {
  1889. if (!sampler._data) {
  1890. sampler._data = {
  1891. noMipMaps: sampler.minFilter === 9728 /* TextureMinFilter.NEAREST */ || sampler.minFilter === 9729 /* TextureMinFilter.LINEAR */,
  1892. samplingMode: GLTFLoader._GetTextureSamplingMode(context, sampler),
  1893. wrapU: GLTFLoader._GetTextureWrapMode(`${context}/wrapS`, sampler.wrapS),
  1894. wrapV: GLTFLoader._GetTextureWrapMode(`${context}/wrapT`, sampler.wrapT),
  1895. };
  1896. }
  1897. return sampler._data;
  1898. }
  1899. /**
  1900. * Loads a glTF image.
  1901. * @param context The context when loading the asset
  1902. * @param image The glTF image property
  1903. * @returns A promise that resolves with the loaded data when the load is complete
  1904. */
  1905. loadImageAsync(context, image) {
  1906. if (!image._data) {
  1907. this.logOpen(`${context} ${image.name || ""}`);
  1908. if (image.uri) {
  1909. image._data = this.loadUriAsync(`${context}/uri`, image, image.uri);
  1910. }
  1911. else {
  1912. const bufferView = ArrayItem.Get(`${context}/bufferView`, this._gltf.bufferViews, image.bufferView);
  1913. image._data = this.loadBufferViewAsync(`/bufferViews/${bufferView.index}`, bufferView);
  1914. }
  1915. this.logClose();
  1916. }
  1917. return image._data;
  1918. }
  1919. /**
  1920. * Loads a glTF uri.
  1921. * @param context The context when loading the asset
  1922. * @param property The glTF property associated with the uri
  1923. * @param uri The base64 or relative uri
  1924. * @returns A promise that resolves with the loaded data when the load is complete
  1925. */
  1926. loadUriAsync(context, property, uri) {
  1927. const extensionPromise = this._extensionsLoadUriAsync(context, property, uri);
  1928. if (extensionPromise) {
  1929. return extensionPromise;
  1930. }
  1931. if (!GLTFLoader._ValidateUri(uri)) {
  1932. throw new Error(`${context}: '${uri}' is invalid`);
  1933. }
  1934. if (IsBase64DataUrl(uri)) {
  1935. const data = new Uint8Array(DecodeBase64UrlToBinary(uri));
  1936. this.log(`${context}: Decoded ${uri.substr(0, 64)}... (${data.length} bytes)`);
  1937. return Promise.resolve(data);
  1938. }
  1939. this.log(`${context}: Loading ${uri}`);
  1940. return this._parent.preprocessUrlAsync(this._rootUrl + uri).then((url) => {
  1941. return new Promise((resolve, reject) => {
  1942. this._parent._loadFile(this._babylonScene, url, (data) => {
  1943. if (!this._disposed) {
  1944. this.log(`${context}: Loaded ${uri} (${data.byteLength} bytes)`);
  1945. resolve(new Uint8Array(data));
  1946. }
  1947. }, true, (request) => {
  1948. reject(new LoadFileError(`${context}: Failed to load '${uri}'${request ? ": " + request.status + " " + request.statusText : ""}`, request));
  1949. });
  1950. });
  1951. });
  1952. }
  1953. /**
  1954. * Adds a JSON pointer to the _internalMetadata of the Babylon object at `<object>._internalMetadata.gltf.pointers`.
  1955. * @param babylonObject the Babylon object with _internalMetadata
  1956. * @param pointer the JSON pointer
  1957. */
  1958. static AddPointerMetadata(babylonObject, pointer) {
  1959. babylonObject.metadata = babylonObject.metadata || {};
  1960. const metadata = (babylonObject._internalMetadata = babylonObject._internalMetadata || {});
  1961. const gltf = (metadata.gltf = metadata.gltf || {});
  1962. const pointers = (gltf.pointers = gltf.pointers || []);
  1963. pointers.push(pointer);
  1964. }
  1965. static _GetTextureWrapMode(context, mode) {
  1966. // Set defaults if undefined
  1967. mode = mode == undefined ? 10497 /* TextureWrapMode.REPEAT */ : mode;
  1968. switch (mode) {
  1969. case 33071 /* TextureWrapMode.CLAMP_TO_EDGE */:
  1970. return Texture.CLAMP_ADDRESSMODE;
  1971. case 33648 /* TextureWrapMode.MIRRORED_REPEAT */:
  1972. return Texture.MIRROR_ADDRESSMODE;
  1973. case 10497 /* TextureWrapMode.REPEAT */:
  1974. return Texture.WRAP_ADDRESSMODE;
  1975. default:
  1976. Logger.Warn(`${context}: Invalid value (${mode})`);
  1977. return Texture.WRAP_ADDRESSMODE;
  1978. }
  1979. }
  1980. static _GetTextureSamplingMode(context, sampler) {
  1981. // Set defaults if undefined
  1982. const magFilter = sampler.magFilter == undefined ? 9729 /* TextureMagFilter.LINEAR */ : sampler.magFilter;
  1983. const minFilter = sampler.minFilter == undefined ? 9987 /* TextureMinFilter.LINEAR_MIPMAP_LINEAR */ : sampler.minFilter;
  1984. if (magFilter === 9729 /* TextureMagFilter.LINEAR */) {
  1985. switch (minFilter) {
  1986. case 9728 /* TextureMinFilter.NEAREST */:
  1987. return Texture.LINEAR_NEAREST;
  1988. case 9729 /* TextureMinFilter.LINEAR */:
  1989. return Texture.LINEAR_LINEAR;
  1990. case 9984 /* TextureMinFilter.NEAREST_MIPMAP_NEAREST */:
  1991. return Texture.LINEAR_NEAREST_MIPNEAREST;
  1992. case 9985 /* TextureMinFilter.LINEAR_MIPMAP_NEAREST */:
  1993. return Texture.LINEAR_LINEAR_MIPNEAREST;
  1994. case 9986 /* TextureMinFilter.NEAREST_MIPMAP_LINEAR */:
  1995. return Texture.LINEAR_NEAREST_MIPLINEAR;
  1996. case 9987 /* TextureMinFilter.LINEAR_MIPMAP_LINEAR */:
  1997. return Texture.LINEAR_LINEAR_MIPLINEAR;
  1998. default:
  1999. Logger.Warn(`${context}/minFilter: Invalid value (${minFilter})`);
  2000. return Texture.LINEAR_LINEAR_MIPLINEAR;
  2001. }
  2002. }
  2003. else {
  2004. if (magFilter !== 9728 /* TextureMagFilter.NEAREST */) {
  2005. Logger.Warn(`${context}/magFilter: Invalid value (${magFilter})`);
  2006. }
  2007. switch (minFilter) {
  2008. case 9728 /* TextureMinFilter.NEAREST */:
  2009. return Texture.NEAREST_NEAREST;
  2010. case 9729 /* TextureMinFilter.LINEAR */:
  2011. return Texture.NEAREST_LINEAR;
  2012. case 9984 /* TextureMinFilter.NEAREST_MIPMAP_NEAREST */:
  2013. return Texture.NEAREST_NEAREST_MIPNEAREST;
  2014. case 9985 /* TextureMinFilter.LINEAR_MIPMAP_NEAREST */:
  2015. return Texture.NEAREST_LINEAR_MIPNEAREST;
  2016. case 9986 /* TextureMinFilter.NEAREST_MIPMAP_LINEAR */:
  2017. return Texture.NEAREST_NEAREST_MIPLINEAR;
  2018. case 9987 /* TextureMinFilter.LINEAR_MIPMAP_LINEAR */:
  2019. return Texture.NEAREST_LINEAR_MIPLINEAR;
  2020. default:
  2021. Logger.Warn(`${context}/minFilter: Invalid value (${minFilter})`);
  2022. return Texture.NEAREST_NEAREST_MIPNEAREST;
  2023. }
  2024. }
  2025. }
  2026. static _GetTypedArrayConstructor(context, componentType) {
  2027. switch (componentType) {
  2028. case 5120 /* AccessorComponentType.BYTE */:
  2029. return Int8Array;
  2030. case 5121 /* AccessorComponentType.UNSIGNED_BYTE */:
  2031. return Uint8Array;
  2032. case 5122 /* AccessorComponentType.SHORT */:
  2033. return Int16Array;
  2034. case 5123 /* AccessorComponentType.UNSIGNED_SHORT */:
  2035. return Uint16Array;
  2036. case 5125 /* AccessorComponentType.UNSIGNED_INT */:
  2037. return Uint32Array;
  2038. case 5126 /* AccessorComponentType.FLOAT */:
  2039. return Float32Array;
  2040. default:
  2041. throw new Error(`${context}: Invalid component type ${componentType}`);
  2042. }
  2043. }
  2044. static _GetTypedArray(context, componentType, bufferView, byteOffset, length) {
  2045. const buffer = bufferView.buffer;
  2046. byteOffset = bufferView.byteOffset + (byteOffset || 0);
  2047. const constructor = GLTFLoader._GetTypedArrayConstructor(`${context}/componentType`, componentType);
  2048. const componentTypeLength = VertexBuffer.GetTypeByteLength(componentType);
  2049. if (byteOffset % componentTypeLength !== 0) {
  2050. // HACK: Copy the buffer if byte offset is not a multiple of component type byte length.
  2051. Logger.Warn(`${context}: Copying buffer as byte offset (${byteOffset}) is not a multiple of component type byte length (${componentTypeLength})`);
  2052. return new constructor(buffer.slice(byteOffset, byteOffset + length * componentTypeLength), 0);
  2053. }
  2054. return new constructor(buffer, byteOffset, length);
  2055. }
  2056. static _GetNumComponents(context, type) {
  2057. switch (type) {
  2058. case "SCALAR":
  2059. return 1;
  2060. case "VEC2":
  2061. return 2;
  2062. case "VEC3":
  2063. return 3;
  2064. case "VEC4":
  2065. return 4;
  2066. case "MAT2":
  2067. return 4;
  2068. case "MAT3":
  2069. return 9;
  2070. case "MAT4":
  2071. return 16;
  2072. }
  2073. throw new Error(`${context}: Invalid type (${type})`);
  2074. }
  2075. static _ValidateUri(uri) {
  2076. return Tools.IsBase64(uri) || uri.indexOf("..") === -1;
  2077. }
  2078. /**
  2079. * @internal
  2080. */
  2081. static _GetDrawMode(context, mode) {
  2082. if (mode == undefined) {
  2083. mode = 4 /* MeshPrimitiveMode.TRIANGLES */;
  2084. }
  2085. switch (mode) {
  2086. case 0 /* MeshPrimitiveMode.POINTS */:
  2087. return Material.PointListDrawMode;
  2088. case 1 /* MeshPrimitiveMode.LINES */:
  2089. return Material.LineListDrawMode;
  2090. case 2 /* MeshPrimitiveMode.LINE_LOOP */:
  2091. return Material.LineLoopDrawMode;
  2092. case 3 /* MeshPrimitiveMode.LINE_STRIP */:
  2093. return Material.LineStripDrawMode;
  2094. case 4 /* MeshPrimitiveMode.TRIANGLES */:
  2095. return Material.TriangleFillMode;
  2096. case 5 /* MeshPrimitiveMode.TRIANGLE_STRIP */:
  2097. return Material.TriangleStripDrawMode;
  2098. case 6 /* MeshPrimitiveMode.TRIANGLE_FAN */:
  2099. return Material.TriangleFanDrawMode;
  2100. }
  2101. throw new Error(`${context}: Invalid mesh primitive mode (${mode})`);
  2102. }
  2103. _compileMaterialsAsync() {
  2104. this._parent._startPerformanceCounter("Compile materials");
  2105. const promises = new Array();
  2106. if (this._gltf.materials) {
  2107. for (const material of this._gltf.materials) {
  2108. if (material._data) {
  2109. for (const babylonDrawMode in material._data) {
  2110. const babylonData = material._data[babylonDrawMode];
  2111. for (const babylonMesh of babylonData.babylonMeshes) {
  2112. // Ensure nonUniformScaling is set if necessary.
  2113. babylonMesh.computeWorldMatrix(true);
  2114. const babylonMaterial = babylonData.babylonMaterial;
  2115. promises.push(babylonMaterial.forceCompilationAsync(babylonMesh));
  2116. promises.push(babylonMaterial.forceCompilationAsync(babylonMesh, { useInstances: true }));
  2117. if (this._parent.useClipPlane) {
  2118. promises.push(babylonMaterial.forceCompilationAsync(babylonMesh, { clipPlane: true }));
  2119. promises.push(babylonMaterial.forceCompilationAsync(babylonMesh, { clipPlane: true, useInstances: true }));
  2120. }
  2121. }
  2122. }
  2123. }
  2124. }
  2125. }
  2126. return Promise.all(promises).then(() => {
  2127. this._parent._endPerformanceCounter("Compile materials");
  2128. });
  2129. }
  2130. _compileShadowGeneratorsAsync() {
  2131. this._parent._startPerformanceCounter("Compile shadow generators");
  2132. const promises = new Array();
  2133. const lights = this._babylonScene.lights;
  2134. for (const light of lights) {
  2135. const generator = light.getShadowGenerator();
  2136. if (generator) {
  2137. promises.push(generator.forceCompilationAsync());
  2138. }
  2139. }
  2140. return Promise.all(promises).then(() => {
  2141. this._parent._endPerformanceCounter("Compile shadow generators");
  2142. });
  2143. }
  2144. _forEachExtensions(action) {
  2145. for (const extension of this._extensions) {
  2146. if (extension.enabled) {
  2147. action(extension);
  2148. }
  2149. }
  2150. }
  2151. _applyExtensions(property, functionName, actionAsync) {
  2152. for (const extension of this._extensions) {
  2153. if (extension.enabled) {
  2154. const id = `${extension.name}.${functionName}`;
  2155. const loaderProperty = property;
  2156. loaderProperty._activeLoaderExtensionFunctions = loaderProperty._activeLoaderExtensionFunctions || {};
  2157. const activeLoaderExtensionFunctions = loaderProperty._activeLoaderExtensionFunctions;
  2158. if (!activeLoaderExtensionFunctions[id]) {
  2159. activeLoaderExtensionFunctions[id] = true;
  2160. try {
  2161. const result = actionAsync(extension);
  2162. if (result) {
  2163. return result;
  2164. }
  2165. }
  2166. finally {
  2167. delete activeLoaderExtensionFunctions[id];
  2168. }
  2169. }
  2170. }
  2171. }
  2172. return null;
  2173. }
  2174. _extensionsOnLoading() {
  2175. this._forEachExtensions((extension) => extension.onLoading && extension.onLoading());
  2176. }
  2177. _extensionsOnReady() {
  2178. this._forEachExtensions((extension) => extension.onReady && extension.onReady());
  2179. }
  2180. _extensionsLoadSceneAsync(context, scene) {
  2181. return this._applyExtensions(scene, "loadScene", (extension) => extension.loadSceneAsync && extension.loadSceneAsync(context, scene));
  2182. }
  2183. _extensionsLoadNodeAsync(context, node, assign) {
  2184. return this._applyExtensions(node, "loadNode", (extension) => extension.loadNodeAsync && extension.loadNodeAsync(context, node, assign));
  2185. }
  2186. _extensionsLoadCameraAsync(context, camera, assign) {
  2187. return this._applyExtensions(camera, "loadCamera", (extension) => extension.loadCameraAsync && extension.loadCameraAsync(context, camera, assign));
  2188. }
  2189. _extensionsLoadVertexDataAsync(context, primitive, babylonMesh) {
  2190. return this._applyExtensions(primitive, "loadVertexData", (extension) => extension._loadVertexDataAsync && extension._loadVertexDataAsync(context, primitive, babylonMesh));
  2191. }
  2192. _extensionsLoadMeshPrimitiveAsync(context, name, node, mesh, primitive, assign) {
  2193. return this._applyExtensions(primitive, "loadMeshPrimitive", (extension) => extension._loadMeshPrimitiveAsync && extension._loadMeshPrimitiveAsync(context, name, node, mesh, primitive, assign));
  2194. }
  2195. _extensionsLoadMaterialAsync(context, material, babylonMesh, babylonDrawMode, assign) {
  2196. return this._applyExtensions(material, "loadMaterial", (extension) => extension._loadMaterialAsync && extension._loadMaterialAsync(context, material, babylonMesh, babylonDrawMode, assign));
  2197. }
  2198. _extensionsCreateMaterial(context, material, babylonDrawMode) {
  2199. return this._applyExtensions(material, "createMaterial", (extension) => extension.createMaterial && extension.createMaterial(context, material, babylonDrawMode));
  2200. }
  2201. _extensionsLoadMaterialPropertiesAsync(context, material, babylonMaterial) {
  2202. return this._applyExtensions(material, "loadMaterialProperties", (extension) => extension.loadMaterialPropertiesAsync && extension.loadMaterialPropertiesAsync(context, material, babylonMaterial));
  2203. }
  2204. _extensionsLoadTextureInfoAsync(context, textureInfo, assign) {
  2205. return this._applyExtensions(textureInfo, "loadTextureInfo", (extension) => extension.loadTextureInfoAsync && extension.loadTextureInfoAsync(context, textureInfo, assign));
  2206. }
  2207. _extensionsLoadTextureAsync(context, texture, assign) {
  2208. return this._applyExtensions(texture, "loadTexture", (extension) => extension._loadTextureAsync && extension._loadTextureAsync(context, texture, assign));
  2209. }
  2210. _extensionsLoadAnimationAsync(context, animation) {
  2211. return this._applyExtensions(animation, "loadAnimation", (extension) => extension.loadAnimationAsync && extension.loadAnimationAsync(context, animation));
  2212. }
  2213. _extensionsLoadAnimationChannelAsync(context, animationContext, animation, channel, onLoad) {
  2214. return this._applyExtensions(animation, "loadAnimationChannel", (extension) => extension._loadAnimationChannelAsync && extension._loadAnimationChannelAsync(context, animationContext, animation, channel, onLoad));
  2215. }
  2216. _extensionsLoadSkinAsync(context, node, skin) {
  2217. return this._applyExtensions(skin, "loadSkin", (extension) => extension._loadSkinAsync && extension._loadSkinAsync(context, node, skin));
  2218. }
  2219. _extensionsLoadUriAsync(context, property, uri) {
  2220. return this._applyExtensions(property, "loadUri", (extension) => extension._loadUriAsync && extension._loadUriAsync(context, property, uri));
  2221. }
  2222. _extensionsLoadBufferViewAsync(context, bufferView) {
  2223. return this._applyExtensions(bufferView, "loadBufferView", (extension) => extension.loadBufferViewAsync && extension.loadBufferViewAsync(context, bufferView));
  2224. }
  2225. _extensionsLoadBufferAsync(context, buffer, byteOffset, byteLength) {
  2226. return this._applyExtensions(buffer, "loadBuffer", (extension) => extension.loadBufferAsync && extension.loadBufferAsync(context, buffer, byteOffset, byteLength));
  2227. }
  2228. /**
  2229. * Helper method called by a loader extension to load an glTF extension.
  2230. * @param context The context when loading the asset
  2231. * @param property The glTF property to load the extension from
  2232. * @param extensionName The name of the extension to load
  2233. * @param actionAsync The action to run
  2234. * @returns The promise returned by actionAsync or null if the extension does not exist
  2235. */
  2236. static LoadExtensionAsync(context, property, extensionName, actionAsync) {
  2237. if (!property.extensions) {
  2238. return null;
  2239. }
  2240. const extensions = property.extensions;
  2241. const extension = extensions[extensionName];
  2242. if (!extension) {
  2243. return null;
  2244. }
  2245. return actionAsync(`${context}/extensions/${extensionName}`, extension);
  2246. }
  2247. /**
  2248. * Helper method called by a loader extension to load a glTF extra.
  2249. * @param context The context when loading the asset
  2250. * @param property The glTF property to load the extra from
  2251. * @param extensionName The name of the extension to load
  2252. * @param actionAsync The action to run
  2253. * @returns The promise returned by actionAsync or null if the extra does not exist
  2254. */
  2255. static LoadExtraAsync(context, property, extensionName, actionAsync) {
  2256. if (!property.extras) {
  2257. return null;
  2258. }
  2259. const extras = property.extras;
  2260. const extra = extras[extensionName];
  2261. if (!extra) {
  2262. return null;
  2263. }
  2264. return actionAsync(`${context}/extras/${extensionName}`, extra);
  2265. }
  2266. /**
  2267. * Checks for presence of an extension.
  2268. * @param name The name of the extension to check
  2269. * @returns A boolean indicating the presence of the given extension name in `extensionsUsed`
  2270. */
  2271. isExtensionUsed(name) {
  2272. return !!this._gltf.extensionsUsed && this._gltf.extensionsUsed.indexOf(name) !== -1;
  2273. }
  2274. /**
  2275. * Increments the indentation level and logs a message.
  2276. * @param message The message to log
  2277. */
  2278. logOpen(message) {
  2279. this._parent._logOpen(message);
  2280. }
  2281. /**
  2282. * Decrements the indentation level.
  2283. */
  2284. logClose() {
  2285. this._parent._logClose();
  2286. }
  2287. /**
  2288. * Logs a message
  2289. * @param message The message to log
  2290. */
  2291. log(message) {
  2292. this._parent._log(message);
  2293. }
  2294. /**
  2295. * Starts a performance counter.
  2296. * @param counterName The name of the performance counter
  2297. */
  2298. startPerformanceCounter(counterName) {
  2299. this._parent._startPerformanceCounter(counterName);
  2300. }
  2301. /**
  2302. * Ends a performance counter.
  2303. * @param counterName The name of the performance counter
  2304. */
  2305. endPerformanceCounter(counterName) {
  2306. this._parent._endPerformanceCounter(counterName);
  2307. }
  2308. }
  2309. GLTFLoader._RegisteredExtensions = {};
  2310. /**
  2311. * The default glTF sampler.
  2312. */
  2313. GLTFLoader.DefaultSampler = { index: -1 };
  2314. GLTFFileLoader._CreateGLTF2Loader = (parent) => new GLTFLoader(parent);
  2315. //# sourceMappingURL=glTFLoader.js.map