(function (exports, Laya) { 'use strict'; class glTFBase64Tool { constructor() { } static init() { if (glTFBase64Tool.lookup) return; glTFBase64Tool.lookup = new Uint8Array(256); for (var i = 0; i < glTFBase64Tool.chars.length; i++) { glTFBase64Tool.lookup[glTFBase64Tool.chars.charCodeAt(i)] = i; } } static isBase64String(str) { return glTFBase64Tool.reg.test(str); } static encode(arraybuffer) { var bytes = new Uint8Array(arraybuffer), i, len = bytes["length"], base64 = ""; for (i = 0; i < len; i += 3) { base64 += glTFBase64Tool.chars[bytes[i] >> 2]; base64 += glTFBase64Tool.chars[((bytes[i] & 3) << 4) | (bytes[i + 1] >> 4)]; base64 += glTFBase64Tool.chars[((bytes[i + 1] & 15) << 2) | (bytes[i + 2] >> 6)]; base64 += glTFBase64Tool.chars[bytes[i + 2] & 63]; } if ((len % 3) === 2) { base64 = base64.substring(0, base64.length - 1) + "="; } else if (len % 3 === 1) { base64 = base64.substring(0, base64.length - 2) + "=="; } return base64; } static decode(base64) { glTFBase64Tool.init(); var bufferLength = base64.length * 0.75, len = base64.length, i, p = 0, encoded1, encoded2, encoded3, encoded4; if (base64[base64.length - 1] === "=") { bufferLength--; if (base64[base64.length - 2] === "=") { bufferLength--; } } var arraybuffer = new ArrayBuffer(bufferLength), bytes = new Uint8Array(arraybuffer); for (i = 0; i < len; i += 4) { encoded1 = glTFBase64Tool.lookup[base64.charCodeAt(i)]; encoded2 = glTFBase64Tool.lookup[base64.charCodeAt(i + 1)]; encoded3 = glTFBase64Tool.lookup[base64.charCodeAt(i + 2)]; encoded4 = glTFBase64Tool.lookup[base64.charCodeAt(i + 3)]; bytes[p++] = (encoded1 << 2) | (encoded2 >> 4); bytes[p++] = ((encoded2 & 15) << 4) | (encoded3 >> 2); bytes[p++] = ((encoded3 & 3) << 6) | (encoded4 & 63); } return arraybuffer; } ; } glTFBase64Tool.chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; glTFBase64Tool.reg = /^\s*data:([a-z]+\/[a-z0-9-+.]+(;[a-z-]+=[a-z0-9-]+)?)?(;base64)?,([a-z0-9!$&',()*+;=\-._~:@\/?%\s]*?)\s*$/i; glTFBase64Tool.reghead = /^\s*data:([a-z]+\/[a-z0-9-+.]+(;[a-z-]+=[a-z0-9-]+)?)?(;base64)?,/i; glTFBase64Tool.lookup = null; class glTFTextureEditor { static PixelArrayToBase64(pixelArray, width, height) { let clampedArray = new Uint8ClampedArray(pixelArray); let imageData = new ImageData(clampedArray, width, height); let canvas = new Laya.HTMLCanvas(true); let ctx = canvas.source.getContext("2d"); canvas.source.width = width; canvas.source.height = height; ctx.putImageData(imageData, 0, 0); let base64 = canvas.source.toDataURL(); return base64; } static GenerateTexture2DWithPixel(pixelArray, width, height, format, mipmap) { let tex = new Laya.Texture2D(width, height, format, mipmap, true); tex.setPixels(pixelArray); if (mipmap) { let gl = Laya.LayaGL.instance; Laya.WebGLContext.bindTexture(gl, tex._glTextureType, tex._glTexture); gl.generateMipmap(tex._glTextureType); Laya.WebGLContext.bindTexture(gl, tex._glTextureType, null); } return tex; } static glTFOcclusionTrans(glTFOcclusion) { let gltfTexPixels = glTFOcclusion.getPixels(); let layaTexPixels = new Uint8Array(gltfTexPixels.length); let pixelCount = gltfTexPixels.length / 4; let r = 0, g = 1; for (let index = 0; index < pixelCount; index++) { let offset = index * 4; let occlusion = gltfTexPixels[offset + r]; layaTexPixels[offset + g] = occlusion; } let layaTex = glTFTextureEditor.GenerateTexture2DWithPixel(layaTexPixels, glTFOcclusion.width, glTFOcclusion.height, Laya.TextureFormat.R8G8B8A8, glTFOcclusion.mipmap); return layaTex; } static glTFMetallicGlossTrans(glTFMetallicGloss, metallicFactor, roughnessFactor) { let gltfTexPixels = glTFMetallicGloss.getPixels(); let layaTexPixels = new Uint8Array(gltfTexPixels.length); let pixelCount = glTFMetallicGloss.width * glTFMetallicGloss.height; let r = 0, g = 1, b = 2, a = 3; for (let index = 0; index < pixelCount; index++) { let offset = index * 4; let metallic = gltfTexPixels[offset + b] * metallicFactor; let smooth = 255 - (gltfTexPixels[offset + g] * roughnessFactor); layaTexPixels[offset + r] = metallic; layaTexPixels[offset + a] = smooth; } let layaTex = glTFTextureEditor.GenerateTexture2DWithPixel(layaTexPixels, glTFMetallicGloss.width, glTFMetallicGloss.height, Laya.TextureFormat.R8G8B8A8, glTFMetallicGloss.mipmap); return layaTex; } } class PrimitiveSubMesh { constructor() { } } class ClipNode { constructor() { } } class glTFUtils { static _parse(glTFData, propertyParams = null, constructParams = null) { glTFUtils._initData(glTFData); if (!glTFUtils._checkglTFVersion(this._glTF.asset)) { console.warn("glTF version wrong!"); return new Laya.Sprite3D(); } glTFUtils._initBufferData(glTFData.buffers); glTFUtils._initTextureData(glTFData.images); glTFUtils._loadMaterials(glTFData.materials); glTFUtils._loadNodes(glTFData.nodes); glTFUtils.buildHierarchy(glTFData.nodes); glTFUtils._loadScenes(glTFData.scenes); glTFUtils._loadAnimations(glTFData.animations); let defaultSceneIndex = (glTFData.scene != undefined) ? glTFData.scene : 0; let defaultScene = glTFUtils._glTFScenes[defaultSceneIndex]; glTFUtils._clearData(); return defaultScene; } static _initData(glTFData) { glTFUtils._glTF = glTFData; (glTFData.buffers) && (glTFUtils._glTFBuffers.length = glTFData.buffers.length); (glTFData.textures) && (glTFUtils._glTFTextures.length = glTFData.textures.length); (glTFData.materials) && (glTFUtils._glTFMaterials.length = glTFData.materials.length); (glTFData.nodes) && (glTFUtils._glTFNodes.length = glTFData.nodes.length); (glTFData.scenes) && (glTFUtils._glTFScenes.length = glTFData.scenes.length); } static _clearData() { glTFUtils._glTF = null; glTFUtils._glTFBuffers.length = 0; glTFUtils._glTFTextures.length = 0; glTFUtils._glTFMaterials.length = 0; glTFUtils._glTFNodes.length = 0; glTFUtils._glTFScenes.length = 0; glTFUtils.NAMEID = 0; } static _checkglTFVersion(asset) { if (asset.version !== "2.0") { return false; } return true; } static RegisterExtra(context, extraName, handler) { let extra = glTFUtils.Extras[context] || (glTFUtils.Extras[context] = {}); extra[extraName] = handler; } static UnRegisterExtra(context, extraName, recoverHandler = true) { let extra = glTFUtils.Extras[context] || (glTFUtils.Extras[context] = {}); if (recoverHandler) { let extraHandler = extra[extraName]; extraHandler && extraHandler.recover(); } delete extra[extraName]; } static ExecuteExtras(context, glTFExtra, createHandler, params) { let contextExtra = glTFUtils.Extras[context]; let extraRes = null; if (contextExtra) { for (const key in glTFExtra) { let extraHandler = contextExtra[key]; extraRes = extraHandler ? extraHandler.runWith([...params, createHandler]) : extraRes; } } extraRes = extraRes || createHandler.runWith(params); createHandler.recover(); return extraRes; } static getNodeRandomName(context) { return `${context}_${glTFUtils.NAMEID++}`; } static _initBufferData(buffers) { if (!buffers) return; buffers.forEach((buffer, index) => { glTFUtils._glTFBuffers[index] = Laya.Loader.getRes(buffer.uri); }); } static getAccessorComponentsNum(type) { switch (type) { case "SCALAR": return 1; case "VEC2": return 2; case "VEC3": return 3; case "VEC4": return 4; case "MAT2": return 4; case "MAT3": return 9; case "MAT4": return 16; default: return 0; } } static getAttributeNum(attriStr) { switch (attriStr) { case "POSITION": return 3; case "NORMAL": return 3; case "COLOR": return 4; case "UV": return 2; case "UV1": return 2; case "BLENDWEIGHT": return 4; case "BLENDINDICES": return 4; case "TANGENT": return 4; default: return 0; } } static _getTypedArrayConstructor(componentType) { switch (componentType) { case 5120: return Int8Array; case 5121: return Uint8Array; case 5122: return Int16Array; case 5123: return Uint16Array; case 5125: return Uint32Array; case 5126: return Float32Array; } } static getBufferwithAccessorIndex(accessorIndex) { let accessor = glTFUtils._glTF.accessors[accessorIndex]; if (!accessor) return null; let bufferView = glTFUtils._glTF.bufferViews[accessor.bufferView]; let buffer = glTFUtils._glTFBuffers[bufferView.buffer]; let count = accessor.count; let contentStride = glTFUtils.getAccessorComponentsNum(accessor.type); let byteOffset = (bufferView.byteOffset || 0) + (accessor.byteOffset || 0); let byteLength = count * contentStride; const constructor = glTFUtils._getTypedArrayConstructor(accessor.componentType); return new constructor(buffer, byteOffset, byteLength); } static _initTextureData(images) { if (!images) return; images.forEach((image, index) => { glTFUtils._glTFTextures[index] = Laya.Loader.getRes(image.uri); }); } static getTextureMipmap(glTFSampler) { if (glTFSampler) return glTFSampler.minFilter === 9729 || glTFSampler.minFilter === 9728; else return true; } static getTextureFormat(glTFImage) { if (glTFImage.mimeType === "image/png") { return 1; } else { return 0; } } static getTextureFilterMode(glTFSampler) { if (!glTFSampler) { return 1; } if (glTFSampler.magFilter === 9728) { return 0; } else if (glTFUtils.getTextureMipmap(glTFSampler)) { if (glTFSampler.minFilter === 9987) return 2; return 1; } return 1; } static getTextureWrapMode(mode) { if (mode === 33071) { return 1; } return 0; } static getTextureConstructParams(glTFImage, glTFSampler) { let constructParams = []; constructParams[0] = 0; constructParams[1] = 0; constructParams[2] = glTFUtils.getTextureFormat(glTFImage); constructParams[3] = glTFUtils.getTextureMipmap(glTFSampler); constructParams[4] = true; return constructParams; } static getTexturePropertyParams(glTFSampler) { let propertyParams = {}; if (!glTFSampler) { return null; } propertyParams.filterMode = glTFUtils.getTextureFilterMode(glTFSampler); propertyParams.wrapModeU = glTFUtils.getTextureWrapMode(glTFSampler.wrapS); propertyParams.wrapModeV = glTFUtils.getTextureWrapMode(glTFSampler.wrapT); propertyParams.anisoLevel = 16; return propertyParams; } static getTexturewithInfo(glTFTextureInfo) { if (glTFTextureInfo.texCoord) { console.warn("glTF Loader: non 0 uv channel unsupported."); } let glTFImage = glTFUtils._glTF.textures[glTFTextureInfo.index]; return glTFUtils._glTFTextures[glTFImage.source]; } static _loadMaterials(glTFMaterials) { if (!glTFMaterials) return; glTFMaterials.forEach((glTFMaterial, index) => { glTFUtils._glTFMaterials[index] = glTFUtils._loadMaterial(glTFMaterial); }); } static _loadMaterial(glTFMaterial) { if (glTFMaterial.extras) { let createHandler = Laya.Handler.create(this, glTFUtils._createdefaultMaterial, null, false); return glTFUtils.ExecuteExtras("MATERIAL", glTFMaterial.extras, createHandler, [glTFMaterial]); } return glTFUtils._createdefaultMaterial(glTFMaterial); } static _createdefaultMaterial(glTFMaterial) { let layaPBRMaterial = new Laya.PBRStandardMaterial(); layaPBRMaterial.name = glTFMaterial.name ? glTFMaterial.name : ""; if (glTFMaterial.pbrMetallicRoughness) { glTFUtils.applyPBRMetallicRoughness(glTFMaterial.pbrMetallicRoughness, layaPBRMaterial); } if (glTFMaterial.normalTexture) { layaPBRMaterial.normalTexture = glTFUtils.getTexturewithInfo(glTFMaterial.normalTexture); if (glTFMaterial.normalTexture.scale != undefined) { layaPBRMaterial.normalTextureScale = glTFMaterial.normalTexture.scale; } } if (glTFMaterial.occlusionTexture) { let occlusionTexture = glTFUtils.getTexturewithInfo(glTFMaterial.occlusionTexture); layaPBRMaterial.occlusionTexture = glTFTextureEditor.glTFOcclusionTrans(occlusionTexture); if (glTFMaterial.occlusionTexture.strength != undefined) { layaPBRMaterial.occlusionTextureStrength = glTFMaterial.occlusionTexture.strength; } } if (glTFMaterial.emissiveTexture) { layaPBRMaterial.emissionTexture = glTFUtils.getTexturewithInfo(glTFMaterial.emissiveTexture); if (layaPBRMaterial.emissionTexture) { layaPBRMaterial.enableEmission = true; } } if (glTFMaterial.emissiveFactor) { layaPBRMaterial.emissionColor.fromArray(glTFMaterial.emissiveFactor); layaPBRMaterial.emissionColor.w = 1.0; layaPBRMaterial.enableEmission = true; } let renderMode = glTFMaterial.alphaMode || "OPAQUE"; switch (renderMode) { case "OPAQUE": { layaPBRMaterial.renderMode = Laya.PBRRenderMode.Opaque; break; } case "BLEND": { layaPBRMaterial.renderMode = Laya.PBRRenderMode.Transparent; break; } case "MASK": { layaPBRMaterial.renderMode = Laya.PBRRenderMode.Cutout; break; } } if (glTFMaterial.alphaCutoff != undefined) { layaPBRMaterial.alphaTestValue = glTFMaterial.alphaCutoff; } if (glTFMaterial.doubleSided) { layaPBRMaterial.cull = Laya.RenderState.CULL_NONE; } return layaPBRMaterial; } static applyPBRMetallicRoughness(pbrMetallicRoughness, layaPBRMaterial) { if (pbrMetallicRoughness.baseColorFactor) { layaPBRMaterial.albedoColor.fromArray(pbrMetallicRoughness.baseColorFactor); } if (pbrMetallicRoughness.baseColorTexture) { layaPBRMaterial.albedoTexture = glTFUtils.getTexturewithInfo(pbrMetallicRoughness.baseColorTexture); } let metallicFactor = layaPBRMaterial.metallic = 1.0; if (pbrMetallicRoughness.metallicFactor != undefined) { metallicFactor = layaPBRMaterial.metallic = pbrMetallicRoughness.metallicFactor; } let roughnessFactor = 1.0; layaPBRMaterial.smoothness = 0.0; if (pbrMetallicRoughness.roughnessFactor != undefined) { roughnessFactor = pbrMetallicRoughness.roughnessFactor; layaPBRMaterial.smoothness = 1.0 - pbrMetallicRoughness.roughnessFactor; } if (pbrMetallicRoughness.metallicRoughnessTexture) { let metallicGlossTexture = glTFUtils.getTexturewithInfo(pbrMetallicRoughness.metallicRoughnessTexture); layaPBRMaterial.metallicGlossTexture = glTFTextureEditor.glTFMetallicGlossTrans(metallicGlossTexture, metallicFactor, roughnessFactor); } } static pickMeshMaterials(glTFMesh) { let materials = []; glTFMesh.primitives.forEach(primitive => { if (primitive.material != undefined) { let material = glTFUtils._glTFMaterials[primitive.material]; materials.push(material); } else { let material = new Laya.PBRStandardMaterial(); materials.push(material); glTFUtils._glTFMaterials.push(material); primitive.material = glTFUtils._glTFMaterials.indexOf(material); } }); return materials; } static _loadScenes(glTFScenes) { if (!glTFScenes) return; glTFScenes.forEach((glTFScene, index) => { glTFUtils._glTFScenes[index] = glTFUtils._loadScene(glTFScene); }); } static _loadScene(glTFScene) { return glTFUtils._createSceneNode(glTFScene); } static _createSceneNode(glTFScene) { let glTFSceneNode = new Laya.Sprite3D(glTFScene.name || "glTF_Scene_node"); glTFScene.nodes.forEach(nodeIndex => { let sprite = glTFUtils._glTFNodes[nodeIndex]; glTFSceneNode.addChild(sprite); }); return glTFSceneNode; } static applyTransform(glTFNode, sprite) { if (glTFNode.matrix) { let localMatrix = sprite.transform.localMatrix; localMatrix.elements.set(glTFNode.matrix); sprite.transform.localMatrix = localMatrix; } else { let localPosition = sprite.transform.localPosition; let localRotation = sprite.transform.localRotation; let localScale = sprite.transform.localScale; glTFNode.translation && localPosition.fromArray(glTFNode.translation); glTFNode.rotation && localRotation.fromArray(glTFNode.rotation); glTFNode.scale && localScale.fromArray(glTFNode.scale); sprite.transform.localPosition = localPosition; sprite.transform.localRotation = localRotation; sprite.transform.localScale = localScale; } } static buildHierarchy(glTFNodes) { glTFNodes.forEach((glTFNode, index) => { let sprite = glTFUtils._glTFNodes[index]; if (glTFNode.children) { glTFNode.children.forEach((childIndex) => { let child = glTFUtils._glTFNodes[childIndex]; sprite.addChild(child); }); } }); glTFNodes.forEach((glTFNode, index) => { let sprite = glTFUtils._glTFNodes[index]; if (sprite instanceof Laya.SkinnedMeshSprite3D) { glTFUtils.fixSkinnedSprite(glTFNode, sprite); } }); } static _loadNodes(glTFNodes) { if (!glTFNodes) { return; } glTFNodes.forEach((glTFNode, index) => { glTFNode.name = glTFNode.name || glTFUtils.getNodeRandomName("node"); }); glTFNodes.forEach((glTFNode, index) => { glTFUtils._glTFNodes[index] = glTFUtils._loadNode(glTFNode); }); } static _loadNode(glTFNode) { return glTFUtils._createSprite3D(glTFNode); } static _createSprite3D(glTFNode) { glTFNode.name = glTFNode.name; if (glTFNode.skin != undefined) { let sprite = glTFUtils._createSkinnedMeshSprite3D(glTFNode); glTFUtils.applyTransform(glTFNode, sprite); return sprite; } else if (glTFNode.mesh != undefined) { let sprite = glTFUtils._createMeshSprite3D(glTFNode); glTFUtils.applyTransform(glTFNode, sprite); return sprite; } else { let sprite = new Laya.Sprite3D(glTFNode.name); glTFUtils.applyTransform(glTFNode, sprite); return sprite; } } static _createMeshSprite3D(glTFNode) { let glTFMesh = glTFUtils._glTF.meshes[glTFNode.mesh]; let mesh = glTFUtils._loadMesh(glTFMesh); let materials = glTFUtils.pickMeshMaterials(glTFMesh); let sprite = new Laya.MeshSprite3D(mesh, glTFNode.name); sprite.meshRenderer.sharedMaterials = materials; return sprite; } static _createSkinnedMeshSprite3D(glTFNode) { let glTFMesh = glTFUtils._glTF.meshes[glTFNode.mesh]; let glTFSkin = glTFUtils._glTF.skins[glTFNode.skin]; let mesh = glTFUtils._loadMesh(glTFMesh, glTFSkin); let materials = glTFUtils.pickMeshMaterials(glTFMesh); let sprite = new Laya.SkinnedMeshSprite3D(mesh, glTFNode.name); sprite.skinnedMeshRenderer.sharedMaterials = materials; return sprite; } static getArrributeBuffer(attributeAccessorIndex, layaDeclarStr, attributeMap, vertexDeclarArr) { let attributeBuffer = glTFUtils.getBufferwithAccessorIndex(attributeAccessorIndex); if (!attributeBuffer) return null; vertexDeclarArr.push(layaDeclarStr); let res = attributeBuffer; attributeMap.set(layaDeclarStr, res); return res; } static getIndexBuffer(attributeAccessorIndex, vertexCount) { let indexBuffer = glTFUtils.getBufferwithAccessorIndex(attributeAccessorIndex); if (indexBuffer) { return new Uint32Array(indexBuffer).reverse(); } else { let indices = new Uint32Array(vertexCount); for (let i = 0; i < vertexCount; i++) { indices[i] = i; } return indices; } } static parseMeshwithSubMeshData(subDatas, layaMesh) { let vertexCount = 0; let indexCount = 0; let vertexDecler = undefined; subDatas.forEach(subData => { vertexCount += subData.vertexCount; indexCount += subData.indices.length; vertexDecler = vertexDecler || subData.vertexDecler; }); let vertexDeclaration = Laya.VertexMesh.getVertexDeclaration(vertexDecler, false); let vertexByteStride = vertexDeclaration.vertexStride; let vertexFloatStride = vertexByteStride / 4; let vertexArray = new Float32Array(vertexFloatStride * vertexCount); let indexArray; let ibFormat = Laya.IndexFormat.UInt32; if (vertexCount < 65536) { indexArray = new Uint16Array(indexCount); ibFormat = Laya.IndexFormat.UInt16; } else { indexArray = new Uint32Array(indexCount); } glTFUtils.fillMeshBuffers(subDatas, vertexArray, indexArray, vertexFloatStride); glTFUtils.generatMesh(vertexArray, indexArray, vertexDeclaration, ibFormat, subDatas, layaMesh); } static fillMeshBuffers(subDatas, vertexArray, indexArray, vertexFloatStride) { let ibPosOffset = 0; let ibVertexOffset = 0; let vbPosOffset = 0; subDatas.forEach((subData) => { let iAOffset = ibPosOffset; let vertexCount = subData.vertexCount; let subIb = subData.indices; for (let index = 0; index < subIb.length; index++) { indexArray[iAOffset + index] = subIb[index] + ibVertexOffset; } ibPosOffset += subIb.length; ibVertexOffset += vertexCount; const fillAttributeBuffer = (value, attriOffset, attriFloatCount = 0) => { let startOffset = vbPosOffset + attriOffset; for (let index = 0; index < vertexCount; index++) { for (let ac = 0; ac < attriFloatCount; ac++) { vertexArray[startOffset + index * vertexFloatStride + ac] = value[index * attriFloatCount + ac]; } } }; let attriOffset = 0; let attributeMap = subData.attributeMap; let position = attributeMap.get("POSITION"); (position) && (fillAttributeBuffer(position, attriOffset, 3), attriOffset += 3); let normal = attributeMap.get("NORMAL"); (normal) && (fillAttributeBuffer(normal, attriOffset, 3), attriOffset += 3); let color = attributeMap.get("COLOR"); (color) && (fillAttributeBuffer(color, attriOffset, 4), attriOffset += 4); let uv = attributeMap.get("UV"); (uv) && (fillAttributeBuffer(uv, attriOffset, 2), attriOffset += 2); let uv1 = attributeMap.get("UV1"); (uv1) && (fillAttributeBuffer(uv1, attriOffset, 2), attriOffset += 2); let blendWeight = attributeMap.get("BLENDWEIGHT"); (blendWeight) && (fillAttributeBuffer(blendWeight, attriOffset, 4), attriOffset += 4); let blendIndices = attributeMap.get("BLENDINDICES"); if (blendIndices) { let blendIndicesUint8 = new Uint8Array(blendIndices); let blendIndicesFloat32 = new Float32Array(blendIndicesUint8.buffer); fillAttributeBuffer(blendIndicesFloat32, attriOffset, 1), attriOffset += 1; } let tangent = attributeMap.get("TANGENT"); (tangent) && (fillAttributeBuffer(tangent, attriOffset, 4), attriOffset += 4); vbPosOffset += vertexCount * vertexFloatStride; }); } static splitSubMeshByBonesCount(attributeMap, indexArray, boneIndicesList, subIndexStartArray, subIndexCountArray) { let maxSubBoneCount = glTFUtils.maxSubBoneCount; let start = 0; let subIndexSet = new Set(); let boneIndexArray = attributeMap.get("BLENDINDICES"); let vertexCount = boneIndexArray.length / 4; let resArray = new Float32Array(boneIndexArray.length); let flagArray = new Array(vertexCount).fill(false); for (let i = 0, n = indexArray.length; i < n; i += 3) { let triangleSet = new Set(); for (let j = i; j < i + 3; j++) { let ibIndex = indexArray[j]; let boneIndexOffset = ibIndex * 4; for (let k = 0; k < 4; k++) { triangleSet.add(boneIndexArray[boneIndexOffset + k]); } } let tempSet = new Set([...subIndexSet, ...triangleSet]); if (tempSet.size > maxSubBoneCount) { let count = i - start; subIndexStartArray.push(start); subIndexCountArray.push(count); let curBoneList = Array.from(subIndexSet); boneIndicesList.push(new Uint16Array(curBoneList)); start = i; subIndexSet = new Set(triangleSet); } else { subIndexSet = tempSet; } if (i == n - 3) { let count = i - start + 3; subIndexStartArray.push(start); subIndexCountArray.push(count); start = i; let curBoneList = Array.from(subIndexSet); boneIndicesList.push(new Uint16Array(curBoneList)); } } let drawCount = boneIndicesList.length; let newAttributeMap = new Map(); attributeMap.forEach((value, key) => { let array = new Array(); newAttributeMap.set(key, array); }); let curMaxIndex = vertexCount - 1; for (let d = 0; d < drawCount; d++) { let k = subIndexStartArray[d]; let l = subIndexCountArray[d]; let bl = boneIndicesList[d]; let batchFlag = new Array(vertexCount).fill(false); let batchMap = new Map(); for (let area = 0; area < l; area++) { let ci = indexArray[area + k]; let biStart = 4 * ci; for (let cbi = biStart; cbi < biStart + 4; cbi++) { let oldBoneIndex = boneIndexArray[cbi]; let newBoneIndex = bl.indexOf(oldBoneIndex); newBoneIndex = newBoneIndex == -1 ? 0 : newBoneIndex; if (flagArray[ci] && !batchFlag[ci]) { newAttributeMap.get("BLENDINDICES").push(newBoneIndex); } else if (flagArray[ci] && batchFlag[ci]) ; else { resArray[cbi] = newBoneIndex; } } if (!flagArray[ci] && !batchFlag[ci]) { batchFlag[ci] = true; batchMap.set(ci, ci); } else if (!flagArray[ci] && batchFlag[ci]) { indexArray[area + k] = batchMap.get(ci); } else if (flagArray[ci] && !batchFlag[ci]) { batchFlag[ci] = true; curMaxIndex++; batchMap.set(ci, curMaxIndex); indexArray[area + k] = curMaxIndex; newAttributeMap.forEach((value, key) => { let attOffset = glTFUtils.getAttributeNum(key); let oldArray = attributeMap.get(key); if (key !== "BLENDINDICES") { for (let index = 0; index < attOffset; index++) { value.push(oldArray[index + ci * attOffset]); } } }); } else if (flagArray[ci] && batchFlag[ci]) { indexArray[area + k] = batchMap.get(ci); } } batchFlag.forEach((value, index) => { flagArray[index] = value || flagArray[index]; }); } newAttributeMap.forEach((value, key) => { let oldFloatArray = attributeMap.get(key); if (key == "BLENDINDICES") { oldFloatArray = resArray; } let newLength = oldFloatArray.length + value.length; let newFloatArray = new Float32Array(newLength); newFloatArray.set(oldFloatArray, 0); newFloatArray.set(value, oldFloatArray.length); attributeMap.set(key, newFloatArray); }); boneIndexArray = null; } static generatMesh(vertexArray, indexArray, vertexDeclaration, ibFormat, subDatas, layaMesh) { let gl = Laya.LayaGL.instance; let vertexBuffer = new Laya.VertexBuffer3D(vertexArray.byteLength, gl.STATIC_DRAW, true); vertexBuffer.vertexDeclaration = vertexDeclaration; vertexBuffer.setData(vertexArray.buffer); let indexBuffer = new Laya.IndexBuffer3D(ibFormat, indexArray.length, gl.STATIC_DRAW, true); indexBuffer.setData(indexArray); layaMesh._indexFormat = ibFormat; layaMesh._indexBuffer = indexBuffer; layaMesh._vertexBuffer = vertexBuffer; layaMesh._setBuffer(vertexBuffer, indexBuffer); layaMesh._vertexCount = vertexBuffer._byteLength / vertexDeclaration.vertexStride; let subMeshOffset = 0; let subMeshCount = subDatas.length; let subMeshes = new Array(subMeshCount); for (let index = 0; index < subMeshCount; index++) { let subData = subDatas[index]; let subMesh = new Laya.SubMesh(layaMesh); subMeshes[index] = subMesh; subMesh._vertexBuffer = vertexBuffer; subMesh._indexBuffer = indexBuffer; let subIndexStart = subMeshOffset; subMeshOffset += subData.indices.length; let subIndexCount = subData.indices.length; subMesh._setIndexRange(subIndexStart, subIndexCount, ibFormat); subMesh._boneIndicesList = subData.boneIndicesList; subMesh._subIndexBufferStart = subData.subIndexStartArray; subMesh._subIndexBufferCount = subData.subIndexCountArray; for (let subIndex = 0; subIndex < subMesh._subIndexBufferStart.length; subIndex++) { subMesh._subIndexBufferStart[subIndex] += subIndexStart; } } layaMesh._setSubMeshes(subMeshes); layaMesh.calculateBounds(); layaMesh._setInstanceBuffer(Laya.Mesh.MESH_INSTANCEBUFFER_TYPE_NORMAL); let memorySize = vertexBuffer._byteLength + indexBuffer._byteLength; layaMesh._setCPUMemory(memorySize); layaMesh._setGPUMemory(memorySize); } static applyglTFSkinData(mesh, subDatas, glTFSkin) { if (!glTFSkin) return; let joints = glTFSkin.joints; let inverseBindMatricesArray = new Float32Array(glTFUtils.getBufferwithAccessorIndex(glTFSkin.inverseBindMatrices)); let boneCount = joints.length; let boneNames = mesh._boneNames = []; joints.forEach(nodeIndex => { let node = glTFUtils._glTF.nodes[nodeIndex]; boneNames.push(node.name); }); mesh._inverseBindPoses = []; mesh._inverseBindPosesBuffer = inverseBindMatricesArray.buffer; for (let index = 0; index < boneCount; index++) { let bindPosesArrayOffset = 16 * index; let matElement = inverseBindMatricesArray.slice(bindPosesArrayOffset, bindPosesArrayOffset + 16); mesh._inverseBindPoses[index] = new Laya.Matrix4x4(matElement[0], matElement[1], matElement[2], matElement[3], matElement[4], matElement[5], matElement[6], matElement[7], matElement[8], matElement[9], matElement[10], matElement[11], matElement[12], matElement[13], matElement[14], matElement[15], matElement); } let subCount = subDatas.length; let skinnedCache = mesh._skinnedMatrixCaches; skinnedCache.length = mesh._inverseBindPoses.length; for (let subIndex = 0; subIndex < subCount; subIndex++) { let submesh = mesh.getSubMesh(subIndex); let drawCount = submesh._subIndexBufferStart.length; for (let drawIndex = 0; drawIndex < drawCount; drawIndex++) { let boneIndices = submesh._boneIndicesList[drawIndex]; for (let bni = 0; bni < boneIndices.length; bni++) { let bn = boneIndices[bni]; skinnedCache[bn] || (skinnedCache[bn] = new Laya.skinnedMatrixCache(subIndex, drawIndex, bni)); } } } for (let index = 0; index < skinnedCache.length; index++) { if (!skinnedCache[index]) { skinnedCache[index] = new Laya.skinnedMatrixCache(0, 0, 0); } } } static _loadMesh(glTFMesh, glTFSkin) { if (glTFMesh.extras) { let createHandler = Laya.Handler.create(this, glTFUtils._createMesh, null, false); return glTFUtils.ExecuteExtras("MESH", glTFMesh.extras, createHandler, [glTFMesh, glTFSkin]); } let mesh = glTFUtils._createMesh(glTFMesh, glTFSkin); return mesh; } static _createMesh(glTFMesh, glTFSkin) { let layaMesh = new Laya.Mesh(); let glTFMeshPrimitives = glTFMesh.primitives; let morphWeights = glTFMesh.weights; let boneCount = (glTFSkin) ? glTFSkin.joints.length : 0; let subDatas = []; glTFMeshPrimitives.forEach((glTFMeshPrimitive) => { let mode = glTFMeshPrimitive.mode; if (mode == undefined) mode = 4; if (4 != mode) { console.warn("glTF Loader: only support gl.TRIANGLES."); debugger; } let vertexDeclarArr = []; let attributeMap = new Map(); let attributes = glTFMeshPrimitive.attributes; let position = glTFUtils.getArrributeBuffer(attributes.POSITION, "POSITION", attributeMap, vertexDeclarArr); let normal = glTFUtils.getArrributeBuffer(attributes.NORMAL, "NORMAL", attributeMap, vertexDeclarArr); let color = glTFUtils.getArrributeBuffer(attributes.COLOR_0, "COLOR", attributeMap, vertexDeclarArr); let uv = glTFUtils.getArrributeBuffer(attributes.TEXCOORD_0, "UV", attributeMap, vertexDeclarArr); let uv1 = glTFUtils.getArrributeBuffer(attributes.TEXCOORD_1, "UV1", attributeMap, vertexDeclarArr); let blendWeight = glTFUtils.getArrributeBuffer(attributes.WEIGHTS_0, "BLENDWEIGHT", attributeMap, vertexDeclarArr); let blendIndices = glTFUtils.getArrributeBuffer(attributes.JOINTS_0, "BLENDINDICES", attributeMap, vertexDeclarArr); let tangent = glTFUtils.getArrributeBuffer(attributes.TANGENT, "TANGENT", attributeMap, vertexDeclarArr); let targets = glTFMeshPrimitive.targets; (targets) && targets.forEach((target, index) => { let weight = morphWeights[index]; let morphPosition = glTFUtils.getBufferwithAccessorIndex(target.POSITION); let morphNormal = glTFUtils.getBufferwithAccessorIndex(target.NORMAL); let morphTangent = glTFUtils.getBufferwithAccessorIndex(target.TANGENT); (morphPosition) && morphPosition.forEach((value, index) => { position[index] += value * weight; }); (morphNormal) && morphNormal.forEach((value, index) => { normal[index] += value * weight; }); (morphTangent) && morphTangent.forEach((value, index) => { tangent[index] += value * weight; }); }); let vertexCount = position.length / 3; let indexArray = glTFUtils.getIndexBuffer(glTFMeshPrimitive.indices, vertexCount); let boneIndicesList = new Array(); let subIndexStartArray = []; let subIndexCountArray = []; if (glTFSkin) { if (boneCount > glTFUtils.maxSubBoneCount) { glTFUtils.splitSubMeshByBonesCount(attributeMap, indexArray, boneIndicesList, subIndexStartArray, subIndexCountArray); vertexCount = attributeMap.get("POSITION").length / 3; } else { subIndexStartArray[0] = 0; subIndexCountArray[0] = indexArray.length; boneIndicesList[0] = new Uint16Array(boneCount); for (let bi = 0; bi < boneCount; bi++) { boneIndicesList[0][bi] = bi; } } } else { subIndexStartArray[0] = 0; subIndexCountArray[0] = indexArray.length; } let vertexDeclaration = vertexDeclarArr.toString(); let subData = new PrimitiveSubMesh(); subDatas.push(subData); subData.attributeMap = attributeMap; subData.indices = indexArray; subData.vertexCount = vertexCount; subData.vertexDecler = vertexDeclaration; subData.boneIndicesList = boneIndicesList; subData.subIndexStartArray = subIndexStartArray; subData.subIndexCountArray = subIndexCountArray; }); glTFUtils.parseMeshwithSubMeshData(subDatas, layaMesh); glTFUtils.applyglTFSkinData(layaMesh, subDatas, glTFSkin); return layaMesh; } static calSkinnedSpriteLocalBounds(skinned) { let render = skinned.skinnedMeshRenderer; let mesh = skinned.meshFilter.sharedMesh; let rootBone = render.rootBone; let oriRootMatrix = rootBone.transform.worldMatrix; let invertRootMatrix = new Laya.Matrix4x4(); oriRootMatrix.invert(invertRootMatrix); let indices = mesh.getIndices(); let positions = []; let boneIndices = []; let boneWeights = []; mesh.getPositions(positions); mesh.getBoneIndices(boneIndices); mesh.getBoneWeights(boneWeights); let oriBoneIndeices = []; mesh._subMeshes.forEach((subMesh, index) => { let bonelists = subMesh._boneIndicesList; bonelists.forEach((bonelist, listIndex) => { let start = subMesh._subIndexBufferStart[listIndex]; let count = subMesh._subIndexBufferCount[listIndex]; let endIndex = count + start; for (let iindex = start; iindex < endIndex; iindex++) { let ii = indices[iindex]; let boneIndex = boneIndices[ii]; let x = bonelist[boneIndex.x]; let y = bonelist[boneIndex.y]; let z = bonelist[boneIndex.z]; let w = bonelist[boneIndex.w]; oriBoneIndeices[ii] = new Laya.Vector4(x, y, z, w); } }); }); let inverseBindPoses = mesh._inverseBindPoses; let bones = render.bones; let ubones = []; let tempMat = new Laya.Matrix4x4(); bones.forEach((bone, index) => { ubones[index] = new Laya.Matrix4x4(); Laya.Matrix4x4.multiply(invertRootMatrix, bone.transform.worldMatrix, tempMat); Laya.Matrix4x4.multiply(tempMat, inverseBindPoses[index], ubones[index]); }); let skinTransform = new Laya.Matrix4x4; let resPos = new Laya.Vector3(); let min = new Laya.Vector3(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE); let max = new Laya.Vector3(-Number.MAX_VALUE, -Number.MAX_VALUE, -Number.MAX_VALUE); for (let index = 0; index < positions.length; index++) { let pos = positions[index]; let boneIndex = oriBoneIndeices[index]; let boneWeight = boneWeights[index]; if (!(boneIndex && boneWeight)) { continue; } for (let ei = 0; ei < 16; ei++) { skinTransform.elements[ei] = ubones[boneIndex.x].elements[ei] * boneWeight.x; skinTransform.elements[ei] += ubones[boneIndex.y].elements[ei] * boneWeight.y; skinTransform.elements[ei] += ubones[boneIndex.z].elements[ei] * boneWeight.z; skinTransform.elements[ei] += ubones[boneIndex.w].elements[ei] * boneWeight.w; } Laya.Vector3.transformV3ToV3(pos, skinTransform, resPos); Laya.Vector3.min(min, resPos, min); Laya.Vector3.max(max, resPos, max); } positions = null; boneIndices = boneWeights = oriBoneIndeices = null; indices = null; ubones = null; render.localBounds.setMin(min); render.localBounds.setMax(max); } static fixSkinnedSprite(glTFNode, skinned) { let skin = glTFUtils._glTF.skins[glTFNode.skin]; let skinnedMeshRenderer = skinned.skinnedMeshRenderer; skin.joints.forEach(nodeIndex => { let bone = glTFUtils._glTFNodes[nodeIndex]; skinnedMeshRenderer.bones.push(bone); }); if (skin.skeleton == undefined) { skin.skeleton = skin.joints[0]; } skinnedMeshRenderer.rootBone = glTFUtils._glTFNodes[skin.skeleton]; glTFUtils.calSkinnedSpriteLocalBounds(skinned); } static getAnimationRoot(channels) { const isContainNode = (nodeArr, findNodeIndex) => { if (!nodeArr) return false; if (nodeArr.indexOf(findNodeIndex) == -1) { for (let index = 0; index < nodeArr.length; index++) { let glTFNode = glTFUtils._glTF.nodes[nodeArr[index]]; if (isContainNode(glTFNode.children, findNodeIndex)) { return true; } } } return true; }; let target = channels[0].target; let spriteIndex = target.node; for (let index = 0; index < glTFUtils._glTF.scenes.length; index++) { let glTFScene = glTFUtils._glTF.scenes[index]; if (isContainNode(glTFScene.nodes, spriteIndex)) { return glTFUtils._glTFScenes[index]; } } return null; } static getAnimationPath(root, curSprite) { let paths = []; if (root == curSprite) return paths; let sprite = curSprite; while (sprite.parent != root) { sprite = sprite.parent; paths.push(sprite.name); } paths = paths.reverse(); paths.push(curSprite.name); return paths; } static _loadAnimations(animations) { if (!animations) return; animations.forEach((animation, index) => { glTFUtils._loadAnimation(animation); }); } static _loadAnimation(animation) { return glTFUtils._createAnimator(animation); } static _createAnimator(animation) { let channels = animation.channels; let samplers = animation.samplers; let animatorRoot = glTFUtils.getAnimationRoot(channels); if (!animatorRoot) { return null; } let animator = animatorRoot.getComponent(Laya.Animator); if (!animator) { animator = animatorRoot.addComponent(Laya.Animator); let animatorLayer = new Laya.AnimatorControllerLayer("glTF_AnimatorLayer"); animator.addControllerLayer(animatorLayer); animatorLayer.defaultWeight = 1.0; } let clip = glTFUtils._createAnimatorClip(animation, animatorRoot); let animatorLayer = animator.getControllerLayer(); let animationName = clip.name; let stateMap = animatorLayer._statesMap; if (stateMap[animationName]) { animationName = clip.name = glTFUtils.getNodeRandomName(animationName); } let animatorState = new Laya.AnimatorState(); animatorState.name = animationName; animatorState.clip = clip; animatorLayer.addState(animatorState); animatorLayer.defaultState = animatorState; animatorLayer.playOnWake = true; return animator; } static _createAnimatorClip(animation, animatorRoot) { let clip = new Laya.AnimationClip(); let duration = 0; let channels = animation.channels; let samplers = animation.samplers; let clipNodes = new Array(channels.length); channels.forEach((channel, index) => { let target = channel.target; let sampler = samplers[channel.sampler]; let clipNode = clipNodes[index] = new ClipNode(); let sprite = glTFUtils._glTFNodes[target.node]; let timeBuffer = glTFUtils.getBufferwithAccessorIndex(sampler.input); let outBuffer = glTFUtils.getBufferwithAccessorIndex(sampler.output); clipNode.timeArray = new Float32Array(timeBuffer); clipNode.valueArray = new Float32Array(outBuffer); clipNode.paths = glTFUtils.getAnimationPath(animatorRoot, sprite); clipNode.propertyOwner = "transform"; clipNode.propertyLength = 1; clipNode.propertise = []; let targetPath = target.path; switch (targetPath) { case "translation": clipNode.propertise.push("localPosition"); clipNode.type = 1; break; case "rotation": clipNode.propertise.push("localRotation"); clipNode.type = 2; break; case "scale": clipNode.propertise.push("localScale"); clipNode.type = 3; break; } clipNode.duration = clipNode.timeArray[clipNode.timeArray.length - 1]; duration = Math.max(duration, clipNode.duration); }); clip.name = animation.name ? animation.name : glTFUtils.getNodeRandomName("glTF_Animation"); clip._duration = duration; clip.islooping = true; clip._frameRate = 30; let nodeCount = clipNodes.length; let nodes = clip._nodes; nodes.count = nodeCount; let nodesMap = clip._nodesMap = {}; let nodesDic = clip._nodesDic = {}; for (let i = 0; i < nodeCount; i++) { let node = new Laya.KeyframeNode(); let gLTFClipNode = clipNodes[i]; nodes.setNodeByIndex(i, node); node._indexInList = i; let type = node.type = gLTFClipNode.type; let pathLength = gLTFClipNode.paths.length; node._setOwnerPathCount(pathLength); let tempPath = gLTFClipNode.paths; for (let j = 0; j < pathLength; j++) { node._setOwnerPathByIndex(j, tempPath[j]); } let nodePath = node._joinOwnerPath("/"); let mapArray = nodesMap[nodePath]; (mapArray) || (nodesMap[nodePath] = mapArray = []); mapArray.push(node); node.propertyOwner = gLTFClipNode.propertyOwner; let propertyLength = gLTFClipNode.propertyLength; node._setPropertyCount(propertyLength); for (let j = 0; j < propertyLength; j++) { node._setPropertyByIndex(j, gLTFClipNode.propertise[j]); } let fullPath = nodePath + "." + node.propertyOwner + "." + node._joinProperty("."); nodesDic[fullPath] = fullPath; node.fullPath = fullPath; let keyframeCount = gLTFClipNode.timeArray.length; for (let j = 0; j < keyframeCount; j++) { switch (type) { case 0: break; case 1: case 3: case 4: let floatArrayKeyframe = new Laya.Vector3Keyframe(); node._setKeyframeByIndex(j, floatArrayKeyframe); let startTimev3 = floatArrayKeyframe.time = gLTFClipNode.timeArray[j]; let inTangent = floatArrayKeyframe.inTangent; let outTangent = floatArrayKeyframe.outTangent; let value = floatArrayKeyframe.value; inTangent.setValue(0, 0, 0); outTangent.setValue(0, 0, 0); value.setValue(gLTFClipNode.valueArray[3 * j], gLTFClipNode.valueArray[3 * j + 1], gLTFClipNode.valueArray[3 * j + 2]); break; case 2: let quaternionKeyframe = new Laya.QuaternionKeyframe(); node._setKeyframeByIndex(j, quaternionKeyframe); let startTimeQu = quaternionKeyframe.time = gLTFClipNode.timeArray[j]; let inTangentQua = quaternionKeyframe.inTangent; let outTangentQua = quaternionKeyframe.outTangent; let valueQua = quaternionKeyframe.value; inTangentQua.setValue(0, 0, 0, 0); outTangentQua.setValue(0, 0, 0, 0); valueQua.x = gLTFClipNode.valueArray[4 * j]; valueQua.y = gLTFClipNode.valueArray[4 * j + 1]; valueQua.z = gLTFClipNode.valueArray[4 * j + 2]; valueQua.w = gLTFClipNode.valueArray[4 * j + 3]; break; } } } return clip; } } glTFUtils.NAMEID = 0; glTFUtils.maxSubBoneCount = 24; glTFUtils.Extensions = {}; glTFUtils.Extras = {}; glTFUtils._glTFBuffers = []; glTFUtils._glTFTextures = []; glTFUtils._glTFMaterials = []; glTFUtils._glTFNodes = []; glTFUtils._glTFScenes = []; class glTFLoader { static init() { let createMap = Laya.LoaderManager.createMap; createMap["gltf"] = [glTFLoader.GLTF, glTFUtils._parse]; let parseMap = Laya.Loader.parserMap; parseMap[glTFLoader.GLTF] = glTFLoader._loadglTF; parseMap[glTFLoader.GLTFBASE64TEX] = glTFLoader._loadBase64Texture; glTFLoader._innerBufferLoaderManager.on(Laya.Event.ERROR, null, glTFLoader._eventLoadManagerError); glTFLoader._innerTextureLoaderManager.on(Laya.Event.ERROR, null, glTFLoader._eventLoadManagerError); } static _loadglTF(loader) { loader._originType = loader.type; loader.on(Laya.Event.LOADED, null, glTFLoader._onglTFLoaded, [loader]); loader.load(loader.url, Laya.Loader.JSON, false, null, true); } static _loadBase64Texture(loader) { let url = loader.url; let type = "nativeimage"; loader.on(Laya.Event.LOADED, null, function (image) { loader._cache = loader._createCache; let tex = Laya.Texture2D._parse(image, loader._propertyParams, loader._constructParams); glTFLoader._endLoad(loader, tex); }); loader.load(url, type, false, null, true); } static formatRelativePath(base, value) { let path; path = base + value; let char1 = value.charAt(0); if (char1 === ".") { let parts = path.split("/"); for (let i = 0, len = parts.length; i < len; i++) { if (parts[i] == '..') { let index = i - 1; if (index > 0 && parts[index] !== '..') { parts.splice(index, 2); i -= 2; } } } path = parts.join('/'); } return path; } static _addglTFInnerUrls(urls, urlMap, urlVersion, glTFBasePath, path, type, constructParams = null, propertyParams = null) { let formatUrl = glTFLoader.formatRelativePath(glTFBasePath, path); (urlVersion) && (formatUrl = formatUrl + urlVersion); urls.push({ url: formatUrl, type: type, constructParams: constructParams, propertyParams: propertyParams }); (urlMap) && (urlMap.push(formatUrl)); return formatUrl; } static _getglTFInnerUrlsCount(glTFData) { let urlCount = 0; if (glTFData.buffers) { glTFData.buffers.forEach(buffer => { if (glTFBase64Tool.isBase64String(buffer.uri)) ; else { urlCount++; } }); } if (glTFData.textures) { urlCount += glTFData.textures.length; } return urlCount; } static _getglTFBufferUrls(glTFData, bufferUrls, urlVersion, glTFBasePath) { if (glTFData.buffers) { glTFData.buffers.forEach(buffer => { if (glTFBase64Tool.isBase64String(buffer.uri)) { let bin = glTFBase64Tool.decode(buffer.uri.replace(glTFBase64Tool.reghead, "")); Laya.Loader.cacheRes(buffer.uri, bin); } else { buffer.uri = glTFLoader._addglTFInnerUrls(bufferUrls, null, urlVersion, glTFBasePath, buffer.uri, Laya.Loader.BUFFER); } }); } } static _getglTFTextureUrls(glTFData, textureUrls, subUrls, urlVersion, glTFBasePath) { if (glTFData.textures) { glTFData.textures.forEach(glTFTexture => { let glTFImage = glTFData.images[glTFTexture.source]; let glTFSampler = glTFData.samplers ? glTFData.samplers[glTFTexture.sampler] : undefined; let constructParams = glTFUtils.getTextureConstructParams(glTFImage, glTFSampler); let propertyParams = glTFUtils.getTexturePropertyParams(glTFSampler); if (glTFImage.bufferView != undefined || glTFImage.bufferView != null) { let bufferView = glTFData.bufferViews[glTFImage.bufferView]; let glTFBuffer = glTFData.buffers[bufferView.buffer]; let buffer = Laya.Loader.getRes(glTFBuffer.uri); let byteOffset = (bufferView.byteOffset || 0); let byteLength = bufferView.byteLength; let arraybuffer = buffer.slice(byteOffset, byteOffset + byteLength); let base64 = glTFBase64Tool.encode(arraybuffer); let base64url = `data:${glTFImage.mimeType};base64,${base64}`; glTFImage.uri = glTFLoader._addglTFInnerUrls(textureUrls, subUrls, urlVersion, "", base64url, glTFLoader.GLTFBASE64TEX, constructParams, propertyParams); } else if (glTFBase64Tool.isBase64String(glTFImage.uri)) { glTFImage.uri = glTFLoader._addglTFInnerUrls(textureUrls, subUrls, urlVersion, "", glTFImage.uri, glTFLoader.GLTFBASE64TEX, constructParams, propertyParams); } else { glTFImage.uri = glTFLoader._addglTFInnerUrls(textureUrls, subUrls, urlVersion, glTFBasePath, glTFImage.uri, Laya.Loader.TEXTURE2D, constructParams, propertyParams); } }); } } static _onglTFLoaded(loader, glTFData) { let url = loader.url; let urlVersion = Laya.Utils3D.getURLVerion(url); let glTFBasePath = Laya.URL.getPath(url); let bufferUrls = []; glTFLoader._getglTFBufferUrls(glTFData, bufferUrls, urlVersion, glTFBasePath); let urlCount = glTFLoader._getglTFInnerUrlsCount(glTFData); let totalProcessCount = urlCount + 1; let weight = 1 / totalProcessCount; glTFLoader._onProcessChange(loader, 0, weight, 1.0); let processCeil = urlCount / totalProcessCount; if (bufferUrls.length > 0) { let processHandler = Laya.Handler.create(null, glTFLoader._onProcessChange, [loader, weight, processCeil], false); glTFLoader._innerBufferLoaderManager._create(bufferUrls, false, Laya.Handler.create(null, glTFLoader._onglTFBufferResourceLoaded, [loader, processHandler, glTFData, urlVersion, glTFBasePath, weight + processCeil * bufferUrls.length, processCeil]), processHandler, null, null, null, 1, true); } else { glTFLoader._onglTFBufferResourceLoaded(loader, null, glTFData, urlVersion, glTFBasePath, weight, processCeil); } } static _onglTFBufferResourceLoaded(loader, processHandler, glTFData, urlVersion, glTFBasePath, processOffset, processCeil) { (processHandler) && (processHandler.recover()); let textureUrls = []; let subUrls = []; glTFLoader._getglTFTextureUrls(glTFData, textureUrls, subUrls, urlVersion, glTFBasePath); if (textureUrls.length > 0) { let process = Laya.Handler.create(null, glTFLoader._onProcessChange, [loader, processOffset, processCeil], false); glTFLoader._innerTextureLoaderManager._create(textureUrls, false, Laya.Handler.create(null, glTFLoader._onglTFTextureResourceLoaded, [loader, process, glTFData, subUrls]), processHandler, null, null, null, 1, true); } else { glTFLoader._onglTFTextureResourceLoaded(loader, processHandler, glTFData, subUrls); } } static _onglTFTextureResourceLoaded(loader, processHandler, glTFData, subUrls) { (processHandler) && (processHandler.recover()); loader._cache = loader._createCache; let item = glTFUtils._parse(glTFData, loader._propertyParams, loader._constructParams); glTFLoader._endLoad(loader, item, subUrls); } static _eventLoadManagerError(msg) { Laya.Laya.loader.event(Laya.Event.ERROR, msg); } static _onProcessChange(loader, offset, weight, process) { process = offset + process * weight; (process < 1.0) && (loader.event(Laya.Event.PROGRESS, process * 2 / 3 + 1 / 3)); } static _endLoad(loader, content = null, subResource = null) { if (subResource) { for (let i = 0; i < subResource.length; i++) { let resource = Laya.Loader.getRes(subResource[i]); (resource) && (resource._removeReference()); } } loader.endLoad(content); } } glTFLoader.GLTF = "GLTF"; glTFLoader.GLTFBASE64TEX = "GLTFBASE64TEX"; glTFLoader._innerBufferLoaderManager = new Laya.LoaderManager(); glTFLoader._innerTextureLoaderManager = new Laya.LoaderManager(); exports.glTFBase64Tool = glTFBase64Tool; exports.glTFLoader = glTFLoader; exports.glTFTextureEditor = glTFTextureEditor; exports.glTFUtils = glTFUtils; }(window.Laya = window.Laya || {}, Laya));