123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393 |
- /**
- * BRFLoader.js
- *
- * @author realor
- */
- import { Solid } from '../core/Solid.js'
- import { Profile } from '../core/Profile.js'
- import { Cord } from '../core/Cord.js'
- import { SolidGeometry } from '../core/SolidGeometry.js'
- import { ProfileGeometry } from '../core/ProfileGeometry.js'
- import { CordGeometry } from '../core/CordGeometry.js'
- import { SolidOptimizer } from '../core/SolidOptimizer.js'
- import { ObjectBuilder } from '../builders/ObjectBuilder.js'
- import { Formula } from '../formula/Formula.js'
- import { Controller } from '../controllers/Controller.js'
- import * as THREE from '../lib/three.module.js'
- class BRFLoader extends THREE.Loader {
- constructor(manager) {
- super(manager)
- this.options = {}
- }
- load(url, onLoad, onProgress, onError) {
- const loader = new FileLoader(this.manager)
- loader.setPath(this.path)
- loader.setRequestHeader(this.requestHeader)
- loader.setWithCredentials(this.withCredentials)
- loader.load(
- url,
- text => {
- try {
- onLoad(this.parse(text))
- } catch (ex) {
- if (onError) {
- onError(ex)
- } else {
- console.error(ex)
- }
- this.manager.itemError(url)
- }
- },
- onProgress,
- onError
- )
- }
- parse(text) {
- let model = JSON.parse(text)
- // parse geometries
- const geometries = model.geometries
- for (let id in geometries) {
- let entry = geometries[id]
- entry._geometry = this.parseGeometry(entry, model)
- }
- // parse materials
- const materials = model.materials
- for (let id in materials) {
- let entry = materials[id]
- entry._material = this.parseMaterial(entry)
- }
- // parse objects
- const objects = model.objects
- for (let id in objects) {
- let entry = objects[id]
- entry._object = this.parseObject(entry, model)
- }
- // build object tree and create object builders and controllers
- for (let id in objects) {
- let entry = objects[id]
- let object = entry._object
- if (entry.children) {
- for (let child of entry.children) {
- object.add(model.objects[child.id]._object)
- }
- }
- if (entry.builder) {
- this.parseBuilder(entry, model)
- }
- if (entry.controllers) {
- this.parseControllers(entry, model)
- }
- }
- // restore formulas
- for (let id in objects) {
- let entry = objects[id]
- let object = entry._object
- const formulas = entry.formulas
- if (formulas) {
- for (let path in formulas) {
- let expression = formulas[path]
- try {
- Formula.create(object, path, expression)
- } catch (ex) {
- console.warn('Error evaluating formula: ' + object.name + '/' + path)
- }
- }
- }
- }
- // build scene
- const root = model.objects[model.root.id]._object
- root.updateMatrixWorld()
- ObjectBuilder.build(root)
- return root
- }
- parseGeometry(entry, model) {
- let geometry = null
- if (entry.type === 'SolidGeometry') {
- geometry = new SolidGeometry()
- geometry.isManifold = entry.isManifold
- geometry.smoothAngle = entry.smoothAngle || 0
- for (let vertex of entry.vertices) {
- let position = new THREE.Vector3(vertex.x, vertex.y, vertex.z)
- geometry.vertices.push(position)
- }
- for (let loops of entry.faces) {
- if (Array.isArray(loops)) {
- var outerLoop = loops[0]
- if (Array.isArray(outerLoop)) {
- let face = geometry.addFace(...outerLoop)
- for (let h = 1; h < loops.length; h++) {
- face.addHole(...loops[h])
- }
- } else {
- // no holes, loops is outerLoop
- outerLoop = loops
- geometry.addFace(...outerLoop)
- }
- }
- }
- geometry.updateFaceNormals()
- if (model.metadata.version < 2) {
- const optimizer = new SolidOptimizer(geometry)
- geometry = optimizer.optimize()
- }
- } else if (entry.type === 'CordGeometry') {
- const points = []
- for (let point of entry.points) {
- let position = new THREE.Vector3(point.x, point.y, point.z)
- points.push(position)
- }
- geometry = new CordGeometry(points)
- } else if (entry.type === 'ProfileGeometry') {
- const path = entry.isClosed ? new THREE.Shape() : new THREE.Path()
- const points = []
- for (let point of entry.points) {
- let position = new THREE.Vector2(point.x, point.y)
- points.push(position)
- }
- path.setFromPoints(points)
- if (entry.isClosed) {
- for (let hole of entry.holes) {
- let holePoints = []
- for (let point of hole) {
- let position = new THREE.Vector2(point.x, point.y)
- holePoints.push(position)
- }
- let holePath = new THREE.Path()
- holePath.setFromPoints(holePoints)
- path.holes.push(holePath)
- }
- }
- geometry = new ProfileGeometry(path)
- } else if (entry.type === 'BufferGeometry') {
- geometry = new THREE.BufferGeometry()
- for (let name in entry.attributes) {
- let attribute = entry.attributes[name]
- let array = this.parseBufferAttributeArray(attribute)
- let itemSize = attribute.itemSize
- let normalized = attribute.normalized
- let typedArray
- switch (attribute.arrayType) {
- case 'Float32Array':
- typedArray = new Float32Array(array)
- break
- case 'Uint32Array':
- typedArray = new Uint32Array(array)
- break
- case 'Uint16Array':
- typedArray = new Uint16Array(array)
- break
- default:
- throw 'Unsupported TypedArray: ' + attribute.arrayType
- }
- let bufferAttribute = new THREE.BufferAttribute(typedArray, itemSize, normalized)
- geometry.setAttribute(name, bufferAttribute)
- }
- }
- return geometry
- }
- parseMaterial(entry) {
- let material = new THREE[entry.type]()
- this.setProperties(material, entry)
- return material
- }
- parseObject(entry, model) {
- let object = null
- if (entry.type === 'Object3D') {
- object = new THREE.Object3D()
- } else if (entry.type === 'Group') {
- object = new THREE.Group()
- } else if (entry.type === 'Solid') {
- object = new Solid()
- object.edgesVisible = entry.edgesVisible
- object.facesVisible = entry.facesVisible
- } else if (entry.type === 'Profile') {
- object = new Profile()
- } else if (entry.type === 'Cord') {
- object = new Cord()
- } else if (entry.type === 'Mesh') {
- object = new THREE.Mesh()
- } else if (entry.type === 'Sprite') {
- object = new THREE.Sprite()
- } else {
- object = new THREE.Object3D()
- console.warn('Unsupported object type: ' + entry.type)
- }
- object.name = entry.name
- object.visible = entry.visible
- object.castShadow = entry.castShadow || false
- object.receiveShadow = entry.receiveShadow || false
- let position = entry.position
- if (position) {
- object.position.set(position.x, position.y, position.z)
- }
- let rotation = entry.rotation
- if (rotation) {
- object.rotation.set(rotation.x, rotation.y, rotation.z)
- }
- let scale = entry.scale
- if (scale) {
- object.scale.set(scale.x, scale.y, scale.z)
- }
- object.updateMatrix()
- if (!(object instanceof THREE.Sprite)) {
- const geometries = model.geometries
- if (entry.geometry && geometries[entry.geometry.id]) {
- let geometry = geometries[entry.geometry.id]._geometry
- if (geometry instanceof SolidGeometry) {
- object.updateGeometry(geometry)
- } else if (geometry instanceof THREE.BufferGeometry) {
- object.geometry = geometry
- }
- }
- }
- const materials = model.materials
- if (entry.material && materials[entry.material.id]) {
- let material = materials[entry.material.id]._material
- if (material) {
- object.material = material
- }
- }
- if (entry.userData) {
- object.userData = entry.userData
- }
- return object
- }
- parseBuilder(entry, model) {
- if (entry.builder) {
- const cls = ObjectBuilder.classes[entry.builder.type]
- if (cls) {
- const builder = new cls()
- const object = entry._object
- object.builder = builder
- this.setProperties(builder, entry.builder, model)
- }
- }
- }
- parseControllers(entry, model) {
- let controllerEntries = entry.controllers
- if (controllerEntries) {
- const object = entry._object
- object.controllers = {}
- for (let name in controllerEntries) {
- let controllerEntry = controllerEntries[name]
- let controllerClass = Controller.classes[controllerEntry.type]
- if (controllerClass) {
- let controller = new controllerClass(object, name)
- object.controllers[name] = controller
- this.setProperties(controller, controllerEntry, model)
- }
- }
- }
- }
- setProperties(element, entry, model) {
- for (let property in entry) {
- if (property !== 'type' && property !== 'id' && property !== 'uuid') {
- if (property in element && !property.startsWith('_')) {
- let value = entry[property]
- this.setPropertyValue(element, property, value, model)
- }
- }
- }
- }
- setPropertyValue(element, property, value, model) {
- let type = typeof value
- if (type === 'object' && value) {
- type = value.type
- if (type === undefined) {
- type = typeof value.z === 'number' ? 'Vector3' : 'Vector2'
- }
- }
- let actualValue = element[property]
- let actualType = typeof actualValue
- if (actualType === 'string' && type === 'string') {
- element[property] = value
- } else if (actualType === 'number' && type === 'number') {
- element[property] = value
- } else if (actualType === 'boolean' && type === 'boolean') {
- element[property] = value
- } else if (actualValue instanceof THREE.Vector3 && type === 'Vector3') {
- element[property].set(value.x, value.y, value.z)
- } else if (actualValue instanceof THREE.Vector2 && type === 'Vector2') {
- element[property].set(value.x, value.y)
- } else if (actualValue instanceof THREE.Euler && type === 'Euler') {
- element[property].set(value.x, value.y, value.z)
- } else if (actualValue instanceof THREE.Color) {
- if (type === 'string') {
- element[property].set(value)
- } else if (type === 'Color') {
- element[property].set(value.r, value.g, value.b)
- }
- } else if (type === '#object') {
- if (model.objects[value.id]) element[property] = model.objects[value.id]._object
- } else if (type === '#geometry') {
- if (model.geometries[value.id]) element[property] = model.geometries[value.id]._geometry
- } else if (type === '#material') {
- if (model.materials[value.id]) element[property] = model.materials[value.id]._material
- } else if (type === 'Texture' && element instanceof THREE.Material) {
- const textureLoader = new THREE.TextureLoader(this.manager)
- const texture = textureLoader.load(value.image)
- element[property] = texture
- element.needsUpdate = true
- } else if (value === null) {
- element[property] = null
- } else {
- console.warn('Invalid value for property [' + property + ']: ' + value + ' (expect ' + actualType + ' but found ' + type + ')')
- }
- }
- parseBufferAttributeArray(attribute) {
- let array = attribute.array
- if (array.length === 0) return array
- if (array[0] instanceof Array) {
- // compressed attribute
- let uncompressedArray = []
- for (let i = 0; i < array.length; i++) {
- let item = array[i]
- if (item instanceof Array) {
- uncompressedArray.push(...item)
- } else if (typeof item === 'number') {
- uncompressedArray.push(...array[item])
- }
- }
- return uncompressedArray
- } else return array
- }
- }
- export { BRFLoader }
|