glTFLoaderUtils.js 10 KB

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