Buckets:
ktongue/docker_container / simsite /frontend /node_modules /three /examples /jsm /loaders /AMFLoader.js
| import { | |
| BufferGeometry, | |
| Color, | |
| FileLoader, | |
| Float32BufferAttribute, | |
| Group, | |
| Loader, | |
| Mesh, | |
| MeshPhongMaterial | |
| } from 'three'; | |
| import * as fflate from '../libs/fflate.module.js'; | |
| /** | |
| * Description: Early release of an AMF Loader following the pattern of the | |
| * example loaders in the three.js project. | |
| * | |
| * Usage: | |
| * const loader = new AMFLoader(); | |
| * loader.load('/path/to/project.amf', function(objecttree) { | |
| * scene.add(objecttree); | |
| * }); | |
| * | |
| * Materials now supported, material colors supported | |
| * Zip support, requires fflate | |
| * No constellation support (yet)! | |
| * | |
| */ | |
| class AMFLoader extends Loader { | |
| constructor( manager ) { | |
| super( manager ); | |
| } | |
| load( url, onLoad, onProgress, onError ) { | |
| const scope = this; | |
| const loader = new FileLoader( scope.manager ); | |
| loader.setPath( scope.path ); | |
| loader.setResponseType( 'arraybuffer' ); | |
| loader.setRequestHeader( scope.requestHeader ); | |
| loader.setWithCredentials( scope.withCredentials ); | |
| loader.load( url, function ( text ) { | |
| try { | |
| onLoad( scope.parse( text ) ); | |
| } catch ( e ) { | |
| if ( onError ) { | |
| onError( e ); | |
| } else { | |
| console.error( e ); | |
| } | |
| scope.manager.itemError( url ); | |
| } | |
| }, onProgress, onError ); | |
| } | |
| parse( data ) { | |
| function loadDocument( data ) { | |
| let view = new DataView( data ); | |
| const magic = String.fromCharCode( view.getUint8( 0 ), view.getUint8( 1 ) ); | |
| if ( magic === 'PK' ) { | |
| let zip = null; | |
| let file = null; | |
| console.log( 'THREE.AMFLoader: Loading Zip' ); | |
| try { | |
| zip = fflate.unzipSync( new Uint8Array( data ) ); | |
| } catch ( e ) { | |
| if ( e instanceof ReferenceError ) { | |
| console.log( 'THREE.AMFLoader: fflate missing and file is compressed.' ); | |
| return null; | |
| } | |
| } | |
| for ( file in zip ) { | |
| if ( file.toLowerCase().slice( - 4 ) === '.amf' ) { | |
| break; | |
| } | |
| } | |
| console.log( 'THREE.AMFLoader: Trying to load file asset: ' + file ); | |
| view = new DataView( zip[ file ].buffer ); | |
| } | |
| const fileText = new TextDecoder().decode( view ); | |
| const xmlData = new DOMParser().parseFromString( fileText, 'application/xml' ); | |
| if ( xmlData.documentElement.nodeName.toLowerCase() !== 'amf' ) { | |
| console.log( 'THREE.AMFLoader: Error loading AMF - no AMF document found.' ); | |
| return null; | |
| } | |
| return xmlData; | |
| } | |
| function loadDocumentScale( node ) { | |
| let scale = 1.0; | |
| let unit = 'millimeter'; | |
| if ( node.documentElement.attributes.unit !== undefined ) { | |
| unit = node.documentElement.attributes.unit.value.toLowerCase(); | |
| } | |
| const scaleUnits = { | |
| millimeter: 1.0, | |
| inch: 25.4, | |
| feet: 304.8, | |
| meter: 1000.0, | |
| micron: 0.001 | |
| }; | |
| if ( scaleUnits[ unit ] !== undefined ) { | |
| scale = scaleUnits[ unit ]; | |
| } | |
| console.log( 'THREE.AMFLoader: Unit scale: ' + scale ); | |
| return scale; | |
| } | |
| function loadMaterials( node ) { | |
| let matName = 'AMF Material'; | |
| const matId = node.attributes.id.textContent; | |
| let color = { r: 1.0, g: 1.0, b: 1.0, a: 1.0 }; | |
| let loadedMaterial = null; | |
| for ( let i = 0; i < node.childNodes.length; i ++ ) { | |
| const matChildEl = node.childNodes[ i ]; | |
| if ( matChildEl.nodeName === 'metadata' && matChildEl.attributes.type !== undefined ) { | |
| if ( matChildEl.attributes.type.value === 'name' ) { | |
| matName = matChildEl.textContent; | |
| } | |
| } else if ( matChildEl.nodeName === 'color' ) { | |
| color = loadColor( matChildEl ); | |
| } | |
| } | |
| loadedMaterial = new MeshPhongMaterial( { | |
| flatShading: true, | |
| color: new Color( color.r, color.g, color.b ), | |
| name: matName | |
| } ); | |
| if ( color.a !== 1.0 ) { | |
| loadedMaterial.transparent = true; | |
| loadedMaterial.opacity = color.a; | |
| } | |
| return { id: matId, material: loadedMaterial }; | |
| } | |
| function loadColor( node ) { | |
| const color = { r: 1.0, g: 1.0, b: 1.0, a: 1.0 }; | |
| for ( let i = 0; i < node.childNodes.length; i ++ ) { | |
| const matColor = node.childNodes[ i ]; | |
| if ( matColor.nodeName === 'r' ) { | |
| color.r = matColor.textContent; | |
| } else if ( matColor.nodeName === 'g' ) { | |
| color.g = matColor.textContent; | |
| } else if ( matColor.nodeName === 'b' ) { | |
| color.b = matColor.textContent; | |
| } else if ( matColor.nodeName === 'a' ) { | |
| color.a = matColor.textContent; | |
| } | |
| } | |
| return color; | |
| } | |
| function loadMeshVolume( node ) { | |
| const volume = { name: '', triangles: [], materialid: null }; | |
| let currVolumeNode = node.firstElementChild; | |
| if ( node.attributes.materialid !== undefined ) { | |
| volume.materialId = node.attributes.materialid.nodeValue; | |
| } | |
| while ( currVolumeNode ) { | |
| if ( currVolumeNode.nodeName === 'metadata' ) { | |
| if ( currVolumeNode.attributes.type !== undefined ) { | |
| if ( currVolumeNode.attributes.type.value === 'name' ) { | |
| volume.name = currVolumeNode.textContent; | |
| } | |
| } | |
| } else if ( currVolumeNode.nodeName === 'triangle' ) { | |
| const v1 = currVolumeNode.getElementsByTagName( 'v1' )[ 0 ].textContent; | |
| const v2 = currVolumeNode.getElementsByTagName( 'v2' )[ 0 ].textContent; | |
| const v3 = currVolumeNode.getElementsByTagName( 'v3' )[ 0 ].textContent; | |
| volume.triangles.push( v1, v2, v3 ); | |
| } | |
| currVolumeNode = currVolumeNode.nextElementSibling; | |
| } | |
| return volume; | |
| } | |
| function loadMeshVertices( node ) { | |
| const vertArray = []; | |
| const normalArray = []; | |
| let currVerticesNode = node.firstElementChild; | |
| while ( currVerticesNode ) { | |
| if ( currVerticesNode.nodeName === 'vertex' ) { | |
| let vNode = currVerticesNode.firstElementChild; | |
| while ( vNode ) { | |
| if ( vNode.nodeName === 'coordinates' ) { | |
| const x = vNode.getElementsByTagName( 'x' )[ 0 ].textContent; | |
| const y = vNode.getElementsByTagName( 'y' )[ 0 ].textContent; | |
| const z = vNode.getElementsByTagName( 'z' )[ 0 ].textContent; | |
| vertArray.push( x, y, z ); | |
| } else if ( vNode.nodeName === 'normal' ) { | |
| const nx = vNode.getElementsByTagName( 'nx' )[ 0 ].textContent; | |
| const ny = vNode.getElementsByTagName( 'ny' )[ 0 ].textContent; | |
| const nz = vNode.getElementsByTagName( 'nz' )[ 0 ].textContent; | |
| normalArray.push( nx, ny, nz ); | |
| } | |
| vNode = vNode.nextElementSibling; | |
| } | |
| } | |
| currVerticesNode = currVerticesNode.nextElementSibling; | |
| } | |
| return { 'vertices': vertArray, 'normals': normalArray }; | |
| } | |
| function loadObject( node ) { | |
| const objId = node.attributes.id.textContent; | |
| const loadedObject = { name: 'amfobject', meshes: [] }; | |
| let currColor = null; | |
| let currObjNode = node.firstElementChild; | |
| while ( currObjNode ) { | |
| if ( currObjNode.nodeName === 'metadata' ) { | |
| if ( currObjNode.attributes.type !== undefined ) { | |
| if ( currObjNode.attributes.type.value === 'name' ) { | |
| loadedObject.name = currObjNode.textContent; | |
| } | |
| } | |
| } else if ( currObjNode.nodeName === 'color' ) { | |
| currColor = loadColor( currObjNode ); | |
| } else if ( currObjNode.nodeName === 'mesh' ) { | |
| let currMeshNode = currObjNode.firstElementChild; | |
| const mesh = { vertices: [], normals: [], volumes: [], color: currColor }; | |
| while ( currMeshNode ) { | |
| if ( currMeshNode.nodeName === 'vertices' ) { | |
| const loadedVertices = loadMeshVertices( currMeshNode ); | |
| mesh.normals = mesh.normals.concat( loadedVertices.normals ); | |
| mesh.vertices = mesh.vertices.concat( loadedVertices.vertices ); | |
| } else if ( currMeshNode.nodeName === 'volume' ) { | |
| mesh.volumes.push( loadMeshVolume( currMeshNode ) ); | |
| } | |
| currMeshNode = currMeshNode.nextElementSibling; | |
| } | |
| loadedObject.meshes.push( mesh ); | |
| } | |
| currObjNode = currObjNode.nextElementSibling; | |
| } | |
| return { 'id': objId, 'obj': loadedObject }; | |
| } | |
| const xmlData = loadDocument( data ); | |
| let amfName = ''; | |
| let amfAuthor = ''; | |
| const amfScale = loadDocumentScale( xmlData ); | |
| const amfMaterials = {}; | |
| const amfObjects = {}; | |
| const childNodes = xmlData.documentElement.childNodes; | |
| let i, j; | |
| for ( i = 0; i < childNodes.length; i ++ ) { | |
| const child = childNodes[ i ]; | |
| if ( child.nodeName === 'metadata' ) { | |
| if ( child.attributes.type !== undefined ) { | |
| if ( child.attributes.type.value === 'name' ) { | |
| amfName = child.textContent; | |
| } else if ( child.attributes.type.value === 'author' ) { | |
| amfAuthor = child.textContent; | |
| } | |
| } | |
| } else if ( child.nodeName === 'material' ) { | |
| const loadedMaterial = loadMaterials( child ); | |
| amfMaterials[ loadedMaterial.id ] = loadedMaterial.material; | |
| } else if ( child.nodeName === 'object' ) { | |
| const loadedObject = loadObject( child ); | |
| amfObjects[ loadedObject.id ] = loadedObject.obj; | |
| } | |
| } | |
| const sceneObject = new Group(); | |
| const defaultMaterial = new MeshPhongMaterial( { | |
| name: Loader.DEFAULT_MATERIAL_NAME, | |
| color: 0xaaaaff, | |
| flatShading: true | |
| } ); | |
| sceneObject.name = amfName; | |
| sceneObject.userData.author = amfAuthor; | |
| sceneObject.userData.loader = 'AMF'; | |
| for ( const id in amfObjects ) { | |
| const part = amfObjects[ id ]; | |
| const meshes = part.meshes; | |
| const newObject = new Group(); | |
| newObject.name = part.name || ''; | |
| for ( i = 0; i < meshes.length; i ++ ) { | |
| let objDefaultMaterial = defaultMaterial; | |
| const mesh = meshes[ i ]; | |
| const vertices = new Float32BufferAttribute( mesh.vertices, 3 ); | |
| let normals = null; | |
| if ( mesh.normals.length ) { | |
| normals = new Float32BufferAttribute( mesh.normals, 3 ); | |
| } | |
| if ( mesh.color ) { | |
| const color = mesh.color; | |
| objDefaultMaterial = defaultMaterial.clone(); | |
| objDefaultMaterial.color = new Color( color.r, color.g, color.b ); | |
| if ( color.a !== 1.0 ) { | |
| objDefaultMaterial.transparent = true; | |
| objDefaultMaterial.opacity = color.a; | |
| } | |
| } | |
| const volumes = mesh.volumes; | |
| for ( j = 0; j < volumes.length; j ++ ) { | |
| const volume = volumes[ j ]; | |
| const newGeometry = new BufferGeometry(); | |
| let material = objDefaultMaterial; | |
| newGeometry.setIndex( volume.triangles ); | |
| newGeometry.setAttribute( 'position', vertices.clone() ); | |
| if ( normals ) { | |
| newGeometry.setAttribute( 'normal', normals.clone() ); | |
| } | |
| if ( amfMaterials[ volume.materialId ] !== undefined ) { | |
| material = amfMaterials[ volume.materialId ]; | |
| } | |
| newGeometry.scale( amfScale, amfScale, amfScale ); | |
| newObject.add( new Mesh( newGeometry, material.clone() ) ); | |
| } | |
| } | |
| sceneObject.add( newObject ); | |
| } | |
| return sceneObject; | |
| } | |
| } | |
| export { AMFLoader }; | |
Xet Storage Details
- Size:
- 10.7 kB
- Xet hash:
- 14821d87ead9ffd94a31fa0c3e0cba0b8c3b6291f88c37df2d8be17609763bfb
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.