threepipe
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586
  1. /* eslint-disable */
  2. // @ts-nocheck
  3. // r152 jsm/MTLLoader.js
  4. import {
  5. Color,
  6. DefaultLoadingManager,
  7. FileLoader,
  8. FrontSide,
  9. Loader,
  10. LoaderUtils,
  11. MeshPhongMaterial,
  12. RepeatWrapping,
  13. SRGBColorSpace,
  14. TextureLoader,
  15. Vector2,
  16. } from 'three';
  17. /**
  18. * Loads a Wavefront .mtl file specifying materials
  19. */
  20. class MTLLoader2 extends Loader {
  21. constructor(manager) {
  22. super(manager);
  23. }
  24. /**
  25. * Loads and parses a MTL asset from a URL.
  26. *
  27. * @param {String} url - URL to the MTL file.
  28. * @param {Function} [onLoad] - Callback invoked with the loaded object.
  29. * @param {Function} [onProgress] - Callback for download progress.
  30. * @param {Function} [onError] - Callback for download errors.
  31. *
  32. * @link setPath setResourcePath
  33. *
  34. * @note In order for relative texture references to resolve correctly
  35. * you must call setResourcePath() explicitly prior to load.
  36. */
  37. load(url, onLoad, onProgress, onError) {
  38. const scope = this;
  39. const path = (this.path === '') ? LoaderUtils.extractUrlBase(url) : this.path;
  40. const loader = new FileLoader(this.manager);
  41. loader.setPath(this.path);
  42. loader.setRequestHeader(this.requestHeader);
  43. loader.setWithCredentials(this.withCredentials);
  44. loader.load(url, function (text) {
  45. try {
  46. onLoad(scope.parse(text, path));
  47. } catch (e) {
  48. if (onError) {
  49. onError(e);
  50. } else {
  51. console.error(e);
  52. }
  53. scope.manager.itemError(url);
  54. }
  55. }, onProgress, onError);
  56. }
  57. setMaterialOptions(value) {
  58. this.materialOptions = value;
  59. return this;
  60. }
  61. /**
  62. * Parses a MTL file.
  63. *
  64. * @param {String} text - Content of MTL file
  65. * @return {MaterialCreator}
  66. *
  67. * @link setPath setResourcePath
  68. *
  69. * @note In order for relative texture references to resolve correctly
  70. * you must call setResourcePath() explicitly prior to parse.
  71. */
  72. parse(text, path) {
  73. const lines = text.split('\n');
  74. let info = {};
  75. const delimiter_pattern = /\s+/;
  76. const materialsInfo = {};
  77. for (let i = 0; i < lines.length; i++) {
  78. let line = lines[i];
  79. line = line.trim();
  80. if (line.length === 0 || line.charAt(0) === '#') {
  81. // Blank line or comment ignore
  82. continue;
  83. }
  84. const pos = line.indexOf(' ');
  85. let key = (pos >= 0) ? line.substring(0, pos) : line;
  86. key = key.toLowerCase();
  87. let value = (pos >= 0) ? line.substring(pos + 1) : '';
  88. value = value.trim();
  89. if (key === 'newmtl') {
  90. // New material
  91. info = {name: value};
  92. materialsInfo[value] = info;
  93. } else {
  94. if (key === 'ka' || key === 'kd' || key === 'ks' || key === 'ke') {
  95. const ss = value.split(delimiter_pattern, 3);
  96. info[key] = [parseFloat(ss[0]), parseFloat(ss[1]), parseFloat(ss[2])];
  97. } else {
  98. info[key] = value;
  99. }
  100. }
  101. }
  102. const materialCreator = new MaterialCreator(this.resourcePath || path, this.materialOptions);
  103. materialCreator.setCrossOrigin(this.crossOrigin);
  104. materialCreator.setManager(this.manager);
  105. materialCreator.setMaterials(materialsInfo);
  106. return materialCreator;
  107. }
  108. }
  109. /**
  110. * Create a new MTLLoader2.MaterialCreator
  111. * @param baseUrl - Url relative to which textures are loaded
  112. * @param options - Set of options on how to construct the materials
  113. * side: Which side to apply the material
  114. * FrontSide (default), THREE.BackSide, THREE.DoubleSide
  115. * wrap: What type of wrapping to apply for textures
  116. * RepeatWrapping (default), THREE.ClampToEdgeWrapping, THREE.MirroredRepeatWrapping
  117. * normalizeRGB: RGBs need to be normalized to 0-1 from 0-255
  118. * Default: false, assumed to be already normalized
  119. * ignoreZeroRGBs: Ignore values of RGBs (Ka,Kd,Ks) that are all 0's
  120. * Default: false
  121. * @constructor
  122. */
  123. class MaterialCreator {
  124. constructor(baseUrl = '', options = {}) {
  125. this.baseUrl = baseUrl;
  126. this.options = options;
  127. this.materialsInfo = {};
  128. this.materials = {};
  129. this.materialsArray = [];
  130. this.nameLookup = {};
  131. this.crossOrigin = 'anonymous';
  132. this.side = (this.options.side !== undefined) ? this.options.side : FrontSide;
  133. this.wrap = (this.options.wrap !== undefined) ? this.options.wrap : RepeatWrapping;
  134. }
  135. setCrossOrigin(value) {
  136. this.crossOrigin = value;
  137. return this;
  138. }
  139. setManager(value) {
  140. this.manager = value;
  141. }
  142. setMaterials(materialsInfo) {
  143. this.materialsInfo = this.convert(materialsInfo);
  144. this.materials = {};
  145. this.materialsArray = [];
  146. this.nameLookup = {};
  147. }
  148. convert(materialsInfo) {
  149. if (!this.options) return materialsInfo;
  150. const converted = {};
  151. for (const mn in materialsInfo) {
  152. // Convert materials info into normalized form based on options
  153. const mat = materialsInfo[mn];
  154. const covmat = {};
  155. converted[mn] = covmat;
  156. for (const prop in mat) {
  157. let save = true;
  158. let value = mat[prop];
  159. const lprop = prop.toLowerCase();
  160. switch (lprop) {
  161. case 'kd':
  162. case 'ka':
  163. case 'ks':
  164. // Diffuse color (color under white light) using RGB values
  165. if (this.options && this.options.normalizeRGB) {
  166. value = [value[0] / 255, value[1] / 255, value[2] / 255];
  167. }
  168. if (this.options && this.options.ignoreZeroRGBs) {
  169. if (value[0] === 0 && value[1] === 0 && value[2] === 0) {
  170. // ignore
  171. save = false;
  172. }
  173. }
  174. break;
  175. default:
  176. break;
  177. }
  178. if (save) {
  179. covmat[lprop] = value;
  180. }
  181. }
  182. }
  183. return converted;
  184. }
  185. async preload() {
  186. for (const mn in this.materialsInfo) {
  187. await this.create(mn);
  188. }
  189. }
  190. getIndex(materialName) {
  191. return this.nameLookup[materialName];
  192. }
  193. async getAsArray() {
  194. let index = 0;
  195. for (const mn in this.materialsInfo) {
  196. this.materialsArray[index] = await this.create(mn);
  197. this.nameLookup[mn] = index;
  198. index++;
  199. }
  200. return this.materialsArray;
  201. }
  202. async create(materialName) {
  203. if (this.materials[materialName] === undefined) {
  204. await this.createMaterial_(materialName);
  205. }
  206. return this.materials[materialName];
  207. }
  208. async createMaterial_(materialName) {
  209. // Create material
  210. const scope = this;
  211. const mat = this.materialsInfo[materialName];
  212. const params = {
  213. name: materialName,
  214. side: this.side
  215. };
  216. function resolveURL(baseUrl, url) {
  217. if (typeof url !== 'string' || url === '')
  218. return '';
  219. // Absolute URL
  220. if (/^https?:\/\//i.test(url)) return url;
  221. return baseUrl + url;
  222. }
  223. async function setMapForType(mapType, value) {
  224. if (params[mapType]) return; // Keep the first encountered texture
  225. const texParams = scope.getTextureParams(value, params);
  226. return new Promise((resolve, reject) => {
  227. let resolved = false;
  228. let res = ()=> (!resolved && (resolved = true) && resolve())
  229. const map = scope.loadTexture(resolveURL(scope.baseUrl, texParams.url), undefined, (map)=>{
  230. params[mapType] = map;
  231. res()
  232. }, undefined, res);
  233. setTimeout(res, 50); // timeout.
  234. map.repeat.copy(texParams.scale);
  235. map.offset.copy(texParams.offset);
  236. map.wrapS = scope.wrap;
  237. map.wrapT = scope.wrap;
  238. if ( mapType === 'map' || mapType === 'emissiveMap' ) {
  239. map.colorSpace = SRGBColorSpace;
  240. }
  241. })
  242. }
  243. /**
  244. *
  245. * @type {string[]}
  246. */
  247. const propList = Array.from(Object.keys(mat?mat:{}));
  248. let hasOpacity = propList.includes('d') || propList.includes('D');
  249. for (const prop of propList) {
  250. const value = mat[prop];
  251. let n;
  252. if (value === '') continue;
  253. switch (prop.toLowerCase()) {
  254. // Ns is material specular exponent
  255. case 'kd':
  256. // Diffuse color (color under white light) using RGB values
  257. params.color = new Color().fromArray( value ).convertSRGBToLinear();
  258. break;
  259. case 'ks':
  260. // Specular color (color when light is reflected from shiny surface) using RGB values
  261. params.specular = new Color().fromArray( value ).convertSRGBToLinear();
  262. break;
  263. case 'ke':
  264. // Emissive using RGB values
  265. params.emissive = new Color().fromArray( value ).convertSRGBToLinear();
  266. break;
  267. case 'map_kd':
  268. // Diffuse texture map
  269. await setMapForType('map', value);
  270. break;
  271. case 'map_ks':
  272. // Specular map
  273. await setMapForType('specularMap', value);
  274. break;
  275. case 'map_ke':
  276. // Emissive map
  277. await setMapForType('emissiveMap', value);
  278. break;
  279. case 'norm':
  280. await setMapForType('normalMap', value);
  281. break;
  282. case 'map_bump':
  283. case 'bump':
  284. // Bump texture map
  285. await setMapForType('bumpMap', value);
  286. break;
  287. case 'map_d':
  288. // Alpha map
  289. await setMapForType('alphaMap', value);
  290. params.transparent = true;
  291. break;
  292. case 'ns':
  293. // The specular exponent (defines the focus of the specular highlight)
  294. // A high exponent results in a tight, concentrated highlight. Ns values normally range from 0 to 1000.
  295. params.shininess = parseFloat(value);
  296. break;
  297. case 'd':
  298. n = parseFloat(value);
  299. if (n < 1) {
  300. params.opacity = n;
  301. params.transparent = true;
  302. }
  303. break;
  304. case 'tr': // is this translucency?
  305. if (hasOpacity) break; // ignore transparency if opacity is present
  306. n = parseFloat(value);
  307. if (this.options && this.options.invertTrProperty) n = 1 - n;
  308. if (n > 0) {
  309. params.opacity = 1 - n;
  310. params.transparent = true;
  311. }
  312. break;
  313. default:
  314. break;
  315. }
  316. }
  317. this.materials[materialName] = new MeshPhongMaterial(params);
  318. return this.materials[materialName];
  319. }
  320. getTextureParams(value, matParams) {
  321. const texParams = {
  322. scale: new Vector2(1, 1),
  323. offset: new Vector2(0, 0)
  324. };
  325. const items = value.split(/\s+/);
  326. let pos;
  327. pos = items.indexOf('-bm');
  328. if (pos >= 0) {
  329. matParams.bumpScale = parseFloat(items[pos + 1]);
  330. items.splice(pos, 2);
  331. }
  332. pos = items.indexOf('-s');
  333. if (pos >= 0) {
  334. texParams.scale.set(parseFloat(items[pos + 1]), parseFloat(items[pos + 2]));
  335. items.splice(pos, 4); // we expect 3 parameters here!
  336. }
  337. pos = items.indexOf('-o');
  338. if (pos >= 0) {
  339. texParams.offset.set(parseFloat(items[pos + 1]), parseFloat(items[pos + 2]));
  340. items.splice(pos, 4); // we expect 3 parameters here!
  341. }
  342. texParams.url = items.join(' ').trim();
  343. return texParams;
  344. }
  345. loadTexture(url, mapping, onLoad, onProgress, onError) {
  346. const manager = (this.manager !== undefined) ? this.manager : DefaultLoadingManager;
  347. let loader = manager.getHandler(url);
  348. if (loader === null) {
  349. loader = new TextureLoader(manager);
  350. }
  351. if (loader.setCrossOrigin) loader.setCrossOrigin(this.crossOrigin);
  352. const texture = loader.load(url, onLoad, onProgress, onError);
  353. if (mapping !== undefined) texture.mapping = mapping;
  354. return texture;
  355. }
  356. }
  357. export {MTLLoader2, type MaterialCreator};