diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..f60955f
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+ "liveServer.settings.port": 8889
+}
\ No newline at end of file
diff --git a/css/alarmMge/alarmMge.css b/css/alarmMge/alarmMge.css
index abc5d27..70ee91e 100644
--- a/css/alarmMge/alarmMge.css
+++ b/css/alarmMge/alarmMge.css
@@ -4,7 +4,7 @@ body {
height: 100%;
margin: 0;
padding: 0;
- color: #fff;
+
font-family: 'Alibaba PuHuiTi R';
}
@@ -17,6 +17,7 @@ body {
.main-box {
width: 100%;
height: 100%;
+ color: #fff;
justify-content: space-evenly;
}
@@ -79,4 +80,22 @@ body {
align-items: center;
margin-top: 4%;
cursor: pointer;
+}
+
+.alarmDialog{
+ color: #000;
+ padding: 15px;
+}
+.layui-input, .layui-select, .layui-textarea {
+ height: 38px;
+ line-height: 1.3;
+ line-height: 38px \9;
+ border-width: 1px;
+ border-style: solid;
+ background-color: transparent;
+ color: #000 !important;
+ border-radius: 2px;
+}
+.black{
+ color: #000;
}
\ No newline at end of file
diff --git a/img/map/bdz.png b/img/map/bdz.png
new file mode 100644
index 0000000..7693e2a
Binary files /dev/null and b/img/map/bdz.png differ
diff --git a/js/pages/alarmMge/alarmMge.js b/js/pages/alarmMge/alarmMge.js
index 9820d7f..82f8589 100644
--- a/js/pages/alarmMge/alarmMge.js
+++ b/js/pages/alarmMge/alarmMge.js
@@ -1,7 +1,10 @@
-let layer,table,buildCheck;
+let layer,table,buildCheck,$,form;
const bidCode = parent.$('#bidPro').val();
-
-layui.use(['layer','table'], function () {
+var isSubOk = false
+var mengIndex = 0
+layui.use(['layer','table','form','jquery'], function () {
+ $ = layui.jquery
+ form = layui.form
layer = layui.layer;
table = layui.table;
//建管单位下拉选
@@ -121,4 +124,60 @@ function init(warnType){
function getQueryList(){
init(buildCheck)
+}
+
+function handleAlarm(){
+ layer.open({
+ type: 1,
+ title: '告警处置', //显示标题栏
+ closeBtn: false,
+ area: '600px;',
+ id: 'LAY_layuipro' //设定一个id,防止重复弹出
+ ,
+ resize: false,
+ btn: ['确定', '取消'],
+ btnAlign: 'c',
+ moveType: 1 //拖拽模式,0或者1
+ ,
+ content: $("#biaodan"),
+ success: function (layero) {
+ $(':focus').blur();
+ // 添加form标识
+ layero.addClass('layui-form');
+ // 将保存按钮改变成提交按钮
+ layero.find('.layui-layer-btn0').attr({
+ 'lay-filter': 'formDemo',
+ 'lay-submit': ''
+ });
+ form.render();
+ },
+ yes: function (layero, index) {
+ form.on('submit(formDemo)', function (data) {
+ //表单数据formData
+ var formData = data.field;
+ console.log(formData)
+ const url = commonUrl + "screen/largeScreen/constructionQuality/qualityInspection";
+ const params = {
+ "roleCode": roleCode,
+ "orgId": orgId,
+ "userId": userId,
+ "bidCode": bidCode
+ }
+ let encryptStr = encryptCBC(JSON.stringify(params));
+ ajaxRequest(url, "POST", encryptStr, true, function () {
+ }, function (result) {
+ console.log(result);
+ if (result.code === 200) {
+
+ } else if (result.code === 500) {
+ layer.msg(result.msg, { icon: 2 });
+ }else if(result.code === 401){
+
+ }
+ }, function (xhr, status, error) {
+ error(xhr, status, error)
+ }, "application/json",aqEnnable);
+ });
+ },
+ });
}
\ No newline at end of file
diff --git a/pages/3Dglb/index.html b/pages/3Dglb/index.html
new file mode 100644
index 0000000..ba67bfb
--- /dev/null
+++ b/pages/3Dglb/index.html
@@ -0,0 +1,50 @@
+
+
+ 3D模型
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pages/3Dglb/lib/3dmodel.js b/pages/3Dglb/lib/3dmodel.js
new file mode 100644
index 0000000..8ac39e7
--- /dev/null
+++ b/pages/3Dglb/lib/3dmodel.js
@@ -0,0 +1,122 @@
+if ( WEBGL.isWebGLAvailable() === false ) {
+ document.body.appendChild( WEBGL.getWebGLErrorMessage() );
+}
+var container, stats, controls;
+var camera, scene, renderer, light, bbox;
+var rotating = true;
+
+init();
+animate();
+
+pauseRotation();
+
+function init() {
+ console.log('init')
+ if (!modelUrl) {
+ return false;
+ }
+ container = document.createElement( 'div' );
+ document.body.appendChild( container );
+
+ scene = new THREE.Scene();
+ bbox = new THREE.Box3();
+
+ scene.background = new THREE.Color( 0xeeeeee );
+ light = new THREE.HemisphereLight( 0xbbbbff, 0x444422, 1.5 );
+ light.position.set( 0, 1, 0 );
+
+ scene.add( light );
+ scene.rotation.y += -3.15;
+ scene.rotation.x += 1.5;
+ scene.rotation.z += 1;
+ var loader = new THREE.GLTFLoader();
+
+ loader.load(modelUrl, function ( gltf ) {
+ gltf.scene.name = '3dmodel';
+ setContent(gltf.scene);
+ scene.add( gltf.scene );
+ }, undefined, function ( e ) {
+ console.error( e );
+ } );
+
+ renderer = new THREE.WebGLRenderer( { antialias: true } );
+ renderer.setPixelRatio( window.devicePixelRatio );
+
+ renderer.setSize( window.innerWidth, window.innerHeight );
+ renderer.gammaOutput = true;
+ container.appendChild( renderer.domElement );
+ window.addEventListener( 'resize', onWindowResize, false );
+
+ camera = new THREE.PerspectiveCamera(20,window.innerWidth / window.innerHeight,0.01,1000);
+
+ controls = new THREE.OrbitControls(camera);
+ // to disable pan
+ controls.enablePan = false;
+ // to disable zoom
+ //controls.enableZoom = false;
+ controls.enableZoom = true;
+ controls.target.set(0,0,0);
+ controls.update();
+}
+
+function onWindowResize() {
+ console.log('onWindowResize')
+ camera.aspect = window.innerWidth / window.innerHeight;
+ camera.updateProjectionMatrix();
+ renderer.setSize( window.innerWidth, window.innerHeight );
+}
+//
+function animate() {
+ console.log('animate')
+ requestAnimationFrame( animate );
+ if (rotating) {
+ // scene.rotation.y += -0.001;
+ } else {
+ // scene.rotation.y = scene.rotation.y;
+ }
+ renderer.render( scene, camera );
+}
+
+function pauseRotation() {
+ console.log('pauseRotation')
+ var modelBorder = document.getElementById("modelBorder");
+ modelBorder.addEventListener("mouseenter", function( event ) {
+ rotating = false;
+ });
+ modelBorder.addEventListener("mouseleave", function( event ) {
+ rotating = true;
+ });
+ modelBorder.addEventListener('touchmove', function(e) {
+ rotating = false;
+ }, false);
+ modelBorder.addEventListener('touchstart', function(e) {
+ rotating = false;
+ }, false);
+ modelBorder.addEventListener('touchend', function(e) {
+ rotating = true;
+ }, false);
+
+}
+
+function setContent(object) {
+ console.log('setContent')
+ object.updateMatrixWorld();
+
+ const box = new THREE.Box3().setFromObject(object);
+ const size = box.getSize(new THREE.Vector3()).length();
+ const boxSize = box.getSize(new THREE.Vector3());
+ const center = box.getCenter(new THREE.Vector3());
+
+ object.position.x += object.position.x - center.x;
+ object.position.y += object.position.y - center.y;
+ object.position.z += object.position.z - center.z;
+
+ camera.position.copy(center);
+ if (boxSize.x > boxSize.y) {
+ camera.position.z = boxSize.x * -2.85
+ } else {
+ camera.position.z = boxSize.y * -2.85
+ }
+ camera.lookAt(0, 0, 0);
+
+}
\ No newline at end of file
diff --git a/pages/3Dglb/lib/GLTFLoader.js b/pages/3Dglb/lib/GLTFLoader.js
new file mode 100644
index 0000000..bea1529
--- /dev/null
+++ b/pages/3Dglb/lib/GLTFLoader.js
@@ -0,0 +1,3364 @@
+/**
+ * @author Rich Tibbett / https://github.com/richtr
+ * @author mrdoob / http://mrdoob.com/
+ * @author Tony Parisi / http://www.tonyparisi.com/
+ * @author Takahiro / https://github.com/takahirox
+ * @author Don McCurdy / https://www.donmccurdy.com
+ */
+
+THREE.GLTFLoader = ( function () {
+
+ function GLTFLoader( manager ) {
+
+ this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+ this.dracoLoader = null;
+
+ }
+
+ GLTFLoader.prototype = {
+
+ constructor: GLTFLoader,
+
+ crossOrigin: 'anonymous',
+
+ load: function ( url, onLoad, onProgress, onError ) {
+
+ var scope = this;
+
+ var resourcePath;
+
+ if ( this.resourcePath !== undefined ) {
+
+ resourcePath = this.resourcePath;
+
+ } else if ( this.path !== undefined ) {
+
+ resourcePath = this.path;
+
+ } else {
+
+ resourcePath = THREE.LoaderUtils.extractUrlBase( url );
+
+ }
+
+ // Tells the LoadingManager to track an extra item, which resolves after
+ // the model is fully loaded. This means the count of items loaded will
+ // be incorrect, but ensures manager.onLoad() does not fire early.
+ scope.manager.itemStart( url );
+
+ var _onError = function ( e ) {
+
+ if ( onError ) {
+
+ onError( e );
+
+ } else {
+
+ console.error( e );
+
+ }
+
+ scope.manager.itemError( url );
+ scope.manager.itemEnd( url );
+
+ };
+
+ var loader = new THREE.FileLoader( scope.manager );
+
+ loader.setPath( this.path );
+ loader.setResponseType( 'arraybuffer' );
+
+ loader.load( url, function ( data ) {
+
+ try {
+
+ scope.parse( data, resourcePath, function ( gltf ) {
+
+ onLoad( gltf );
+
+ scope.manager.itemEnd( url );
+
+ }, _onError );
+
+ } catch ( e ) {
+
+ _onError( e );
+
+ }
+
+ }, onProgress, _onError );
+
+ },
+
+ setCrossOrigin: function ( value ) {
+
+ this.crossOrigin = value;
+ return this;
+
+ },
+
+ setPath: function ( value ) {
+
+ this.path = value;
+ return this;
+
+ },
+
+ setResourcePath: function ( value ) {
+
+ this.resourcePath = value;
+ return this;
+
+ },
+
+ setDRACOLoader: function ( dracoLoader ) {
+
+ this.dracoLoader = dracoLoader;
+ return this;
+
+ },
+
+ parse: function ( data, path, onLoad, onError ) {
+
+ var content;
+ var extensions = {};
+
+ if ( typeof data === 'string' ) {
+
+ content = data;
+
+ } else {
+
+ var magic = THREE.LoaderUtils.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 = THREE.LoaderUtils.decodeText( new Uint8Array( data ) );
+
+ }
+
+ }
+
+ var json = JSON.parse( content );
+
+ if ( json.asset === undefined || json.asset.version[ 0 ] < 2 ) {
+
+ if ( onError ) onError( new Error( 'THREE.GLTFLoader: Unsupported asset. glTF versions >=2.0 are supported. Use LegacyGLTFLoader instead.' ) );
+ return;
+
+ }
+
+ if ( json.extensionsUsed ) {
+
+ for ( var i = 0; i < json.extensionsUsed.length; ++ i ) {
+
+ var extensionName = json.extensionsUsed[ i ];
+ var extensionsRequired = json.extensionsRequired || [];
+
+ switch ( extensionName ) {
+
+ case EXTENSIONS.KHR_LIGHTS_PUNCTUAL:
+ extensions[ extensionName ] = new GLTFLightsExtension( json );
+ break;
+
+ case EXTENSIONS.KHR_MATERIALS_UNLIT:
+ extensions[ extensionName ] = new GLTFMaterialsUnlitExtension( json );
+ break;
+
+ case EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS:
+ extensions[ extensionName ] = new GLTFMaterialsPbrSpecularGlossinessExtension( json );
+ break;
+
+ case EXTENSIONS.KHR_DRACO_MESH_COMPRESSION:
+ extensions[ extensionName ] = new GLTFDracoMeshCompressionExtension( json, this.dracoLoader );
+ break;
+
+ case EXTENSIONS.MSFT_TEXTURE_DDS:
+ extensions[ EXTENSIONS.MSFT_TEXTURE_DDS ] = new GLTFTextureDDSExtension( json );
+ break;
+
+ case EXTENSIONS.KHR_TEXTURE_TRANSFORM:
+ extensions[ EXTENSIONS.KHR_TEXTURE_TRANSFORM ] = new GLTFTextureTransformExtension( json );
+ break;
+
+ default:
+
+ if ( extensionsRequired.indexOf( extensionName ) >= 0 ) {
+
+ console.warn( 'THREE.GLTFLoader: Unknown extension "' + extensionName + '".' );
+
+ }
+
+ }
+
+ }
+
+ }
+
+ var parser = new GLTFParser( json, extensions, {
+
+ path: path || this.resourcePath || '',
+ crossOrigin: this.crossOrigin,
+ manager: this.manager
+
+ } );
+
+ parser.parse( function ( scene, scenes, cameras, animations, json ) {
+
+ var glTF = {
+ scene: scene,
+ scenes: scenes,
+ cameras: cameras,
+ animations: animations,
+ asset: json.asset,
+ parser: parser,
+ userData: {}
+ };
+
+ addUnknownExtensionsToUserData( extensions, glTF, json );
+
+ onLoad( glTF );
+
+ }, onError );
+
+ }
+
+ };
+
+ /* GLTFREGISTRY */
+
+ function GLTFRegistry() {
+
+ var objects = {};
+
+ return {
+
+ get: function ( key ) {
+
+ return objects[ key ];
+
+ },
+
+ add: function ( key, object ) {
+
+ objects[ key ] = object;
+
+ },
+
+ remove: function ( key ) {
+
+ delete objects[ key ];
+
+ },
+
+ removeAll: function () {
+
+ objects = {};
+
+ }
+
+ };
+
+ }
+
+ /*********************************/
+ /********** 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',
+ KHR_TEXTURE_TRANSFORM: 'KHR_texture_transform',
+ MSFT_TEXTURE_DDS: 'MSFT_texture_dds'
+ };
+
+ /**
+ * DDS Texture Extension
+ *
+ * Specification:
+ * https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Vendor/MSFT_texture_dds
+ *
+ */
+ function GLTFTextureDDSExtension() {
+
+ if ( ! THREE.DDSLoader ) {
+
+ throw new Error( 'THREE.GLTFLoader: Attempting to load .dds texture without importing THREE.DDSLoader' );
+
+ }
+
+ this.name = EXTENSIONS.MSFT_TEXTURE_DDS;
+ this.ddsLoader = new THREE.DDSLoader();
+
+ }
+
+ /**
+ * Lights Extension
+ *
+ * Specification: PENDING
+ */
+ function GLTFLightsExtension( json ) {
+
+ this.name = EXTENSIONS.KHR_LIGHTS_PUNCTUAL;
+
+ var extension = ( json.extensions && json.extensions[ EXTENSIONS.KHR_LIGHTS_PUNCTUAL ] ) || {};
+ this.lightDefs = extension.lights || [];
+
+ }
+
+ GLTFLightsExtension.prototype.loadLight = function ( lightIndex ) {
+
+ var lightDef = this.lightDefs[ lightIndex ];
+ var lightNode;
+
+ var color = new THREE.Color( 0xffffff );
+ if ( lightDef.color !== undefined ) color.fromArray( lightDef.color );
+
+ var range = lightDef.range !== undefined ? lightDef.range : 0;
+
+ switch ( lightDef.type ) {
+
+ case 'directional':
+ lightNode = new THREE.DirectionalLight( color );
+ lightNode.target.position.set( 0, 0, -1 );
+ lightNode.add( lightNode.target );
+ break;
+
+ case 'point':
+ lightNode = new THREE.PointLight( color );
+ lightNode.distance = range;
+ break;
+
+ case 'spot':
+ lightNode = new THREE.SpotLight( color );
+ lightNode.distance = range;
+ // Handle spotlight properties.
+ lightDef.spot = lightDef.spot || {};
+ lightDef.spot.innerConeAngle = lightDef.spot.innerConeAngle !== undefined ? lightDef.spot.innerConeAngle : 0;
+ lightDef.spot.outerConeAngle = lightDef.spot.outerConeAngle !== undefined ? lightDef.spot.outerConeAngle : Math.PI / 4.0;
+ lightNode.angle = lightDef.spot.outerConeAngle;
+ lightNode.penumbra = 1.0 - lightDef.spot.innerConeAngle / lightDef.spot.outerConeAngle;
+ lightNode.target.position.set( 0, 0, -1 );
+ lightNode.add( lightNode.target );
+ break;
+
+ default:
+ throw new Error( 'THREE.GLTFLoader: Unexpected light type, "' + lightDef.type + '".' );
+
+ }
+
+ lightNode.decay = 2;
+
+ if ( lightDef.intensity !== undefined ) lightNode.intensity = lightDef.intensity;
+
+ lightNode.name = lightDef.name || ( 'light_' + lightIndex );
+
+ return Promise.resolve( lightNode );
+
+ };
+
+ /**
+ * Unlit Materials Extension (pending)
+ *
+ * PR: https://github.com/KhronosGroup/glTF/pull/1163
+ */
+ function GLTFMaterialsUnlitExtension( json ) {
+
+ this.name = EXTENSIONS.KHR_MATERIALS_UNLIT;
+
+ }
+
+ GLTFMaterialsUnlitExtension.prototype.getMaterialType = function ( material ) {
+
+ return THREE.MeshBasicMaterial;
+
+ };
+
+ GLTFMaterialsUnlitExtension.prototype.extendParams = function ( materialParams, material, parser ) {
+
+ var pending = [];
+
+ materialParams.color = new THREE.Color( 1.0, 1.0, 1.0 );
+ materialParams.opacity = 1.0;
+
+ var metallicRoughness = material.pbrMetallicRoughness;
+
+ if ( metallicRoughness ) {
+
+ if ( Array.isArray( metallicRoughness.baseColorFactor ) ) {
+
+ var array = metallicRoughness.baseColorFactor;
+
+ materialParams.color.fromArray( array );
+ materialParams.opacity = array[ 3 ];
+
+ }
+
+ if ( metallicRoughness.baseColorTexture !== undefined ) {
+
+ pending.push( parser.assignTexture( materialParams, 'map', metallicRoughness.baseColorTexture ) );
+
+ }
+
+ }
+
+ return Promise.all( pending );
+
+ };
+
+ /* 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 };
+
+ 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: THREE.LoaderUtils.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 = THREE.LoaderUtils.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.' );
+
+ }
+
+ }
+
+ /**
+ * DRACO Mesh Compression Extension
+ *
+ * Specification: https://github.com/KhronosGroup/glTF/pull/874
+ */
+ function GLTFDracoMeshCompressionExtension( json, dracoLoader ) {
+
+ if ( ! dracoLoader ) {
+
+ throw new Error( 'THREE.GLTFLoader: No DRACOLoader instance provided.' );
+
+ }
+
+ this.name = EXTENSIONS.KHR_DRACO_MESH_COMPRESSION;
+ this.json = json;
+ this.dracoLoader = dracoLoader;
+ THREE.DRACOLoader.getDecoderModule();
+
+ }
+
+ GLTFDracoMeshCompressionExtension.prototype.decodePrimitive = function ( primitive, parser ) {
+
+ var json = this.json;
+ var dracoLoader = this.dracoLoader;
+ var bufferViewIndex = primitive.extensions[ this.name ].bufferView;
+ var gltfAttributeMap = primitive.extensions[ this.name ].attributes;
+ var threeAttributeMap = {};
+ var attributeNormalizedMap = {};
+ var attributeTypeMap = {};
+
+ for ( var attributeName in gltfAttributeMap ) {
+
+ if ( ! ( attributeName in ATTRIBUTES ) ) continue;
+
+ threeAttributeMap[ ATTRIBUTES[ attributeName ] ] = gltfAttributeMap[ attributeName ];
+
+ }
+
+ for ( attributeName in primitive.attributes ) {
+
+ if ( ATTRIBUTES[ attributeName ] !== undefined && gltfAttributeMap[ attributeName ] !== undefined ) {
+
+ var accessorDef = json.accessors[ primitive.attributes[ attributeName ] ];
+ var componentType = WEBGL_COMPONENT_TYPES[ accessorDef.componentType ];
+
+ attributeTypeMap[ ATTRIBUTES[ attributeName ] ] = componentType;
+ attributeNormalizedMap[ ATTRIBUTES[ attributeName ] ] = accessorDef.normalized === true;
+
+ }
+
+ }
+
+ return parser.getDependency( 'bufferView', bufferViewIndex ).then( function ( bufferView ) {
+
+ return new Promise( function ( resolve ) {
+
+ dracoLoader.decodeDracoFile( bufferView, function ( geometry ) {
+
+ for ( var attributeName in geometry.attributes ) {
+
+ var attribute = geometry.attributes[ attributeName ];
+ var normalized = attributeNormalizedMap[ attributeName ];
+
+ if ( normalized !== undefined ) attribute.normalized = normalized;
+
+ }
+
+ resolve( geometry );
+
+ }, threeAttributeMap, attributeTypeMap );
+
+ } );
+
+ } );
+
+ };
+
+ /**
+ * Texture Transform Extension
+ *
+ * Specification:
+ */
+ function GLTFTextureTransformExtension( json ) {
+
+ this.name = EXTENSIONS.KHR_TEXTURE_TRANSFORM;
+
+ }
+
+ GLTFTextureTransformExtension.prototype.extendTexture = function ( texture, transform ) {
+
+ texture = texture.clone();
+
+ if ( transform.offset !== undefined ) {
+
+ texture.offset.fromArray( transform.offset );
+
+ }
+
+ if ( transform.rotation !== undefined ) {
+
+ texture.rotation = transform.rotation;
+
+ }
+
+ if ( transform.scale !== undefined ) {
+
+ texture.repeat.fromArray( transform.scale );
+
+ }
+
+ if ( transform.texCoord !== undefined ) {
+
+ console.warn( 'THREE.GLTFLoader: Custom UV sets in "' + this.name + '" extension not yet supported.' );
+
+ }
+
+ texture.needsUpdate = true;
+
+ return texture;
+
+ };
+
+ /**
+ * Specular-Glossiness Extension
+ *
+ * Specification: https://github.com/KhronosGroup/glTF/tree/master/extensions/2.0/Khronos/KHR_materials_pbrSpecularGlossiness
+ */
+ function GLTFMaterialsPbrSpecularGlossinessExtension() {
+
+ return {
+
+ name: EXTENSIONS.KHR_MATERIALS_PBR_SPECULAR_GLOSSINESS,
+
+ specularGlossinessParams: [
+ 'color',
+ 'map',
+ 'lightMap',
+ 'lightMapIntensity',
+ 'aoMap',
+ 'aoMapIntensity',
+ 'emissive',
+ 'emissiveIntensity',
+ 'emissiveMap',
+ 'bumpMap',
+ 'bumpScale',
+ 'normalMap',
+ 'displacementMap',
+ 'displacementScale',
+ 'displacementBias',
+ 'specularMap',
+ 'specular',
+ 'glossinessMap',
+ 'glossiness',
+ 'alphaMap',
+ 'envMap',
+ 'envMapIntensity',
+ 'refractionRatio',
+ ],
+
+ getMaterialType: function () {
+
+ return THREE.ShaderMaterial;
+
+ },
+
+ extendParams: function ( params, material, parser ) {
+
+ var pbrSpecularGlossiness = material.extensions[ this.name ];
+
+ var shader = THREE.ShaderLib[ 'standard' ];
+
+ var uniforms = THREE.UniformsUtils.clone( shader.uniforms );
+
+ var specularMapParsFragmentChunk = [
+ '#ifdef USE_SPECULARMAP',
+ ' uniform sampler2D specularMap;',
+ '#endif'
+ ].join( '\n' );
+
+ var glossinessMapParsFragmentChunk = [
+ '#ifdef USE_GLOSSINESSMAP',
+ ' uniform sampler2D glossinessMap;',
+ '#endif'
+ ].join( '\n' );
+
+ var specularMapFragmentChunk = [
+ 'vec3 specularFactor = specular;',
+ '#ifdef USE_SPECULARMAP',
+ ' vec4 texelSpecular = texture2D( specularMap, vUv );',
+ ' texelSpecular = sRGBToLinear( texelSpecular );',
+ ' // reads channel RGB, compatible with a glTF Specular-Glossiness (RGBA) texture',
+ ' specularFactor *= texelSpecular.rgb;',
+ '#endif'
+ ].join( '\n' );
+
+ var glossinessMapFragmentChunk = [
+ 'float glossinessFactor = glossiness;',
+ '#ifdef USE_GLOSSINESSMAP',
+ ' vec4 texelGlossiness = texture2D( glossinessMap, vUv );',
+ ' // reads channel A, compatible with a glTF Specular-Glossiness (RGBA) texture',
+ ' glossinessFactor *= texelGlossiness.a;',
+ '#endif'
+ ].join( '\n' );
+
+ var lightPhysicalFragmentChunk = [
+ 'PhysicalMaterial material;',
+ 'material.diffuseColor = diffuseColor.rgb;',
+ 'material.specularRoughness = clamp( 1.0 - glossinessFactor, 0.04, 1.0 );',
+ 'material.specularColor = specularFactor.rgb;',
+ ].join( '\n' );
+
+ var fragmentShader = shader.fragmentShader
+ .replace( 'uniform float roughness;', 'uniform vec3 specular;' )
+ .replace( 'uniform float metalness;', 'uniform float glossiness;' )
+ .replace( '#include ', specularMapParsFragmentChunk )
+ .replace( '#include ', glossinessMapParsFragmentChunk )
+ .replace( '#include ', specularMapFragmentChunk )
+ .replace( '#include ', glossinessMapFragmentChunk )
+ .replace( '#include ', lightPhysicalFragmentChunk );
+
+ delete uniforms.roughness;
+ delete uniforms.metalness;
+ delete uniforms.roughnessMap;
+ delete uniforms.metalnessMap;
+
+ uniforms.specular = { value: new THREE.Color().setHex( 0x111111 ) };
+ uniforms.glossiness = { value: 0.5 };
+ uniforms.specularMap = { value: null };
+ uniforms.glossinessMap = { value: null };
+
+ params.vertexShader = shader.vertexShader;
+ params.fragmentShader = fragmentShader;
+ params.uniforms = uniforms;
+ params.defines = { 'STANDARD': '' };
+
+ params.color = new THREE.Color( 1.0, 1.0, 1.0 );
+ params.opacity = 1.0;
+
+ var pending = [];
+
+ if ( Array.isArray( pbrSpecularGlossiness.diffuseFactor ) ) {
+
+ var array = pbrSpecularGlossiness.diffuseFactor;
+
+ params.color.fromArray( array );
+ params.opacity = array[ 3 ];
+
+ }
+
+ if ( pbrSpecularGlossiness.diffuseTexture !== undefined ) {
+
+ pending.push( parser.assignTexture( params, 'map', pbrSpecularGlossiness.diffuseTexture ) );
+
+ }
+
+ params.emissive = new THREE.Color( 0.0, 0.0, 0.0 );
+ params.glossiness = pbrSpecularGlossiness.glossinessFactor !== undefined ? pbrSpecularGlossiness.glossinessFactor : 1.0;
+ params.specular = new THREE.Color( 1.0, 1.0, 1.0 );
+
+ if ( Array.isArray( pbrSpecularGlossiness.specularFactor ) ) {
+
+ params.specular.fromArray( pbrSpecularGlossiness.specularFactor );
+
+ }
+
+ if ( pbrSpecularGlossiness.specularGlossinessTexture !== undefined ) {
+
+ var specGlossMapDef = pbrSpecularGlossiness.specularGlossinessTexture;
+ pending.push( parser.assignTexture( params, 'glossinessMap', specGlossMapDef ) );
+ pending.push( parser.assignTexture( params, 'specularMap', specGlossMapDef ) );
+
+ }
+
+ return Promise.all( pending );
+
+ },
+
+ createMaterial: function ( params ) {
+
+ // setup material properties based on MeshStandardMaterial for Specular-Glossiness
+
+ var material = new THREE.ShaderMaterial( {
+ defines: params.defines,
+ vertexShader: params.vertexShader,
+ fragmentShader: params.fragmentShader,
+ uniforms: params.uniforms,
+ fog: true,
+ lights: true,
+ opacity: params.opacity,
+ transparent: params.transparent
+ } );
+
+ material.isGLTFSpecularGlossinessMaterial = true;
+
+ material.color = params.color;
+
+ material.map = params.map === undefined ? null : params.map;
+
+ material.lightMap = null;
+ material.lightMapIntensity = 1.0;
+
+ material.aoMap = params.aoMap === undefined ? null : params.aoMap;
+ material.aoMapIntensity = 1.0;
+
+ material.emissive = params.emissive;
+ material.emissiveIntensity = 1.0;
+ material.emissiveMap = params.emissiveMap === undefined ? null : params.emissiveMap;
+
+ material.bumpMap = params.bumpMap === undefined ? null : params.bumpMap;
+ material.bumpScale = 1;
+
+ material.normalMap = params.normalMap === undefined ? null : params.normalMap;
+ if ( params.normalScale ) material.normalScale = params.normalScale;
+
+ material.displacementMap = null;
+ material.displacementScale = 1;
+ material.displacementBias = 0;
+
+ material.specularMap = params.specularMap === undefined ? null : params.specularMap;
+ material.specular = params.specular;
+
+ material.glossinessMap = params.glossinessMap === undefined ? null : params.glossinessMap;
+ material.glossiness = params.glossiness;
+
+ material.alphaMap = null;
+
+ material.envMap = params.envMap === undefined ? null : params.envMap;
+ material.envMapIntensity = 1.0;
+
+ material.refractionRatio = 0.98;
+
+ material.extensions.derivatives = true;
+
+ return material;
+
+ },
+
+ /**
+ * Clones a GLTFSpecularGlossinessMaterial instance. The ShaderMaterial.copy() method can
+ * copy only properties it knows about or inherits, and misses many properties that would
+ * normally be defined by MeshStandardMaterial.
+ *
+ * This method allows GLTFSpecularGlossinessMaterials to be cloned in the process of
+ * loading a glTF model, but cloning later (e.g. by the user) would require these changes
+ * AND also updating `.onBeforeRender` on the parent mesh.
+ *
+ * @param {THREE.ShaderMaterial} source
+ * @return {THREE.ShaderMaterial}
+ */
+ cloneMaterial: function ( source ) {
+
+ var target = source.clone();
+
+ target.isGLTFSpecularGlossinessMaterial = true;
+
+ var params = this.specularGlossinessParams;
+
+ for ( var i = 0, il = params.length; i < il; i ++ ) {
+
+ target[ params[ i ] ] = source[ params[ i ] ];
+
+ }
+
+ return target;
+
+ },
+
+ // Here's based on refreshUniformsCommon() and refreshUniformsStandard() in WebGLRenderer.
+ refreshUniforms: function ( renderer, scene, camera, geometry, material, group ) {
+
+ if ( material.isGLTFSpecularGlossinessMaterial !== true ) {
+
+ return;
+
+ }
+
+ var uniforms = material.uniforms;
+ var defines = material.defines;
+
+ uniforms.opacity.value = material.opacity;
+
+ uniforms.diffuse.value.copy( material.color );
+ uniforms.emissive.value.copy( material.emissive ).multiplyScalar( material.emissiveIntensity );
+
+ uniforms.map.value = material.map;
+ uniforms.specularMap.value = material.specularMap;
+ uniforms.alphaMap.value = material.alphaMap;
+
+ uniforms.lightMap.value = material.lightMap;
+ uniforms.lightMapIntensity.value = material.lightMapIntensity;
+
+ uniforms.aoMap.value = material.aoMap;
+ uniforms.aoMapIntensity.value = material.aoMapIntensity;
+
+ // uv repeat and offset setting priorities
+ // 1. color map
+ // 2. specular map
+ // 3. normal map
+ // 4. bump map
+ // 5. alpha map
+ // 6. emissive map
+
+ var uvScaleMap;
+
+ if ( material.map ) {
+
+ uvScaleMap = material.map;
+
+ } else if ( material.specularMap ) {
+
+ uvScaleMap = material.specularMap;
+
+ } else if ( material.displacementMap ) {
+
+ uvScaleMap = material.displacementMap;
+
+ } else if ( material.normalMap ) {
+
+ uvScaleMap = material.normalMap;
+
+ } else if ( material.bumpMap ) {
+
+ uvScaleMap = material.bumpMap;
+
+ } else if ( material.glossinessMap ) {
+
+ uvScaleMap = material.glossinessMap;
+
+ } else if ( material.alphaMap ) {
+
+ uvScaleMap = material.alphaMap;
+
+ } else if ( material.emissiveMap ) {
+
+ uvScaleMap = material.emissiveMap;
+
+ }
+
+ if ( uvScaleMap !== undefined ) {
+
+ // backwards compatibility
+ if ( uvScaleMap.isWebGLRenderTarget ) {
+
+ uvScaleMap = uvScaleMap.texture;
+
+ }
+
+ if ( uvScaleMap.matrixAutoUpdate === true ) {
+
+ uvScaleMap.updateMatrix();
+
+ }
+
+ uniforms.uvTransform.value.copy( uvScaleMap.matrix );
+
+ }
+
+ uniforms.envMap.value = material.envMap;
+ uniforms.envMapIntensity.value = material.envMapIntensity;
+ uniforms.flipEnvMap.value = ( material.envMap && material.envMap.isCubeTexture ) ? - 1 : 1;
+
+ uniforms.refractionRatio.value = material.refractionRatio;
+
+ uniforms.specular.value.copy( material.specular );
+ uniforms.glossiness.value = material.glossiness;
+
+ uniforms.glossinessMap.value = material.glossinessMap;
+
+ uniforms.emissiveMap.value = material.emissiveMap;
+ uniforms.bumpMap.value = material.bumpMap;
+ uniforms.normalMap.value = material.normalMap;
+
+ uniforms.displacementMap.value = material.displacementMap;
+ uniforms.displacementScale.value = material.displacementScale;
+ uniforms.displacementBias.value = material.displacementBias;
+
+ if ( uniforms.glossinessMap.value !== null && defines.USE_GLOSSINESSMAP === undefined ) {
+
+ defines.USE_GLOSSINESSMAP = '';
+ // set USE_ROUGHNESSMAP to enable vUv
+ defines.USE_ROUGHNESSMAP = '';
+
+ }
+
+ if ( uniforms.glossinessMap.value === null && defines.USE_GLOSSINESSMAP !== undefined ) {
+
+ delete defines.USE_GLOSSINESSMAP;
+ delete defines.USE_ROUGHNESSMAP;
+
+ }
+
+ }
+
+ };
+
+ }
+
+ /*********************************/
+ /********** INTERPOLATION ********/
+ /*********************************/
+
+ // Spline Interpolation
+ // Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#appendix-c-spline-interpolation
+ function GLTFCubicSplineInterpolant( parameterPositions, sampleValues, sampleSize, resultBuffer ) {
+
+ THREE.Interpolant.call( this, parameterPositions, sampleValues, sampleSize, resultBuffer );
+
+ }
+
+ GLTFCubicSplineInterpolant.prototype = Object.create( THREE.Interpolant.prototype );
+ GLTFCubicSplineInterpolant.prototype.constructor = GLTFCubicSplineInterpolant;
+
+ GLTFCubicSplineInterpolant.prototype.copySampleValue_ = function ( index ) {
+
+ // Copies a sample value to the result buffer. See description of glTF
+ // CUBICSPLINE values layout in interpolate_() function below.
+
+ var result = this.resultBuffer,
+ values = this.sampleValues,
+ valueSize = this.valueSize,
+ offset = index * valueSize * 3 + valueSize;
+
+ for ( var i = 0; i !== valueSize; i ++ ) {
+
+ result[ i ] = values[ offset + i ];
+
+ }
+
+ return result;
+
+ };
+
+ GLTFCubicSplineInterpolant.prototype.beforeStart_ = GLTFCubicSplineInterpolant.prototype.copySampleValue_;
+
+ GLTFCubicSplineInterpolant.prototype.afterEnd_ = GLTFCubicSplineInterpolant.prototype.copySampleValue_;
+
+ GLTFCubicSplineInterpolant.prototype.interpolate_ = function ( i1, t0, t, t1 ) {
+
+ var result = this.resultBuffer;
+ var values = this.sampleValues;
+ var stride = this.valueSize;
+
+ var stride2 = stride * 2;
+ var stride3 = stride * 3;
+
+ var td = t1 - t0;
+
+ var p = ( t - t0 ) / td;
+ var pp = p * p;
+ var ppp = pp * p;
+
+ var offset1 = i1 * stride3;
+ var offset0 = offset1 - stride3;
+
+ var s0 = 2 * ppp - 3 * pp + 1;
+ var s1 = ppp - 2 * pp + p;
+ var s2 = - 2 * ppp + 3 * pp;
+ var s3 = ppp - pp;
+
+ // Layout of keyframe output values for CUBICSPLINE animations:
+ // [ inTangent_1, splineVertex_1, outTangent_1, inTangent_2, splineVertex_2, ... ]
+ for ( var i = 0; i !== stride; i ++ ) {
+
+ var p0 = values[ offset0 + i + stride ]; // splineVertex_k
+ var m0 = values[ offset0 + i + stride2 ] * td; // outTangent_k * (t_k+1 - t_k)
+ var p1 = values[ offset1 + i + stride ]; // splineVertex_k+1
+ var m1 = values[ offset1 + i ] * td; // inTangent_k+1 * (t_k+1 - t_k)
+
+ result[ i ] = s0 * p0 + s1 * m0 + s2 * p1 + s3 * m1;
+
+ }
+
+ return result;
+
+ };
+
+ /*********************************/
+ /********** 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_TYPE = {
+ 5126: Number,
+ //35674: THREE.Matrix2,
+ 35675: THREE.Matrix3,
+ 35676: THREE.Matrix4,
+ 35664: THREE.Vector2,
+ 35665: THREE.Vector3,
+ 35666: THREE.Vector4,
+ 35678: THREE.Texture
+ };
+
+ var WEBGL_COMPONENT_TYPES = {
+ 5120: Int8Array,
+ 5121: Uint8Array,
+ 5122: Int16Array,
+ 5123: Uint16Array,
+ 5125: Uint32Array,
+ 5126: Float32Array
+ };
+
+ var WEBGL_FILTERS = {
+ 9728: THREE.NearestFilter,
+ 9729: THREE.LinearFilter,
+ 9984: THREE.NearestMipMapNearestFilter,
+ 9985: THREE.LinearMipMapNearestFilter,
+ 9986: THREE.NearestMipMapLinearFilter,
+ 9987: THREE.LinearMipMapLinearFilter
+ };
+
+ var WEBGL_WRAPPINGS = {
+ 33071: THREE.ClampToEdgeWrapping,
+ 33648: THREE.MirroredRepeatWrapping,
+ 10497: THREE.RepeatWrapping
+ };
+
+ var WEBGL_SIDES = {
+ 1028: THREE.BackSide, // Culling front
+ 1029: THREE.FrontSide // Culling back
+ //1032: THREE.NoSide // Culling front and back, what to do?
+ };
+
+ var WEBGL_DEPTH_FUNCS = {
+ 512: THREE.NeverDepth,
+ 513: THREE.LessDepth,
+ 514: THREE.EqualDepth,
+ 515: THREE.LessEqualDepth,
+ 516: THREE.GreaterEqualDepth,
+ 517: THREE.NotEqualDepth,
+ 518: THREE.GreaterEqualDepth,
+ 519: THREE.AlwaysDepth
+ };
+
+ var WEBGL_BLEND_EQUATIONS = {
+ 32774: THREE.AddEquation,
+ 32778: THREE.SubtractEquation,
+ 32779: THREE.ReverseSubtractEquation
+ };
+
+ var WEBGL_BLEND_FUNCS = {
+ 0: THREE.ZeroFactor,
+ 1: THREE.OneFactor,
+ 768: THREE.SrcColorFactor,
+ 769: THREE.OneMinusSrcColorFactor,
+ 770: THREE.SrcAlphaFactor,
+ 771: THREE.OneMinusSrcAlphaFactor,
+ 772: THREE.DstAlphaFactor,
+ 773: THREE.OneMinusDstAlphaFactor,
+ 774: THREE.DstColorFactor,
+ 775: THREE.OneMinusDstColorFactor,
+ 776: THREE.SrcAlphaSaturateFactor
+ // The followings are not supported by Three.js yet
+ //32769: CONSTANT_COLOR,
+ //32770: ONE_MINUS_CONSTANT_COLOR,
+ //32771: CONSTANT_ALPHA,
+ //32772: ONE_MINUS_CONSTANT_COLOR
+ };
+
+ var WEBGL_TYPE_SIZES = {
+ 'SCALAR': 1,
+ 'VEC2': 2,
+ 'VEC3': 3,
+ 'VEC4': 4,
+ 'MAT2': 4,
+ 'MAT3': 9,
+ 'MAT4': 16
+ };
+
+ var ATTRIBUTES = {
+ POSITION: 'position',
+ NORMAL: 'normal',
+ TEXCOORD_0: 'uv',
+ TEXCOORD_1: 'uv2',
+ COLOR_0: 'color',
+ WEIGHTS_0: 'skinWeight',
+ JOINTS_0: 'skinIndex',
+ };
+
+ var PATH_PROPERTIES = {
+ scale: 'scale',
+ translation: 'position',
+ rotation: 'quaternion',
+ weights: 'morphTargetInfluences'
+ };
+
+ var INTERPOLATION = {
+ CUBICSPLINE: THREE.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: THREE.InterpolateLinear,
+ STEP: THREE.InterpolateDiscrete
+ };
+
+ var STATES_ENABLES = {
+ 2884: 'CULL_FACE',
+ 2929: 'DEPTH_TEST',
+ 3042: 'BLEND',
+ 3089: 'SCISSOR_TEST',
+ 32823: 'POLYGON_OFFSET_FILL',
+ 32926: 'SAMPLE_ALPHA_TO_COVERAGE'
+ };
+
+ var ALPHA_MODES = {
+ OPAQUE: 'OPAQUE',
+ MASK: 'MASK',
+ BLEND: 'BLEND'
+ };
+
+ var MIME_TYPE_FORMATS = {
+ 'image/png': THREE.RGBAFormat,
+ 'image/jpeg': THREE.RGBFormat
+ };
+
+ /* 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;
+
+ }
+
+ /**
+ * 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
+ } );
+
+ }
+
+ function addUnknownExtensionsToUserData( knownExtensions, object, objectDef ) {
+
+ // Add unknown glTF extensions to an object's userData.
+
+ for ( var name in objectDef.extensions ) {
+
+ if ( knownExtensions[ name ] === undefined ) {
+
+ object.userData.gltfExtensions = object.userData.gltfExtensions || {};
+ object.userData.gltfExtensions[ name ] = objectDef.extensions[ name ];
+
+ }
+
+ }
+
+ }
+
+ /**
+ * @param {THREE.Object3D|THREE.Material|THREE.BufferGeometry} object
+ * @param {GLTF.definition} gltfDef
+ */
+ function assignExtrasToUserData( object, gltfDef ) {
+
+ if ( gltfDef.extras !== undefined ) {
+
+ if ( typeof gltfDef.extras === 'object' ) {
+
+ object.userData = gltfDef.extras;
+
+ } else {
+
+ console.warn( 'THREE.GLTFLoader: Ignoring primitive type .extras, ' + gltfDef.extras );
+
+ }
+
+ }
+
+ }
+
+ /**
+ * Specification: https://github.com/KhronosGroup/glTF/blob/master/specification/2.0/README.md#morph-targets
+ *
+ * @param {THREE.BufferGeometry} geometry
+ * @param {Array} targets
+ * @param {GLTFParser} parser
+ * @return {Promise}
+ */
+ function addMorphTargets( geometry, targets, parser ) {
+
+ 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 Promise.resolve( geometry );
+
+ var pendingPositionAccessors = [];
+ var pendingNormalAccessors = [];
+
+ for ( var i = 0, il = targets.length; i < il; i ++ ) {
+
+ var target = targets[ i ];
+
+ if ( hasMorphPosition ) {
+
+ // TODO: Error-prone use of a callback inside a loop.
+ var accessor = target.POSITION !== undefined
+ ? parser.getDependency( 'accessor', target.POSITION )
+ .then( function ( accessor ) {
+ // Cloning not to pollute original accessor below
+ return cloneBufferAttribute( accessor );
+ } )
+ : geometry.attributes.position;
+
+ pendingPositionAccessors.push( accessor );
+
+ }
+
+ if ( hasMorphNormal ) {
+
+ // TODO: Error-prone use of a callback inside a loop.
+ var accessor = target.NORMAL !== undefined
+ ? parser.getDependency( 'accessor', target.NORMAL )
+ .then( function ( accessor ) {
+ return cloneBufferAttribute( accessor );
+ } )
+ : geometry.attributes.normal;
+
+ pendingNormalAccessors.push( accessor );
+
+ }
+
+ }
+
+ return Promise.all( [
+ Promise.all( pendingPositionAccessors ),
+ Promise.all( pendingNormalAccessors )
+ ] ).then( function ( accessors ) {
+
+ var morphPositions = accessors[ 0 ];
+ var morphNormals = accessors[ 1 ];
+
+ 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.
+
+ if ( target.POSITION !== undefined ) {
+
+ var positionAttribute = morphPositions[ i ];
+ positionAttribute.name = attributeName;
+
+ var position = geometry.attributes.position;
+
+ 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 )
+ );
+
+ }
+
+ }
+
+ }
+
+ if ( hasMorphNormal ) {
+
+ // see target.POSITION's comment
+
+ if ( target.NORMAL !== undefined ) {
+
+ var normalAttribute = morphNormals[ i ];
+ normalAttribute.name = attributeName;
+
+ var normal = geometry.attributes.normal;
+
+ 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 )
+ );
+
+ }
+
+ }
+
+ }
+
+ }
+
+ if ( hasMorphPosition ) geometry.morphAttributes.position = morphPositions;
+ if ( hasMorphNormal ) geometry.morphAttributes.normal = morphNormals;
+
+ return geometry;
+
+ } );
+
+ }
+
+ /**
+ * @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 ) {
+
+ var dracoExtA = a.extensions ? a.extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ] : undefined;
+ var dracoExtB = b.extensions ? b.extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ] : undefined;
+
+ if ( dracoExtA && dracoExtB ) {
+
+ if ( dracoExtA.bufferView !== dracoExtB.bufferView ) return false;
+
+ return isObjectEqual( dracoExtA.attributes, dracoExtB.attributes );
+
+ }
+
+ 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;
+
+ }
+
+ 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 ] = attribute.getX( i );
+ if ( itemSize >= 2 ) array[ i + 1 ] = attribute.getY( i );
+ if ( itemSize >= 3 ) array[ i + 2 ] = attribute.getZ( i );
+ if ( itemSize >= 4 ) array[ i + 3 ] = attribute.getW( i );
+
+ }
+
+ return new THREE.BufferAttribute( array, itemSize, attribute.normalized );
+
+ }
+
+ return attribute.clone();
+
+ }
+
+ /**
+ * 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} 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 ( primitive.extensions && primitive.extensions[ EXTENSIONS.KHR_DRACO_MESH_COMPRESSION ] ) 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;
+
+ }
+
+ /* GLTF PARSER */
+
+ function GLTFParser( json, extensions, options ) {
+
+ this.json = json || {};
+ this.extensions = extensions || {};
+ this.options = options || {};
+
+ // loader object cache
+ this.cache = new GLTFRegistry();
+
+ // BufferGeometry caching
+ this.primitiveCache = [];
+ this.multiplePrimitivesCache = [];
+ this.multiPassGeometryCache = [];
+
+ 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' );
+
+ }
+
+ GLTFParser.prototype.parse = function ( 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.
+ */
+ GLTFParser.prototype.markDefs = function () {
+
+ 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}
+ */
+ GLTFParser.prototype.getDependency = function ( type, index ) {
+
+ 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;
+
+ case 'light':
+ dependency = this.extensions[ EXTENSIONS.KHR_LIGHTS_PUNCTUAL ].loadLight( 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>}
+ */
+ GLTFParser.prototype.getDependencies = function ( type ) {
+
+ 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} types
+ * @return {Promise