2021-07-21 23:11:13 +08:00

1340 lines
62 KiB
JavaScript

(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));