glTFLoaderUtils.js 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. /**
  2. * @author Tony Parisi / http://www.tonyparisi.com/
  3. */
  4. THREE.GLTFLoaderUtils = Object.create(Object, {
  5. // errors
  6. MISSING_DESCRIPTION: { value: "MISSING_DESCRIPTION" },
  7. INVALID_PATH: { value: "INVALID_PATH" },
  8. INVALID_TYPE: { value: "INVALID_TYPE" },
  9. XMLHTTPREQUEST_STATUS_ERROR: { value: "XMLHTTPREQUEST_STATUS_ERROR" },
  10. NOT_FOUND: { value: "NOT_FOUND" },
  11. // misc constants
  12. ARRAY_BUFFER: { value: "ArrayBuffer" },
  13. _streams : { value:{}, writable: true },
  14. _streamsStatus: { value: {}, writable: true },
  15. _resources: { value: {}, writable: true },
  16. _resourcesStatus: { value: {}, writable: true },
  17. // initialization
  18. init: {
  19. value: function() {
  20. this._streams = {};
  21. this._streamsStatus = {};
  22. this._resources = {};
  23. this._resourcesStatus = {};
  24. }
  25. },
  26. //manage entries
  27. _containsResource: {
  28. enumerable: false,
  29. value: function(resourceID) {
  30. return this._resources[resourceID] ? true : false;
  31. }
  32. },
  33. _storeResource: {
  34. enumerable: false,
  35. value: function(resourceID, resource) {
  36. if (!resourceID) {
  37. console.log("ERROR: entry does not contain id, cannot store");
  38. return;
  39. }
  40. if (this._containsResource[resourceID]) {
  41. console.log("WARNING: resource:"+resourceID+" is already stored, overriding");
  42. }
  43. this._resources[resourceID] = resource;
  44. }
  45. },
  46. _getResource: {
  47. enumerable: false,
  48. value: function(resourceID) {
  49. return this._resources[resourceID];
  50. }
  51. },
  52. _loadStream: {
  53. value: function(path, type, delegate) {
  54. var dataUriRegex = /^data:(.*?)(;base64)?,(.*)$/;
  55. function decodeDataUriText(isBase64, data) {
  56. var result = decodeURIComponent(data);
  57. if (isBase64) {
  58. return atob(result);
  59. }
  60. return result;
  61. }
  62. function decodeDataUriArrayBuffer(isBase64, data) {
  63. var byteString = decodeDataUriText(isBase64, data);
  64. var buffer = new ArrayBuffer(byteString.length);
  65. var view = new Uint8Array(buffer);
  66. for (var i = 0; i < byteString.length; i++) {
  67. view[i] = byteString.charCodeAt(i);
  68. }
  69. return buffer;
  70. }
  71. function decodeDataUri(dataUriRegexResult, responseType) {
  72. responseType = typeof responseType !== 'undefined' ? responseType : '';
  73. var mimeType = dataUriRegexResult[1];
  74. var isBase64 = !!dataUriRegexResult[2];
  75. var data = dataUriRegexResult[3];
  76. switch (responseType) {
  77. case '':
  78. case 'text':
  79. return decodeDataUriText(isBase64, data);
  80. case 'ArrayBuffer':
  81. return decodeDataUriArrayBuffer(isBase64, data);
  82. case 'blob':
  83. var buffer = decodeDataUriArrayBuffer(isBase64, data);
  84. return new Blob([buffer], {
  85. type : mimeType
  86. });
  87. case 'document':
  88. var parser = new DOMParser();
  89. return parser.parseFromString(decodeDataUriText(isBase64, data), mimeType);
  90. case 'json':
  91. return JSON.parse(decodeDataUriText(isBase64, data));
  92. default:
  93. throw 'Unhandled responseType: ' + responseType;
  94. }
  95. }
  96. var dataUriRegexResult = dataUriRegex.exec(path);
  97. if (dataUriRegexResult !== null) {
  98. delegate.streamAvailable(path, decodeDataUri(dataUriRegexResult, type));
  99. return;
  100. }
  101. var self = this;
  102. if (!type) {
  103. delegate.handleError(THREE.GLTFLoaderUtils.INVALID_TYPE, null);
  104. return;
  105. }
  106. if (!path) {
  107. delegate.handleError(THREE.GLTFLoaderUtils.INVALID_PATH);
  108. return;
  109. }
  110. var xhr = new XMLHttpRequest();
  111. xhr.open('GET', path, true);
  112. xhr.responseType = (type === this.ARRAY_BUFFER) ? "arraybuffer" : "text";
  113. //if this is not specified, 1 "big blob" scenes fails to load.
  114. xhr.setRequestHeader("If-Modified-Since", "Sat, 01 Jan 1970 00:00:00 GMT");
  115. xhr.onload = function(e) {
  116. if ((xhr.status == 200) || (xhr.status == 206)) {
  117. delegate.streamAvailable(path, xhr.response);
  118. } else {
  119. delegate.handleError(THREE.GLTFLoaderUtils.XMLHTTPREQUEST_STATUS_ERROR, this.status);
  120. }
  121. };
  122. xhr.send(null);
  123. }
  124. },
  125. send: { value: 0, writable: true },
  126. requested: { value: 0, writable: true },
  127. _handleRequest: {
  128. value: function(request) {
  129. var resourceStatus = this._resourcesStatus[request.id];
  130. if (resourceStatus)
  131. {
  132. this._resourcesStatus[request.id]++;
  133. }
  134. else
  135. {
  136. this._resourcesStatus[request.id] = 1;
  137. }
  138. var streamStatus = this._streamsStatus[request.uri];
  139. if (streamStatus && streamStatus.status === "loading" )
  140. {
  141. streamStatus.requests.push(request);
  142. return;
  143. }
  144. this._streamsStatus[request.uri] = { status : "loading", requests : [request] };
  145. var self = this;
  146. var processResourceDelegate = {};
  147. processResourceDelegate.streamAvailable = function(path, res_) {
  148. var streamStatus = self._streamsStatus[path];
  149. var requests = streamStatus.requests;
  150. requests.forEach( function(req_) {
  151. var subArray = res_.slice(req_.range[0], req_.range[1]);
  152. var convertedResource = req_.delegate.convert(subArray, req_.ctx);
  153. self._storeResource(req_.id, convertedResource);
  154. req_.delegate.resourceAvailable(convertedResource, req_.ctx);
  155. --self._resourcesStatus[req_.id];
  156. }, this);
  157. delete self._streamsStatus[path];
  158. };
  159. processResourceDelegate.handleError = function(errorCode, info) {
  160. request.delegate.handleError(errorCode, info);
  161. }
  162. this._loadStream(request.uri, request.type, processResourceDelegate);
  163. }
  164. },
  165. _elementSizeForGLType: {
  166. value: function(componentType, type) {
  167. var nElements = 0;
  168. switch(type) {
  169. case "SCALAR" :
  170. nElements = 1;
  171. break;
  172. case "VEC2" :
  173. nElements = 2;
  174. break;
  175. case "VEC3" :
  176. nElements = 3;
  177. break;
  178. case "VEC4" :
  179. nElements = 4;
  180. break;
  181. case "MAT2" :
  182. nElements = 4;
  183. break;
  184. case "MAT3" :
  185. nElements = 9;
  186. break;
  187. case "MAT4" :
  188. nElements = 16;
  189. break;
  190. default :
  191. debugger;
  192. break;
  193. }
  194. switch (componentType) {
  195. case WebGLRenderingContext.FLOAT :
  196. return Float32Array.BYTES_PER_ELEMENT * nElements;
  197. case WebGLRenderingContext.UNSIGNED_BYTE :
  198. return Uint8Array.BYTES_PER_ELEMENT * nElements;
  199. case WebGLRenderingContext.UNSIGNED_SHORT :
  200. return Uint16Array.BYTES_PER_ELEMENT * nElements;
  201. default :
  202. debugger;
  203. return null;
  204. }
  205. }
  206. },
  207. _handleWrappedBufferViewResourceLoading: {
  208. value: function(wrappedBufferView, delegate, ctx) {
  209. var bufferView = wrappedBufferView.bufferView;
  210. var buffer = bufferView.buffer;
  211. var byteOffset = wrappedBufferView.byteOffset + bufferView.description.byteOffset;
  212. var range = [byteOffset , (this._elementSizeForGLType(wrappedBufferView.componentType, wrappedBufferView.type) * wrappedBufferView.count) + byteOffset];
  213. this._handleRequest({ "id" : wrappedBufferView.id,
  214. "range" : range,
  215. "type" : buffer.description.type,
  216. "uri" : buffer.description.uri,
  217. "delegate" : delegate,
  218. "ctx" : ctx }, null);
  219. }
  220. },
  221. getBuffer: {
  222. value: function(wrappedBufferView, delegate, ctx) {
  223. var savedBuffer = this._getResource(wrappedBufferView.id);
  224. if (false) { // savedBuffer) {
  225. return savedBuffer;
  226. } else {
  227. this._handleWrappedBufferViewResourceLoading(wrappedBufferView, delegate, ctx);
  228. }
  229. return null;
  230. }
  231. },
  232. getFile: {
  233. value: function(request, delegate, ctx) {
  234. request.delegate = delegate;
  235. request.ctx = ctx;
  236. this._handleRequest({ "id" : request.id,
  237. "uri" : request.uri,
  238. "range" : [0],
  239. "type" : "text",
  240. "delegate" : delegate,
  241. "ctx" : ctx }, null);
  242. return null;
  243. }
  244. },
  245. });