import * as THREE from "../../../libs/three.js/build/three.module.js"; import { EventDispatcher } from "../../EventDispatcher.js"; import { Utils } from "../../utils.js"; import {LineDraw} from "../../utils/DrawUtil"; import CurveCtrl from "../../objects/tool/CurveCtrl"; const colors = { position: 'red', target : 'blue' } let lineMats const getLineMat = function(name){ if(!lineMats){ lineMats = { position: LineDraw.createFatLineMat({ color: colors.position, lineWidth: 3 }), target : LineDraw.createFatLineMat({ color: colors.target, lineWidth: 3 }), frustum: LineDraw.createFatLineMat({ color: colors.position, lineWidth: 2 }), } } return lineMats[name] } export class CameraAnimation extends EventDispatcher{ constructor(viewer){ super(); this.viewer = viewer; this.selectedElement = null; //this.controlPoints = []; this.uuid = THREE.Math.generateUUID(); this.node = new THREE.Object3D(); this.node.name = "camera animation"; this.viewer.scene.scene.add(this.node); this.frustum = this.createFrustum(); this.node.add(this.frustum); this.name = "Camera Animation"; this.duration = 5; this.t = 0; // "centripetal", "chordal", "catmullrom" this.curveType = "centripetal" this.visible = true; this.createPath(); this.addEventListener('dispose', ()=>{ this.dispose() }) } static defaultFromView(viewer){ const animation = new CameraAnimation(viewer); const camera = viewer.scene.getActiveCamera(); const target = viewer.scene.view.getPivot(); const cpCenter = new THREE.Vector3( 0.3 * camera.position.x + 0.7 * target.x, 0.3 * camera.position.y + 0.7 * target.y, 0.3 * camera.position.z + 0.7 * target.z, ); const targetCenter = new THREE.Vector3( 0.05 * camera.position.x + 0.95 * target.x, 0.05 * camera.position.y + 0.95 * target.y, 0.05 * camera.position.z + 0.95 * target.z, ); const r = 2//camera.position.distanceTo(target) * 0.3; //const dir = target.clone().sub(camera.position).normalize(); const angle = Utils.computeAzimuth(camera.position, target); const n = 5; for(let i = 0; i < n; i++){ let u = 1.5 * Math.PI * (i / n) + angle; const dx = r * Math.cos(u); const dy = r * Math.sin(u); const cpPos = [ cpCenter.x + dx, cpCenter.y + dy, cpCenter.z, ]; const targetPos = [ targetCenter.x + dx * 0.1, targetCenter.y + dy * 0.1, targetCenter.z, ]; animation.createControlPoint(); animation.posCurve.points[i].fromArray(cpPos) animation.targetCurve.points[i].fromArray(targetPos) } animation.changeCallback() return animation; } createControlPoint(index){ const length = this.posCurve.points.length const position = new THREE.Vector3 const target = new THREE.Vector3 if(index === undefined){ index = length; } if(length >= 2 && index === 0){ const dir = new THREE.Vector3().subVectors(this.posCurve.points[0], this.posCurve.points[1] ) position.copy(this.posCurve.points[0]).add(dir); const tDir = new THREE.Vector3().subVectors(this.targetCurve.points[0], this.targetCurve.points[1] ) target.copy(this.targetCurve.points[0]).add(dir); }else if(length >= 2 && index === length){ const dir = new THREE.Vector3().subVectors(this.posCurve.points[length-1], this.posCurve.points[length-2] ) position.copy(this.posCurve.points[length-2]).add(dir); const tDir = new THREE.Vector3().subVectors(this.targetCurve.points[length-1], this.targetCurve.points[length-2] ) target.copy(this.targetCurve.points[length-2]).add(dir); }else if(length >= 2){ position.copy(this.posCurve.points[index-1].clone().add(this.posCurve.points[index]).multiplyScalar(0.5)); target.copy(this.targetCurve.points[index-1].clone().add(this.targetCurve.points[index]).multiplyScalar(0.5)); } this.posCurve.addPoint(position, index) this.targetCurve.addPoint(target, index) this.dispatchEvent({ type: "controlpoint_added", index }); } removeControlPoint(index){ this.posCurve.removePoint(index) this.targetCurve.removePoint(index) this.dispatchEvent({ type: "controlpoint_removed", index }); } createPath(){ this.posCurve = new CurveCtrl([],getLineMat('position'), colors.position); this.targetCurve = new CurveCtrl([], getLineMat('target'), colors.target); this.node.add(this.posCurve) this.node.add(this.targetCurve) } createFrustum(){ const f = 0.3; const positions = [ new THREE.Vector3( 0, 0, 0), new THREE.Vector3(-f, -f, +1), new THREE.Vector3( 0, 0, 0), new THREE.Vector3( f, -f, +1), new THREE.Vector3( 0, 0, 0), new THREE.Vector3( f, f, +1), new THREE.Vector3( 0, 0, 0), new THREE.Vector3(-f, f, +1), new THREE.Vector3(-f, -f, +1), new THREE.Vector3( f, -f, +1), new THREE.Vector3( f, -f, +1), new THREE.Vector3( f, f, +1), new THREE.Vector3( f, f, +1), new THREE.Vector3(-f, f, +1), new THREE.Vector3(-f, f, +1), new THREE.Vector3(-f, -f, +1), ] //geometry.computeBoundingSphere();//? const line = LineDraw.createFatLine( positions, {material:getLineMat('frustum')}) //line.scale.set(20, 20, 20); return line; } at(t){ if(t > 1){ t = 1; }else if(t < 0){ t = 0; } const camPos = this.posCurve.getPointAt(t); const target = this.targetCurve.getPointAt(t); const frame = { position: camPos, target: target, }; return frame; } set(t){ this.t = t; } setVisible(visible){ this.node.visible = visible; this.posCurve.visible = visible this.targetCurve.visible = visible this.visible = visible; } setDuration(duration){ this.duration = duration; } getDuration(duration){ return this.duration; } play(){ const tStart = performance.now(); const duration = this.duration; const originalyVisible = this.visible; this.setVisible(false); const onUpdate = (delta) => { let tNow = performance.now(); let elapsed = (tNow - tStart) / 1000; let t = elapsed / duration; this.set(t); const frame = this.at(t); viewer.scene.view.position.copy(frame.position); viewer.scene.view.lookAt(frame.target); this.updateFrustum() if(t > 1){ this.setVisible(originalyVisible); this.viewer.removeEventListener("update", onUpdate); } }; this.viewer.addEventListener("update", onUpdate); } updateFrustum(){ // const frame = this.at(this.t); const frustum = this.frustum; frustum.position.copy(frame.position); frustum.lookAt(...frame.target.toArray()); } changeCallback(){ this.posCurve.update() this.targetCurve.update() this.updateFrustum() } dispose(){//add this.posCurve.dispatchEvent({type:'dispose'}) this.targetCurve.dispatchEvent({type:'dispose'}) this.node.parent.remove(this.node); } } //scene.removeCameraAnimation