| import{ |
| Component, |
| ElementRef, |
| EventEmitter, |
| HostListener, |
| OnInit, |
| Output, |
| ViewChild, |
| Input |
| }from'@angular/core'; |
| import{Object3D,TextureLoader,Group,TGALoader,MeshPhongMaterial,CanvasTexture,Vector3}from'three'; |
| import*asTHREEfrom'three'; |
| import'./js/EnableThreeExamples'; |
| import'three/examples/js/loaders/OBJLoader'; |
| import'three/examples/js/loaders/MTLLoader'; |
| import'three/examples/js/loaders/TDSLoader'; |
| import'three/examples/js/loaders/TGALoader'; |
| import'three/examples/js/controls/OrbitControls'; |
| import'three/examples/js/controls/TrackballControls.js'; |
| |
| import{ThreeDModel}from'../models/3d.model'; |
| |
| |
| @Component({ |
| selector: 'app-scene', |
| templateUrl: './app-scene.component.html', |
| styleUrls: ['./app-scene.component.css'] |
| }) |
| exportclassAppSceneComponentimplementsOnInit{ |
| |
| _selectedModel: ThreeDModel; |
| @Input() |
| setselectedModel(value: ThreeDModel){ |
| this._selectedModel=value; |
| this.changeModel(); |
| } |
| getselectedModel(): ThreeDModel{ |
| returnthis._selectedModel; |
| } |
| @Input()selectedLogo: ThreeDModel; |
| @Input()imageSize: number; |
| @Output()clickIntersect=newEventEmitter<Object3D>(); |
| @ViewChild('canvas')privatecanvasRef: ElementRef; |
| |
| privaterenderer: THREE.WebGLRenderer; |
| privatecamera: THREE.PerspectiveCamera; |
| publicadvertCanvas: HTMLCanvasElement; |
| publicscene: THREE.Scene; |
| publiccontrols: THREE.OrbitControls; |
| privatecurrentObject: Object3D; |
| privateobject: Object3D; |
| privateloader; |
| publiccolor: THREE.Color; |
| publictexture: THREE.CanvasTexture; |
| privatenormalLoader: TextureLoader; |
| privatetextureLoader: TGALoader; |
| |
| publicfieldOfView=60; |
| publicnearClippingPane=1; |
| publicfarClippingPane=1100; |
| publiccanvasSize=4096; |
| privateanimationTimer; |
| backgroundScene: THREE.Scene; |
| backgroundCamera: THREE.Camera; |
| |
| /* LIFECYCLE */ |
| ngOnInit(){ |
| window['a']=this; |
| this.createBackground(); |
| this.createCamera(); |
| this.createLight(); |
| this.startRendering(); |
| this.addControls(); |
| } |
| |
| createBackground(): any{ |
| consttexture=THREE.ImageUtils.loadTexture('assets/background.jpg'); |
| constbackgroundMesh=newTHREE.Mesh( |
| newTHREE.PlaneGeometry(4,4,0), |
| newTHREE.MeshBasicMaterial({ |
| map: texture |
| })); |
| |
| (backgroundMesh.materialasany).depthTest=false; |
| (backgroundMesh.materialasany).depthWrite=false; |
| |
| // Create your background scene |
| this.backgroundScene=newTHREE.Scene(); |
| this.backgroundCamera=newTHREE.Camera(); |
| this.backgroundScene.add(this.backgroundCamera); |
| this.backgroundScene.add(backgroundMesh); |
| } |
| |
| privategetcanvas(): HTMLCanvasElement{ |
| returnthis.canvasRef.nativeElement; |
| } |
| |
| privatechangeModel(){ |
| if(!this.selectedModel){ |
| return; |
| } |
| this.initNewScene(); |
| letobj; |
| if(obj=this.scene.getObjectByName('currObj')){ |
| obj.parent.remove(obj); |
| this.animationTimer=null; |
| this.render(); |
| } |
| // if (this.selectedModel.name === 'Astronaut') { |
| // this.animationTimer = null; |
| // this.createSce(); |
| // return; |
| // } |
| if(this.selectedModel.name==='Astronaut'){ |
| this.animationTimer=null; |
| this.createAstronaut2(); |
| return; |
| } |
| if(this.selectedModel.name==='Arian-1'){ |
| this.animationTimer=null; |
| this.createRocket(); |
| return; |
| } |
| this.createScene(); |
| } |
| |
| privatecreateRocket(){ |
| lettexture; |
| constmanager=newTHREE.LoadingManager(()=>{}); |
| this.textureLoader=newTHREE.TextureLoader(manager); |
| texture=this.textureLoader.load('assets/rocket1/rocket.jpg'); |
| |
| this.loader=new(THREEasany).MTLLoader(); |
| this.loader.setPath(`assets/rocket1/`); |
| this.loader.load(`12217_rocket_v1_l1.mtl`,e=>{ |
| e.preload(); |
| new(THREEasany).OBJLoader() |
| .setPath('assets/rocket1/') |
| .load('12217_rocket_v1_l1.obj',(object)=>{ |
| this.object=object; |
| window['r']=this.object; |
| this.object.position.set(0,-5.5,0); |
| this.object.scale.set(0.01,0.01,0.01); |
| this.object.receiveShadow=false; |
| this.animationTimer=this.startObjectAnimation(this.object); |
| // this.camera.lookAt(this.object.position); |
| this.object.traverse(child=>{ |
| this.addCanvasTexture(child,this.object,texture); |
| }); |
| this.object.name='currObj'; |
| this.scene.add(this.object); |
| this.render(); |
| },x=>{},err=>{}); |
| }); |
| } |
| |
| privatecreateSce(){ |
| this.loader=new(THREEasany).TDSLoader(); |
| this.loader.setResourcePath(`assets/astronaut/textures/`); |
| this.loader.load(`assets/astronaut/astronaut.3ds`,e=>this.onAstroLoading(e)); |
| this.textureLoader=newTHREE.TGALoader(); |
| this.render(); |
| } |
| |
| privatecreateAstronaut2(){ |
| new(THREEasany).OBJLoader() |
| .setPath('assets/astronaut2/') |
| .load('astronaut2.obj',(object)=>{ |
| this.object=object; |
| window['e']=this.object; |
| this.object.traverse(child=>{ |
| this.addCanvasTexture(child,this.object,texture); |
| }); |
| this.object.position.set(0,-7,0); |
| this.object.receiveShadow=false; |
| this.object.scale.set(8,8,8); |
| this.animationTimer=this.startObjectAnimation(this.object); |
| this.object.name='currObj'; |
| this.scene.add(this.object); |
| this.render(); |
| },x=>{},err=>{}); |
| lettexture; |
| constmanager=newTHREE.LoadingManager(()=>{}); |
| this.textureLoader=newTHREE.TextureLoader(manager); |
| texture=this.textureLoader.load('assets/astronaut2/z2_Color_s.jpg'); |
| |
| // this.loader = new (THREE as any).MTLLoader(); |
| // this.loader.setPath(`assets/astronaut2/`); |
| // this.loader.load(`astronaut2.mtl`, e => { |
| // e.preload(); |
| |
| // }); |
| } |
| |
| startObjectAnimation(object: Object3D){ |
| returnsetInterval(()=>{ |
| object.rotateY(0.1); |
| this.render(); |
| },100); |
| } |
| |
| privateonAstroLoading(collada: Group){ |
| collada.children.forEach((e: THREE.Mesh)=>{ |
| consttext=this.textureLoader.load('assets/astronaut/textures/astnt1_1.tga'); |
| this.addCanvasTexture(e,this.object,text); |
| }); |
| collada.name='currObj'; |
| this.scene.add(collada); |
| } |
| |
| privatecreateScene(){ |
| this.loader=new(THREEasany).TDSLoader(); |
| this.normalLoader=newTextureLoader(); |
| constnormal=this.normalLoader.load(`assets/${this.selectedModel.path}/textures/${this.selectedModel.normal}`); |
| |
| this.loader.setResourcePath(`assets/${this.selectedModel.path}/textures/`); |
| this.loader.load(`assets/${this.selectedModel.path}/${this.selectedModel.path}.3ds`, |
| e=>this.onModelLoadingCompleted(e,normal)); |
| } |
| |
| privateinitNewScene(){ |
| this.scene=newTHREE.Scene(); |
| // this.scene.add(new THREE.AxesHelper(200)); |
| constlight=newTHREE.HemisphereLight(); |
| this.scene.add(light); |
| if(this.camera){ |
| this.scene.add(this.camera); |
| } |
| } |
| |
| privateonModelLoadingCompleted(collada,normal){ |
| this.currentObject=collada; |
| this.currentObject.traverse(object=>{ |
| this.addCanvasTexture(object,this.currentObject,normal,true); |
| }); |
| this.currentObject.receiveShadow=false; |
| this.currentObject.name='currObj'; |
| this.scene.add(this.currentObject); |
| this.animationTimer=this.startObjectAnimation(this.currentObject); |
| this.render(); |
| } |
| |
| privatesetupCanvas(): void{ |
| this.advertCanvas=document.createElement('canvas'); |
| this.advertCanvas.width=this.canvasSize; |
| this.advertCanvas.height=this.canvasSize; |
| this.color=newTHREE.Color(); |
| this.texture=newTHREE.CanvasTexture(this.advertCanvas); |
| } |
| |
| privateaddCanvasTexture(object,currentObject,normal,isNormal=false): void{ |
| this.setupCanvas(); |
| constchild=objectasany; |
| |
| if(!object.isMesh&&!child.material){ |
| return; |
| } |
| |
| isNormal ? child.material.normalMap=normal : child.material.map=normal; |
| child.material.lightMap=normal; |
| |
| currentObject.add(newTHREE.Mesh(child.geometry,newMeshPhongMaterial({ |
| map: this.texture, |
| alphaTest: 0.5 |
| }))); |
| } |
| |
| privatecreateLight(){ |
| // var directionalLight = new THREE.DirectionalLight( 0xffeedd ); |
| // directionalLight.position.set( 0, 0, 2 ); |
| // this.scene.add( directionalLight ); |
| } |
| |
| privatecreateCamera(){ |
| constaspectRatio=this.getAspectRatio(); |
| this.camera=newTHREE.PerspectiveCamera( |
| this.fieldOfView, |
| aspectRatio, |
| this.nearClippingPane, |
| this.farClippingPane |
| ); |
| |
| |
| /** |
| * x: -13.51642127467603 |
| y: 0.20744722870848967 |
| z: -19.82593506593228 |
| */ |
| // Set position and look at |
| this.camera.position.x=-10; |
| this.camera.position.y=0.75; |
| this.camera.position.z=-17; |
| this.camera.rotation.x=-2; |
| this.camera.rotation.y=0; |
| this.camera.rotation.z=-2; |
| } |
| |
| privategetAspectRatio(): number{ |
| constheight=this.canvas.clientHeight; |
| if(height===0){ |
| return0; |
| } |
| returnthis.canvas.clientWidth/this.canvas.clientHeight; |
| } |
| |
| privatestartRendering(){ |
| this.renderer=newTHREE.WebGLRenderer({ |
| canvas: this.canvas, |
| antialias: true |
| }); |
| this.renderer.setPixelRatio(devicePixelRatio); |
| this.renderer.setSize(this.canvas.clientWidth,this.canvas.clientHeight); |
| |
| this.renderer.shadowMap.enabled=true; |
| this.renderer.shadowMap.type=THREE.PCFSoftShadowMap; |
| this.renderer.setClearColor(0x1B1B1B,1); |
| this.renderer.autoClear=true; |
| |
| this.render(); |
| } |
| |
| publicrender(){ |
| if(this.renderer&&this.scene){ |
| this.renderer.autoClear=false; |
| this.renderer.clear(); |
| this.renderer.render(this.backgroundScene,this.backgroundCamera); |
| this.renderer.render(this.scene,this.camera); |
| } |
| } |
| |
| publicaddControls(){ |
| this.controls=newTHREE.OrbitControls(this.camera); |
| this.controls.rotateSpeed=1.0; |
| this.controls.zoomSpeed=1.2; |
| this.controls.addEventListener('change',()=>this.render()); |
| } |
| |
| /* EVENTS */ |
| |
| publiconClick(event: MouseEvent){ |
| // Example of mesh selection/pick: |
| constraycaster=newTHREE.Raycaster(); |
| constmouse=newTHREE.Vector2(); |
| mouse.x=((event.clientX-300)/this.renderer.domElement.clientWidth)*2-1; |
| mouse.y=-(event.clientY/this.renderer.domElement.clientHeight)*2+1; |
| raycaster.setFromCamera(mouse,this.camera); |
| |
| constobj: THREE.Object3D[]=[]; |
| this.findAllObjects(obj,this.scene); |
| constintersects=raycaster.intersectObjects(obj); |
| // console.log('Scene has ' + obj.length + ' objects'); |
| // console.log(intersects.length + ' intersected objects found'); |
| |
| if(intersects&&intersects.length){ |
| intersects.forEach((intersect,index)=>{ |
| if(index>1){ |
| return; |
| } |
| |
| // console.log(intersect); |
| constobject=intersect.object; |
| if(intersect.uv&&object&&(objectasany).material&&(objectasany).material.mapinstanceofCanvasTexture){ |
| this.addCanvasObject({ |
| offsetX: intersect.uv.x*this.canvasSize, |
| offsetY: (1-intersect.uv.y)*this.canvasSize |
| }); |
| } |
| this.clickIntersect.emit(object); |
| }); |
| } |
| } |
| |
| publicaddCanvasObject(event: {offsetX: number,offsetY: number}): void{ |
| if(!this.selectedLogo){ |
| return; |
| } |
| |
| constctx=this.advertCanvas.getContext('2d'); |
| constimage=newImage(750,750); |
| image.src=this.selectedLogo.path; |
| ctx.drawImage(image, |
| event.offsetX-this.imageSize/2, |
| event.offsetY-this.imageSize/2, |
| this.imageSize,this.imageSize |
| ); |
| this.texture.needsUpdate=true; |
| this.render(); |
| } |
| |
| privatefindAllObjects(pred: THREE.Object3D[],parent: THREE.Object3D){ |
| // NOTE: Better to keep separate array of selected objects |
| if(parent.children.length>0){ |
| parent.children.forEach((i)=>{ |
| pred.push(i); |
| this.findAllObjects(pred,i); |
| }); |
| } |
| } |
| |
| @HostListener('window:resize',['$event']) |
| publiconResize(){ |
| this.canvas.style.width='100%'; |
| this.canvas.style.height='100%'; |
| // console.log('onResize: ' + this.canvas.clientWidth + ', ' + this.canvas.clientHeight); |
| |
| this.camera.aspect=this.getAspectRatio(); |
| this.camera.updateProjectionMatrix(); |
| this.renderer.setSize(this.canvas.clientWidth,this.canvas.clientHeight); |
| this.render(); |
| } |
| |
| publicsplitByMaterial(geometry,materials){ |
| constparts=[]; |
| constfaces=['a','b','c']; |
| letgeo,vMap,iMat; |
| |
| geometry.faces.forEach(face=>{ |
| if(face.materialIndex!==iMat){ |
| if(iMat){ |
| this.addPart(materials,iMat,parts,geo); |
| } |
| |
| geo=newTHREE.Geometry(); |
| vMap={}; |
| iMat=face.materialIndex; |
| } |
| |
| constf=face.clone(); |
| faces.forEach(p=>{ |
| constiv=face[p]; |
| |
| if(!vMap.hasOwnProperty(iv)){ |
| vMap[iv]=geo.vertices.push(geometry.vertices[iv])-1; |
| } |
| |
| f[p]=vMap[iv]; |
| }); |
| geo.faces.push(f); |
| }); |
| |
| this.addPart(materials,iMat,parts,geo); |
| returnparts; |
| } |
| |
| privateaddPart(materials,iMat,parts,geo): void{ |
| constmat=materials ? Object.assign({},materials[iMat]) : {}; |
| mat.side=THREE.DoubleSide; |
| parts.push(newTHREE.Mesh(geo,mat)); |
| } |
| } |