BRFLoader.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. /**
  2. * BRFLoader.js
  3. *
  4. * @author realor
  5. */
  6. import { Solid } from '../core/Solid.js'
  7. import { Profile } from '../core/Profile.js'
  8. import { Cord } from '../core/Cord.js'
  9. import { SolidGeometry } from '../core/SolidGeometry.js'
  10. import { ProfileGeometry } from '../core/ProfileGeometry.js'
  11. import { CordGeometry } from '../core/CordGeometry.js'
  12. import { SolidOptimizer } from '../core/SolidOptimizer.js'
  13. import { ObjectBuilder } from '../builders/ObjectBuilder.js'
  14. import { Formula } from '../formula/Formula.js'
  15. import { Controller } from '../controllers/Controller.js'
  16. import * as THREE from '../lib/three.module.js'
  17. class BRFLoader extends THREE.Loader {
  18. constructor(manager) {
  19. super(manager)
  20. this.options = {}
  21. }
  22. load(url, onLoad, onProgress, onError) {
  23. const loader = new FileLoader(this.manager)
  24. loader.setPath(this.path)
  25. loader.setRequestHeader(this.requestHeader)
  26. loader.setWithCredentials(this.withCredentials)
  27. loader.load(
  28. url,
  29. text => {
  30. try {
  31. onLoad(this.parse(text))
  32. } catch (ex) {
  33. if (onError) {
  34. onError(ex)
  35. } else {
  36. console.error(ex)
  37. }
  38. this.manager.itemError(url)
  39. }
  40. },
  41. onProgress,
  42. onError
  43. )
  44. }
  45. parse(text) {
  46. let model = JSON.parse(text)
  47. // parse geometries
  48. const geometries = model.geometries
  49. for (let id in geometries) {
  50. let entry = geometries[id]
  51. entry._geometry = this.parseGeometry(entry, model)
  52. }
  53. // parse materials
  54. const materials = model.materials
  55. for (let id in materials) {
  56. let entry = materials[id]
  57. entry._material = this.parseMaterial(entry)
  58. }
  59. // parse objects
  60. const objects = model.objects
  61. for (let id in objects) {
  62. let entry = objects[id]
  63. entry._object = this.parseObject(entry, model)
  64. }
  65. // build object tree and create object builders and controllers
  66. for (let id in objects) {
  67. let entry = objects[id]
  68. let object = entry._object
  69. if (entry.children) {
  70. for (let child of entry.children) {
  71. object.add(model.objects[child.id]._object)
  72. }
  73. }
  74. if (entry.builder) {
  75. this.parseBuilder(entry, model)
  76. }
  77. if (entry.controllers) {
  78. this.parseControllers(entry, model)
  79. }
  80. }
  81. // restore formulas
  82. for (let id in objects) {
  83. let entry = objects[id]
  84. let object = entry._object
  85. const formulas = entry.formulas
  86. if (formulas) {
  87. for (let path in formulas) {
  88. let expression = formulas[path]
  89. try {
  90. Formula.create(object, path, expression)
  91. } catch (ex) {
  92. console.warn('Error evaluating formula: ' + object.name + '/' + path)
  93. }
  94. }
  95. }
  96. }
  97. // build scene
  98. const root = model.objects[model.root.id]._object
  99. root.updateMatrixWorld()
  100. ObjectBuilder.build(root)
  101. return root
  102. }
  103. parseGeometry(entry, model) {
  104. let geometry = null
  105. if (entry.type === 'SolidGeometry') {
  106. geometry = new SolidGeometry()
  107. geometry.isManifold = entry.isManifold
  108. geometry.smoothAngle = entry.smoothAngle || 0
  109. for (let vertex of entry.vertices) {
  110. let position = new THREE.Vector3(vertex.x, vertex.y, vertex.z)
  111. geometry.vertices.push(position)
  112. }
  113. for (let loops of entry.faces) {
  114. if (Array.isArray(loops)) {
  115. var outerLoop = loops[0]
  116. if (Array.isArray(outerLoop)) {
  117. let face = geometry.addFace(...outerLoop)
  118. for (let h = 1; h < loops.length; h++) {
  119. face.addHole(...loops[h])
  120. }
  121. } else {
  122. // no holes, loops is outerLoop
  123. outerLoop = loops
  124. geometry.addFace(...outerLoop)
  125. }
  126. }
  127. }
  128. geometry.updateFaceNormals()
  129. if (model.metadata.version < 2) {
  130. const optimizer = new SolidOptimizer(geometry)
  131. geometry = optimizer.optimize()
  132. }
  133. } else if (entry.type === 'CordGeometry') {
  134. const points = []
  135. for (let point of entry.points) {
  136. let position = new THREE.Vector3(point.x, point.y, point.z)
  137. points.push(position)
  138. }
  139. geometry = new CordGeometry(points)
  140. } else if (entry.type === 'ProfileGeometry') {
  141. const path = entry.isClosed ? new THREE.Shape() : new THREE.Path()
  142. const points = []
  143. for (let point of entry.points) {
  144. let position = new THREE.Vector2(point.x, point.y)
  145. points.push(position)
  146. }
  147. path.setFromPoints(points)
  148. if (entry.isClosed) {
  149. for (let hole of entry.holes) {
  150. let holePoints = []
  151. for (let point of hole) {
  152. let position = new THREE.Vector2(point.x, point.y)
  153. holePoints.push(position)
  154. }
  155. let holePath = new THREE.Path()
  156. holePath.setFromPoints(holePoints)
  157. path.holes.push(holePath)
  158. }
  159. }
  160. geometry = new ProfileGeometry(path)
  161. } else if (entry.type === 'BufferGeometry') {
  162. geometry = new THREE.BufferGeometry()
  163. for (let name in entry.attributes) {
  164. let attribute = entry.attributes[name]
  165. let array = this.parseBufferAttributeArray(attribute)
  166. let itemSize = attribute.itemSize
  167. let normalized = attribute.normalized
  168. let typedArray
  169. switch (attribute.arrayType) {
  170. case 'Float32Array':
  171. typedArray = new Float32Array(array)
  172. break
  173. case 'Uint32Array':
  174. typedArray = new Uint32Array(array)
  175. break
  176. case 'Uint16Array':
  177. typedArray = new Uint16Array(array)
  178. break
  179. default:
  180. throw 'Unsupported TypedArray: ' + attribute.arrayType
  181. }
  182. let bufferAttribute = new THREE.BufferAttribute(typedArray, itemSize, normalized)
  183. geometry.setAttribute(name, bufferAttribute)
  184. }
  185. }
  186. return geometry
  187. }
  188. parseMaterial(entry) {
  189. let material = new THREE[entry.type]()
  190. this.setProperties(material, entry)
  191. return material
  192. }
  193. parseObject(entry, model) {
  194. let object = null
  195. if (entry.type === 'Object3D') {
  196. object = new THREE.Object3D()
  197. } else if (entry.type === 'Group') {
  198. object = new THREE.Group()
  199. } else if (entry.type === 'Solid') {
  200. object = new Solid()
  201. object.edgesVisible = entry.edgesVisible
  202. object.facesVisible = entry.facesVisible
  203. } else if (entry.type === 'Profile') {
  204. object = new Profile()
  205. } else if (entry.type === 'Cord') {
  206. object = new Cord()
  207. } else if (entry.type === 'Mesh') {
  208. object = new THREE.Mesh()
  209. } else if (entry.type === 'Sprite') {
  210. object = new THREE.Sprite()
  211. } else {
  212. object = new THREE.Object3D()
  213. console.warn('Unsupported object type: ' + entry.type)
  214. }
  215. object.name = entry.name
  216. object.visible = entry.visible
  217. object.castShadow = entry.castShadow || false
  218. object.receiveShadow = entry.receiveShadow || false
  219. let position = entry.position
  220. if (position) {
  221. object.position.set(position.x, position.y, position.z)
  222. }
  223. let rotation = entry.rotation
  224. if (rotation) {
  225. object.rotation.set(rotation.x, rotation.y, rotation.z)
  226. }
  227. let scale = entry.scale
  228. if (scale) {
  229. object.scale.set(scale.x, scale.y, scale.z)
  230. }
  231. object.updateMatrix()
  232. if (!(object instanceof THREE.Sprite)) {
  233. const geometries = model.geometries
  234. if (entry.geometry && geometries[entry.geometry.id]) {
  235. let geometry = geometries[entry.geometry.id]._geometry
  236. if (geometry instanceof SolidGeometry) {
  237. object.updateGeometry(geometry)
  238. } else if (geometry instanceof THREE.BufferGeometry) {
  239. object.geometry = geometry
  240. }
  241. }
  242. }
  243. const materials = model.materials
  244. if (entry.material && materials[entry.material.id]) {
  245. let material = materials[entry.material.id]._material
  246. if (material) {
  247. object.material = material
  248. }
  249. }
  250. if (entry.userData) {
  251. object.userData = entry.userData
  252. }
  253. return object
  254. }
  255. parseBuilder(entry, model) {
  256. if (entry.builder) {
  257. const cls = ObjectBuilder.classes[entry.builder.type]
  258. if (cls) {
  259. const builder = new cls()
  260. const object = entry._object
  261. object.builder = builder
  262. this.setProperties(builder, entry.builder, model)
  263. }
  264. }
  265. }
  266. parseControllers(entry, model) {
  267. let controllerEntries = entry.controllers
  268. if (controllerEntries) {
  269. const object = entry._object
  270. object.controllers = {}
  271. for (let name in controllerEntries) {
  272. let controllerEntry = controllerEntries[name]
  273. let controllerClass = Controller.classes[controllerEntry.type]
  274. if (controllerClass) {
  275. let controller = new controllerClass(object, name)
  276. object.controllers[name] = controller
  277. this.setProperties(controller, controllerEntry, model)
  278. }
  279. }
  280. }
  281. }
  282. setProperties(element, entry, model) {
  283. for (let property in entry) {
  284. if (property !== 'type' && property !== 'id' && property !== 'uuid') {
  285. if (property in element && !property.startsWith('_')) {
  286. let value = entry[property]
  287. this.setPropertyValue(element, property, value, model)
  288. }
  289. }
  290. }
  291. }
  292. setPropertyValue(element, property, value, model) {
  293. let type = typeof value
  294. if (type === 'object' && value) {
  295. type = value.type
  296. if (type === undefined) {
  297. type = typeof value.z === 'number' ? 'Vector3' : 'Vector2'
  298. }
  299. }
  300. let actualValue = element[property]
  301. let actualType = typeof actualValue
  302. if (actualType === 'string' && type === 'string') {
  303. element[property] = value
  304. } else if (actualType === 'number' && type === 'number') {
  305. element[property] = value
  306. } else if (actualType === 'boolean' && type === 'boolean') {
  307. element[property] = value
  308. } else if (actualValue instanceof THREE.Vector3 && type === 'Vector3') {
  309. element[property].set(value.x, value.y, value.z)
  310. } else if (actualValue instanceof THREE.Vector2 && type === 'Vector2') {
  311. element[property].set(value.x, value.y)
  312. } else if (actualValue instanceof THREE.Euler && type === 'Euler') {
  313. element[property].set(value.x, value.y, value.z)
  314. } else if (actualValue instanceof THREE.Color) {
  315. if (type === 'string') {
  316. element[property].set(value)
  317. } else if (type === 'Color') {
  318. element[property].set(value.r, value.g, value.b)
  319. }
  320. } else if (type === '#object') {
  321. if (model.objects[value.id]) element[property] = model.objects[value.id]._object
  322. } else if (type === '#geometry') {
  323. if (model.geometries[value.id]) element[property] = model.geometries[value.id]._geometry
  324. } else if (type === '#material') {
  325. if (model.materials[value.id]) element[property] = model.materials[value.id]._material
  326. } else if (type === 'Texture' && element instanceof THREE.Material) {
  327. const textureLoader = new THREE.TextureLoader(this.manager)
  328. const texture = textureLoader.load(value.image)
  329. element[property] = texture
  330. element.needsUpdate = true
  331. } else if (value === null) {
  332. element[property] = null
  333. } else {
  334. console.warn('Invalid value for property [' + property + ']: ' + value + ' (expect ' + actualType + ' but found ' + type + ')')
  335. }
  336. }
  337. parseBufferAttributeArray(attribute) {
  338. let array = attribute.array
  339. if (array.length === 0) return array
  340. if (array[0] instanceof Array) {
  341. // compressed attribute
  342. let uncompressedArray = []
  343. for (let i = 0; i < array.length; i++) {
  344. let item = array[i]
  345. if (item instanceof Array) {
  346. uncompressedArray.push(...item)
  347. } else if (typeof item === 'number') {
  348. uncompressedArray.push(...array[item])
  349. }
  350. }
  351. return uncompressedArray
  352. } else return array
  353. }
  354. }
  355. export { BRFLoader }