babylon.glTFLoader.ts 71 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823
  1. /// <reference path="../../../../dist/preview release/babylon.d.ts"/>
  2. module BABYLON.GLTF1 {
  3. /**
  4. * Tokenizer. Used for shaders compatibility
  5. * Automatically map world, view, projection, worldViewProjection, attributes and so on
  6. */
  7. enum ETokenType {
  8. IDENTIFIER = 1,
  9. UNKNOWN = 2,
  10. END_OF_INPUT = 3
  11. }
  12. class Tokenizer {
  13. private _toParse: string;
  14. private _pos: number = 0;
  15. private _maxPos: number;
  16. public currentToken: ETokenType = ETokenType.UNKNOWN;
  17. public currentIdentifier: string = "";
  18. public currentString: string = "";
  19. public isLetterOrDigitPattern: RegExp = /^[a-zA-Z0-9]+$/;
  20. constructor(toParse: string) {
  21. this._toParse = toParse;
  22. this._maxPos = toParse.length;
  23. }
  24. public getNextToken(): ETokenType {
  25. if (this.isEnd()) return ETokenType.END_OF_INPUT;
  26. this.currentString = this.read();
  27. this.currentToken = ETokenType.UNKNOWN;
  28. if (this.currentString === "_" || this.isLetterOrDigitPattern.test(this.currentString)) {
  29. this.currentToken = ETokenType.IDENTIFIER;
  30. this.currentIdentifier = this.currentString;
  31. while (!this.isEnd() && (this.isLetterOrDigitPattern.test(this.currentString = this.peek()) || this.currentString === "_")) {
  32. this.currentIdentifier += this.currentString;
  33. this.forward();
  34. }
  35. }
  36. return this.currentToken;
  37. }
  38. public peek(): string {
  39. return this._toParse[this._pos];
  40. }
  41. public read(): string {
  42. return this._toParse[this._pos++];
  43. }
  44. public forward(): void {
  45. this._pos++;
  46. }
  47. public isEnd(): boolean {
  48. return this._pos >= this._maxPos;
  49. }
  50. }
  51. /**
  52. * Values
  53. */
  54. var glTFTransforms = ["MODEL", "VIEW", "PROJECTION", "MODELVIEW", "MODELVIEWPROJECTION", "JOINTMATRIX"];
  55. var babylonTransforms = ["world", "view", "projection", "worldView", "worldViewProjection", "mBones"];
  56. var glTFAnimationPaths = ["translation", "rotation", "scale"];
  57. var babylonAnimationPaths = ["position", "rotationQuaternion", "scaling"];
  58. /**
  59. * Parse
  60. */
  61. var parseBuffers = (parsedBuffers: any, gltfRuntime: IGLTFRuntime) => {
  62. for (var buf in parsedBuffers) {
  63. var parsedBuffer = parsedBuffers[buf];
  64. gltfRuntime.buffers[buf] = parsedBuffer;
  65. gltfRuntime.buffersCount++;
  66. }
  67. };
  68. var parseShaders = (parsedShaders: any, gltfRuntime: IGLTFRuntime) => {
  69. for (var sha in parsedShaders) {
  70. var parsedShader = parsedShaders[sha];
  71. gltfRuntime.shaders[sha] = parsedShader;
  72. gltfRuntime.shaderscount++;
  73. }
  74. };
  75. var parseObject = (parsedObjects: any, runtimeProperty: string, gltfRuntime: IGLTFRuntime) => {
  76. for (var object in parsedObjects) {
  77. var parsedObject = parsedObjects[object];
  78. (<any>gltfRuntime)[runtimeProperty][object] = parsedObject;
  79. }
  80. };
  81. /**
  82. * Utils
  83. */
  84. var normalizeUVs = (buffer: any) => {
  85. if (!buffer) {
  86. return;
  87. }
  88. for (var i = 0; i < buffer.length / 2; i++) {
  89. buffer[i * 2 + 1] = 1.0 - buffer[i * 2 + 1];
  90. }
  91. };
  92. var getAttribute = (attributeParameter: IGLTFTechniqueParameter): Nullable<string> => {
  93. if (attributeParameter.semantic === "NORMAL") {
  94. return "normal";
  95. } else if (attributeParameter.semantic === "POSITION") {
  96. return "position";
  97. } else if (attributeParameter.semantic === "JOINT") {
  98. return "matricesIndices";
  99. } else if (attributeParameter.semantic === "WEIGHT") {
  100. return "matricesWeights";
  101. } else if (attributeParameter.semantic === "COLOR") {
  102. return "color";
  103. } else if (attributeParameter.semantic && attributeParameter.semantic.indexOf("TEXCOORD_") !== -1) {
  104. var channel = Number(attributeParameter.semantic.split("_")[1]);
  105. return "uv" + (channel === 0 ? "" : channel + 1);
  106. }
  107. return null;
  108. };
  109. /**
  110. * Loads and creates animations
  111. */
  112. var loadAnimations = (gltfRuntime: IGLTFRuntime) => {
  113. for (var anim in gltfRuntime.animations) {
  114. var animation: IGLTFAnimation = gltfRuntime.animations[anim];
  115. if (!animation.channels || !animation.samplers) {
  116. continue;
  117. }
  118. var lastAnimation: Nullable<Animation> = null;
  119. for (var i = 0; i < animation.channels.length; i++) {
  120. // Get parameters and load buffers
  121. var channel = animation.channels[i];
  122. var sampler: IGLTFAnimationSampler = animation.samplers[channel.sampler];
  123. if (!sampler) {
  124. continue;
  125. }
  126. var inputData: Nullable<string> = null;
  127. var outputData: Nullable<string> = null;
  128. if (animation.parameters) {
  129. inputData = animation.parameters[sampler.input];
  130. outputData = animation.parameters[sampler.output];
  131. }
  132. else {
  133. inputData = sampler.input;
  134. outputData = sampler.output;
  135. }
  136. var bufferInput = GLTFUtils.GetBufferFromAccessor(gltfRuntime, gltfRuntime.accessors[inputData]);
  137. var bufferOutput = GLTFUtils.GetBufferFromAccessor(gltfRuntime, gltfRuntime.accessors[outputData]);
  138. var targetID = channel.target.id;
  139. var targetNode: any = gltfRuntime.scene.getNodeByID(targetID);
  140. if (targetNode === null) {
  141. targetNode = gltfRuntime.scene.getNodeByName(targetID);
  142. }
  143. if (targetNode === null) {
  144. Tools.Warn("Creating animation named " + anim + ". But cannot find node named " + targetID + " to attach to");
  145. continue;
  146. }
  147. var isBone = targetNode instanceof Bone;
  148. // Get target path (position, rotation or scaling)
  149. var targetPath = channel.target.path;
  150. var targetPathIndex = glTFAnimationPaths.indexOf(targetPath);
  151. if (targetPathIndex !== -1) {
  152. targetPath = babylonAnimationPaths[targetPathIndex];
  153. }
  154. // Determine animation type
  155. var animationType = Animation.ANIMATIONTYPE_MATRIX;
  156. if (!isBone) {
  157. if (targetPath === "rotationQuaternion") {
  158. animationType = Animation.ANIMATIONTYPE_QUATERNION;
  159. targetNode.rotationQuaternion = new Quaternion();
  160. }
  161. else {
  162. animationType = Animation.ANIMATIONTYPE_VECTOR3;
  163. }
  164. }
  165. // Create animation and key frames
  166. var babylonAnimation: Nullable<Animation> = null;
  167. var keys = [];
  168. var arrayOffset = 0;
  169. var modifyKey = false;
  170. if (isBone && lastAnimation && lastAnimation.getKeys().length === bufferInput.length) {
  171. babylonAnimation = lastAnimation;
  172. modifyKey = true;
  173. }
  174. if (!modifyKey) {
  175. babylonAnimation = new Animation(anim, isBone ? "_matrix" : targetPath, 1, animationType, Animation.ANIMATIONLOOPMODE_CYCLE);
  176. }
  177. // For each frame
  178. for (var j = 0; j < bufferInput.length; j++) {
  179. var value: any = null;
  180. if (targetPath === "rotationQuaternion") { // VEC4
  181. value = Quaternion.FromArray([bufferOutput[arrayOffset], bufferOutput[arrayOffset + 1], bufferOutput[arrayOffset + 2], bufferOutput[arrayOffset + 3]]);
  182. arrayOffset += 4;
  183. }
  184. else { // Position and scaling are VEC3
  185. value = Vector3.FromArray([bufferOutput[arrayOffset], bufferOutput[arrayOffset + 1], bufferOutput[arrayOffset + 2]]);
  186. arrayOffset += 3;
  187. }
  188. if (isBone) {
  189. var bone = <Bone>targetNode;
  190. var translation = Vector3.Zero();
  191. var rotationQuaternion = new Quaternion();
  192. var scaling = Vector3.Zero();
  193. // Warning on decompose
  194. var mat = bone.getBaseMatrix();
  195. if (modifyKey && lastAnimation) {
  196. mat = lastAnimation.getKeys()[j].value;
  197. }
  198. mat.decompose(scaling, rotationQuaternion, translation);
  199. if (targetPath === "position") {
  200. translation = value;
  201. }
  202. else if (targetPath === "rotationQuaternion") {
  203. rotationQuaternion = value;
  204. }
  205. else {
  206. scaling = value;
  207. }
  208. value = Matrix.Compose(scaling, rotationQuaternion, translation);
  209. }
  210. if (!modifyKey) {
  211. keys.push({
  212. frame: bufferInput[j],
  213. value: value
  214. });
  215. }
  216. else if (lastAnimation) {
  217. lastAnimation.getKeys()[j].value = value;
  218. }
  219. }
  220. // Finish
  221. if (!modifyKey && babylonAnimation) {
  222. babylonAnimation.setKeys(keys);
  223. targetNode.animations.push(babylonAnimation);
  224. }
  225. lastAnimation = babylonAnimation;
  226. gltfRuntime.scene.stopAnimation(targetNode);
  227. gltfRuntime.scene.beginAnimation(targetNode, 0, bufferInput[bufferInput.length - 1], true, 1.0);
  228. }
  229. }
  230. };
  231. /**
  232. * Returns the bones transformation matrix
  233. */
  234. var configureBoneTransformation = (node: IGLTFNode): Matrix => {
  235. var mat: Nullable<Matrix> = null;
  236. if (node.translation || node.rotation || node.scale) {
  237. var scale = Vector3.FromArray(node.scale || [1, 1, 1]);
  238. var rotation = Quaternion.FromArray(node.rotation || [0, 0, 0, 1]);
  239. var position = Vector3.FromArray(node.translation || [0, 0, 0]);
  240. mat = Matrix.Compose(scale, rotation, position);
  241. }
  242. else {
  243. mat = Matrix.FromArray(node.matrix);
  244. }
  245. return mat;
  246. };
  247. /**
  248. * Returns the parent bone
  249. */
  250. var getParentBone = (gltfRuntime: IGLTFRuntime, skins: IGLTFSkins, jointName: string, newSkeleton: Skeleton): Nullable<Bone> => {
  251. // Try to find
  252. for (var i = 0; i < newSkeleton.bones.length; i++) {
  253. if (newSkeleton.bones[i].name === jointName) {
  254. return newSkeleton.bones[i];
  255. }
  256. }
  257. // Not found, search in gltf nodes
  258. var nodes = gltfRuntime.nodes;
  259. for (var nde in nodes) {
  260. var node: IGLTFNode = nodes[nde];
  261. if (!node.jointName) {
  262. continue;
  263. }
  264. var children = node.children;
  265. for (var i = 0; i < children.length; i++) {
  266. var child: IGLTFNode = gltfRuntime.nodes[children[i]];
  267. if (!child.jointName) {
  268. continue;
  269. }
  270. if (child.jointName === jointName) {
  271. var mat = configureBoneTransformation(node);
  272. var bone = new Bone(node.name || "", newSkeleton, getParentBone(gltfRuntime, skins, node.jointName, newSkeleton), mat);
  273. bone.id = nde;
  274. return bone;
  275. }
  276. }
  277. }
  278. return null;
  279. }
  280. /**
  281. * Returns the appropriate root node
  282. */
  283. var getNodeToRoot = (nodesToRoot: INodeToRoot[], id: string): Nullable<Bone> => {
  284. for (var i = 0; i < nodesToRoot.length; i++) {
  285. var nodeToRoot = nodesToRoot[i];
  286. for (var j = 0; j < nodeToRoot.node.children.length; j++) {
  287. var child = nodeToRoot.node.children[j];
  288. if (child === id) {
  289. return nodeToRoot.bone;
  290. }
  291. }
  292. }
  293. return null;
  294. };
  295. /**
  296. * Returns the node with the joint name
  297. */
  298. var getJointNode = (gltfRuntime: IGLTFRuntime, jointName: string): Nullable<IJointNode> => {
  299. var nodes = gltfRuntime.nodes;
  300. var node: IGLTFNode = nodes[jointName];
  301. if (node) {
  302. return {
  303. node: node,
  304. id: jointName
  305. };
  306. }
  307. for (var nde in nodes) {
  308. node = nodes[nde];
  309. if (node.jointName === jointName) {
  310. return {
  311. node: node,
  312. id: nde
  313. };
  314. }
  315. }
  316. return null;
  317. }
  318. /**
  319. * Checks if a nodes is in joints
  320. */
  321. var nodeIsInJoints = (skins: IGLTFSkins, id: string): boolean => {
  322. for (var i = 0; i < skins.jointNames.length; i++) {
  323. if (skins.jointNames[i] === id) {
  324. return true;
  325. }
  326. }
  327. return false;
  328. }
  329. /**
  330. * Fills the nodes to root for bones and builds hierarchy
  331. */
  332. var getNodesToRoot = (gltfRuntime: IGLTFRuntime, newSkeleton: Skeleton, skins: IGLTFSkins, nodesToRoot: INodeToRoot[]) => {
  333. // Creates nodes for root
  334. for (var nde in gltfRuntime.nodes) {
  335. var node: IGLTFNode = gltfRuntime.nodes[nde];
  336. var id = nde;
  337. if (!node.jointName || nodeIsInJoints(skins, node.jointName)) {
  338. continue;
  339. }
  340. // Create node to root bone
  341. var mat = configureBoneTransformation(node);
  342. var bone = new Bone(node.name || "", newSkeleton, null, mat);
  343. bone.id = id;
  344. nodesToRoot.push({ bone: bone, node: node, id: id });
  345. }
  346. // Parenting
  347. for (var i = 0; i < nodesToRoot.length; i++) {
  348. var nodeToRoot = nodesToRoot[i];
  349. var children = nodeToRoot.node.children;
  350. for (var j = 0; j < children.length; j++) {
  351. var child: Nullable<INodeToRoot> = null;
  352. for (var k = 0; k < nodesToRoot.length; k++) {
  353. if (nodesToRoot[k].id === children[j]) {
  354. child = nodesToRoot[k];
  355. break;
  356. }
  357. }
  358. if (child) {
  359. (<any>child.bone)._parent = nodeToRoot.bone;
  360. nodeToRoot.bone.children.push(child.bone);
  361. }
  362. }
  363. }
  364. };
  365. /**
  366. * Imports a skeleton
  367. */
  368. var importSkeleton = (gltfRuntime: IGLTFRuntime, skins: IGLTFSkins, mesh: Mesh, newSkeleton: Skeleton | undefined, id: string): Skeleton => {
  369. if (!newSkeleton) {
  370. newSkeleton = new Skeleton(skins.name || "", "", gltfRuntime.scene);
  371. }
  372. if (!skins.babylonSkeleton) {
  373. return newSkeleton;
  374. }
  375. // Find the root bones
  376. var nodesToRoot: INodeToRoot[] = [];
  377. var nodesToRootToAdd: Bone[] = [];
  378. getNodesToRoot(gltfRuntime, newSkeleton, skins, nodesToRoot);
  379. newSkeleton.bones = [];
  380. // Joints
  381. for (var i = 0; i < skins.jointNames.length; i++) {
  382. var jointNode = getJointNode(gltfRuntime, skins.jointNames[i]);
  383. if (!jointNode) {
  384. continue;
  385. }
  386. var node = jointNode.node;
  387. if (!node) {
  388. Tools.Warn("Joint named " + skins.jointNames[i] + " does not exist");
  389. continue;
  390. }
  391. var id = jointNode.id;
  392. // Optimize, if the bone already exists...
  393. var existingBone = gltfRuntime.scene.getBoneByID(id);
  394. if (existingBone) {
  395. newSkeleton.bones.push(existingBone);
  396. continue;
  397. }
  398. // Search for parent bone
  399. var foundBone = false;
  400. var parentBone: Nullable<Bone> = null;
  401. for (var j = 0; j < i; j++) {
  402. let jointNode = getJointNode(gltfRuntime, skins.jointNames[j]);
  403. if (!jointNode) {
  404. continue;
  405. }
  406. var joint: IGLTFNode = jointNode.node;
  407. if (!joint) {
  408. Tools.Warn("Joint named " + skins.jointNames[j] + " does not exist when looking for parent");
  409. continue;
  410. }
  411. var children = joint.children;
  412. if (!children) {
  413. continue;
  414. }
  415. foundBone = false;
  416. for (var k = 0; k < children.length; k++) {
  417. if (children[k] === id) {
  418. parentBone = getParentBone(gltfRuntime, skins, skins.jointNames[j], newSkeleton);
  419. foundBone = true;
  420. break;
  421. }
  422. }
  423. if (foundBone) {
  424. break;
  425. }
  426. }
  427. // Create bone
  428. var mat = configureBoneTransformation(node);
  429. if (!parentBone && nodesToRoot.length > 0) {
  430. parentBone = getNodeToRoot(nodesToRoot, id);
  431. if (parentBone) {
  432. if (nodesToRootToAdd.indexOf(parentBone) === -1) {
  433. nodesToRootToAdd.push(parentBone);
  434. }
  435. }
  436. }
  437. var bone = new Bone(node.jointName || "", newSkeleton, parentBone, mat);
  438. bone.id = id;
  439. }
  440. // Polish
  441. var bones = newSkeleton.bones;
  442. newSkeleton.bones = [];
  443. for (var i = 0; i < skins.jointNames.length; i++) {
  444. var jointNode = getJointNode(gltfRuntime, skins.jointNames[i]);
  445. if (!jointNode) {
  446. continue;
  447. }
  448. for (var j = 0; j < bones.length; j++) {
  449. if (bones[j].id === jointNode.id) {
  450. newSkeleton.bones.push(bones[j]);
  451. break;
  452. }
  453. }
  454. }
  455. newSkeleton.prepare();
  456. // Finish
  457. for (var i = 0; i < nodesToRootToAdd.length; i++) {
  458. newSkeleton.bones.push(nodesToRootToAdd[i]);
  459. }
  460. return newSkeleton;
  461. };
  462. /**
  463. * Imports a mesh and its geometries
  464. */
  465. var importMesh = (gltfRuntime: IGLTFRuntime, node: IGLTFNode, meshes: string[], id: string, newMesh: Mesh): Mesh => {
  466. if (!newMesh) {
  467. newMesh = new Mesh(node.name || "", gltfRuntime.scene);
  468. newMesh.id = id;
  469. }
  470. if (!node.babylonNode) {
  471. return newMesh;
  472. }
  473. const subMaterials: Material[] = [];
  474. var vertexData: Nullable<VertexData> = null;
  475. var verticesStarts = new Array<number>();
  476. var verticesCounts = new Array<number>();
  477. var indexStarts = new Array<number>();
  478. var indexCounts = new Array<number>();
  479. for (var meshIndex = 0; meshIndex < meshes.length; meshIndex++) {
  480. var meshID = meshes[meshIndex];
  481. var mesh: IGLTFMesh = gltfRuntime.meshes[meshID];
  482. if (!mesh) {
  483. continue;
  484. }
  485. // Positions, normals and UVs
  486. for (var i = 0; i < mesh.primitives.length; i++) {
  487. // Temporary vertex data
  488. var tempVertexData = new VertexData();
  489. var primitive = mesh.primitives[i];
  490. if (primitive.mode !== 4) {
  491. // continue;
  492. }
  493. var attributes = primitive.attributes;
  494. var accessor: Nullable<IGLTFAccessor> = null;
  495. var buffer: any = null;
  496. // Set positions, normal and uvs
  497. for (var semantic in attributes) {
  498. // Link accessor and buffer view
  499. accessor = gltfRuntime.accessors[attributes[semantic]];
  500. buffer = GLTFUtils.GetBufferFromAccessor(gltfRuntime, accessor);
  501. if (semantic === "NORMAL") {
  502. tempVertexData.normals = new Float32Array(buffer.length);
  503. (<Float32Array>tempVertexData.normals).set(buffer);
  504. }
  505. else if (semantic === "POSITION") {
  506. if (GLTFFileLoader.HomogeneousCoordinates) {
  507. tempVertexData.positions = new Float32Array(buffer.length - buffer.length / 4);
  508. for (var j = 0; j < buffer.length; j += 4) {
  509. tempVertexData.positions[j] = buffer[j];
  510. tempVertexData.positions[j + 1] = buffer[j + 1];
  511. tempVertexData.positions[j + 2] = buffer[j + 2];
  512. }
  513. }
  514. else {
  515. tempVertexData.positions = new Float32Array(buffer.length);
  516. (<Float32Array>tempVertexData.positions).set(buffer);
  517. }
  518. verticesCounts.push(tempVertexData.positions.length);
  519. }
  520. else if (semantic.indexOf("TEXCOORD_") !== -1) {
  521. var channel = Number(semantic.split("_")[1]);
  522. var uvKind = VertexBuffer.UVKind + (channel === 0 ? "" : (channel + 1));
  523. var uvs = new Float32Array(buffer.length);
  524. (<Float32Array>uvs).set(buffer);
  525. normalizeUVs(uvs);
  526. tempVertexData.set(uvs, uvKind);
  527. }
  528. else if (semantic === "JOINT") {
  529. tempVertexData.matricesIndices = new Float32Array(buffer.length);
  530. (<Float32Array>tempVertexData.matricesIndices).set(buffer);
  531. }
  532. else if (semantic === "WEIGHT") {
  533. tempVertexData.matricesWeights = new Float32Array(buffer.length);
  534. (<Float32Array>tempVertexData.matricesWeights).set(buffer);
  535. }
  536. else if (semantic === "COLOR") {
  537. tempVertexData.colors = new Float32Array(buffer.length);
  538. (<Float32Array>tempVertexData.colors).set(buffer);
  539. }
  540. }
  541. // Indices
  542. accessor = gltfRuntime.accessors[primitive.indices];
  543. if (accessor) {
  544. buffer = GLTFUtils.GetBufferFromAccessor(gltfRuntime, accessor);
  545. tempVertexData.indices = new Int32Array(buffer.length);
  546. (<Float32Array>tempVertexData.indices).set(buffer);
  547. indexCounts.push(tempVertexData.indices.length);
  548. }
  549. else {
  550. // Set indices on the fly
  551. var indices: number[] = [];
  552. for (var j = 0; j < (<FloatArray>tempVertexData.positions).length / 3; j++) {
  553. indices.push(j);
  554. }
  555. tempVertexData.indices = new Int32Array(indices);
  556. indexCounts.push(tempVertexData.indices.length);
  557. }
  558. if (!vertexData) {
  559. vertexData = tempVertexData;
  560. }
  561. else {
  562. vertexData.merge(tempVertexData);
  563. }
  564. // Sub material
  565. let material = gltfRuntime.scene.getMaterialByID(primitive.material);
  566. subMaterials.push(material === null ? GLTFUtils.GetDefaultMaterial(gltfRuntime.scene) : material);
  567. // Update vertices start and index start
  568. verticesStarts.push(verticesStarts.length === 0 ? 0 : verticesStarts[verticesStarts.length - 1] + verticesCounts[verticesCounts.length - 2]);
  569. indexStarts.push(indexStarts.length === 0 ? 0 : indexStarts[indexStarts.length - 1] + indexCounts[indexCounts.length - 2]);
  570. }
  571. }
  572. let material: StandardMaterial | MultiMaterial;
  573. if (subMaterials.length > 1) {
  574. material = new MultiMaterial("multimat" + id, gltfRuntime.scene);
  575. (material as MultiMaterial).subMaterials = subMaterials;
  576. }
  577. else {
  578. material = new StandardMaterial("multimat" + id, gltfRuntime.scene);
  579. }
  580. if (subMaterials.length === 1) {
  581. material = (subMaterials[0] as StandardMaterial);
  582. }
  583. if (!newMesh.material) {
  584. newMesh.material = material;
  585. }
  586. // Apply geometry
  587. new Geometry(id, gltfRuntime.scene, vertexData!, false, newMesh);
  588. newMesh.computeWorldMatrix(true);
  589. // Apply submeshes
  590. newMesh.subMeshes = [];
  591. var index = 0;
  592. for (var meshIndex = 0; meshIndex < meshes.length; meshIndex++) {
  593. var meshID = meshes[meshIndex];
  594. var mesh: IGLTFMesh = gltfRuntime.meshes[meshID];
  595. if (!mesh) {
  596. continue;
  597. }
  598. for (var i = 0; i < mesh.primitives.length; i++) {
  599. if (mesh.primitives[i].mode !== 4) {
  600. //continue;
  601. }
  602. SubMesh.AddToMesh(index, verticesStarts[index], verticesCounts[index], indexStarts[index], indexCounts[index], newMesh, newMesh, true);
  603. index++;
  604. }
  605. }
  606. // Finish
  607. return newMesh;
  608. };
  609. /**
  610. * Configure node transformation from position, rotation and scaling
  611. */
  612. var configureNode = (newNode: any, position: Vector3, rotation: Quaternion, scaling: Vector3) => {
  613. if (newNode.position) {
  614. newNode.position = position;
  615. }
  616. if (newNode.rotationQuaternion || newNode.rotation) {
  617. newNode.rotationQuaternion = rotation;
  618. }
  619. if (newNode.scaling) {
  620. newNode.scaling = scaling;
  621. }
  622. };
  623. /**
  624. * Configures node from transformation matrix
  625. */
  626. var configureNodeFromMatrix = (newNode: Mesh, node: IGLTFNode, parent: Nullable<Node>) => {
  627. if (node.matrix) {
  628. var position = new Vector3(0, 0, 0);
  629. var rotation = new Quaternion();
  630. var scaling = new Vector3(0, 0, 0);
  631. var mat = Matrix.FromArray(node.matrix);
  632. mat.decompose(scaling, rotation, position);
  633. configureNode(newNode, position, rotation, scaling);
  634. }
  635. else if (node.translation && node.rotation && node.scale) {
  636. configureNode(newNode, Vector3.FromArray(node.translation), Quaternion.FromArray(node.rotation), Vector3.FromArray(node.scale));
  637. }
  638. newNode.computeWorldMatrix(true);
  639. };
  640. /**
  641. * Imports a node
  642. */
  643. var importNode = (gltfRuntime: IGLTFRuntime, node: IGLTFNode, id: string, parent: Nullable<Node>): Nullable<Node> => {
  644. var lastNode: Nullable<Node> = null;
  645. if (gltfRuntime.importOnlyMeshes && (node.skin || node.meshes)) {
  646. if (gltfRuntime.importMeshesNames && gltfRuntime.importMeshesNames.length > 0 && gltfRuntime.importMeshesNames.indexOf(node.name || "") === -1) {
  647. return null;
  648. }
  649. }
  650. // Meshes
  651. if (node.skin) {
  652. if (node.meshes) {
  653. var skin: IGLTFSkins = gltfRuntime.skins[node.skin];
  654. var newMesh = importMesh(gltfRuntime, node, node.meshes, id, <Mesh>node.babylonNode);
  655. newMesh.skeleton = gltfRuntime.scene.getLastSkeletonByID(node.skin);
  656. if (newMesh.skeleton === null) {
  657. newMesh.skeleton = importSkeleton(gltfRuntime, skin, newMesh, skin.babylonSkeleton, node.skin);
  658. if (!skin.babylonSkeleton) {
  659. skin.babylonSkeleton = newMesh.skeleton;
  660. }
  661. }
  662. lastNode = newMesh;
  663. }
  664. }
  665. else if (node.meshes) {
  666. /**
  667. * Improve meshes property
  668. */
  669. var newMesh = importMesh(gltfRuntime, node, node.mesh ? [node.mesh] : node.meshes, id, <Mesh>node.babylonNode);
  670. lastNode = newMesh;
  671. }
  672. // Lights
  673. else if (node.light && !node.babylonNode && !gltfRuntime.importOnlyMeshes) {
  674. var light: IGLTFLight = gltfRuntime.lights[node.light];
  675. if (light) {
  676. if (light.type === "ambient") {
  677. var ambienLight: IGLTFAmbienLight = (<any>light)[light.type];
  678. var hemiLight = new HemisphericLight(node.light, Vector3.Zero(), gltfRuntime.scene);
  679. hemiLight.name = node.name || "";
  680. if (ambienLight.color) {
  681. hemiLight.diffuse = Color3.FromArray(ambienLight.color);
  682. }
  683. lastNode = hemiLight;
  684. }
  685. else if (light.type === "directional") {
  686. var directionalLight: IGLTFDirectionalLight = (<any>light)[light.type];
  687. var dirLight = new DirectionalLight(node.light, Vector3.Zero(), gltfRuntime.scene);
  688. dirLight.name = node.name || "";
  689. if (directionalLight.color) {
  690. dirLight.diffuse = Color3.FromArray(directionalLight.color);
  691. }
  692. lastNode = dirLight;
  693. }
  694. else if (light.type === "point") {
  695. var pointLight: IGLTFPointLight = (<any>light)[light.type];
  696. var ptLight = new PointLight(node.light, Vector3.Zero(), gltfRuntime.scene);
  697. ptLight.name = node.name || "";
  698. if (pointLight.color) {
  699. ptLight.diffuse = Color3.FromArray(pointLight.color);
  700. }
  701. lastNode = ptLight;
  702. }
  703. else if (light.type === "spot") {
  704. var spotLight: IGLTFSpotLight = (<any>light)[light.type];
  705. var spLight = new SpotLight(node.light, Vector3.Zero(), Vector3.Zero(), 0, 0, gltfRuntime.scene);
  706. spLight.name = node.name || "";
  707. if (spotLight.color) {
  708. spLight.diffuse = Color3.FromArray(spotLight.color);
  709. }
  710. if (spotLight.fallOfAngle) {
  711. spLight.angle = spotLight.fallOfAngle;
  712. }
  713. if (spotLight.fallOffExponent) {
  714. spLight.exponent = spotLight.fallOffExponent;
  715. }
  716. lastNode = spLight;
  717. }
  718. }
  719. }
  720. // Cameras
  721. else if (node.camera && !node.babylonNode && !gltfRuntime.importOnlyMeshes) {
  722. var camera: IGLTFCamera = gltfRuntime.cameras[node.camera];
  723. if (camera) {
  724. if (camera.type === "orthographic") {
  725. var orthoCamera = new FreeCamera(node.camera, Vector3.Zero(), gltfRuntime.scene);
  726. orthoCamera.name = node.name || "";
  727. orthoCamera.mode = Camera.ORTHOGRAPHIC_CAMERA;
  728. orthoCamera.attachControl(<HTMLElement>gltfRuntime.scene.getEngine().getRenderingCanvas());
  729. lastNode = orthoCamera;
  730. }
  731. else if (camera.type === "perspective") {
  732. var perspectiveCamera: IGLTFCameraPerspective = (<any>camera)[camera.type];
  733. var persCamera = new FreeCamera(node.camera, Vector3.Zero(), gltfRuntime.scene);
  734. persCamera.name = node.name || "";
  735. persCamera.attachControl(<HTMLElement>gltfRuntime.scene.getEngine().getRenderingCanvas());
  736. if (!perspectiveCamera.aspectRatio) {
  737. perspectiveCamera.aspectRatio = gltfRuntime.scene.getEngine().getRenderWidth() / gltfRuntime.scene.getEngine().getRenderHeight();
  738. }
  739. if (perspectiveCamera.znear && perspectiveCamera.zfar) {
  740. persCamera.maxZ = perspectiveCamera.zfar;
  741. persCamera.minZ = perspectiveCamera.znear;
  742. }
  743. lastNode = persCamera;
  744. }
  745. }
  746. }
  747. // Empty node
  748. if (!node.jointName) {
  749. if (node.babylonNode) {
  750. return node.babylonNode;
  751. }
  752. else if (lastNode === null) {
  753. var dummy = new Mesh(node.name || "", gltfRuntime.scene);
  754. node.babylonNode = dummy;
  755. lastNode = dummy;
  756. }
  757. }
  758. if (lastNode !== null) {
  759. if (node.matrix && lastNode instanceof Mesh) {
  760. configureNodeFromMatrix(lastNode, node, parent);
  761. }
  762. else {
  763. var translation = node.translation || [0, 0, 0];
  764. var rotation = node.rotation || [0, 0, 0, 1];
  765. var scale = node.scale || [1, 1, 1];
  766. configureNode(lastNode, Vector3.FromArray(translation), Quaternion.FromArray(rotation), Vector3.FromArray(scale));
  767. }
  768. lastNode.updateCache(true);
  769. node.babylonNode = lastNode;
  770. }
  771. return lastNode;
  772. };
  773. /**
  774. * Traverses nodes and creates them
  775. */
  776. var traverseNodes = (gltfRuntime: IGLTFRuntime, id: string, parent: Nullable<Node>, meshIncluded: boolean = false) => {
  777. var node: IGLTFNode = gltfRuntime.nodes[id];
  778. var newNode: Nullable<Node> = null;
  779. if (gltfRuntime.importOnlyMeshes && !meshIncluded && gltfRuntime.importMeshesNames) {
  780. if (gltfRuntime.importMeshesNames.indexOf(node.name || "") !== -1 || gltfRuntime.importMeshesNames.length === 0) {
  781. meshIncluded = true;
  782. }
  783. else {
  784. meshIncluded = false;
  785. }
  786. }
  787. else {
  788. meshIncluded = true;
  789. }
  790. if (!node.jointName && meshIncluded) {
  791. newNode = importNode(gltfRuntime, node, id, parent);
  792. if (newNode !== null) {
  793. newNode.id = id;
  794. newNode.parent = parent;
  795. }
  796. }
  797. if (node.children) {
  798. for (var i = 0; i < node.children.length; i++) {
  799. traverseNodes(gltfRuntime, node.children[i], newNode, meshIncluded);
  800. }
  801. }
  802. };
  803. /**
  804. * do stuff after buffers, shaders are loaded (e.g. hook up materials, load animations, etc.)
  805. */
  806. var postLoad = (gltfRuntime: IGLTFRuntime) => {
  807. // Nodes
  808. var currentScene: IGLTFScene = <IGLTFScene>gltfRuntime.currentScene;
  809. if (currentScene) {
  810. for (var i = 0; i < currentScene.nodes.length; i++) {
  811. traverseNodes(gltfRuntime, currentScene.nodes[i], null);
  812. }
  813. }
  814. else {
  815. for (var thing in gltfRuntime.scenes) {
  816. currentScene = <IGLTFScene>gltfRuntime.scenes[thing];
  817. for (var i = 0; i < currentScene.nodes.length; i++) {
  818. traverseNodes(gltfRuntime, currentScene.nodes[i], null);
  819. }
  820. }
  821. }
  822. // Set animations
  823. loadAnimations(gltfRuntime);
  824. for (var i = 0; i < gltfRuntime.scene.skeletons.length; i++) {
  825. var skeleton = gltfRuntime.scene.skeletons[i];
  826. gltfRuntime.scene.beginAnimation(skeleton, 0, Number.MAX_VALUE, true, 1.0);
  827. }
  828. };
  829. /**
  830. * onBind shaderrs callback to set uniforms and matrices
  831. */
  832. var onBindShaderMaterial = (mesh: AbstractMesh, gltfRuntime: IGLTFRuntime, unTreatedUniforms: { [key: string]: IGLTFTechniqueParameter }, shaderMaterial: ShaderMaterial, technique: IGLTFTechnique, material: IGLTFMaterial, onSuccess: (shaderMaterial: ShaderMaterial) => void) => {
  833. var materialValues = material.values || technique.parameters;
  834. for (var unif in unTreatedUniforms) {
  835. var uniform: IGLTFTechniqueParameter = unTreatedUniforms[unif];
  836. var type = uniform.type;
  837. if (type === EParameterType.FLOAT_MAT2 || type === EParameterType.FLOAT_MAT3 || type === EParameterType.FLOAT_MAT4) {
  838. if (uniform.semantic && !uniform.source && !uniform.node) {
  839. GLTFUtils.SetMatrix(gltfRuntime.scene, mesh, uniform, unif, <Effect>shaderMaterial.getEffect());
  840. }
  841. else if (uniform.semantic && (uniform.source || uniform.node)) {
  842. var source = gltfRuntime.scene.getNodeByName(uniform.source || uniform.node || "");
  843. if (source === null) {
  844. source = gltfRuntime.scene.getNodeByID(uniform.source || uniform.node || "");
  845. }
  846. if (source === null) {
  847. continue;
  848. }
  849. GLTFUtils.SetMatrix(gltfRuntime.scene, source, uniform, unif, <Effect>shaderMaterial.getEffect());
  850. }
  851. }
  852. else {
  853. var value = (<any>materialValues)[technique.uniforms[unif]];
  854. if (!value) {
  855. continue;
  856. }
  857. if (type === EParameterType.SAMPLER_2D) {
  858. var texture = gltfRuntime.textures[material.values ? value : uniform.value].babylonTexture;
  859. if (texture === null || texture === undefined) {
  860. continue;
  861. }
  862. (<Effect>shaderMaterial.getEffect()).setTexture(unif, texture);
  863. }
  864. else {
  865. GLTFUtils.SetUniform(<Effect>(shaderMaterial.getEffect()), unif, value, type);
  866. }
  867. }
  868. }
  869. onSuccess(shaderMaterial);
  870. };
  871. /**
  872. * Prepare uniforms to send the only one time
  873. * Loads the appropriate textures
  874. */
  875. var prepareShaderMaterialUniforms = (gltfRuntime: IGLTFRuntime, shaderMaterial: ShaderMaterial, technique: IGLTFTechnique, material: IGLTFMaterial, unTreatedUniforms: { [key: string]: IGLTFTechniqueParameter }) => {
  876. var materialValues = material.values || technique.parameters;
  877. var techniqueUniforms = technique.uniforms;
  878. /**
  879. * Prepare values here (not matrices)
  880. */
  881. for (var unif in unTreatedUniforms) {
  882. var uniform: IGLTFTechniqueParameter = unTreatedUniforms[unif];
  883. var type = uniform.type;
  884. var value = (<any>materialValues)[techniqueUniforms[unif]];
  885. if (value === undefined) {
  886. // In case the value is the same for all materials
  887. value = <any>uniform.value;
  888. }
  889. if (!value) {
  890. continue;
  891. }
  892. var onLoadTexture = (uniformName: Nullable<string>) => {
  893. return (texture: Texture) => {
  894. if (uniform.value && uniformName) {
  895. // Static uniform
  896. shaderMaterial.setTexture(uniformName, texture);
  897. delete unTreatedUniforms[uniformName];
  898. }
  899. }
  900. };
  901. // Texture (sampler2D)
  902. if (type === EParameterType.SAMPLER_2D) {
  903. GLTFLoaderExtension.LoadTextureAsync(gltfRuntime, material.values ? value : uniform.value, onLoadTexture(unif), () => onLoadTexture(null));
  904. }
  905. // Others
  906. else {
  907. if (uniform.value && GLTFUtils.SetUniform(shaderMaterial, unif, material.values ? value : uniform.value, type)) {
  908. // Static uniform
  909. delete unTreatedUniforms[unif];
  910. }
  911. }
  912. }
  913. };
  914. /**
  915. * Shader compilation failed
  916. */
  917. var onShaderCompileError = (program: IGLTFProgram, shaderMaterial: ShaderMaterial, onError: (message: string) => void) => {
  918. return (effect: Effect, error: string) => {
  919. shaderMaterial.dispose(true);
  920. onError("Cannot compile program named " + program.name + ". Error: " + error + ". Default material will be applied");
  921. };
  922. };
  923. /**
  924. * Shader compilation success
  925. */
  926. var onShaderCompileSuccess = (gltfRuntime: IGLTFRuntime, shaderMaterial: ShaderMaterial, technique: IGLTFTechnique, material: IGLTFMaterial, unTreatedUniforms: { [key: string]: IGLTFTechniqueParameter }, onSuccess: (shaderMaterial: ShaderMaterial) => void) => {
  927. return (_: Effect) => {
  928. prepareShaderMaterialUniforms(gltfRuntime, shaderMaterial, technique, material, unTreatedUniforms);
  929. shaderMaterial.onBind = (mesh: AbstractMesh) => {
  930. onBindShaderMaterial(mesh, gltfRuntime, unTreatedUniforms, shaderMaterial, technique, material, onSuccess);
  931. };
  932. };
  933. };
  934. /**
  935. * Returns the appropriate uniform if already handled by babylon
  936. */
  937. var parseShaderUniforms = (tokenizer: Tokenizer, technique: IGLTFTechnique, unTreatedUniforms: { [key: string]: IGLTFTechniqueParameter }): string => {
  938. for (var unif in technique.uniforms) {
  939. var uniform = technique.uniforms[unif];
  940. var uniformParameter: IGLTFTechniqueParameter = technique.parameters[uniform];
  941. if (tokenizer.currentIdentifier === unif) {
  942. if (uniformParameter.semantic && !uniformParameter.source && !uniformParameter.node) {
  943. var transformIndex = glTFTransforms.indexOf(uniformParameter.semantic);
  944. if (transformIndex !== -1) {
  945. delete unTreatedUniforms[unif];
  946. return babylonTransforms[transformIndex];
  947. }
  948. }
  949. }
  950. }
  951. return tokenizer.currentIdentifier;
  952. };
  953. /**
  954. * All shaders loaded. Create materials one by one
  955. */
  956. var importMaterials = (gltfRuntime: IGLTFRuntime) => {
  957. // Create materials
  958. for (var mat in gltfRuntime.materials) {
  959. GLTFLoaderExtension.LoadMaterialAsync(gltfRuntime, mat, (material: Material) => { }, () => { });
  960. }
  961. };
  962. /**
  963. * Implementation of the base glTF spec
  964. */
  965. export class GLTFLoaderBase {
  966. public static CreateRuntime(parsedData: any, scene: Scene, rootUrl: string): IGLTFRuntime {
  967. var gltfRuntime: IGLTFRuntime = {
  968. extensions: {},
  969. accessors: {},
  970. buffers: {},
  971. bufferViews: {},
  972. meshes: {},
  973. lights: {},
  974. cameras: {},
  975. nodes: {},
  976. images: {},
  977. textures: {},
  978. shaders: {},
  979. programs: {},
  980. samplers: {},
  981. techniques: {},
  982. materials: {},
  983. animations: {},
  984. skins: {},
  985. extensionsUsed: [],
  986. scenes: {},
  987. buffersCount: 0,
  988. shaderscount: 0,
  989. scene: scene,
  990. rootUrl: rootUrl,
  991. loadedBufferCount: 0,
  992. loadedBufferViews: {},
  993. loadedShaderCount: 0,
  994. importOnlyMeshes: false,
  995. dummyNodes: []
  996. }
  997. // Parse
  998. if (parsedData.extensions) {
  999. parseObject(parsedData.extensions, "extensions", gltfRuntime);
  1000. }
  1001. if (parsedData.extensionsUsed) {
  1002. parseObject(parsedData.extensionsUsed, "extensionsUsed", gltfRuntime);
  1003. }
  1004. if (parsedData.buffers) {
  1005. parseBuffers(parsedData.buffers, gltfRuntime);
  1006. }
  1007. if (parsedData.bufferViews) {
  1008. parseObject(parsedData.bufferViews, "bufferViews", gltfRuntime);
  1009. }
  1010. if (parsedData.accessors) {
  1011. parseObject(parsedData.accessors, "accessors", gltfRuntime);
  1012. }
  1013. if (parsedData.meshes) {
  1014. parseObject(parsedData.meshes, "meshes", gltfRuntime);
  1015. }
  1016. if (parsedData.lights) {
  1017. parseObject(parsedData.lights, "lights", gltfRuntime);
  1018. }
  1019. if (parsedData.cameras) {
  1020. parseObject(parsedData.cameras, "cameras", gltfRuntime);
  1021. }
  1022. if (parsedData.nodes) {
  1023. parseObject(parsedData.nodes, "nodes", gltfRuntime);
  1024. }
  1025. if (parsedData.images) {
  1026. parseObject(parsedData.images, "images", gltfRuntime);
  1027. }
  1028. if (parsedData.textures) {
  1029. parseObject(parsedData.textures, "textures", gltfRuntime);
  1030. }
  1031. if (parsedData.shaders) {
  1032. parseShaders(parsedData.shaders, gltfRuntime);
  1033. }
  1034. if (parsedData.programs) {
  1035. parseObject(parsedData.programs, "programs", gltfRuntime);
  1036. }
  1037. if (parsedData.samplers) {
  1038. parseObject(parsedData.samplers, "samplers", gltfRuntime);
  1039. }
  1040. if (parsedData.techniques) {
  1041. parseObject(parsedData.techniques, "techniques", gltfRuntime);
  1042. }
  1043. if (parsedData.materials) {
  1044. parseObject(parsedData.materials, "materials", gltfRuntime);
  1045. }
  1046. if (parsedData.animations) {
  1047. parseObject(parsedData.animations, "animations", gltfRuntime);
  1048. }
  1049. if (parsedData.skins) {
  1050. parseObject(parsedData.skins, "skins", gltfRuntime);
  1051. }
  1052. if (parsedData.scenes) {
  1053. gltfRuntime.scenes = parsedData.scenes;
  1054. }
  1055. if (parsedData.scene && parsedData.scenes) {
  1056. gltfRuntime.currentScene = parsedData.scenes[parsedData.scene];
  1057. }
  1058. return gltfRuntime;
  1059. }
  1060. public static LoadBufferAsync(gltfRuntime: IGLTFRuntime, id: string, onSuccess: (buffer: ArrayBufferView) => void, onError: (message: string) => void, onProgress?: () => void): void {
  1061. var buffer: IGLTFBuffer = gltfRuntime.buffers[id];
  1062. if (Tools.IsBase64(buffer.uri)) {
  1063. setTimeout(() => onSuccess(new Uint8Array(Tools.DecodeBase64(buffer.uri))));
  1064. }
  1065. else {
  1066. Tools.LoadFile(gltfRuntime.rootUrl + buffer.uri, data => onSuccess(new Uint8Array(data as ArrayBuffer)), onProgress, undefined, true, request => {
  1067. if (request) {
  1068. onError(request.status + " " + request.statusText);
  1069. }
  1070. });
  1071. }
  1072. }
  1073. public static LoadTextureBufferAsync(gltfRuntime: IGLTFRuntime, id: string, onSuccess: (buffer: Nullable<ArrayBufferView>) => void, onError: (message: string) => void): void {
  1074. var texture: IGLTFTexture = gltfRuntime.textures[id];
  1075. if (!texture || !texture.source) {
  1076. onError("");
  1077. return;
  1078. }
  1079. if (texture.babylonTexture) {
  1080. onSuccess(null);
  1081. return;
  1082. }
  1083. var source: IGLTFImage = gltfRuntime.images[texture.source];
  1084. if (Tools.IsBase64(source.uri)) {
  1085. setTimeout(() => onSuccess(new Uint8Array(Tools.DecodeBase64(source.uri))));
  1086. }
  1087. else {
  1088. Tools.LoadFile(gltfRuntime.rootUrl + source.uri, data => onSuccess(new Uint8Array(data as ArrayBuffer)), undefined, undefined, true, request => {
  1089. if (request) {
  1090. onError(request.status + " " + request.statusText);
  1091. }
  1092. });
  1093. }
  1094. }
  1095. public static CreateTextureAsync(gltfRuntime: IGLTFRuntime, id: string, buffer: Nullable<ArrayBufferView>, onSuccess: (texture: Texture) => void, onError: (message: string) => void): void {
  1096. var texture: IGLTFTexture = gltfRuntime.textures[id];
  1097. if (texture.babylonTexture) {
  1098. onSuccess(texture.babylonTexture);
  1099. return;
  1100. }
  1101. var sampler: IGLTFSampler = gltfRuntime.samplers[texture.sampler];
  1102. var createMipMaps =
  1103. (sampler.minFilter === ETextureFilterType.NEAREST_MIPMAP_NEAREST) ||
  1104. (sampler.minFilter === ETextureFilterType.NEAREST_MIPMAP_LINEAR) ||
  1105. (sampler.minFilter === ETextureFilterType.LINEAR_MIPMAP_NEAREST) ||
  1106. (sampler.minFilter === ETextureFilterType.LINEAR_MIPMAP_LINEAR);
  1107. var samplingMode = Texture.BILINEAR_SAMPLINGMODE;
  1108. var blob = new Blob([buffer]);
  1109. var blobURL = URL.createObjectURL(blob);
  1110. var revokeBlobURL = () => URL.revokeObjectURL(blobURL);
  1111. var newTexture = new Texture(blobURL, gltfRuntime.scene, !createMipMaps, true, samplingMode, revokeBlobURL, revokeBlobURL);
  1112. if (sampler.wrapS !== undefined) {
  1113. newTexture.wrapU = GLTFUtils.GetWrapMode(sampler.wrapS);
  1114. }
  1115. if (sampler.wrapT !== undefined) {
  1116. newTexture.wrapV = GLTFUtils.GetWrapMode(sampler.wrapT);
  1117. }
  1118. newTexture.name = id;
  1119. texture.babylonTexture = newTexture;
  1120. onSuccess(newTexture);
  1121. }
  1122. public static LoadShaderStringAsync(gltfRuntime: IGLTFRuntime, id: string, onSuccess: (shaderString: string | ArrayBuffer) => void, onError?: (message: string) => void): void {
  1123. var shader: IGLTFShader = gltfRuntime.shaders[id];
  1124. if (Tools.IsBase64(shader.uri)) {
  1125. var shaderString = atob(shader.uri.split(",")[1]);
  1126. if (onSuccess) {
  1127. onSuccess(shaderString);
  1128. }
  1129. }
  1130. else {
  1131. Tools.LoadFile(gltfRuntime.rootUrl + shader.uri, onSuccess, undefined, undefined, false, request => {
  1132. if (request && onError) {
  1133. onError(request.status + " " + request.statusText);
  1134. }
  1135. });
  1136. }
  1137. }
  1138. public static LoadMaterialAsync(gltfRuntime: IGLTFRuntime, id: string, onSuccess: (material: Material) => void, onError: (message: string) => void): void {
  1139. var material: IGLTFMaterial = gltfRuntime.materials[id];
  1140. if (!material.technique) {
  1141. if (onError) {
  1142. onError("No technique found.");
  1143. }
  1144. return;
  1145. }
  1146. var technique: IGLTFTechnique = gltfRuntime.techniques[material.technique];
  1147. if (!technique) {
  1148. var defaultMaterial = new StandardMaterial(id, gltfRuntime.scene);
  1149. defaultMaterial.diffuseColor = new Color3(0.5, 0.5, 0.5);
  1150. defaultMaterial.sideOrientation = Material.CounterClockWiseSideOrientation;
  1151. onSuccess(defaultMaterial);
  1152. return;
  1153. }
  1154. var program: IGLTFProgram = gltfRuntime.programs[technique.program];
  1155. var states: IGLTFTechniqueStates = technique.states;
  1156. var vertexShader: string = Effect.ShadersStore[program.vertexShader + "VertexShader"];
  1157. var pixelShader: string = Effect.ShadersStore[program.fragmentShader + "PixelShader"];
  1158. var newVertexShader = "";
  1159. var newPixelShader = "";
  1160. var vertexTokenizer = new Tokenizer(vertexShader);
  1161. var pixelTokenizer = new Tokenizer(pixelShader);
  1162. var unTreatedUniforms: { [key: string]: IGLTFTechniqueParameter } = {};
  1163. var uniforms = [];
  1164. var attributes = [];
  1165. var samplers = [];
  1166. // Fill uniform, sampler2D and attributes
  1167. for (var unif in technique.uniforms) {
  1168. var uniform = technique.uniforms[unif];
  1169. var uniformParameter: IGLTFTechniqueParameter = technique.parameters[uniform];
  1170. unTreatedUniforms[unif] = uniformParameter;
  1171. if (uniformParameter.semantic && !uniformParameter.node && !uniformParameter.source) {
  1172. var transformIndex = glTFTransforms.indexOf(uniformParameter.semantic);
  1173. if (transformIndex !== -1) {
  1174. uniforms.push(babylonTransforms[transformIndex]);
  1175. delete unTreatedUniforms[unif];
  1176. }
  1177. else {
  1178. uniforms.push(unif);
  1179. }
  1180. }
  1181. else if (uniformParameter.type === EParameterType.SAMPLER_2D) {
  1182. samplers.push(unif);
  1183. }
  1184. else {
  1185. uniforms.push(unif);
  1186. }
  1187. }
  1188. for (var attr in technique.attributes) {
  1189. var attribute = technique.attributes[attr];
  1190. var attributeParameter: IGLTFTechniqueParameter = technique.parameters[attribute];
  1191. if (attributeParameter.semantic) {
  1192. attributes.push(getAttribute(attributeParameter));
  1193. }
  1194. }
  1195. // Configure vertex shader
  1196. while (!vertexTokenizer.isEnd() && vertexTokenizer.getNextToken()) {
  1197. var tokenType = vertexTokenizer.currentToken;
  1198. if (tokenType !== ETokenType.IDENTIFIER) {
  1199. newVertexShader += vertexTokenizer.currentString;
  1200. continue;
  1201. }
  1202. var foundAttribute = false;
  1203. for (var attr in technique.attributes) {
  1204. var attribute = technique.attributes[attr];
  1205. var attributeParameter: IGLTFTechniqueParameter = technique.parameters[attribute];
  1206. if (vertexTokenizer.currentIdentifier === attr && attributeParameter.semantic) {
  1207. newVertexShader += getAttribute(attributeParameter);
  1208. foundAttribute = true;
  1209. break;
  1210. }
  1211. }
  1212. if (foundAttribute) {
  1213. continue;
  1214. }
  1215. newVertexShader += parseShaderUniforms(vertexTokenizer, technique, unTreatedUniforms);
  1216. }
  1217. // Configure pixel shader
  1218. while (!pixelTokenizer.isEnd() && pixelTokenizer.getNextToken()) {
  1219. var tokenType = pixelTokenizer.currentToken;
  1220. if (tokenType !== ETokenType.IDENTIFIER) {
  1221. newPixelShader += pixelTokenizer.currentString;
  1222. continue;
  1223. }
  1224. newPixelShader += parseShaderUniforms(pixelTokenizer, technique, unTreatedUniforms);
  1225. }
  1226. // Create shader material
  1227. var shaderPath = {
  1228. vertex: program.vertexShader + id,
  1229. fragment: program.fragmentShader + id
  1230. };
  1231. var options = {
  1232. attributes: attributes,
  1233. uniforms: uniforms,
  1234. samplers: samplers,
  1235. needAlphaBlending: states && states.enable && states.enable.indexOf(3042) !== -1
  1236. };
  1237. Effect.ShadersStore[program.vertexShader + id + "VertexShader"] = newVertexShader;
  1238. Effect.ShadersStore[program.fragmentShader + id + "PixelShader"] = newPixelShader;
  1239. var shaderMaterial = new ShaderMaterial(id, gltfRuntime.scene, shaderPath, options);
  1240. shaderMaterial.onError = onShaderCompileError(program, shaderMaterial, onError);
  1241. shaderMaterial.onCompiled = onShaderCompileSuccess(gltfRuntime, shaderMaterial, technique, material, unTreatedUniforms, onSuccess);
  1242. shaderMaterial.sideOrientation = Material.CounterClockWiseSideOrientation;
  1243. if (states && states.functions) {
  1244. var functions = states.functions;
  1245. if (functions.cullFace && functions.cullFace[0] !== ECullingType.BACK) {
  1246. shaderMaterial.backFaceCulling = false;
  1247. }
  1248. var blendFunc = functions.blendFuncSeparate;
  1249. if (blendFunc) {
  1250. if (blendFunc[0] === EBlendingFunction.SRC_ALPHA && blendFunc[1] === EBlendingFunction.ONE_MINUS_SRC_ALPHA && blendFunc[2] === EBlendingFunction.ONE && blendFunc[3] === EBlendingFunction.ONE) {
  1251. shaderMaterial.alphaMode = Engine.ALPHA_COMBINE;
  1252. }
  1253. else if (blendFunc[0] === EBlendingFunction.ONE && blendFunc[1] === EBlendingFunction.ONE && blendFunc[2] === EBlendingFunction.ZERO && blendFunc[3] === EBlendingFunction.ONE) {
  1254. shaderMaterial.alphaMode = Engine.ALPHA_ONEONE;
  1255. }
  1256. else if (blendFunc[0] === EBlendingFunction.SRC_ALPHA && blendFunc[1] === EBlendingFunction.ONE && blendFunc[2] === EBlendingFunction.ZERO && blendFunc[3] === EBlendingFunction.ONE) {
  1257. shaderMaterial.alphaMode = Engine.ALPHA_ADD;
  1258. }
  1259. else if (blendFunc[0] === EBlendingFunction.ZERO && blendFunc[1] === EBlendingFunction.ONE_MINUS_SRC_COLOR && blendFunc[2] === EBlendingFunction.ONE && blendFunc[3] === EBlendingFunction.ONE) {
  1260. shaderMaterial.alphaMode = Engine.ALPHA_SUBTRACT;
  1261. }
  1262. else if (blendFunc[0] === EBlendingFunction.DST_COLOR && blendFunc[1] === EBlendingFunction.ZERO && blendFunc[2] === EBlendingFunction.ONE && blendFunc[3] === EBlendingFunction.ONE) {
  1263. shaderMaterial.alphaMode = Engine.ALPHA_MULTIPLY;
  1264. }
  1265. else if (blendFunc[0] === EBlendingFunction.SRC_ALPHA && blendFunc[1] === EBlendingFunction.ONE_MINUS_SRC_COLOR && blendFunc[2] === EBlendingFunction.ONE && blendFunc[3] === EBlendingFunction.ONE) {
  1266. shaderMaterial.alphaMode = Engine.ALPHA_MAXIMIZED;
  1267. }
  1268. }
  1269. }
  1270. }
  1271. }
  1272. /**
  1273. * glTF V1 Loader
  1274. */
  1275. export class GLTFLoader implements IGLTFLoader {
  1276. public static Extensions: { [name: string]: GLTFLoaderExtension } = {};
  1277. public static RegisterExtension(extension: GLTFLoaderExtension): void {
  1278. if (GLTFLoader.Extensions[extension.name]) {
  1279. Tools.Error("Tool with the same name \"" + extension.name + "\" already exists");
  1280. return;
  1281. }
  1282. GLTFLoader.Extensions[extension.name] = extension;
  1283. }
  1284. // #region Stubs for IGLTFLoader interface
  1285. public coordinateSystemMode = GLTFLoaderCoordinateSystemMode.AUTO;
  1286. public animationStartMode = GLTFLoaderAnimationStartMode.FIRST;
  1287. public compileMaterials = false;
  1288. public useClipPlane = false;
  1289. public compileShadowGenerators = false;
  1290. public onDisposeObservable = new Observable<IGLTFLoader>();
  1291. public onMeshLoadedObservable = new Observable<AbstractMesh>();
  1292. public onTextureLoadedObservable = new Observable<BaseTexture>();
  1293. public onMaterialLoadedObservable = new Observable<Material>();
  1294. public onCompleteObservable = new Observable<IGLTFLoader>();
  1295. public onExtensionLoadedObservable = new Observable<IGLTFLoaderExtension>();
  1296. /**
  1297. * State of the loader
  1298. */
  1299. public state: Nullable<GLTFLoaderState> = null;
  1300. public dispose(): void {}
  1301. // #endregion
  1302. private _importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: (meshes: AbstractMesh[], skeletons: Skeleton[]) => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string) => void): boolean {
  1303. scene.useRightHandedSystem = true;
  1304. GLTFLoaderExtension.LoadRuntimeAsync(scene, data, rootUrl, gltfRuntime => {
  1305. gltfRuntime.importOnlyMeshes = true;
  1306. if (meshesNames === "") {
  1307. gltfRuntime.importMeshesNames = [];
  1308. }
  1309. else if (typeof meshesNames === "string") {
  1310. gltfRuntime.importMeshesNames = [meshesNames];
  1311. }
  1312. else if (meshesNames && !(meshesNames instanceof Array)) {
  1313. gltfRuntime.importMeshesNames = [meshesNames];
  1314. }
  1315. else {
  1316. gltfRuntime.importMeshesNames = [];
  1317. Tools.Warn("Argument meshesNames must be of type string or string[]");
  1318. }
  1319. // Create nodes
  1320. this._createNodes(gltfRuntime);
  1321. var meshes = new Array<AbstractMesh>();
  1322. var skeletons = new Array<Skeleton>();
  1323. // Fill arrays of meshes and skeletons
  1324. for (var nde in gltfRuntime.nodes) {
  1325. var node: IGLTFNode = gltfRuntime.nodes[nde];
  1326. if (node.babylonNode instanceof AbstractMesh) {
  1327. meshes.push(<AbstractMesh>node.babylonNode);
  1328. }
  1329. }
  1330. for (var skl in gltfRuntime.skins) {
  1331. var skin: IGLTFSkins = gltfRuntime.skins[skl];
  1332. if (skin.babylonSkeleton instanceof Skeleton) {
  1333. skeletons.push(skin.babylonSkeleton);
  1334. }
  1335. }
  1336. // Load buffers, shaders, materials, etc.
  1337. this._loadBuffersAsync(gltfRuntime, () => {
  1338. this._loadShadersAsync(gltfRuntime, () => {
  1339. importMaterials(gltfRuntime);
  1340. postLoad(gltfRuntime);
  1341. if (!GLTFFileLoader.IncrementalLoading && onSuccess) {
  1342. onSuccess(meshes, skeletons);
  1343. }
  1344. });
  1345. }, onProgress);
  1346. if (GLTFFileLoader.IncrementalLoading && onSuccess) {
  1347. onSuccess(meshes, skeletons);
  1348. }
  1349. }, onError);
  1350. return true;
  1351. }
  1352. /**
  1353. * Imports one or more meshes from a loaded gltf file and adds them to the scene
  1354. * @param meshesNames a string or array of strings of the mesh names that should be loaded from the file
  1355. * @param scene the scene the meshes should be added to
  1356. * @param data gltf data containing information of the meshes in a loaded file
  1357. * @param rootUrl root url to load from
  1358. * @param onProgress event that fires when loading progress has occured
  1359. * @returns a promise containg the loaded meshes, particles, skeletons and animations
  1360. */
  1361. public importMeshAsync(meshesNames: any, scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<{ meshes: AbstractMesh[], particleSystems: ParticleSystem[], skeletons: Skeleton[], animationGroups: AnimationGroup[] }> {
  1362. return new Promise((resolve, reject) => {
  1363. this._importMeshAsync(meshesNames, scene, data, rootUrl, (meshes, skeletons) => {
  1364. resolve({
  1365. meshes: meshes,
  1366. particleSystems: [],
  1367. skeletons: skeletons,
  1368. animationGroups: []
  1369. });
  1370. }, onProgress, message => {
  1371. reject(new Error(message));
  1372. });
  1373. });
  1374. }
  1375. private _loadAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onSuccess: () => void, onProgress?: (event: SceneLoaderProgressEvent) => void, onError?: (message: string) => void): void {
  1376. scene.useRightHandedSystem = true;
  1377. GLTFLoaderExtension.LoadRuntimeAsync(scene, data, rootUrl, gltfRuntime => {
  1378. // Load runtime extensios
  1379. GLTFLoaderExtension.LoadRuntimeExtensionsAsync(gltfRuntime, () => {
  1380. // Create nodes
  1381. this._createNodes(gltfRuntime);
  1382. // Load buffers, shaders, materials, etc.
  1383. this._loadBuffersAsync(gltfRuntime, () => {
  1384. this._loadShadersAsync(gltfRuntime, () => {
  1385. importMaterials(gltfRuntime);
  1386. postLoad(gltfRuntime);
  1387. if (!GLTFFileLoader.IncrementalLoading) {
  1388. onSuccess();
  1389. }
  1390. });
  1391. });
  1392. if (GLTFFileLoader.IncrementalLoading) {
  1393. onSuccess();
  1394. }
  1395. }, onError);
  1396. }, onError);
  1397. }
  1398. /**
  1399. * Imports all objects from a loaded gltf file and adds them to the scene
  1400. * @param scene the scene the objects should be added to
  1401. * @param data gltf data containing information of the meshes in a loaded file
  1402. * @param rootUrl root url to load from
  1403. * @param onProgress event that fires when loading progress has occured
  1404. * @returns a promise which completes when objects have been loaded to the scene
  1405. */
  1406. public loadAsync(scene: Scene, data: IGLTFLoaderData, rootUrl: string, onProgress?: (event: SceneLoaderProgressEvent) => void): Promise<void> {
  1407. return new Promise((resolve, reject) => {
  1408. this._loadAsync(scene, data, rootUrl, () => {
  1409. resolve();
  1410. }, onProgress, message => {
  1411. reject(new Error(message));
  1412. });
  1413. });
  1414. }
  1415. private _loadShadersAsync(gltfRuntime: IGLTFRuntime, onload: () => void): void {
  1416. var hasShaders = false;
  1417. var processShader = (sha: string, shader: IGLTFShader) => {
  1418. GLTFLoaderExtension.LoadShaderStringAsync(gltfRuntime, sha, shaderString => {
  1419. if (shaderString instanceof ArrayBuffer) {
  1420. return;
  1421. }
  1422. gltfRuntime.loadedShaderCount++;
  1423. if (shaderString) {
  1424. Effect.ShadersStore[sha + (shader.type === EShaderType.VERTEX ? "VertexShader" : "PixelShader")] = shaderString;
  1425. }
  1426. if (gltfRuntime.loadedShaderCount === gltfRuntime.shaderscount) {
  1427. onload();
  1428. }
  1429. }, () => {
  1430. Tools.Error("Error when loading shader program named " + sha + " located at " + shader.uri);
  1431. });
  1432. };
  1433. for (var sha in gltfRuntime.shaders) {
  1434. hasShaders = true;
  1435. var shader: IGLTFShader = gltfRuntime.shaders[sha];
  1436. if (shader) {
  1437. processShader.bind(this, sha, shader)();
  1438. }
  1439. else {
  1440. Tools.Error("No shader named: " + sha);
  1441. }
  1442. }
  1443. if (!hasShaders) {
  1444. onload();
  1445. }
  1446. };
  1447. private _loadBuffersAsync(gltfRuntime: IGLTFRuntime, onLoad: () => void, onProgress?: (event: SceneLoaderProgressEvent) => void): void {
  1448. var hasBuffers = false;
  1449. var processBuffer = (buf: string, buffer: IGLTFBuffer) => {
  1450. GLTFLoaderExtension.LoadBufferAsync(gltfRuntime, buf, bufferView => {
  1451. gltfRuntime.loadedBufferCount++;
  1452. if (bufferView) {
  1453. if (bufferView.byteLength != gltfRuntime.buffers[buf].byteLength) {
  1454. Tools.Error("Buffer named " + buf + " is length " + bufferView.byteLength + ". Expected: " + buffer.byteLength); // Improve error message
  1455. }
  1456. gltfRuntime.loadedBufferViews[buf] = bufferView;
  1457. }
  1458. if (gltfRuntime.loadedBufferCount === gltfRuntime.buffersCount) {
  1459. onLoad();
  1460. }
  1461. }, () => {
  1462. Tools.Error("Error when loading buffer named " + buf + " located at " + buffer.uri);
  1463. });
  1464. };
  1465. for (var buf in gltfRuntime.buffers) {
  1466. hasBuffers = true;
  1467. var buffer: IGLTFBuffer = gltfRuntime.buffers[buf];
  1468. if (buffer) {
  1469. processBuffer.bind(this, buf, buffer)();
  1470. }
  1471. else {
  1472. Tools.Error("No buffer named: " + buf);
  1473. }
  1474. }
  1475. if (!hasBuffers) {
  1476. onLoad();
  1477. }
  1478. }
  1479. private _createNodes(gltfRuntime: IGLTFRuntime): void {
  1480. var currentScene = <IGLTFScene>gltfRuntime.currentScene;
  1481. if (currentScene) {
  1482. // Only one scene even if multiple scenes are defined
  1483. for (var i = 0; i < currentScene.nodes.length; i++) {
  1484. traverseNodes(gltfRuntime, currentScene.nodes[i], null);
  1485. }
  1486. }
  1487. else {
  1488. // Load all scenes
  1489. for (var thing in gltfRuntime.scenes) {
  1490. currentScene = <IGLTFScene>gltfRuntime.scenes[thing];
  1491. for (var i = 0; i < currentScene.nodes.length; i++) {
  1492. traverseNodes(gltfRuntime, currentScene.nodes[i], null);
  1493. }
  1494. }
  1495. }
  1496. }
  1497. };
  1498. GLTFFileLoader.CreateGLTFLoaderV1 = () => new GLTFLoader();
  1499. }