/** * @author Tony Parisi / http://www.tonyparisi.com/ */ THREE.GLTFLoaderUtils = Object.create(Object, { // errors INVALID_PATH_ERROR: { value: "INVALID_PATH" }, INVALID_TYPE_ERROR: { value: "INVALID_TYPE" }, XMLHTTPREQUEST_STATUS_ERROR: { value: "XMLHTTPREQUEST_STATUS_ERROR" }, // misc constants ARRAY_BUFFER: { value: "ArrayBuffer" }, // webgl constants (from https://www.opengl.org/registry/api/GL/glcorearb.h) WEBGL_CONSTANTS: { value: { 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, TRIANGLES: 4, UNSIGNED_BYTE: 5121, UNSIGNED_SHORT: 5123 } }, _streams : { value:{}, writable: true }, _streamsStatus: { value: {}, writable: true }, _resources: { value: {}, writable: true }, _resourcesStatus: { value: {}, writable: true }, // initialization init: { value: function() { this._streams = {}; this._streamsStatus = {}; this._resources = {}; this._resourcesStatus = {}; } }, //manage entries _containsResource: { enumerable: false, value: function(resourceID) { return this._resources[resourceID] ? true : false; } }, _storeResource: { enumerable: false, value: function(resourceID, resource) { if (!resourceID) { console.log("ERROR: entry does not contain id, cannot store"); return; } if (this._containsResource[resourceID]) { console.log("WARNING: resource:"+resourceID+" is already stored, overriding"); } this._resources[resourceID] = resource; } }, _getResource: { enumerable: false, value: function(resourceID) { return this._resources[resourceID]; } }, _loadStream: { value: function(path, type, delegate) { var dataUriRegex = /^data:(.*?)(;base64)?,(.*)$/; function decodeDataUriText(isBase64, data) { var result = decodeURIComponent(data); if (isBase64) { return atob(result); } return result; } function decodeDataUriArrayBuffer(isBase64, data) { var byteString = decodeDataUriText(isBase64, data); var buffer = new ArrayBuffer(byteString.length); var view = new Uint8Array(buffer); for (var i = 0; i < byteString.length; i++) { view[i] = byteString.charCodeAt(i); } return buffer; } function decodeDataUri(dataUriRegexResult, responseType) { responseType = typeof responseType !== 'undefined' ? responseType : ''; var mimeType = dataUriRegexResult[1]; var isBase64 = !!dataUriRegexResult[2]; var data = dataUriRegexResult[3]; switch (responseType) { case '': case 'text': return decodeDataUriText(isBase64, data); case 'ArrayBuffer': return decodeDataUriArrayBuffer(isBase64, data); case 'blob': var buffer = decodeDataUriArrayBuffer(isBase64, data); return new Blob([buffer], { type : mimeType }); case 'document': var parser = new DOMParser(); return parser.parseFromString(decodeDataUriText(isBase64, data), mimeType); case 'json': return JSON.parse(decodeDataUriText(isBase64, data)); default: throw 'Unhandled responseType: ' + responseType; } } var dataUriRegexResult = dataUriRegex.exec(path); if (dataUriRegexResult !== null) { delegate.streamAvailable(path, decodeDataUri(dataUriRegexResult, type)); return; } var self = this; if (!type) { delegate.handleError(THREE.GLTFLoaderUtils.INVALID_TYPE_ERROR, null); return; } if (!path) { delegate.handleError(THREE.GLTFLoaderUtils.INVALID_PATH_ERROR); return; } var xhr = new XMLHttpRequest(); xhr.open('GET', path, true); xhr.responseType = (type === THREE.GLTFLoaderUtils.ARRAY_BUFFER) ? "arraybuffer" : "text"; //if this is not specified, 1 "big blob" scenes fails to load. xhr.setRequestHeader("If-Modified-Since", "Sat, 01 Jan 1970 00:00:00 GMT"); xhr.onload = function(e) { if ((xhr.status == 200) || (xhr.status == 206)) { delegate.streamAvailable(path, xhr.response); } else { delegate.handleError(THREE.GLTFLoaderUtils.XMLHTTPREQUEST_STATUS_ERROR, this.status); } }; xhr.send(null); } }, send: { value: 0, writable: true }, requested: { value: 0, writable: true }, _handleRequest: { value: function(request) { var resourceStatus = this._resourcesStatus[request.id]; if (resourceStatus) { this._resourcesStatus[request.id]++; } else { this._resourcesStatus[request.id] = 1; } var streamStatus = this._streamsStatus[request.uri]; if (streamStatus && streamStatus.status === "loading" ) { streamStatus.requests.push(request); return; } this._streamsStatus[request.uri] = { status : "loading", requests : [request] }; var self = this; var processResourceDelegate = {}; processResourceDelegate.streamAvailable = function(path, res_) { var streamStatus = self._streamsStatus[path]; var requests = streamStatus.requests; requests.forEach( function(req_) { var subArray = res_.slice(req_.range[0], req_.range[1]); var convertedResource = req_.delegate.convert(subArray, req_.ctx); self._storeResource(req_.id, convertedResource); req_.delegate.resourceAvailable(convertedResource, req_.ctx); --self._resourcesStatus[req_.id]; }, this); delete self._streamsStatus[path]; }; processResourceDelegate.handleError = function(errorCode, info) { request.delegate.handleError(errorCode, info); } this._loadStream(request.uri, request.type, processResourceDelegate); } }, _elementSizeForGLType: { value: function(componentType, type) { var nElements = 0; switch(type) { case "SCALAR" : nElements = 1; break; case "VEC2" : nElements = 2; break; case "VEC3" : nElements = 3; break; case "VEC4" : nElements = 4; break; case "MAT2" : nElements = 4; break; case "MAT3" : nElements = 9; break; case "MAT4" : nElements = 16; break; default : debugger; break; } switch (componentType) { case THREE.GLTFLoaderUtils.WEBGL_CONSTANTS.FLOAT : return Float32Array.BYTES_PER_ELEMENT * nElements; case THREE.GLTFLoaderUtils.WEBGL_CONSTANTS.UNSIGNED_BYTE : return Uint8Array.BYTES_PER_ELEMENT * nElements; case THREE.GLTFLoaderUtils.WEBGL_CONSTANTS.UNSIGNED_SHORT : return Uint16Array.BYTES_PER_ELEMENT * nElements; default : debugger; return null; } } }, _handleWrappedBufferViewResourceLoading: { value: function(wrappedBufferView, delegate, ctx) { var bufferView = wrappedBufferView.bufferView; var buffer = bufferView.buffer; var byteOffset = wrappedBufferView.byteOffset + bufferView.description.byteOffset; var range = [byteOffset , (this._elementSizeForGLType(wrappedBufferView.componentType, wrappedBufferView.type) * wrappedBufferView.count) + byteOffset]; this._handleRequest({ "id" : wrappedBufferView.id, "range" : range, "type" : buffer.description.type, "uri" : buffer.description.uri, "delegate" : delegate, "ctx" : ctx }, null); } }, getBuffer: { value: function(wrappedBufferView, delegate, ctx) { var savedBuffer = this._getResource(wrappedBufferView.id); if (false) { // savedBuffer) { return savedBuffer; } else { this._handleWrappedBufferViewResourceLoading(wrappedBufferView, delegate, ctx); } return null; } }, getFile: { value: function(request, delegate, ctx) { request.delegate = delegate; request.ctx = ctx; this._handleRequest({ "id" : request.id, "uri" : request.uri, "range" : [0], "type" : "text", "delegate" : delegate, "ctx" : ctx }, null); return null; } }, });