import * as THREE from './lib/three.module.js'; import { OrbitControls } from './lib/OrbitControls.js'; //import { OrthographicTrackballControls } from './lib/OrthographicTrackballControls.js'; import { TransformControls } from './lib/TransformControls.js'; import {matmul2, euler_angle_to_rotate_matrix} from "./util.js" function ViewManager(mainViewContainer, webglScene, webglMainScene, renderer, globalRenderFunc, on_box_changed, cfg){ this.mainViewContainer = mainViewContainer; this.globalRenderFunc = globalRenderFunc; this.webglScene = webglScene; this.webglMainScene = webglMainScene; this.renderer = renderer; this.mainView = cfg.disableMainView?null:create_main_view(webglMainScene, renderer, this.globalRenderFunc, this.mainViewContainer, on_box_changed); this.boxViewList = []; this.addBoxView = function(subviewsUi){ let boxview = new BoxView(subviewsUi, this.mainViewContainer, this.webglScene, this.renderer, this); this.boxViewList.push(boxview); return boxview; } this.onWindowResize = function(){ if (this.mainView) this.mainView.onWindowResize(); }; this.render = function(){ console.log("render verything"); if (this.mainView) this.mainView.renderAll(); this.boxViewList.forEach(v=>{ //if (v.ui.style.display != 'none') //we have pseudo box now. render as commanded. v.render() }); }; this.setColorScheme = function(){ let scheme = document.documentElement.className; if (scheme == "theme-dark") { this.mainView.backgroundColor = new THREE.Color( 0.0, 0.0, 0.0 ); this.boxViewList.forEach(v=>{ v.views[0].backgroundColor = new THREE.Color( 0.1, 0.05, 0.05 ); v.views[1].backgroundColor = new THREE.Color( 0.05, 0.1, 0.05 ); v.views[2].backgroundColor = new THREE.Color( 0.05, 0.05, 0.1 ); }); } else{ this.mainView.backgroundColor = new THREE.Color( 1.0, 1.0, 1.0 ); this.boxViewList.forEach(v=>{ v.views[0].backgroundColor = new THREE.Color( 0.95, 0.9, 0.9 ); v.views[1].backgroundColor = new THREE.Color( 0.9, 0.95, 0.9 ); v.views[2].backgroundColor = new THREE.Color( 0.9, 0.9, 0.95 ); }) } }; //this.setColorScheme(); // no public funcs below function create_main_view(scene, renderer, globalRenderFunc, container, on_box_changed){ var view ={}; view.backgroundColor= (document.documentElement.className == "theme-dark") ? new THREE.Color( 0.0, 0.0, 0.0 ) : new THREE.Color( 1.0, 1.0, 1.0 ); view.zoom_ratio = 1.0; //useless for mainview let camera = new THREE.PerspectiveCamera( 65, container.clientWidth / container.clientHeight, 1, 500 ); camera.position.x = 0; camera.position.z = 50; camera.position.y = 0; camera.up.set( 0, 0, 1); camera.lookAt( 0, 0, 0 ); camera.name = "main view camera"; view.camera_perspective = camera; view.camera = camera; // make a blind camera to clean background when batch editing is enabled. camera = new THREE.PerspectiveCamera( 65, container.clientWidth / container.clientHeight, 1, 500 ); camera.position.x = -1000; camera.position.z = -1000; camera.position.y = -1000; camera.up.set( 0, 0, 1); camera.lookAt( 0, 0, 0 ); view.blind_camera = camera; view.container = container; view.renderer = renderer; view.scene = scene; view.active = true; view.disable = function(){ this.active = false; this.renderWithCamera(this.blind_camera); }; view.dumpPose = function() { console.log(this.camera.position, this.camera.rotation); }; view.enable = function(){ this.active = true; this.render(); }; //var cameraOrthoHelper = new THREE.CameraHelper( camera ); //cameraOrthoHelper.visible=true; //scene.add( cameraOrthoHelper ); view.render=function(){ console.log("render mainview."); if (this.active){ //this.switch_camera(false); this.renderWithCamera(this.camera); } // else // { // this.renderWithCamera(this.blind_camera); // } }; view.renderAll = function(){ console.log("render mainview."); if (this.active){ //this.switch_camera(false); this.renderWithCamera(this.camera); } else { this.renderWithCamera(this.blind_camera); } } view.clearView = function(){ this.renderWithCamera(this.blind_camera); }; view.renderWithCamera = function(camera){ var left = 0; var bottom = 0; var width = this.container.scrollWidth; var height = this.container.scrollHeight; // update viewport, so the operating lines over these views // will be updated in time. //console.log(left,bottom, width, height); this.renderer.setViewport( left, bottom, width, height ); this.renderer.setScissor( left, bottom, width, height ); this.renderer.setClearColor(view.backgroundColor ); this.renderer.setScissorTest( true ); this.renderer.render( this.scene, camera ); }; var orbit_perspective = new OrbitControls( view.camera_perspective, view.container ); orbit_perspective.update(); orbit_perspective.addEventListener( 'change', globalRenderFunc ); //orbit_perspective.enabled = true; view.orbit_perspective = orbit_perspective; var transform_control = new TransformControls(view.camera_perspective , view.container ); transform_control.setSpace("local"); transform_control.addEventListener( 'change', globalRenderFunc ); transform_control.addEventListener( 'objectChange', function(e){on_box_changed(e.target.object);}); transform_control.addEventListener( 'dragging-changed', function ( event ) { view.orbit_perspective.enabled = ! event.value; } ); transform_control.visible = false; //transform_control.enabled = false; scene.add( transform_control ); view.transform_control_perspective = transform_control; var width = container.clientWidth; var height = container.clientHeight; var asp = width/height; //camera = new THREE.OrthographicCamera(-800*asp, 800*asp, 800, -800, -800, 800); // camera.position.x = 0; // camera.position.z = 0; // camera.position.y = 0; // camera.up.set( 1, 0, 0); // camera.lookAt( 0, 0, -3 ); //camera = new THREE.OrthographicCamera( container.clientWidth / - 2, container.clientWidth / 2, container.clientHeight / 2, container.clientHeight / - 2, -400, 400 ); // camera = new THREE.OrthographicCamera(-asp*200, asp*200, 200, -200, -200, 200 ); // camera.position.z = 50; // var cameraOrthoHelper = new THREE.CameraHelper( camera ); // cameraOrthoHelper.visible=true; // scene.add( cameraOrthoHelper ); //view.camera_orth = camera; // var orbit_orth = new OrbitControls( view.camera_orth, view.container ); // orbit_orth.update(); // orbit_orth.addEventListener( 'change', render ); // orbit_orth.enabled = false; // view.orbit_orth = orbit_orth; // var orbit_orth = new OrthographicTrackballControls( view.camera_orth, view.container ); // orbit_orth.rotateSpeed = 1.0; // orbit_orth.zoomSpeed = 1.2; // orbit_orth.noZoom = false; // orbit_orth.noPan = false; // orbit_orth.noRotate = false; // orbit_orth.staticMoving = true; // orbit_orth.dynamicDampingFactor = 0.3; // orbit_orth.keys = [ 65, 83, 68 ]; // orbit_orth.addEventListener( 'change', globalRenderFunc ); // orbit_orth.enabled=true; // view.orbit_orth = orbit_orth; // transform_control = new TransformControls(view.camera_orth, view.container ); // transform_control.setSpace("local"); // transform_control.addEventListener( 'change', globalRenderFunc ); // transform_control.addEventListener( 'objectChange', function(e){on_box_changed(e.target.object);} ); // transform_control.addEventListener( 'dragging-changed', function ( event ) { // view.orbit_orth.enabled = ! event.value; // } ); // transform_control.visible = false; // //transform_control.enabled = true; // //scene.add( transform_control ); // view.transform_control_orth = transform_control; view.camera = view.camera_perspective; view.orbit = view.orbit_perspective; view.transform_control = view.transform_control_perspective; view.switch_camera = function(birdseye) { if (!birdseye && (this.camera === this.camera_orth)){ this.camera = this.camera_perspective; this.orbit_orth.enabled=false; this.orbit_perspective.enabled=true; this.orbit = this.orbit_perspective; this.transform_control_perspective.detach(); this.transform_control_orth.detach(); this.transform_control_orth.enabled=false; this.transform_control_perspective.enabled=true; //this.transform_control_perspective.visible = false; //this.transform_control_orth.visible = false; this.transform_control = this.transform_control_perspective; } else if (birdseye && (this.camera === this.camera_perspective)) { this.camera = this.camera_orth; this.orbit_orth.enabled=true; this.orbit_perspective.enabled=false; this.orbit = this.orbit_orth; this.transform_control_perspective.detach(); this.transform_control_orth.detach(); this.transform_control_orth.enabled=true; this.transform_control_perspective.enabled=false; this.transform_control = this.transform_control_orth; } this.camera.updateProjectionMatrix(); }; view.reset_camera = function(){ var camera = this.camera_perspective; camera.position.x = 0; camera.position.z = 50; camera.position.y = 0; camera.up.set( 0, 0, 1); camera.lookAt( 0, 0, 0 ); camera.updateProjectionMatrix(); this.orbit_perspective.reset(); // this func will call render() }; view.look_at = function(p){ if (this.orbit === this.orbit_perspective){ this.orbit.target.x=p.x; this.orbit.target.y=p.y; this.orbit.target.z=p.z; this.orbit.update(); } }; view.onWindowResize = function(){ var asp = container.clientWidth/container.clientHeight; // this.camera_orth.left = -asp*200; // this.camera_orth.right = asp*200; // this.camera_orth.top = 200; // this.camera_orth.bottom = -200 // this.camera_orth.updateProjectionMatrix(); // this.orbit_orth.handleResize(); // this.orbit_orth.update(); this.camera_perspective.aspect = container.clientWidth / container.clientHeight; this.camera_perspective.updateProjectionMatrix(); }; view.reset_birdseye = function(){ //this.orbit_orth.reset(); // }; view.rotate_birdseye = function(){ //this.camera_orth.up.set( 1, 0, 0); //this.orbit_orth.update(); } view.detach_control = function(){ this.transform_control.detach(); } view.target0 = view.orbit.target.clone(); view.position0 = view.camera.position.clone(); view.zoom0 = view.camera.zoom; view.scale0 = null; view.save_orbit_state = function(highlight_obj_scale){ this.target0.copy( this.orbit.target ); this.position0.copy( this.camera.position ); this.zoom0 = this.camera.zoom; this.scale0 = {x: highlight_obj_scale.x, y: highlight_obj_scale.y, z: highlight_obj_scale.z}; } view.restore_relative_orbit_state = function(highlight_obj_scale){ if (view.scale0){ // restore last viewpoint var obj_size = Math.sqrt(view.scale0.x*view.scale0.x + view.scale0.y*view.scale0.y + view.scale0.z*view.scale0.z); var target_obj_size = Math.sqrt(highlight_obj_scale.x*highlight_obj_scale.x + highlight_obj_scale.y*highlight_obj_scale.y + highlight_obj_scale.z*highlight_obj_scale.z); var ratio = target_obj_size/obj_size; this.camera.position.x = this.orbit.target.x + (this.position0.x - this.target0.x)*ratio; this.camera.position.y = this.orbit.target.y + (this.position0.y - this.target0.y)*ratio; this.camera.position.z = this.orbit.target.z + (this.position0.z - this.target0.z)*ratio; this.camera.zoom = this.zoom0; } else { // not saved yet, set default viewpoint this.camera.position.set( this.orbit.target.x + highlight_obj_scale.x*3, this.orbit.target.y + highlight_obj_scale.y*3, this.orbit.target.z + highlight_obj_scale.z*3); } // target is set } return view; } } function BoxView(ui, mainViewContainer, scene, renderer, viewManager){ this.viewManager = viewManager; this.mainViewContainer = mainViewContainer; this.ui = ui; //sub-views this.baseOffset = function(){ // ui offset return { top: this.ui.offsetTop, left: this.ui.offsetLeft } }; this.defaultBox = { position: {x: -100, y: -100, z: 0}, rotation: {x: 0, y: 0, z: 0}, scale: {x: 5, y: 5, z: 5}, }; this.box = this.defaultBox; this.attachBox = function(box){ this.box = box; this.views.forEach(v=>{ //this.box.world.webglGroup.add(v.camera); //this.box.world.webglGroup.add(v.cameraHelper); this.box.world.webglGroup.add(v.cameraContainer); this.box.world.webglGroup.add(v.cameraHelper); //seems camerahelp shold be added to top-most scene only. }); this.onBoxChanged(); }; this.detach = function(){ this.box = this.defaultBox; this.onBoxChanged(); }; this.onBoxChanged=function(dontRender){ this.updateCameraPose(this.box); this.updateCameraRange(this.box); if (!dontRender) this.render(); }; this.updateCameraPose = function(box){ this.views.forEach((v)=>v.updateCameraPose(box)); }; this.updateCameraRange = function(box){ this.views.forEach((v)=>v.updateCameraRange(box)); }; this.hidden = function(){ return this.ui.style.display == 'none'; }; this.render = function(){ if (!this.hidden()) this.views.forEach((v)=>v.render()); } var scope = this; scope.projViewProto = { render(){ let vp = this.getViewPort(); this.renderer.setViewport( vp.left, vp.bottom, vp.width, vp.height ); this.renderer.setScissor( vp.left, vp.bottom, vp.width, vp.height ); this.renderer.setClearColor(this.backgroundColor ); this.renderer.setScissorTest( true ); this.renderer.render( this.scene, this.camera ); }, getViewPort(){ return { left : this.placeHolderUi.offsetLeft + scope.baseOffset().left, bottom : this.container.scrollHeight - (scope.baseOffset().top + this.placeHolderUi.offsetTop + this.placeHolderUi.clientHeight), width : this.placeHolderUi.clientWidth, height : this.placeHolderUi.clientHeight, zoom_ratio: this.zoom_ratio, } }, }; this.views = [ createTopView(scene, renderer, mainViewContainer), createSideView(scene, renderer, mainViewContainer), createBackView(scene, renderer, mainViewContainer), ]; function createTopView(scene, renderer, container){ let view = Object.create(scope.projViewProto); view.name="topview"; view.zoom_ratio = 1.0; view.backgroundColor =  (document.documentElement.className == "theme-dark") ? new THREE.Color( 0.1, 0.05, 0.05 ) : new THREE.Color( 0.95, 0.9, 0.9 ); view.container = container; view.scene = scene; view.renderer = renderer; view.placeHolderUi = ui.querySelector("#z-view-manipulator"); //var camera = new THREE.PerspectiveCamera( 65, container.clientWidth / container.clientHeight, 1, 800 ); var width = container.clientWidth; var height = container.clientHeight; var asp = width/height; var camera = new THREE.OrthographicCamera( -3*asp, 3*asp, 3, -3, -3, 3 ); var cameraOrthoHelper = new THREE.CameraHelper( camera ); cameraOrthoHelper.visible=false; //scene.add( cameraOrthoHelper ); view.cameraHelper = cameraOrthoHelper; camera.position.set(0,0,0); camera.up.set( 1, 0, 0); camera.lookAt( 0, 0, -3 ); view.camera = camera; view.cameraContainer = new THREE.Group(); view.cameraContainer.name = "topview-camera"; view.cameraContainer.add(camera); view.updateCameraPose=function(box){ var p = box.position; var r = box.rotation; //console.log(r); // this.cameraContainer.position.set(p.x, p.y, p.z); this.cameraContainer.rotation.set(r.x, r.y, r.z); }; view.updateCameraRange=function(box){ var exp_camera_width, exp_camera_height, exp_camera_clip; //view.width = 0.2;//params["side view width"]; var view_width = view.placeHolderUi.clientWidth; var view_height = view.placeHolderUi.clientHeight; exp_camera_height = box.scale.x*1.5*view.zoom_ratio; exp_camera_width = box.scale.y*1.5*view.zoom_ratio; exp_camera_clip = box.scale.z + 0.8; if (exp_camera_width/exp_camera_height > view_width/view_height){ //increase height exp_camera_height = exp_camera_width * view_height/view_width; } else { exp_camera_width = exp_camera_height * view_width/view_height; } this.camera.top = exp_camera_height/2; this.camera.bottom = exp_camera_height/-2; this.camera.right = exp_camera_width/2; this.camera.left = exp_camera_width/-2; this.camera.near = exp_camera_clip/-2; this.camera.far = exp_camera_clip/2; // this.camera.scale.x = box.scale.x; // this.camera.scale.y = box.scale.y; // this.camera.scale.z = box.scale.z; //camera.aspect = view_width / view_height; this.camera.updateProjectionMatrix(); this.cameraHelper.update(); }; return view; } function createSideView(scene, renderer, container){ let view = Object.create(scope.projViewProto); view.name="sideview"; view.zoom_ratio = 1.0; //view.backgroundColor=new THREE.Color( 0.1, 0.2, 0.1 ); view.backgroundColor=(document.documentElement.className == "theme-dark") ? new THREE.Color( 0.05, 0.1, 0.05 ) : new THREE.Color( 0.9, 0.95, 0.9 ); view.container = container; view.scene = scene; view.renderer = renderer; view.placeHolderUi = ui.querySelector("#y-view-manipulator"); //var camera = new THREE.PerspectiveCamera( 65, container.clientWidth / container.clientHeight, 1, 800 ); var width = container.clientWidth; var height = container.clientHeight; var asp = width/height; var camera = new THREE.OrthographicCamera( -3*asp, 3*asp, 3, -3, -3, 3 ); var cameraOrthoHelper = new THREE.CameraHelper( camera ); cameraOrthoHelper.visible=false; //scene.add( cameraOrthoHelper ); view["cameraHelper"] = cameraOrthoHelper; view.cameraContainer = new THREE.Group(); view.cameraContainer.name = "sideview-camera"; view.cameraContainer.position.x = 0; view.cameraContainer.position.z = 0; view.cameraContainer.position.y = 0; view.cameraContainer.rotation.x=0; //Math.PI/2; view.cameraContainer.rotation.y=0; view.cameraContainer.rotation.z=0; //view.cameraContainer.updateProjectionMatrix(); view.cameraContainer.add(camera); camera.position.set( 0, 0, 0); camera.up.set(0,0,1); camera.lookAt( 0, 3, 0 ); //camera.up.set( 0, 1, 0); //camera.lookAt( 0, 0, -3 ); // camera should not be changed again? view.camera = camera; view.updateCameraPose=function(box){ var p = box.position; var r = box.rotation; view.cameraContainer.position.x = p.x; view.cameraContainer.position.y = p.y; view.cameraContainer.position.z = p.z; view.cameraContainer.rotation.x=r.x; view.cameraContainer.rotation.y=r.y; view.cameraContainer.rotation.z=r.z; //view.cameraContainer.updateProjectionMatrix(); // var trans_matrix = euler_angle_to_rotate_matrix(r, p); // this.camera.position.x= p.x; // this.camera.position.y= p.y; // this.camera.position.z= p.z; // var up = matmul2(trans_matrix, [0, 0, 1, 0], 4); // this.camera.up.set( up[0], up[1], up[2]); // var at = matmul2(trans_matrix, [0, 1, 0, 1], 4); // this.camera.lookAt( at[0], at[1], at[2] ); // this.camera.updateProjectionMatrix(); // this.cameraHelper.update(); }; view.updateCameraRange=function(box){ var exp_camera_width, exp_camera_height, exp_camera_clip; //view.width = 0.2;//params["side view width"]; var view_width = view.placeHolderUi.clientWidth; var view_height = view.placeHolderUi.clientHeight; exp_camera_width = box.scale.x*1.5*view.zoom_ratio; exp_camera_height = box.scale.z*1.5*view.zoom_ratio; exp_camera_clip = box.scale.y*1.2; if (exp_camera_width/exp_camera_height > view_width/view_height){ //increase height exp_camera_height = exp_camera_width * view_height/view_width; } else { exp_camera_width = exp_camera_height * view_width/view_height; } this.camera.top = exp_camera_height/2; this.camera.bottom = exp_camera_height/-2; this.camera.right = exp_camera_width/2; this.camera.left = exp_camera_width/-2; this.camera.near = exp_camera_clip/-2; this.camera.far = exp_camera_clip/2; //camera.aspect = view_width / view_height; this.camera.updateProjectionMatrix(); this.cameraHelper.update(); }; return view; } function createBackView(scene, renderer, container){ let view = Object.create(scope.projViewProto); view.name="backview"; view.zoom_ratio = 1.0; //view.backgroundColor=new THREE.Color( 0.2, 0.1, 0.1 ); view.backgroundColor= (document.documentElement.className == "theme-dark") ? new THREE.Color( 0.05, 0.05, 0.1 ) : new THREE.Color( 0.9, 0.9, 0.95 ); view.container = container; view.scene = scene; view.renderer = renderer; view.placeHolderUi = ui.querySelector("#x-view-manipulator"); //var camera = new THREE.PerspectiveCamera( 65, container.clientWidth / container.clientHeight, 1, 800 ); var width = container.clientWidth; var height = container.clientHeight; var asp = width/height; var camera = new THREE.OrthographicCamera( -3*asp, 3*asp, 3, -3, -3, 3 ); var cameraOrthoHelper = new THREE.CameraHelper( camera ); cameraOrthoHelper.visible=false; //scene.add( cameraOrthoHelper ); view["cameraHelper"] = cameraOrthoHelper; camera.position.set(0,0,0); camera.up.set(0,0,1); camera.lookAt(3,0,0); view.camera = camera; view.cameraContainer = new THREE.Group(); view.cameraContainer.name = "backview-camera"; view.cameraContainer.position.set(0,0,0); view.cameraContainer.rotation.set(0,0,0); view.cameraContainer.add(camera); view.updateCameraPose=function(box){ let p = box.position; let r = box.rotation; // let trans_matrix = euler_angle_to_rotate_matrix(r, p); // this.camera.position.x= p.x; // this.camera.position.y= p.y; // this.camera.position.z= p.z; // var up3 = matmul2(trans_matrix, [0, 0, 1, 0], 4); // this.camera.up.set( up3[0], up3[1], up3[2]); // var at3 = matmul2(trans_matrix, [1, 0, 0, 1], 4); // this.camera.lookAt( at3[0], at3[1], at3[2] ); // this.camera.updateProjectionMatrix(); // this.cameraHelper.update(); this.cameraContainer.position.set(p.x, p.y, p.z); this.cameraContainer.rotation.set(r.x, r.y, r.z); }; view.updateCameraRange=function(box){ var exp_camera_width, exp_camera_height, exp_camera_clip; //view.width = 0.2;//params["side view width"]; var view_width = view.placeHolderUi.clientWidth; var view_height = view.placeHolderUi.clientHeight; exp_camera_width = box.scale.y*1.5*view.zoom_ratio; exp_camera_height = box.scale.z*1.5*view.zoom_ratio; exp_camera_clip = box.scale.x*1.2; if (exp_camera_width/exp_camera_height > view_width/view_height){ //increase height exp_camera_height = exp_camera_width * view_height/view_width; } else { exp_camera_width = exp_camera_height * view_width/view_height; } this.camera.top = exp_camera_height/2; this.camera.bottom = exp_camera_height/-2; this.camera.right = exp_camera_width/2; this.camera.left = exp_camera_width/-2; this.camera.near = exp_camera_clip/-2; this.camera.far = exp_camera_clip/2; //camera.aspect = view_width / view_height; this.camera.updateProjectionMatrix(); this.cameraHelper.update(); }; return view; } } export {ViewManager}