import { EventDispatcher } from "../../2d/events";
import { GlobalLoader } from "../../2d/loader";
import { rgb2hex, getEnv, decodeText, ArrayBufferToBase64 } from "../../2d/utils";
import { LightMaterial } from "../materials/LightMaterial";
import { Geometry } from "../Geometry";
import { Texture } from "../../2d/texture";
import { Scene3D } from "../Scene3D";
import { Object3D } from "../Object3D";
import { BaseMaterial, BaseMaterialParamsInt, RenderSideType } from "../materials/BaseMaterial";
import { Mesh3D } from "../Mesh3D";
import { PerspectiveCamera } from "../cameras/PerspectiveCamera";
import { RAD_TO_DEG } from "../../2d/const";
import { Matrix4 } from "..";
import { arraySlice } from "../animation/utils";
import { InterpolateSmooth, InterpolateLinear, InterpolateDiscrete } from "../animation/utils";

import { AnimationType3D, AnimationTrack3D } from "../animation/AnimationTrack3D";
// import { AnimationClip3D } from "../animation/AnimationClip3D";
import { Camera } from "../cameras/Camera";
import { SkinnedMesh3D } from "../bones/SkinnedMesh3D";
import { Skeleton3D } from "../bones/Skeleton3D";
import { Bone3D } from "../bones/Bone3D";
import { AnimationClip } from "../../2d/AnimationClip";
import { AnimationManager } from "../../2d/AnimationManager";
import { atob_base64Decode } from "./base64";

// var THREE: any = {}
/* BINARY EXTENSION */

var BINARY_EXTENSION_BUFFER_NAME = 'binary_glTF';
var BINARY_EXTENSION_HEADER_MAGIC = 'glTF';//这个用到了
var BINARY_EXTENSION_HEADER_LENGTH = 12;
var BINARY_EXTENSION_CHUNK_TYPES = { JSON: 0x4E4F534A, BIN: 0x004E4942 };

/*********************************/
/********** EXTENSIONS ***********/
/*********************************/

var EXTENSIONS = {
	KHR_BINARY_GLTF: 'KHR_binary_glTF',
	KHR_DRACO_MESH_COMPRESSION: 'KHR_draco_mesh_compression',
	KHR_LIGHTS_PUNCTUAL: 'KHR_lights_punctual',
	KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS: 'KHR_materials_pbrSpecularGlossiness',
	KHR_MATERIALS_UNLIT: 'KHR_materials_unlit',
	MSFT_TEXTURE_DDS: 'MSFT_texture_dds'
};

/*********************************/
/********** INTERNALS ************/
/*********************************/

/* CONSTANTS */

var WEBGL_CONSTANTS = {
	FLOAT: 5126,
	//FLOAT_MAT2: 35674,
	FLOAT_MAT3: 35675,
	FLOAT_MAT4: 35676,
	FLOAT_VEC2: 35664,
	FLOAT_VEC3: 35665,
	FLOAT_VEC4: 35666,
	LINEAR: 9729,
	REPEAT: 10497,
	SAMPLER_2D: 35678,
	POINTS: 0,
	LINES: 1,
	LINE_LOOP: 2,
	LINE_STRIP: 3,
	TRIANGLES: 4,
	TRIANGLE_STRIP: 5,
	TRIANGLE_FAN: 6,
	UNSIGNED_BYTE: 5121,
	UNSIGNED_SHORT: 5123
};


var WEBGL_COMPONENT_TYPES = {
	5120: Int8Array,
	5121: Uint8Array,
	5122: Int16Array,
	5123: Uint16Array,
	5125: Uint32Array,
	5126: Float32Array
};


var WEBGL_TYPE_SIZES = {
	'SCALAR': 1,
	'VEC2': 2,
	'VEC3': 3,
	'VEC4': 4,
	'MAT2': 4,
	'MAT3': 9,
	'MAT4': 16
};

//暂时没用，以后改成和three一样的有用
var ATTRIBUTES = {
	POSITION: 'position',
	NORMAL: 'normal',
	TEXCOORD_0: 'uv',
	TEXCOORD0: 'uv', // deprecated
	TEXCOORD: 'uv', // deprecated
	TEXCOORD_1: 'uv2',
	COLOR_0: 'color',
	COLOR0: 'color', // deprecated
	COLOR: 'color', // deprecated
	WEIGHTS_0: 'skinWeight',
	WEIGHT: 'skinWeight', // deprecated
	JOINTS_0: 'skinIndex',
	JOINT: 'skinIndex' // deprecated
};

/**
 * 变形枚举
 */
var PATH_PROPERTIES = {
	scale: 'scale',
	translation: 'position',
	rotation: 'quaternion',
	weights: 'morphTargetInfluences',
	position: "position"
};

/**
 * 补间类型，暂时只有线性的InterpolateLinear
 */
var INTERPOLATION = {
	CUBICSPLINE: InterpolateSmooth, // We use custom interpolation GLTFCubicSplineInterpolation for CUBICSPLINE.
	// KeyframeTrack.optimize() can't handle glTF Cubic Spline output values layout,
	// using THREE.InterpolateSmooth for KeyframeTrack instantiation to prevent optimization.
	// See KeyframeTrack.optimize() for the detail.
	LINEAR: InterpolateLinear,
	STEP: InterpolateDiscrete
};

export interface GltfData {
	scene: Scene3D,
	scenes: Scene3D[],
	cameras: Camera[],
	animations: AnimationClip[],
	animationManager: AnimationManager,
	asset: { generator: string, version: string },
	parser: GLTFParser,
	// userData: {}
}

/**
 * 直接拿数据解析
 * @param json 
 * @param onLoad 
 * @param onError 
 * @param extensions 
 */
 export function parseGltf(
	json: any,
	onLoad: (gltf: GltfData) => void,
	onError: (err: any) => void,
	extensions?
) {
	//如果是字符串
	if (typeof json === 'string') json = JSON.parse(json);
	//
	var parser = new GLTFParser(json, extensions);
	parser.parse(function (scene, scenes, cameras, animations, json) {
		const obj: any = {
			scene,
			scenes,
			cameras,
			animations,
			asset: json.asset,
			parser,
			// userData: {}
		}
		//有动画，创建一个管理器
		if (animations && animations.length) obj.animationManager = new AnimationManager(animations);
		onLoad(obj)
	}, onError);
}

/**
 * 直接按函数导出，不用类，
 * TODO 后续是否考虑优化，gltf格式其实不需要用二进制解析，再说吧，就怕后缀能修改，
 * @param url 
 * @param onLoad 
 * @param onError 
 */
export function loadGltf(
	url: string,
	onLoad: (gltf: GltfData) => void,
	onError: (err: any) => void
) {
	let callback = (s: boolean, res: any) => {
		if (s) {//得到二进制数据
			try {
				var content: string;
				var extensions = {};
				if (typeof res === 'string') {
					content = res;
				} else {
					var magic = decodeText(new Uint8Array(res, 0, 4));
					if (magic === BINARY_EXTENSION_HEADER_MAGIC) {
						try {
							extensions[EXTENSIONS.KHR_BINARY_GLTF] = new GLTFBinaryExtension(res);
						} catch (error) {
							if (onError) onError(error);
							return;
						}
						content = extensions[EXTENSIONS.KHR_BINARY_GLTF].content;
					} else {
						content = decodeText(new Uint8Array(res));
					}
				}
				var json = JSON.parse(content);
				// console.log(json)
				if (json.asset === undefined || json.asset.version[0] < 2) {
					onError && onError("版本格式有问题")
					return;
				}

				parseGltf(json, onLoad, onError, extensions)

				// var parser = new GLTFParser(json, extensions);

				// parser.parse(function (scene, scenes, cameras, animations, json) {
				// 	const obj: any = {
				// 		scene,
				// 		scenes,
				// 		cameras,
				// 		animations,
				// 		asset: json.asset,
				// 		parser,
				// 		// userData: {}
				// 	}
				// 	//有动画，创建一个管理器
				// 	if (animations && animations.length) obj.animationManager = new AnimationManager(animations);
				// 	onLoad(obj)
				// }, onError);
			} catch (e) {
				onError && onError(e);
			}
		} else {
			onError && onError(res)
		}
	}
	// if (getEnv() == "tb") {
	// 	GlobalLoader.tbLoad(callback, url, "ArrayBuffer")
	// } else {
	// 	GlobalLoader.loadRawWeb(callback, url, "arraybuffer")
	// }
	GlobalLoader.loadAB(callback, url);
}

/**
 * 去掉了除mesh和node以外的所有东西（骨骼，线等都没有）
 * 动画需要烘焙打点
 */
// export class GLTFLoader extends EventDispatcher {
// 	constructor() {//anonymous
// 		super();
// 	}
// 	load(
// 		url: string,
// 		onLoad: (gltf) => void,
// 		onError: (err: any) => void
// 	) {
// 		var scope = this;

// 		let callback = (s: boolean, res: any) => {
// 			if (s) {//得到二进制数据
// 				try {
// 					scope.parse(res, function (gltf) {
// 						onLoad(gltf);
// 					}, onError);
// 				} catch (e) {
// 					onError(e);
// 				}
// 			} else {
// 				onError && onError(res)
// 			}
// 		}
// 		if (getEnv() == "tb") {
// 			GlobalLoader.tbLoad(callback, url, "ArrayBuffer")
// 		} else {
// 			GlobalLoader.loadRawWeb(callback, url, "arraybuffer")
// 		}
// 	}

// 	parse(data: any, onLoad: (res: any) => void, onError: (err: any) => void) {
// 		var content: string;
// 		var extensions = {};
// 		if (typeof data === 'string') {
// 			content = data;
// 		} else {
// 			var magic = decodeText(new Uint8Array(data, 0, 4));
// 			if (magic === BINARY_EXTENSION_HEADER_MAGIC) {
// 				try {
// 					extensions[EXTENSIONS.KHR_BINARY_GLTF] = new GLTFBinaryExtension(data);
// 				} catch (error) {
// 					if (onError) onError(error);
// 					return;
// 				}
// 				content = extensions[EXTENSIONS.KHR_BINARY_GLTF].content;
// 			} else {
// 				content = decodeText(new Uint8Array(data));
// 			}
// 		}

// 		var json = JSON.parse(content);

// 		// console.log(json)

// 		if (json.asset === undefined || json.asset.version[0] < 2) {
// 			onError && onError("版本格式有问题")
// 			return;
// 		}


// 		var parser = new GLTFParser(json, extensions);

// 		parser.parse(function (scene, scenes, cameras, animations, json) {

// 			var glTF = {
// 				scene,
// 				scenes,
// 				cameras,
// 				animations,
// 				asset: json.asset,
// 				parser,
// 				userData: {}
// 			};

// 			// addUnknownExtensionsToUserData(extensions, glTF, json);
// 			// console.log(22222)
// 			onLoad(glTF);

// 		}, onError);

// 	}
// }

/**
 * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#default-material
 */
function createDefaultMaterial() {
	// return new THREE.MeshStandardMaterial({
	// 	color: 0xFFFFFF,
	// 	emissive: 0x000000,
	// 	metalness: 1,
	// 	roughness: 1,
	// 	transparent: false,
	// 	depthTest: true,
	// 	side: THREE.FrontSide
	// });
	return new LightMaterial({
		color: 0xFFFFFF,
		side: 0
	})

}

/**
 * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#morph-targets
 *
 * @param {THREE.BufferGeometry} geometry
 * @param {Array<GLTF.Target>} targets
 * @param {Array<THREE.BufferAttribute>} accessors
 */
function addMorphTargets(geometry: Geometry, targets, accessors) {

	var hasMorphPosition = false;
	var hasMorphNormal = false;

	for (var i = 0, il = targets.length; i < il; i++) {

		var target = targets[i];

		if (target.POSITION !== undefined) hasMorphPosition = true;
		if (target.NORMAL !== undefined) hasMorphNormal = true;

		if (hasMorphPosition && hasMorphNormal) break;

	}

	if (!hasMorphPosition && !hasMorphNormal) return;

	var morphPositions = [];
	var morphNormals = [];

	for (var i = 0, il = targets.length; i < il; i++) {

		var target = targets[i];
		var attributeName = 'morphTarget' + i;

		if (hasMorphPosition) {

			// Three.js morph position is absolute value. The formula is
			//   basePosition
			//     + weight0 * ( morphPosition0 - basePosition )
			//     + weight1 * ( morphPosition1 - basePosition )
			//     ...
			// while the glTF one is relative
			//   basePosition
			//     + weight0 * glTFmorphPosition0
			//     + weight1 * glTFmorphPosition1
			//     ...
			// then we need to convert from relative to absolute here.
			let mPostions;
			if (target.POSITION !== undefined) {
				mPostions = [];
				// Cloning not to pollute original accessor
				var positionAttribute = cloneBufferAttribute(accessors[target.POSITION]);
				positionAttribute.name = attributeName;

				var position = geometry._vertices;

				for (var j = 0, jl = positionAttribute.count; j < jl; j++) {

					// positionAttribute.setXYZ(
					// 	j,
					// 	positionAttribute.getX(j) + position.getX(j),
					// 	positionAttribute.getY(j) + position.getY(j),
					// 	positionAttribute.getZ(j) + position.getZ(j)
					// );

					mPostions.push(
						positionAttribute.getX(j) + position[j * 3],
						positionAttribute.getY(j) + position[j * 3 + 1],
						positionAttribute.getZ(j) + position[j * 3 + 2],
					)
				}
			} else {
				//用同一个数组吧，一个修改都改
				mPostions = geometry._vertices;
			}
			morphPositions.push(mPostions);
		}

		if (hasMorphNormal) {

			// see target.POSITION's comment
			let mNormals;
			// var normalAttribute;

			if (target.NORMAL !== undefined) {
				mNormals = []
				var normalAttribute = cloneBufferAttribute(accessors[target.NORMAL]);
				normalAttribute.name = attributeName;

				// var normal = geometry.attributes.normal;
				var normal = geometry._normals;

				for (var j = 0, jl = normalAttribute.count; j < jl; j++) {

					// normalAttribute.setXYZ(
					// 	j,
					// 	normalAttribute.getX(j) + normal.getX(j),
					// 	normalAttribute.getY(j) + normal.getY(j),
					// 	normalAttribute.getZ(j) + normal.getZ(j)
					// );
					mNormals.push(
						normalAttribute.getX(j) + normal[j * 3],
						normalAttribute.getY(j) + normal[j * 3 + 1],
						normalAttribute.getZ(j) + normal[j * 3 + 2],
					)
				}

			} else {

				mNormals = geometry._normals;

			}

			morphNormals.push(mNormals);

		}

	}
	//morphPositions是个positionAttribute的数组，我那边应该只需要数据，步长啥的都自己干
	if (hasMorphPosition) geometry._morphPositions = morphPositions;
	if (hasMorphNormal) geometry._morphNormals = morphNormals;

}


function cloneBufferAttribute(attribute) {

	if (attribute.isInterleavedBufferAttribute) {

		var count = attribute.count;
		var itemSize = attribute.itemSize;
		var array = attribute.array.slice(0, count * itemSize);

		for (var i = 0; i < count; ++i) {

			array[i * itemSize] = attribute.getX(i);
			if (itemSize >= 2) array[i * itemSize + 1] = attribute.getY(i);
			if (itemSize >= 3) array[i * itemSize + 2] = attribute.getZ(i);
			if (itemSize >= 4) array[i * itemSize + 3] = attribute.getW(i);

		}

		return new BufferAttribute(array, itemSize, attribute.normalized);

	}

	return attribute.clone();

}

/**
 * @param {THREE.Mesh} mesh
 * @param {GLTF.Mesh} meshDef
 */
function updateMorphTargets(mesh, meshDef) {

	mesh.updateMorphTargets();

	if (meshDef.weights !== undefined) {

		for (var i = 0, il = meshDef.weights.length; i < il; i++) {

			mesh.morphTargetInfluences[i] = meshDef.weights[i];

		}

	}

	// .extras has user-defined data, so check that .extras.targetNames is an array.
	if (meshDef.extras && Array.isArray(meshDef.extras.targetNames)) {

		var targetNames = meshDef.extras.targetNames;

		if (mesh.morphTargetInfluences.length === targetNames.length) {

			mesh.morphTargetDictionary = {};

			for (var i = 0, il = targetNames.length; i < il; i++) {

				mesh.morphTargetDictionary[targetNames[i]] = i;

			}

		} else {

			console.warn('THREE.GLTFLoader: Invalid extras.targetNames length. Ignoring names.');

		}

	}

}

function isPrimitiveEqual(a, b) {

	if (a.indices !== b.indices) {

		return false;

	}

	return isObjectEqual(a.attributes, b.attributes);

}

function isObjectEqual(a, b) {

	if (Object.keys(a).length !== Object.keys(b).length) return false;

	for (var key in a) {

		if (a[key] !== b[key]) return false;

	}

	return true;

}

function isArrayEqual(a, b) {

	if (a.length !== b.length) return false;

	for (var i = 0, il = a.length; i < il; i++) {

		if (a[i] !== b[i]) return false;

	}

	return true;

}

function getCachedGeometry(cache, newPrimitive) {

	for (var i = 0, il = cache.length; i < il; i++) {

		var cached = cache[i];

		if (isPrimitiveEqual(cached.primitive, newPrimitive)) return cached.promise;

	}

	return null;

}

function getCachedCombinedGeometry(cache, geometries) {

	for (var i = 0, il = cache.length; i < il; i++) {

		var cached = cache[i];

		if (isArrayEqual(geometries, cached.baseGeometries)) return cached.geometry;

	}

	return null;

}

function getCachedMultiPassGeometry(cache, geometry, primitives) {

	for (var i = 0, il = cache.length; i < il; i++) {

		var cached = cache[i];

		if (geometry === cached.baseGeometry && isArrayEqual(primitives, cached.primitives)) return cached.geometry;

	}

	return null;

}


/**
 * Checks if we can build a single Mesh with MultiMaterial from multiple primitives.
 * Returns true if all primitives use the same attributes/morphAttributes/mode
 * and also have index. Otherwise returns false.
 *
 * @param {Array<GLTF.Primitive>} primitives
 * @return {Boolean}
 */
function isMultiPassGeometry(primitives) {

	if (primitives.length < 2) return false;

	var primitive0 = primitives[0];
	var targets0 = primitive0.targets || [];

	if (primitive0.indices === undefined) return false;

	for (var i = 1, il = primitives.length; i < il; i++) {

		var primitive = primitives[i];

		if (primitive0.mode !== primitive.mode) return false;
		if (primitive.indices === undefined) return false;
		if (!isObjectEqual(primitive0.attributes, primitive.attributes)) return false;

		var targets = primitive.targets || [];

		if (targets0.length !== targets.length) return false;

		for (var j = 0, jl = targets0.length; j < jl; j++) {

			if (!isObjectEqual(targets0[j], targets[j])) return false;

		}

	}

	return true;

}


class GLTFParser {

	//缓存
	cache = new GLTFRegistry();
	// BufferGeometry caching
	primitiveCache = [];
	multiplePrimitivesCache = [];
	multiPassGeometryCache = [];
	constructor(
		public json: any = {},
		public extensions = {},
		public options = {}
	) {
		//下面两个加载器暂时不需要
		// this.textureLoader = new THREE.TextureLoader(this.options.manager);
		// this.textureLoader.setCrossOrigin(this.options.crossOrigin);

		// this.fileLoader = new THREE.FileLoader(this.options.manager);
		// this.fileLoader.setResponseType('arraybuffer');
	}
	parse(onLoad, onError) {
		var json = this.json;

		// Clear the loader cache
		this.cache.removeAll();

		// Mark the special nodes/meshes in json for efficient parse
		this.markDefs();

		// Fire the callback on complete
		this.getMultiDependencies([

			'scene',
			'animation',//先去掉动画，加载模型
			'camera'

		]).then(function (dependencies) {

			var scenes = dependencies.scenes || [];
			var scene = scenes[json.scene || 0];
			var animations = dependencies.animations || [];
			var cameras = dependencies.cameras || [];

			onLoad(scene, scenes, cameras, animations, json);

		}).catch(onError);
	}
	/**
	 * Marks the special nodes/meshes in json for efficient parse.
	 */
	markDefs() {
		var nodeDefs = this.json.nodes || [];
		var skinDefs = this.json.skins || [];
		var meshDefs = this.json.meshes || [];

		var meshReferences = {};
		var meshUses = {};

		// Nothing in the node definition indicates whether it is a Bone or an
		// Object3D. Use the skins' joint references to mark bones.
		for (var skinIndex = 0, skinLength = skinDefs.length; skinIndex < skinLength; skinIndex++) {

			var joints = skinDefs[skinIndex].joints;

			for (var i = 0, il = joints.length; i < il; i++) {

				nodeDefs[joints[i]].isBone = true;

			}

		}

		// Meshes can (and should) be reused by multiple nodes in a glTF asset. To
		// avoid having more than one THREE.Mesh with the same name, count
		// references and rename instances below.
		//
		// Example: CesiumMilkTruck sample model reuses "Wheel" meshes.
		for (var nodeIndex = 0, nodeLength = nodeDefs.length; nodeIndex < nodeLength; nodeIndex++) {

			var nodeDef = nodeDefs[nodeIndex];

			if (nodeDef.mesh !== undefined) {

				if (meshReferences[nodeDef.mesh] === undefined) {

					meshReferences[nodeDef.mesh] = meshUses[nodeDef.mesh] = 0;

				}

				meshReferences[nodeDef.mesh]++;

				// Nothing in the mesh definition indicates whether it is
				// a SkinnedMesh or Mesh. Use the node's mesh reference
				// to mark SkinnedMesh if node has skin.
				if (nodeDef.skin !== undefined) {

					meshDefs[nodeDef.mesh].isSkinnedMesh = true;

				}

			}

		}

		this.json.meshReferences = meshReferences;
		this.json.meshUses = meshUses;

	}
	/**
	 * Requests the specified dependency asynchronously, with caching.
	 * @param {string} type
	 * @param {number} index
	 * @return {Promise<Object>}
	 */
	getDependency(type: string, index: number): Promise<any> {
		var cacheKey = type + ':' + index;
		var dependency = this.cache.get(cacheKey);
		if (!dependency) {
			switch (type) {

				case 'scene':
					dependency = this.loadScene(index);
					break;

				case 'node':
					dependency = this.loadNode(index);
					break;

				case 'mesh':
					dependency = this.loadMesh(index);
					break;

				case 'accessor':
					dependency = this.loadAccessor(index);
					break;

				case 'bufferView':
					dependency = this.loadBufferView(index);
					break;

				case 'buffer':
					dependency = this.loadBuffer(index);
					break;

				case 'material':
					dependency = this.loadMaterial(index);
					break;

				case 'texture':
					dependency = this.loadTexture(index);
					break;

				case 'skin':
					dependency = this.loadSkin(index);
					break;

				case 'animation':
					dependency = this.loadAnimation(index);
					break;

				case 'camera':
					dependency = this.loadCamera(index);
					break;

				default:
					throw new Error('Unknown type: ' + type);

			}

			this.cache.add(cacheKey, dependency);

		}

		return dependency;

	};

	/**
	 * Requests all dependencies of the specified type asynchronously, with caching.
	 * @param {string} type
	 * @return {Promise<Array<Object>>}
	 */
	getDependencies(type: string): Promise<any[]> {

		var dependencies = this.cache.get(type);

		if (!dependencies) {

			var parser = this;
			var defs = this.json[type + (type === 'mesh' ? 'es' : 's')] || [];

			dependencies = Promise.all(defs.map(function (def, index) {

				return parser.getDependency(type, index);

			}));

			this.cache.add(type, dependencies);

		}

		return dependencies;

	};

	/**
	 * Requests all multiple dependencies of the specified types asynchronously, with caching.
	 * @param {Array<string>} types
	 * @return {Promise<Object<Array<Object>>>}
	 */
	getMultiDependencies(types): Promise<any> {

		var results = {};
		var pendings = [];

		for (var i = 0, il = types.length; i < il; i++) {

			var type = types[i];
			var value = this.getDependencies(type);

			value = value.then(function (key, value) {

				results[key] = value;

			}.bind(this, type + (type === 'mesh' ? 'es' : 's')));

			pendings.push(value);

		}

		return Promise.all(pendings).then(function () {

			return results;

		});

	};

	/**
	 * ok
	 * @param {number} bufferIndex
	 * @return {Promise<ArrayBuffer>}
	 */
	loadBuffer(bufferIndex: number): Promise<ArrayBuffer> {
		//基本只需要一次，而且没有bufferDef.uri
		var bufferDef = this.json.buffers[bufferIndex];
		// var loader = this.fileLoader;

		if (bufferDef.type && bufferDef.type !== 'arraybuffer') {

			throw new Error('THREE.GLTFLoader: ' + bufferDef.type + ' buffer type is not supported.');

		}

		// If present, GLB container is required to be the first buffer.
		if (bufferDef.uri === undefined && bufferIndex === 0) {

			return Promise.resolve(this.extensions[EXTENSIONS.KHR_BINARY_GLTF].body);

		}

		//基本不用其他的加载
		// var options = this.options;

		return new Promise(function (resolve, reject) {
			//考虑下base64的加载，其他路径的再说
			// if (/^data:.*,.*$/i.test(bufferDef.uri)) {
			var dataUriRegex = /^data:(.*?)(;base64)?,(.*)$/;
			var dataUriRegexResult = bufferDef.uri.match(dataUriRegex);
			if (dataUriRegexResult) {
				var mimeType = dataUriRegexResult[1];
				var isBase64 = !!dataUriRegexResult[2];
				var data = dataUriRegexResult[3];
				data = decodeURIComponent(data);
				if (isBase64) data = (window && window.atob || atob_base64Decode)(data);//考虑小程序兼容性
				var view = new Uint8Array(data.length);
				for (var i = 0; i < data.length; i++) {
					view[i] = data.charCodeAt(i);
				}
				resolve(view.buffer)
				return
			}
			// };
			reject("阿三的")
			// loader.load(resolveURL(bufferDef.uri, options.path), resolve, undefined, function () {

			// 	reject(new Error('THREE.GLTFLoader: Failed to load buffer "' + bufferDef.uri + '".'));

			// });

		});

	};

	/**
	 * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#buffers-and-buffer-views
	 * @param {number} bufferViewIndex
	 * @return {Promise<ArrayBuffer>}
	 */
	loadBufferView(bufferViewIndex: number): Promise<ArrayBuffer> {

		var bufferViewDef = this.json.bufferViews[bufferViewIndex];

		return this.getDependency('buffer', bufferViewDef.buffer).then(function (buffer) {

			var byteLength = bufferViewDef.byteLength || 0;
			var byteOffset = bufferViewDef.byteOffset || 0;
			return buffer.slice(byteOffset, byteOffset + byteLength);

		});

	};

	/**
	 * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#accessors
	 * @param {number} accessorIndex
	 * @return {Promise<BufferAttribute|InterleavedBufferAttribute>}
	 */
	loadAccessor(accessorIndex: number): Promise<BufferAttribute | InterleavedBufferAttribute> {

		var parser = this;
		var json = this.json;

		var accessorDef = this.json.accessors[accessorIndex];

		if (accessorDef.bufferView === undefined && accessorDef.sparse === undefined) {

			// Ignore empty accessors, which may be used to declare runtime
			// information about attributes coming from another source (e.g. Draco
			// compression extension).
			return null;

		}

		var pendingBufferViews = [];

		if (accessorDef.bufferView !== undefined) {

			pendingBufferViews.push(this.getDependency('bufferView', accessorDef.bufferView));

		} else {

			pendingBufferViews.push(null);

		}

		if (accessorDef.sparse !== undefined) {

			pendingBufferViews.push(this.getDependency('bufferView', accessorDef.sparse.indices.bufferView));
			pendingBufferViews.push(this.getDependency('bufferView', accessorDef.sparse.values.bufferView));

		}

		return Promise.all(pendingBufferViews).then(function (bufferViews) {

			var bufferView = bufferViews[0];

			var itemSize = WEBGL_TYPE_SIZES[accessorDef.type];
			var TypedArray = WEBGL_COMPONENT_TYPES[accessorDef.componentType];

			// For VEC3: itemSize is 3, elementBytes is 4, itemBytes is 12.
			var elementBytes = TypedArray.BYTES_PER_ELEMENT;
			var itemBytes = elementBytes * itemSize;
			var byteOffset = accessorDef.byteOffset || 0;
			var byteStride = accessorDef.bufferView !== undefined ? json.bufferViews[accessorDef.bufferView].byteStride : undefined;
			var normalized = accessorDef.normalized === true;
			var array, bufferAttribute: BufferAttribute //| InterleavedBufferAttribute;

			// The buffer is not interleaved if the stride is the item size in bytes.
			if (byteStride && byteStride !== itemBytes) {

				var ibCacheKey = 'InterleavedBuffer:' + accessorDef.bufferView + ':' + accessorDef.componentType;
				var ib = parser.cache.get(ibCacheKey);

				if (!ib) {

					// Use the full buffer if it's interleaved.
					array = new TypedArray(bufferView);

					// Integer parameters to IB/IBA are in array elements, not bytes.
					ib = new InterleavedBuffer(array, byteStride / elementBytes);

					parser.cache.add(ibCacheKey, ib);

				}

				// bufferAttribute = new InterleavedBufferAttribute(ib, itemSize, byteOffset / elementBytes, normalized)
				bufferAttribute = cloneBufferAttribute(new InterleavedBufferAttribute(ib, itemSize, byteOffset / elementBytes, normalized))

			} else {

				if (bufferView === null) {

					array = new TypedArray(accessorDef.count * itemSize);

				} else {

					array = new TypedArray(bufferView, byteOffset, accessorDef.count * itemSize);

				}

				bufferAttribute = new BufferAttribute(array, itemSize, normalized);

			}

			// https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#sparse-accessors
			if (accessorDef.sparse !== undefined) {

				var itemSizeIndices = WEBGL_TYPE_SIZES.SCALAR;
				var TypedArrayIndices = WEBGL_COMPONENT_TYPES[accessorDef.sparse.indices.componentType];

				var byteOffsetIndices = accessorDef.sparse.indices.byteOffset || 0;
				var byteOffsetValues = accessorDef.sparse.values.byteOffset || 0;

				var sparseIndices = new TypedArrayIndices(bufferViews[1], byteOffsetIndices, accessorDef.sparse.count * itemSizeIndices);
				var sparseValues = new TypedArray(bufferViews[2], byteOffsetValues, accessorDef.sparse.count * itemSize);

				if (bufferView !== null) {
					//@ts-ignore Avoid modifying the original ArrayBuffer, if the bufferView wasn't initialized with zeroes.
					bufferAttribute.setArray(bufferAttribute.array.slice());
				}

				for (var i = 0, il = sparseIndices.length; i < il; i++) {

					var index = sparseIndices[i];

					bufferAttribute.setX(index, sparseValues[i * itemSize]);
					if (itemSize >= 2) bufferAttribute.setY(index, sparseValues[i * itemSize + 1]);
					if (itemSize >= 3) bufferAttribute.setZ(index, sparseValues[i * itemSize + 2]);
					if (itemSize >= 4) bufferAttribute.setW(index, sparseValues[i * itemSize + 3]);
					if (itemSize >= 5) throw new Error('THREE.GLTFLoader: Unsupported itemSize in sparse BufferAttribute.');

				}

			}

			return bufferAttribute;

		});

	};

	/**
	 * ok
	 * @param {number} textureIndex
	 * @return {Promise<Texture>}
	 */
	loadTexture(textureIndex): Promise<Texture> {

		var parser = this;
		var json = this.json;
		var options = this.options;
		// var textureLoader = this.textureLoader;

		// var URL = window.URL// || window.webkitURL;

		var textureDef = json.textures[textureIndex];

		var textureExtensions = textureDef.extensions || {};

		var source;

		if (textureExtensions[EXTENSIONS.MSFT_TEXTURE_DDS]) {

			source = json.images[textureExtensions[EXTENSIONS.MSFT_TEXTURE_DDS].source];

		} else {

			source = json.images[textureDef.source];

		}

		var sourceURI = source.uri;
		// var isObjectURL = false;

		if (source.bufferView !== undefined) {

			// Load binary image data from bufferView, if provided.

			sourceURI = parser.getDependency('bufferView', source.bufferView).then(function (bufferView) {

				// if (window.arrayBufferToBase64 != undefined) {
				var base64 = ArrayBufferToBase64(bufferView);
				var base64String = `data:${source.mimeType};base64,${base64}`;
				return base64String;
				// }

				// isObjectURL = true;
				// var blob = new Blob([bufferView], { type: source.mimeType });
				// sourceURI = URL.createObjectURL(blob);
				// return sourceURI;

			});

		}

		return Promise.resolve(sourceURI).then(function (sourceURI) {

			// Load Texture resource.

			// var loader = THREE.Loader.Handlers.get(sourceURI);

			// if (!loader) {

			// 	loader = textureExtensions[EXTENSIONS.MSFT_TEXTURE_DDS]
			// 		? parser.extensions[EXTENSIONS.MSFT_TEXTURE_DDS].ddsLoader
			// 		: textureLoader;

			// }

			return new Promise(function (resolve, reject) {
				GlobalLoader.loadImage((s, image) => {
					if (s) {
						resolve(Texture.fromImage(image));
					} else {
						reject()
					}
				}, resolveURL(sourceURI, ""/*options.path*/))
				// loader.load(resolveURL(sourceURI, options.path), resolve, undefined, reject);

			});

		}).then(function (texture: Texture) {

			// Clean up resources and configure Texture.

			// if (isObjectURL === true) {

			// 	URL.revokeObjectURL(sourceURI);

			// }

			// texture.flipY = false;

			if (textureDef.name !== undefined) texture["name"] = textureDef.name;

			// Ignore unknown mime types, like DDS files.
			// if (source.mimeType in MIME_TYPE_FORMATS) {

			// 	texture.format = MIME_TYPE_FORMATS[source.mimeType];

			// }

			// var samplers = json.samplers || {};
			// var sampler = samplers[textureDef.sampler] || {};

			// texture.magFilter = WEBGL_FILTERS[sampler.magFilter] || THREE.LinearFilter;
			// texture.minFilter = WEBGL_FILTERS[sampler.minFilter] || THREE.LinearMipMapLinearFilter;
			// texture.wrapS = WEBGL_WRAPPINGS[sampler.wrapS] || THREE.RepeatWrapping;
			// texture.wrapT = WEBGL_WRAPPINGS[sampler.wrapT] || THREE.RepeatWrapping;

			return texture;

		});

	};

	/**
	 * Asynchronously assigns a texture to the given material parameters.
	 * @param {Object} materialParams
	 * @param {string} textureName
	 * @param {number} textureIndex
	 * @return {Promise}
	 */
	assignTexture(materialParams, textureName, textureIndex) {

		return this.getDependency('texture', textureIndex).then(function (texture) {

			materialParams[textureName] = texture;

		});

	};

	/**
	 * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#materials
	 * @param {number} materialIndex
	 * @return {Promise<Material>}
	 */
	loadMaterial(materialIndex: number): Promise<BaseMaterial> {

		var parser = this;
		var json = this.json;
		var extensions = this.extensions;
		var materialDef = json.materials[materialIndex];

		var materialType;
		var materialParams: BaseMaterialParamsInt = {};
		var materialExtensions = materialDef.extensions || {};

		var pending = [];

		if (materialExtensions[EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS]) {

			// var sgExtension = extensions[EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS];
			// materialType = sgExtension.getMaterialType(materialDef);
			// pending.push(sgExtension.extendParams(materialParams, materialDef, parser));

			//先将扩展简单处理
			materialType = LightMaterial;
			var pbrSpecularGlossiness = materialDef.extensions[EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS];
			materialParams.color = 0xffffff;
			materialParams.alpha = 1.0;
			if (Array.isArray(pbrSpecularGlossiness.diffuseFactor)) {
				var array = pbrSpecularGlossiness.diffuseFactor;
				materialParams.color = rgb2hex(array);
				materialParams.alpha = array[3];
			}
			if (pbrSpecularGlossiness.diffuseTexture !== undefined) {
				pending.push(parser.assignTexture(materialParams, 'map', pbrSpecularGlossiness.diffuseTexture.index));
			}
		}
		// else if (materialExtensions[EXTENSIONS.KHR_MATERIALS_UNLIT]) {

		// 	var kmuExtension = extensions[EXTENSIONS.KHR_MATERIALS_UNLIT];
		// 	materialType = kmuExtension.getMaterialType(materialDef);
		// 	pending.push(kmuExtension.extendParams(materialParams, materialDef, parser));

		// } //上面这种就简单点，加到下面
		else {

			// Specification:
			// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#metallic-roughness-material

			materialType = materialExtensions[EXTENSIONS.KHR_MATERIALS_UNLIT] ? BaseMaterial : LightMaterial//THREE.MeshStandardMaterial;

			var metallicRoughness = materialDef.pbrMetallicRoughness || {};

			materialParams.color = 0xffffff;
			materialParams.alpha = 1.0;

			//颜色透明度
			if (Array.isArray(metallicRoughness.baseColorFactor)) {
				var array = metallicRoughness.baseColorFactor;
				materialParams.color = rgb2hex(array)
				materialParams.alpha = array[3];
			}
			//贴图
			if (metallicRoughness.baseColorTexture !== undefined) {
				pending.push(parser.assignTexture(materialParams, 'map', metallicRoughness.baseColorTexture.index));
			}

			// materialParams.metalness = metallicRoughness.metallicFactor !== undefined ? metallicRoughness.metallicFactor : 1.0;
			// materialParams.roughness = metallicRoughness.roughnessFactor !== undefined ? metallicRoughness.roughnessFactor : 1.0;

			// if (metallicRoughness.metallicRoughnessTexture !== undefined) {

			// 	var textureIndex = metallicRoughness.metallicRoughnessTexture.index;
			// 	pending.push(parser.assignTexture(materialParams, 'metalnessMap', textureIndex));
			// 	pending.push(parser.assignTexture(materialParams, 'roughnessMap', textureIndex));

			// }

		}

		if (materialDef.doubleSided === true) {

			materialParams.side = RenderSideType.DoubleSide;

		}

		// var alphaMode = materialDef.alphaMode || ALPHA_MODES.OPAQUE;

		// if (alphaMode === ALPHA_MODES.BLEND) {

		// 	materialParams.transparent = true;

		// } else {

		// 	materialParams.transparent = false;

		// 	if (alphaMode === ALPHA_MODES.MASK) {

		// 		materialParams.alphaTest = materialDef.alphaCutoff !== undefined ? materialDef.alphaCutoff : 0.5;

		// 	}

		// }

		// if (materialDef.normalTexture !== undefined && materialType !== THREE.MeshBasicMaterial) {

		// 	pending.push(parser.assignTexture(materialParams, 'normalMap', materialDef.normalTexture.index));

		// 	materialParams.normalScale = new THREE.Vector2(1, 1);

		// 	if (materialDef.normalTexture.scale !== undefined) {

		// 		materialParams.normalScale.set(materialDef.normalTexture.scale, materialDef.normalTexture.scale);

		// 	}

		// }

		// if (materialDef.occlusionTexture !== undefined && materialType !== THREE.MeshBasicMaterial) {

		// 	pending.push(parser.assignTexture(materialParams, 'aoMap', materialDef.occlusionTexture.index));

		// 	if (materialDef.occlusionTexture.strength !== undefined) {

		// 		materialParams.aoMapIntensity = materialDef.occlusionTexture.strength;

		// 	}

		// }

		// if (materialDef.emissiveFactor !== undefined && materialType !== THREE.MeshBasicMaterial) {

		// 	materialParams.emissive = new THREE.Color().fromArray(materialDef.emissiveFactor);

		// }

		// if (materialDef.emissiveTexture !== undefined && materialType !== THREE.MeshBasicMaterial) {

		// 	pending.push(parser.assignTexture(materialParams, 'emissiveMap', materialDef.emissiveTexture.index));

		// }

		return Promise.all(pending).then(function () {

			var material: BaseMaterial;

			// if (materialType === THREE.ShaderMaterial) {

			// 	material = extensions[EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS].createMaterial(materialParams);

			// } else {
			//@ts-ignore
			material = new materialType(materialParams);

			// }
			//@ts-ignore
			if (materialDef.name !== undefined) material.name = materialDef.name;

			// Normal map textures use OpenGL conventions:
			// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#materialnormaltexture
			// if (material.normalScale) {

			// 	material.normalScale.y = - material.normalScale.y;

			// }

			// baseColorTexture, emissiveTexture, and specularGlossinessTexture use sRGB encoding.
			// if (material.map) material.map.encoding = THREE.sRGBEncoding;
			// if (material.emissiveMap) material.emissiveMap.encoding = THREE.sRGBEncoding;
			// if (material.specularMap) material.specularMap.encoding = THREE.sRGBEncoding;

			// assignExtrasToUserData(material, materialDef);

			// if (materialDef.extensions) addUnknownExtensionsToUserData(extensions, material, materialDef);

			return material;

		});

	};

	/**
	 * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#geometry
	 *
	 * Creates BufferGeometries from primitives.
	 * If we can build a single BufferGeometry with .groups from multiple primitives, returns one BufferGeometry.
	 * Otherwise, returns BufferGeometries without .groups as many as primitives.
	 *
	 * @param {Array<Object>} primitives
	 * @return {Promise<Array<BufferGeometry>>}
	 */
	loadGeometries(primitives): Promise<Geometry[]> {

		var parser = this;
		var extensions = this.extensions;
		var cache = this.primitiveCache;

		// var isMultiPass = isMultiPassGeometry(primitives);
		// var originalPrimitives;

		// if (isMultiPass) {

		// 	originalPrimitives = primitives; // save original primitives and use later

		// 	// We build a single BufferGeometry with .groups from multiple primitives
		// 	// because all primitives share the same attributes/morph/mode and have indices.

		// 	primitives = [primitives[0]];

		// 	// Sets .groups and combined indices to a geometry later in this method.

		// }

		return this.getDependencies('accessor').then(function (accessors) {

			var pending = [];

			for (var i = 0, il = primitives.length; i < il; i++) {

				var primitive = primitives[i];

				// See if we've already created this geometry
				var cached = getCachedGeometry(cache, primitive);

				if (cached) {

					// Use the cached geometry if it exists
					pending.push(cached);

				} else if (primitive.extensions && primitive.extensions[EXTENSIONS.KHR_DRACO_MESH_COMPRESSION]) {
					//暂时没有扩展的
					// Use DRACO geometry if available
					// var geometryPromise = extensions[EXTENSIONS.KHR_DRACO_MESH_COMPRESSION]
					// 	.decodePrimitive(primitive, parser)
					// 	.then(function (geometry) {

					// 		addPrimitiveAttributes(geometry, primitive, accessors);

					// 		return geometry;

					// 	});

					// cache.push({ primitive: primitive, promise: geometryPromise });

					// pending.push(geometryPromise);

				} else {

					// Otherwise create a new geometry
					// var geometry = new Geometry();

					var geometry = addPrimitiveAttributes(primitive, accessors);

					var geometryPromise: any = Promise.resolve(geometry);

					// Cache this geometry
					cache.push({ primitive: primitive, promise: geometryPromise });

					pending.push(geometryPromise);

				}

			}

			return Promise.all(pending).then(function (geometries) {

				// if (isMultiPass) {

				// 	var baseGeometry = geometries[0];

				// 	// See if we've already created this combined geometry
				// 	var cache = parser.multiPassGeometryCache;
				// 	var cached = getCachedMultiPassGeometry(cache, baseGeometry, originalPrimitives);

				// 	if (cached !== null) return [cached.geometry];

				// 	// Cloning geometry because of index override.
				// 	// Attributes can be reused so cloning by myself here.

				// 	// NORMAL: 2
				// 	// POSITION: 1
				// 	// TEXCOORD_0: 3

				// 	//顶点
				// 	var _vertices = [];


				// 	var geometry = new Geometry();

				// 	geometry["name"] = baseGeometry.name;
				// 	geometry["userData"] = baseGeometry.userData;

				// 	for (var key in baseGeometry.attributes) geometry.addAttribute(key, baseGeometry.attributes[key]);
				// 	for (var key in baseGeometry.morphAttributes) geometry.morphAttributes[key] = baseGeometry.morphAttributes[key];

				// 	var indices = [];
				// 	var offset = 0;

				// 	for (var i = 0, il = originalPrimitives.length; i < il; i++) {

				// 		var accessor = accessors[originalPrimitives[i].indices];

				// 		for (var j = 0, jl = accessor.count; j < jl; j++) indices.push(accessor.array[j]);

				// 		geometry.addGroup(offset, accessor.count, i);

				// 		offset += accessor.count;

				// 	}

				// 	geometry.setIndex(indices);

				// 	cache.push({ geometry: geometry, baseGeometry: baseGeometry, primitives: originalPrimitives });

				// 	return [geometry];

				// } 
				// else if (geometries.length > 1 && THREE.BufferGeometryUtils !== undefined) {

				// 	// Tries to merge geometries with BufferGeometryUtils if possible

				// 	for (var i = 1, il = primitives.length; i < il; i++) {

				// 		// can't merge if draw mode is different
				// 		if (primitives[0].mode !== primitives[i].mode) return geometries;

				// 	}

				// 	// See if we've already created this combined geometry
				// 	var cache = parser.multiplePrimitivesCache;
				// 	var cached = getCachedCombinedGeometry(cache, geometries);

				// 	if (cached) {

				// 		if (cached.geometry !== null) return [cached.geometry];

				// 	} else {

				// 		var geometry = THREE.BufferGeometryUtils.mergeBufferGeometries(geometries, true);

				// 		cache.push({ geometry: geometry, baseGeometries: geometries });

				// 		if (geometry !== null) return [geometry];

				// 	}

				// }

				return geometries;

			});

		});

	};

	/**
	 * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#meshes
	 * @param {number} meshIndex
	 * @return {Promise<Object3D|Mesh3D>}
	 */
	loadMesh(meshIndex) {

		var scope = this;
		var json = this.json;
		var extensions = this.extensions;

		var meshDef = json.meshes[meshIndex];

		return this.getMultiDependencies([

			'accessor',
			'material'

		]).then(function (dependencies) {

			var primitives = meshDef.primitives;
			var originalMaterials = [];

			for (var i = 0, il = primitives.length; i < il; i++) {

				originalMaterials[i] = primitives[i].material === undefined
					? createDefaultMaterial()
					: dependencies.materials[primitives[i].material];

			}

			return scope.loadGeometries(primitives).then(function (geometries) {

				var isMultiMaterial = false//geometries.length === 1 && geometries[0].groups.length > 0;

				var meshes = [];

				for (var i = 0, il = geometries.length; i < il; i++) {

					var geometry = geometries[i];
					var primitive = primitives[i];

					// 1. create Mesh

					var mesh: Mesh3D;

					// var material = isMultiMaterial ? originalMaterials : originalMaterials[i];
					var material: BaseMaterial = originalMaterials[i];

					if (primitive.mode === WEBGL_CONSTANTS.TRIANGLES ||
						primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP ||
						primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN ||
						primitive.mode === undefined) {

						// .isSkinnedMesh isn't in glTF spec. See .markDefs()
						mesh = meshDef.isSkinnedMesh === true
							? new SkinnedMesh3D(geometry, material)
							: new Mesh3D(geometry, material);
						// mesh = new Mesh3D(geometry, material);

						if (mesh.instanceType === "SkinnedMesh3D") {
							// we normalize floating point skin weight array to fix malformed assets (see #15319)
							// it's important to skip this for non-float32 data since normalizeSkinWeights assumes non-normalized inputs
							//@ts-ignore
							mesh.normalizeSkinWeights();
						}
						// if (primitive.mode === WEBGL_CONSTANTS.TRIANGLE_STRIP) {

						// 	mesh.drawMode = THREE.TriangleStripDrawMode;

						// } else if (primitive.mode === WEBGL_CONSTANTS.TRIANGLE_FAN) {

						// 	mesh.drawMode = THREE.TriangleFanDrawMode;

						// }

					}
					// else if (primitive.mode === WEBGL_CONSTANTS.LINES) {

					// 	mesh = new THREE.LineSegments(geometry, material);

					// } else if (primitive.mode === WEBGL_CONSTANTS.LINE_STRIP) {

					// 	mesh = new THREE.Line(geometry, material);

					// } else if (primitive.mode === WEBGL_CONSTANTS.LINE_LOOP) {

					// 	mesh = new THREE.LineLoop(geometry, material);

					// } else if (primitive.mode === WEBGL_CONSTANTS.POINTS) {

					// 	mesh = new THREE.Points(geometry, material);

					// } 
					else {

						throw new Error('THREE.GLTFLoader: Primitive mode unsupported: ' + primitive.mode);

					}

					// if (Object.keys(mesh.geometry.morphAttributes).length > 0) {

					// 	updateMorphTargets(mesh, meshDef);

					// }
					//变形的权重加上
					if (mesh.geometry._morphPositions) updateMorphTargets(mesh, meshDef);

					mesh.name = meshDef.name || ('mesh_' + meshIndex);

					if (geometries.length > 1) mesh.name += '_' + i;

					// assignExtrasToUserData(mesh, meshDef);

					meshes.push(mesh);

					// 2. update Material depending on Mesh and BufferGeometry

					// var materials = isMultiMaterial ? mesh.material : [mesh.material];
					var material: BaseMaterial = mesh.material;

					// var useVertexColors = geometry.attributes.color !== undefined;
					// var useFlatShading = geometry.attributes.normal === undefined;
					var useSkinning = mesh.instanceType == "SkinnedMesh3D";
					var useMorphTargets = !!geometry._morphPositions;
					var useMorphNormals = useMorphTargets && !!geometry._morphNormals;
					//用相同的材质有覆盖的风险，所以有才设置吧，反正mesh也会判断一次
					// if (useMorphTargets) material.morphTargets = useMorphTargets;
					// if (useMorphNormals) material.morphNormals = useMorphNormals;
					if (/*useVertexColors || useFlatShading ||*/ useSkinning || useMorphTargets) {

						var cacheKey = 'ClonedMaterial:' + material.instanceId + ':';

						// if (material.isGLTFSpecularGlossinessMaterial) cacheKey += 'specular-glossiness:';
						if (useSkinning) cacheKey += 'skinning:';
						// if (useVertexColors) cacheKey += 'vertex-colors:';
						// if (useFlatShading) cacheKey += 'flat-shading:';
						if (useMorphTargets) cacheKey += 'morph-targets:';
						if (useMorphNormals) cacheKey += 'morph-normals:';

						var cachedMaterial: BaseMaterial = scope.cache.get(cacheKey);

						if (!cachedMaterial) {

							cachedMaterial = /*material.isGLTFSpecularGlossinessMaterial
								? extensions[EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS].cloneMaterial(material)
								:*/ material.clone();

							if (useSkinning) cachedMaterial.skinning = true;
							// if (useVertexColors) cachedMaterial.vertexColors = THREE.VertexColors;
							// if (useFlatShading) cachedMaterial.flatShading = true;
							if (useMorphTargets) cachedMaterial.morphTargets = true;
							if (useMorphNormals) cachedMaterial.morphNormals = true;

							scope.cache.add(cacheKey, cachedMaterial);

						}

						material = cachedMaterial;

					}
					mesh.material = material
					// for (var j = 0, jl = materials.length; j < jl; j++) {

					// 	var material = materials[j];

					// 	if (mesh.isPoints) {

					// 		var cacheKey = 'PointsMaterial:' + material.uuid;

					// 		var pointsMaterial = scope.cache.get(cacheKey);

					// 		if (!pointsMaterial) {

					// 			pointsMaterial = new THREE.PointsMaterial();
					// 			THREE.Material.prototype.copy.call(pointsMaterial, material);
					// 			pointsMaterial.color.copy(material.color);
					// 			pointsMaterial.map = material.map;
					// 			pointsMaterial.lights = false; // PointsMaterial doesn't support lights yet

					// 			scope.cache.add(cacheKey, pointsMaterial);

					// 		}

					// 		material = pointsMaterial;

					// 	} else if (mesh.isLine) {

					// 		var cacheKey = 'LineBasicMaterial:' + material.uuid;

					// 		var lineMaterial = scope.cache.get(cacheKey);

					// 		if (!lineMaterial) {

					// 			lineMaterial = new THREE.LineBasicMaterial();
					// 			THREE.Material.prototype.copy.call(lineMaterial, material);
					// 			lineMaterial.color.copy(material.color);
					// 			lineMaterial.lights = false; // LineBasicMaterial doesn't support lights yet

					// 			scope.cache.add(cacheKey, lineMaterial);

					// 		}

					// 		material = lineMaterial;

					// 	}

					// 	// Clone the material if it will be modified
					// 	if (useVertexColors || useFlatShading || useSkinning || useMorphTargets) {

					// 		var cacheKey = 'ClonedMaterial:' + material.uuid + ':';

					// 		if (material.isGLTFSpecularGlossinessMaterial) cacheKey += 'specular-glossiness:';
					// 		if (useSkinning) cacheKey += 'skinning:';
					// 		if (useVertexColors) cacheKey += 'vertex-colors:';
					// 		if (useFlatShading) cacheKey += 'flat-shading:';
					// 		if (useMorphTargets) cacheKey += 'morph-targets:';
					// 		if (useMorphNormals) cacheKey += 'morph-normals:';

					// 		var cachedMaterial = scope.cache.get(cacheKey);

					// 		if (!cachedMaterial) {

					// 			cachedMaterial = material.isGLTFSpecularGlossinessMaterial
					// 				? extensions[EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS].cloneMaterial(material)
					// 				: material.clone();

					// 			if (useSkinning) cachedMaterial.skinning = true;
					// 			if (useVertexColors) cachedMaterial.vertexColors = THREE.VertexColors;
					// 			if (useFlatShading) cachedMaterial.flatShading = true;
					// 			if (useMorphTargets) cachedMaterial.morphTargets = true;
					// 			if (useMorphNormals) cachedMaterial.morphNormals = true;

					// 			scope.cache.add(cacheKey, cachedMaterial);

					// 		}

					// 		material = cachedMaterial;

					// 	}

					// 	materials[j] = material;

					// 	// workarounds for mesh and geometry
					// 	//暂时不管这个aoMap
					// 	// if (material.aoMap && geometry.attributes.uv2 === undefined && geometry.attributes.uv !== undefined) {

					// 	// 	console.log('THREE.GLTFLoader: Duplicating UVs to support aoMap.');
					// 	// 	geometry.addAttribute('uv2', new THREE.BufferAttribute(geometry.attributes.uv.array, 2));

					// 	// }

					// 	if (material.isGLTFSpecularGlossinessMaterial) {

					// 		// for GLTFSpecularGlossinessMaterial(ShaderMaterial) uniforms runtime update
					// 		mesh.onBeforeRender = extensions[EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS].refreshUniforms;

					// 	}

					// }

					// mesh.material = isMultiMaterial ? materials : materials[0];

				}

				if (meshes.length === 1) {

					return meshes[0];

				}

				var group = new Object3D();

				for (var i = 0; i < meshes.length; i++) {

					group.addChild(meshes[i]);

				}

				return group;

			});

		});

	};

	/**
	 * ok
	 * @param {number} cameraIndex
	 * @return {Promise<PerspectiveCamera>}
	 */
	loadCamera(cameraIndex: number): Promise<PerspectiveCamera> {

		var camera: PerspectiveCamera;
		var cameraDef = this.json.cameras[cameraIndex];
		var params = cameraDef[cameraDef.type];

		if (!params) {
			console.warn('模型相机参数异常');
			return;
		}
		if (cameraDef.type === 'perspective') {
			camera = new PerspectiveCamera(
				RAD_TO_DEG * params.yfov,
				params.aspectRatio || 1,
				params.znear || 1,
				params.zfar || 2e6
			);
		}
		// else if (cameraDef.type === 'orthographic') {

		// 	camera = new THREE.OrthographicCamera(params.xmag / - 2, params.xmag / 2, params.ymag / 2, params.ymag / - 2, params.znear, params.zfar);

		// }

		if (cameraDef.name !== undefined) camera.name = cameraDef.name;

		// assignExtrasToUserData(camera, cameraDef);

		return Promise.resolve(camera);

	};

	/**
	 * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#skins
	 * @param {number} skinIndex
	 * @return {Promise<Object>}
	 */
	loadSkin(skinIndex) {

		var skinDef = this.json.skins[skinIndex];

		var skinEntry: any = { joints: skinDef.joints };

		if (skinDef.inverseBindMatrices === undefined) {

			return Promise.resolve(skinEntry);

		}

		return this.getDependency('accessor', skinDef.inverseBindMatrices).then(function (accessor) {

			skinEntry.inverseBindMatrices = accessor;

			return skinEntry;

		});

	};

	/**
	 * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#animations
	 * @param {number} animationIndex
	 * @return {Promise<AnimationClip>}
	 */
	loadAnimation(animationIndex: number) {

		var json = this.json;

		var animationDef = json.animations[animationIndex];

		return this.getMultiDependencies([

			'accessor',
			'node'

		]).then(function (dependencies) {

			var tracks: AnimationTrack3D[] = [];

			for (var i = 0, il = animationDef.channels.length; i < il; i++) {

				var channel = animationDef.channels[i];
				var sampler = animationDef.samplers[channel.sampler];

				if (sampler) {

					var target = channel.target;
					var name = target.node !== undefined ? target.node : target.id; // NOTE: target.id is deprecated.
					var input = animationDef.parameters !== undefined ? animationDef.parameters[sampler.input] : sampler.input;
					var output = animationDef.parameters !== undefined ? animationDef.parameters[sampler.output] : sampler.output;

					var inputAccessor = dependencies.accessors[input];
					var outputAccessor = dependencies.accessors[output];
					// console.log(2222222)
					// console.log(inputAccessor)
					// console.log(outputAccessor)
					var node: Object3D = dependencies.nodes[name];
					// console.log("node:", node)
					if (node) {

						node.updateLocalMatrix();

						var TypedKeyframeTrack;

						switch (PATH_PROPERTIES[target.path]) {

							case PATH_PROPERTIES.weights:

								TypedKeyframeTrack = AnimationType3D.morphTargetInfluences;
								// console.log("weights")
								break;

							case PATH_PROPERTIES.rotation:

								TypedKeyframeTrack = AnimationType3D.quaternion;
								// console.log("rotation")
								break;

							case PATH_PROPERTIES.position:
								TypedKeyframeTrack = AnimationType3D.position;
								break;
							case PATH_PROPERTIES.scale:
								TypedKeyframeTrack = AnimationType3D.scale;
								break;
							default:
								TypedKeyframeTrack = AnimationType3D.position;
								// console.log("position,scale")
								break;
						}

						var targetName = node.name ? node.name : node.instanceId;

						//TODO，暂时不用，以后用到再说
						var interpolation = sampler.interpolation !== undefined ? INTERPOLATION[sampler.interpolation] : InterpolateLinear;

						//处理顶点变形的权重
						var targetNames = [];
						if (PATH_PROPERTIES[target.path] === PATH_PROPERTIES.weights) {
							// node can be THREE.Group here but
							// PATH_PROPERTIES.weights(morphTargetInfluences) should be
							// the property of a mesh object under group.
							//递归处理 TODO
							traverse(node, (object) => {
								if ((object.instanceType === "Mesh3D" || object.instanceType === "SkinnedMesh3D") &&
									object["morphTargetInfluences"]) {
									targetNames.push(object.name ? object.name : object.instanceId);
								}
							})
						} else {
							targetNames.push(targetName);
						}
						// console.log("targetNames:", targetNames)
						// KeyframeTrack.optimize() will modify given 'times' and 'values'
						// buffers before creating a truncated copy to keep. Because buffers may
						// be reused by other tracks, make copies here.
						for (var j = 0, jl = targetNames.length; j < jl; j++) {

							// var track = new TypedKeyframeTrack(
							// 	targetNames[j] + '.' + PATH_PROPERTIES[target.path],
							// 	arraySlice(inputAccessor.array, 0),
							// 	arraySlice(outputAccessor.array, 0),
							// 	interpolation
							// );
							var track = new AnimationTrack3D(
								node,
								TypedKeyframeTrack,
								arraySlice(inputAccessor.array, 0),
								arraySlice(outputAccessor.array, 0),
							)

							// Here is the trick to enable custom interpolation.
							// Overrides .createInterpolant in a factory method which creates custom interpolation.
							// if (sampler.interpolation === 'CUBICSPLINE') {

							// 	track.createInterpolant = function InterpolantFactoryMethodGLTFCubicSpline(result) {

							// 		// A CUBICSPLINE keyframe in glTF has three output values for each input value,
							// 		// representing inTangent, splineVertex, and outTangent. As a result, track.getValueSize()
							// 		// must be divided by three to get the interpolant's sampleSize argument.

							// 		return new GLTFCubicSplineInterpolant(this.times, this.values, this.getValueSize() / 3, result);

							// 	};

							// 	// Workaround, provide an alternate way to know if the interpolant type is cubis spline to track.
							// 	// track.getInterpolation() doesn't return valid value for custom interpolant.
							// 	track.createInterpolant.isInterpolantFactoryMethodGLTFCubicSpline = true;

							// }

							tracks.push(track);

						}

					}

				}

			}

			var name = animationDef.name !== undefined ? animationDef.name : 'animation_' + animationIndex;

			// return new THREE.AnimationClip(name, undefined, tracks);
			//计算总时间
			var duration = 0
			for (let i = 0, n = tracks.length; i !== n; ++i) {
				var tt = tracks[i];
				duration = Math.max(duration, tt.times[tt.times.length - 1]);
			}
			var clip = new AnimationClip(tracks, duration);
			clip.name = name;
			return clip;
		});

	};

	/**
	 * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#nodes-and-hierarchy
	 * @param {number} nodeIndex
	 * @return {Promise<Object3D>}
	 */
	loadNode(nodeIndex: number): Promise<Object3D> {

		var json = this.json;
		var extensions = this.extensions;

		var meshReferences = json.meshReferences;
		var meshUses = json.meshUses;

		var nodeDef = json.nodes[nodeIndex];

		return this.getMultiDependencies([

			'mesh',
			'skin',
			'camera',
			'light'

		]).then(function (dependencies) {

			var node: Object3D;

			// .isBone isn't in glTF spec. See .markDefs
			if (nodeDef.isBone === true) {

				node = new Bone3D();
				// return null//临时解决

			} else if (nodeDef.mesh !== undefined) {

				var mesh = dependencies.meshes[nodeDef.mesh];

				if (meshReferences[nodeDef.mesh] > 1) {

					var instanceNum = meshUses[nodeDef.mesh]++;

					node = mesh.clone();
					node.name += '_instance_' + instanceNum;

					// onBeforeRender copy for Specular-Glossiness
					// node.onBeforeRender = mesh.onBeforeRender;

					for (var i = 0, il = node.children.length; i < il; i++) {

						node.children[i].name += '_instance_' + instanceNum;
						// node.children[i].onBeforeRender = mesh.children[i].onBeforeRender;

					}

				} else {

					node = mesh;

				}

			} else if (nodeDef.camera !== undefined) {

				node = dependencies.cameras[nodeDef.camera];

			} else if (nodeDef.extensions
				&& nodeDef.extensions[EXTENSIONS.KHR_LIGHTS_PUNCTUAL]
				&& nodeDef.extensions[EXTENSIONS.KHR_LIGHTS_PUNCTUAL].light !== undefined) {

				var lights = extensions[EXTENSIONS.KHR_LIGHTS_PUNCTUAL].lights;
				node = lights[nodeDef.extensions[EXTENSIONS.KHR_LIGHTS_PUNCTUAL].light];

			} else {

				// node = new THREE.Object3D();
				node = new Object3D();

			}

			if (nodeDef.name !== undefined) {

				node.name = sanitizeNodeName(nodeDef.name);
				// node.name = "null";

			}

			// assignExtrasToUserData(node, nodeDef);

			// if (nodeDef.extensions) addUnknownExtensionsToUserData(extensions, node, nodeDef);

			if (nodeDef.matrix !== undefined) {

				var matrix = new Matrix4();
				matrix.fromArray(nodeDef.matrix);
				node.applyMatrix(matrix);
			} else {
				if (nodeDef.translation !== undefined) {
					node.position.fromArray(nodeDef.translation);
				}

				if (nodeDef.rotation !== undefined) {

					node.quaternion.fromArray(nodeDef.rotation);

				}

				if (nodeDef.scale !== undefined) {

					node.scale.fromArray(nodeDef.scale);

				}

			}

			return node;

		});

	};

	/**
	 * Specification: https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#scenes
	 * @param {number} sceneIndex
	 * @return {Promise<THREE.Scene>}
	 */
	loadScene = function () {

		// scene node hierachy builder

		function buildNodeHierachy(nodeId, parentObject, json, allNodes, skins) {

			var node = allNodes[nodeId];
			var nodeDef = json.nodes[nodeId];

			// build skeleton here as well
			//骨骼以后再说
			if (nodeDef.skin !== undefined) {//暂时应该没有骨骼

				// var meshes = node.isGroup === true ? node.children : [node];
				var meshes: SkinnedMesh3D[] = node.instanceType == "Object3D" ? node.children : [node];

				for (var i = 0; i < meshes.length; i++) {

					var mesh = meshes[i];
					var skinEntry = skins[nodeDef.skin];

					var bones = [];
					var boneInverses = [];

					for (var j = 0, jl = skinEntry.joints.length; j < jl; j++) {

						var jointId = skinEntry.joints[j];
						var jointNode = allNodes[jointId];

						if (jointNode) {

							bones.push(jointNode);

							var mat = new Matrix4();

							if (skinEntry.inverseBindMatrices !== undefined) {

								mat.fromArray(skinEntry.inverseBindMatrices.array, j * 16);

							}

							boneInverses.push(mat);

						} else {

							console.warn('Joint "%s" could not be found.', jointId);

						}

					}

					mesh.bind(new Skeleton3D(bones, boneInverses), mesh._worldMatrix);

				}

			}

			// build node hierachy

			parentObject.addChild(node);

			if (nodeDef.children) {

				var children = nodeDef.children;

				for (var i = 0; i < children.length; i++) {

					var child = children[i];
					//临时处理，骨骼有问题，
					if (node) buildNodeHierachy(child, node, json, allNodes, skins);

				}

			}

		}

		return function loadScene(sceneIndex) {

			var json = this.json;
			var extensions = this.extensions;
			var sceneDef = this.json.scenes[sceneIndex];

			return this.getMultiDependencies([

				'node',
				'skin'

			]).then(function (dependencies) {

				var scene = new Scene3D();
				if (sceneDef.name !== undefined) scene.name = sceneDef.name;

				// assignExtrasToUserData(scene, sceneDef);

				// if (sceneDef.extensions) addUnknownExtensionsToUserData(extensions, scene, sceneDef);

				var nodeIds = sceneDef.nodes || [];

				for (var i = 0, il = nodeIds.length; i < il; i++) {

					buildNodeHierachy(nodeIds[i], scene, json, dependencies.nodes, dependencies.skins);

				}

				return scene;

			});

		};

	}();
}


/**
 * 
 * @param  {GLTF.Primitive} primitiveDef
 * @param  {Array<THREE.BufferAttribute>} accessors
 */
function addPrimitiveAttributes(primitiveDef, accessors) {

	var attributes = primitiveDef.attributes;
	// POSITION: 1
	// NORMAL: 2
	// TEXCOORD_0: 3

	var verticesAttribute = accessors[attributes["POSITION"]];
	var normalAttribute = accessors[attributes["NORMAL"]];
	var uvAttribute = accessors[attributes["TEXCOORD_0"]] || accessors[attributes["TEXCOORD0"]];

	// var indices = [];
	// var vertices = [];
	// var normals = [];
	// var uvs = [];

	//取顶点数据

	// for (var gltfAttributeName in attributes) {

	// 	var threeAttributeName = ATTRIBUTES[gltfAttributeName];
	// 	var bufferAttribute = accessors[attributes[gltfAttributeName]];

	// 	// Skip attributes already provided by e.g. Draco extension.
	// 	if (!threeAttributeName) continue;
	// 	if (threeAttributeName in geometry.attributes) continue;

	// 	geometry.addAttribute(threeAttributeName, bufferAttribute);

	// }

	// if (primitiveDef.indices !== undefined && !geometry.index) {

	// 	geometry.setIndex(accessors[primitiveDef.indices]);

	// }

	var geo = new Geometry(
		attributeToArray(verticesAttribute),
		attributeToArray(accessors[primitiveDef.indices]),
		normalAttribute ? attributeToArray(normalAttribute) : null,
		null,
		uvAttribute ? attributeToArray(uvAttribute) : null
	)
	//骨骼相关的
	var skinWeight = accessors[attributes["WEIGHTS_0"]] || accessors[attributes["WEIGHTS"]];
	if (skinWeight) geo._skinWeight = attributeToArray(skinWeight);
	var skinIndex = accessors[attributes["JOINTS_0"]] || accessors[attributes["JOINT"]];
	if (skinIndex) geo._skinIndex = attributeToArray(skinIndex);

	//加变形的数据
	if (primitiveDef.targets !== undefined) {
		addMorphTargets(geo, primitiveDef.targets, accessors);
	}
	// assignExtrasToUserData(geometry, primitiveDef);
	return geo
}


//用统一的，因为不止这里用到
// function decodeText(array) {

// 	if (typeof TextDecoder !== 'undefined') {
// 		return new TextDecoder().decode(array);
// 	}

// 	// Avoid the String.fromCharCode.apply(null, array) shortcut, which
// 	// throws a "maximum call stack size exceeded" error for large arrays.

// 	var s = '';

// 	for (var i = 0, il = array.length; i < il; i++) {

// 		// Implicitly assumes little-endian.
// 		s += String.fromCharCode(array[i]);

// 	}

// 	// Merges multi-byte utf-8 characters.
// 	return decodeURIComponent(escape(s));

// }

function GLTFBinaryExtension(data) {
	this.name = EXTENSIONS.KHR_BINARY_GLTF;
	this.content = null;
	this.body = null;
	var headerView = new DataView(data, 0, BINARY_EXTENSION_HEADER_LENGTH);
	this.header = {
		magic: decodeText(new Uint8Array(data.slice(0, 4))),
		version: headerView.getUint32(4, true),
		length: headerView.getUint32(8, true)
	};

	if (this.header.magic !== BINARY_EXTENSION_HEADER_MAGIC) {

		throw new Error('THREE.GLTFLoader: Unsupported glTF-Binary header.');

	} else if (this.header.version < 2.0) {

		throw new Error('THREE.GLTFLoader: Legacy binary file detected. Use LegacyGLTFLoader instead.');

	}

	var chunkView = new DataView(data, BINARY_EXTENSION_HEADER_LENGTH);
	var chunkIndex = 0;

	while (chunkIndex < chunkView.byteLength) {

		var chunkLength = chunkView.getUint32(chunkIndex, true);
		chunkIndex += 4;

		var chunkType = chunkView.getUint32(chunkIndex, true);
		chunkIndex += 4;

		if (chunkType === BINARY_EXTENSION_CHUNK_TYPES.JSON) {

			var contentArray = new Uint8Array(data, BINARY_EXTENSION_HEADER_LENGTH + chunkIndex, chunkLength);
			this.content = decodeText(contentArray);

		} else if (chunkType === BINARY_EXTENSION_CHUNK_TYPES.BIN) {

			var byteOffset = BINARY_EXTENSION_HEADER_LENGTH + chunkIndex;
			this.body = data.slice(byteOffset, byteOffset + chunkLength);

		}

		// Clients must ignore chunks with unknown types.

		chunkIndex += chunkLength;

	}

	if (this.content === null) {
		throw new Error('THREE.GLTFLoader: JSON content not found.');
	}
}


/* UTILITY FUNCTIONS */

function resolveURL(url, path) {

	// Invalid URL
	if (typeof url !== 'string' || url === '') return '';

	// Absolute URL http://,https://,//
	if (/^(https?:)?\/\//i.test(url)) return url;

	// Data URI
	if (/^data:.*,.*$/i.test(url)) return url;

	// Blob URL
	if (/^blob:.*$/i.test(url)) return url;

	// Relative URL
	return path + url;

}

//用统一的，不止一个地方要用
// function ArrayBufferToBase64(buff) {
// 	var alph =
// 		"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
// 		enc = "",
// 		n,
// 		p,
// 		bits,
// 		d = new Uint8Array(buff);
// 	var len = buff.byteLength * 8;
// 	for (var offset = 0; offset < len; offset += 6) {
// 		n = (offset / 8) | 0;
// 		p = offset % 8;
// 		bits = ((d[n] || 0) << p) >> 2;
// 		if (p > 2) {
// 			bits |= (d[n + 1] || 0) >> (10 - p);
// 		}
// 		enc += alph.charAt(bits & 63);
// 	}
// 	enc += p == 4 ? "=" : p == 6 ? "==" : "";
// 	return enc;
// }

// class GLTFRegistry {
// 	private static objects = {}
// 	constructor() { }
// 	get(key: string) {
// 		return GLTFRegistry.objects[key];
// 	}
// 	add(key: string, object: any) {
// 		GLTFRegistry.objects[key] = object;
// 	}
// 	remove(key: string) {
// 		delete GLTFRegistry.objects[key];
// 	}
// 	removeAll() {
// 		GLTFRegistry.objects = {};
// 	}
// }
//改成只缓存自己一个praser的，否则多个模型加载会取错缓存
class GLTFRegistry {
	private objects = {}
	constructor() { }
	get(key: string) {
		return this.objects[key];
	}
	add(key: string, object: any) {
		this.objects[key] = object;
	}
	remove(key: string) {
		delete this.objects[key];
	}
	removeAll() {
		this.objects = {};
	}
}

//仿three的，暂时用于记录数据，到时自己的也这么用？onUpload以后再说
class BufferAttribute {
	public count: number;
	public name: string;
	/**
	 * 
	 * @param array Int8Array,Uint8Array, Int16Array,Uint16Array,Uint32Array,Float32Array
	 * @param itemSize 步长，比如位置是3
	 * @param normalized 
	 */
	constructor(
		public array: any,
		public itemSize: number,
		public normalized: boolean = false
	) {
		if (Array.isArray(array)) {
			throw new TypeError('array必须士类型化数组');
		}
		this.count = array !== undefined ? array.length / itemSize : 0;
	}
	/**
	 * 重写覆盖
	 * @param value 普通数组，或类型化数组
	 * @param offset 
	 */
	set(value, offset: number = 0) {
		this.array.set(value, offset);
		return this;
	}
	/**
	 * 重置数据
	 * @param array 
	 */
	setArray(array) {
		if (Array.isArray(array)) {
			throw new TypeError('array必须士类型化数组');
		}
		this.count = array !== undefined ? array.length / this.itemSize : 0;
		this.array = array;

		return this;
	}
	/**
	 * 
	 * @param index 第几个点
	 */
	getX(index: number) {
		return this.array[index * this.itemSize];
	}

	setX(index: number, x: number) {
		this.array[index * this.itemSize] = x;
		return this;
	}

	getY(index: number) {
		return this.array[index * this.itemSize + 1];
	}

	setY(index: number, y: number) {
		this.array[index * this.itemSize + 1] = y;
		return this;
	}

	getZ(index: number) {
		return this.array[index * this.itemSize + 2];
	}

	setZ(index: number, z: number) {
		this.array[index * this.itemSize + 2] = z;
		return this;
	}

	getW(index: number) {
		return this.array[index * this.itemSize + 3];
	}

	setW(index: number, w: number) {
		this.array[index * this.itemSize + 3] = w;
		return this;
	}

	setXY(index: number, x: number, y: number) {
		index *= this.itemSize;
		this.array[index + 0] = x;
		this.array[index + 1] = y;
		return this;
	}

	setXYZ(index: number, x: number, y: number, z: number) {
		index *= this.itemSize;
		this.array[index + 0] = x;
		this.array[index + 1] = y;
		this.array[index + 2] = z;
		return this;
	}

	setXYZW(index: number, x: number, y: number, z: number, w: number) {
		index *= this.itemSize;
		this.array[index + 0] = x;
		this.array[index + 1] = y;
		this.array[index + 2] = z;
		this.array[index + 3] = w;
		return this;
	}
	copy(source: BufferAttribute) {

		this.name = source.name;
		this.array = new source.array.constructor(source.array);
		this.itemSize = source.itemSize;
		this.count = source.count;
		this.normalized = source.normalized;
		return this;
	}
	clone() {
		return new BufferAttribute(this.array, this.itemSize).copy(this);
	}
}



class InterleavedBuffer {
	public count: number;
	constructor(
		public array?,
		public stride?: number
	) {
		this.count = array !== undefined ? array.length / stride : 0;
	}
	setArray(array) {

		if (Array.isArray(array)) {

			throw new TypeError('THREE.BufferAttribute: array should be a Typed Array.');

		}

		this.count = array !== undefined ? array.length / this.stride : 0;
		this.array = array;

		return this;

	};
	copy(source: InterleavedBuffer) {
		this.array = new source.array.constructor(source.array);
		this.count = source.count;
		this.stride = source.stride;
		return this;
	}

	set(value, offset = 0) {
		this.array.set(value, offset);
		return this;
	}

	clone() {
		return new InterleavedBuffer().copy(this);
	}
}

//特殊用处，貌似不会感到几何里，临时用来解码数据用
class InterleavedBufferAttribute {
	data: InterleavedBuffer;
	isInterleavedBufferAttribute = true;
	constructor(
		interleavedBuffer: InterleavedBuffer,
		public itemSize: number,
		public offset: number,
		public normalized: boolean = false
	) {
		this.data = interleavedBuffer;
	};
	get count() {
		return this.data.count;
	}
	get array() {
		return this.data.array;
	}
	setX(index, x) {
		this.data.array[index * this.data.stride + this.offset] = x;
		return this;
	}

	setY(index, y) {
		this.data.array[index * this.data.stride + this.offset + 1] = y;
		return this;
	}

	setZ(index, z) {
		this.data.array[index * this.data.stride + this.offset + 2] = z;
		return this;
	}

	setW(index, w) {
		this.data.array[index * this.data.stride + this.offset + 3] = w;
		return this;
	}

	getX(index) {
		return this.data.array[index * this.data.stride + this.offset];
	}

	getY(index) {
		return this.data.array[index * this.data.stride + this.offset + 1];
	}

	getZ(index) {
		return this.data.array[index * this.data.stride + this.offset + 2];
	}

	getW(index) {
		return this.data.array[index * this.data.stride + this.offset + 3];
	}

	setXY(index, x, y) {
		index = index * this.data.stride + this.offset;
		this.data.array[index + 0] = x;
		this.data.array[index + 1] = y;
		return this;
	}

	setXYZ(index, x, y, z) {
		index = index * this.data.stride + this.offset;
		this.data.array[index + 0] = x;
		this.data.array[index + 1] = y;
		this.data.array[index + 2] = z;
		return this;
	}

	setXYZW(index, x, y, z, w) {
		index = index * this.data.stride + this.offset;
		this.data.array[index + 0] = x;
		this.data.array[index + 1] = y;
		this.data.array[index + 2] = z;
		this.data.array[index + 3] = w;
		return this;
	}
}

//取出数组数据,位置法线都是3位,uv两位,截取位数
function attributeToArray(attr: BufferAttribute) {
	var arr = [];
	// var data: Float32Array = attr.array;
	for (var i = 0; i < attr.count; i++) {
		arr.push(attr.getX(i))
		if (attr.itemSize >= 2) arr.push(attr.getY(i));
		if (attr.itemSize >= 3) arr.push(attr.getZ(i));
		if (attr.itemSize >= 4) arr.push(attr.getW(i));
	}
	return arr;
}


var reservedRe = new RegExp('[' + '\\[\\]\\.:\\/' + ']', 'g');
function sanitizeNodeName(name) {

	return name.replace(/\s/g, '_').replace(reservedRe, '');
};


/**
 * 深度递归处理Object3D
 * @param object 
 * @param callback 
 */
function traverse(object: Object3D, callback: (o: Object3D) => void) {
	callback(object);
	var children = object.children;
	for (var i = 0, l = children.length; i < l; i++) {
		let c = children[i];
		traverse(c, callback);
	}
}

function decodeBase64() {

}