IOManager.js 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. /**
  2. * IOManager.js
  3. *
  4. * @author realor
  5. */
  6. import { ObjectUtils } from '../utils/ObjectUtils.js'
  7. import { WebUtils } from '../utils/WebUtils.js'
  8. import * as THREE from '../lib/three.module.js'
  9. class IOManager {
  10. static formats = {}
  11. static getFormat(fileName) {
  12. let extension = null
  13. if (typeof fileName === 'string') {
  14. let index = fileName.lastIndexOf('.')
  15. if (index !== -1) {
  16. extension = fileName.substring(index + 1).toLowerCase()
  17. }
  18. }
  19. if (extension === null) return null
  20. for (let formatName in this.formats) {
  21. let formatInfo = this.formats[formatName]
  22. if (formatInfo.extensions.indexOf(extension) !== -1) return formatName
  23. }
  24. return null
  25. }
  26. static getFormatInfo(fileName) {
  27. let formatName = this.getFormat(fileName)
  28. let formatInfo = formatName ? IOManager.formats[formatName] : IOManager.formats[fileName]
  29. return formatInfo || null
  30. }
  31. static load(intent) {
  32. let formatName = intent.format
  33. let url = intent.url
  34. let data = intent.data
  35. let onCompleted = intent.onCompleted // onCompleted(object3D)
  36. let onProgress = intent.onProgress // onProgress({progress: 0..100, message: text})
  37. let onError = intent.onError // onError(error)
  38. let manager = intent.manager // LoadingManager
  39. let units = intent.units || 'm' // application units
  40. let basicAuthCredentials = intent.basicAuthCredentials
  41. try {
  42. if (!formatName && url) {
  43. formatName = this.getFormat(url)
  44. }
  45. if (!formatName) throw "Can't determinate format"
  46. let formatInfo = IOManager.formats[formatName]
  47. let loader
  48. if (formatInfo && formatInfo.loader) {
  49. loader = new formatInfo.loader.class(manager)
  50. loader.loadMethod = formatInfo.loader.loadMethod || 0
  51. } else throw 'Unsupported format: ' + formatName
  52. const options = Object.assign({}, formatInfo.loader.options, intent.options)
  53. if (data) {
  54. this.parseData(loader, url, data, units, onCompleted, onProgress, onError, options)
  55. } else {
  56. let request = new XMLHttpRequest()
  57. let length = -1
  58. request.onreadystatechange = () => {
  59. if (request.readyState === 4) {
  60. if (onProgress) onProgress({ progress: 100, message: '' })
  61. if (request.status === 0 || request.status === 200 || request.status === 207) {
  62. if (formatInfo.loader.dataType === 'arraybuffer') {
  63. data = request.response.arrayBuffer()
  64. } else {
  65. data = request.responseText
  66. }
  67. this.parseData(loader, url, data, units, onCompleted, onProgress, onError, options)
  68. } else {
  69. if (onError) {
  70. let message = WebUtils.getHttpStatusMessage(request.status)
  71. onError(message + ' (HTTP ' + request.status + ')')
  72. }
  73. }
  74. } else if (request.readyState === 3) {
  75. if (onProgress) {
  76. if (length === -1) {
  77. length = request.getResponseHeader('Content-Length')
  78. } else {
  79. let progress
  80. if (length > 0) {
  81. progress = Math.round((100 * request.responseText.length) / length)
  82. } else {
  83. progress = undefined
  84. }
  85. let message = 'Downloading file...'
  86. onProgress({ progress: progress, message: message })
  87. }
  88. }
  89. }
  90. }
  91. request.open('GET', url, true)
  92. if (basicAuthCredentials) {
  93. WebUtils.setBasicAuthorization(request, basicAuthCredentials.username, basicAuthCredentials.password)
  94. }
  95. request.send()
  96. }
  97. } catch (ex) {
  98. if (onError) onError(ex)
  99. }
  100. }
  101. static export(intent) {
  102. let formatName = intent.format
  103. let fileName = intent.name
  104. let onCompleted = intent.onCompleted
  105. let onProgress = intent.onProgress
  106. let onError = intent.onError
  107. let object = intent.object
  108. try {
  109. if (!formatName && fileName) {
  110. formatName = this.getFormat(fileName)
  111. }
  112. if (!formatName) throw "Can't determinate format"
  113. let formatInfo = IOManager.formats[formatName]
  114. let exporter
  115. if (formatInfo && formatInfo.exporter) {
  116. exporter = new formatInfo.exporter.class()
  117. exporter.exportMethod = formatInfo.exporter.exportMethod || 0
  118. exporter.mimeType = formatInfo.mimeType || 'application/octet-stream'
  119. } else throw 'Unsupported format: ' + formatName
  120. const options = Object.assign({}, formatInfo.exporter.options, intent.options)
  121. this.parseObject(exporter, object, onCompleted, onProgress, onError, options)
  122. } catch (ex) {
  123. if (onError) onError(ex)
  124. }
  125. }
  126. static parseData(loader, url, data, units, onCompleted, onProgress, onError, options) {
  127. const loadCompleted = model => {
  128. ObjectUtils.scaleModel(model, units)
  129. model.traverse(object => object.updateMatrix())
  130. if (onCompleted) onCompleted(model)
  131. }
  132. try {
  133. if (loader.loadMethod === 1) {
  134. // ColladaLoader
  135. let path = THREE.LoaderUtils.extractUrlBase(url)
  136. let result = loader.parse(data, path)
  137. loadCompleted(result.scene)
  138. } else if (loader.loadMethod === 2) {
  139. // IFCLoader
  140. loader.parse(data, loadCompleted, onProgress, onError, options)
  141. } else if (loader.loadMethod === 3) {
  142. // GLTFLoader
  143. let path = THREE.LoaderUtils.extractUrlBase(url)
  144. loader.parse(data, path, result => loadCompleted(result.scene), onError)
  145. } // general case: BRFLoader, STLLoader, OBJLoader...
  146. else {
  147. let result = loader.parse(data)
  148. let object = this.createObject(result)
  149. loadCompleted(object)
  150. }
  151. } catch (ex) {
  152. if (onError) onError(ex)
  153. }
  154. }
  155. static parseObject(exporter, object, onCompleted, onProgress, onError, options) {
  156. const exportCompleted = result => {
  157. let data = ''
  158. if (result) {
  159. let mimeType = exporter.mimeType
  160. if (typeof result === 'string') {
  161. data = new Blob([result], { type: mimeType })
  162. } else if (result instanceof ArrayBuffer) {
  163. data = new Blob([result], { type: mimeType })
  164. } else if (typeof result.data === 'string') {
  165. data = new Blob([result.data], { type: mimeType })
  166. } else if (typeof result === 'object') {
  167. data = new Blob([JSON.stringify(result)], { type: mimeType })
  168. } else {
  169. console.warn('Unsupported export result', result)
  170. }
  171. }
  172. if (onCompleted) onCompleted(data)
  173. }
  174. try {
  175. if (exporter.exportMethod === 1) {
  176. exporter.parse(object, exportCompleted, onError, options)
  177. } // general export method
  178. else {
  179. exportCompleted(exporter.parse(object, options))
  180. }
  181. } catch (ex) {
  182. if (onError) onError(ex)
  183. }
  184. }
  185. static createObject(result) {
  186. if (result instanceof THREE.BufferGeometry) {
  187. let geometry = result
  188. let material = new THREE.MeshPhongMaterial({ color: 0x008000, side: THREE.DoubleSide })
  189. return new THREE.Mesh(geometry, material)
  190. } else if (result instanceof THREE.Object3D) {
  191. return result
  192. }
  193. }
  194. static getSupportedLoaderExtensions() {
  195. const extensions = []
  196. const formats = IOManager.formats
  197. for (let formatName in formats) {
  198. let formatInfo = formats[formatName]
  199. extensions.push(...formatInfo.extensions)
  200. }
  201. return extensions
  202. }
  203. }
  204. export { IOManager }