source: js/human_3d_alignment/src/visualization/sviewer.js @ 881

Last change on this file since 881 was 881, checked in by Maciej Komosinski, 5 years ago

Initial, prototype version of a javascript app to test how humans align two 3D structures

File size: 11.4 KB
Line 
1/*global Module*/
2"use strict";
3
4import * as THREE from 'three';
5import TrackballControls from 'three-trackballcontrols';
6import PartMeshFactory from './partmeshfactory';
7import JointMeshFactory from './jointmeshfactory';
8import Scene from './scene';
9import Camera from './camera';
10import { geometry } from './transformations';
11
12/** Camera class configuration for Viewer */
13export const cameraconfig = {
14    fieldOfView: 75,
15    near: 0.1,
16    far: 1000,
17    autoRotateSpeed: 2,
18    defaultSettings: {
19        target: {
20            x: 0,
21            y: 0,
22            z: 0
23        },
24        position: {
25            x: 0.56,
26            y: 0.56,
27            z: 0.56
28        },
29    }
30};
31
32/** Light configuration for Viewer */
33export const lightconfig = {
34    ambient: {
35        color: 0x303030
36    },
37    directional: {
38        top: {
39            color: 0x808080,
40            intensity: 1.0,
41            positionZ: 1000
42        },
43        bottom: {
44            color: 0x808080,
45            intensity: 1.0,
46            positionZ: -1000
47        }
48    }
49};
50
51/**
52 * Class for visualizing static Framsticks creatures. Connects all visualization
53 * classes and render scene in given HTML canvas. For resizing it is recommended
54 * to put canvas in resizable div and then compute new widths and heights from
55 * this parent.
56 */
57class Viewer {
58    /**
59     * Default constructor for Viewer. It initializes all 3D visualization objects
60     * in Scene  without creature Meshes. It needs canvas to be created in
61     * HTML - it does not create canvas on its own, like default WebGLRenderer.
62     * Width and height should be taken from canvas parent
63     * @param {number} width width of canvas
64     * @param {number} height height of canvas
65     * @param {Element} canvasdom DOM reference to canvas.
66     */
67    constructor(width, height, canvasdom) {
68        this.parts = [];
69        this.joints = [];
70        this.meshes = [];
71        this.parts2 = [];
72        this.joints2 = [];
73        this.meshes2 = [];
74        this.creature1 = new THREE.Group();
75        this.creature2 = new THREE.Group();
76        this.partfactory = new PartMeshFactory(geometry.part);
77        this.jointfactory = new JointMeshFactory(geometry.joint);
78        this.renderer = new THREE.WebGLRenderer({
79            antialias: true,
80            devicePixelRatio: window.devicePixelRatio || 1,
81            canvas: canvasdom
82        });
83        this.renderer.setSize(width, height);
84        this.raycaster = new THREE.Raycaster();
85        this.scene = new Scene();
86        this.camera = new Camera(cameraconfig, this.renderer.domElement, false);
87        //console.log(this.camera);
88        let ambientLight = new THREE.AmbientLight(lightconfig.ambient.color);
89        this.scene.add(ambientLight);
90
91        let topDirectionalLight = new THREE.DirectionalLight(
92            lightconfig.directional.top.color,
93            lightconfig.directional.top.intensity);
94        topDirectionalLight.position.set(0, 0, lightconfig.directional.top.positionZ);
95        this.scene.add(topDirectionalLight);
96
97        let bottomDirectionalLight = new THREE.DirectionalLight(
98            lightconfig.directional.bottom.color,
99            lightconfig.directional.bottom.intensity);
100        bottomDirectionalLight.position.set(0, 0, lightconfig.directional.bottom.positionZ);
101        this.scene.add(bottomDirectionalLight);
102        this.renderer.setClearColor('gray');
103        this.rezoom = true;
104        this.renderScene = this.renderScene.bind(this);
105        this.selectedelements = [];
106
107        this.mouse = new THREE.Vector2();
108        this.offset = new THREE.Vector3();
109        this.INTERSECTED = null;
110        this.SELECTED = null;
111
112        this.plane = new THREE.Mesh( new THREE.PlaneGeometry(500, 500, 8, 8 ), new THREE.MeshBasicMaterial( { color: 0x000000, opacity: 0.25, transparent: true, wireframe: true } ) );
113        this.plane.visible = true;
114        this.scene.add( this.plane );
115
116        this.controls = new TrackballControls( this.camera.getPerspectiveCamera() );
117        this.projector = new THREE.Projector();
118        this.controls.target = new THREE.Vector3(0, 0, 0);
119        this.controls.maxDistance = 150;
120        this.controls2 = null;
121    }
122
123    addMesh(meshes, parts, joints, model, creature)
124    {
125        let partsforjoints = [];
126        meshes = [];
127        parts = [];
128        joints = [];
129        for (let i = 0; i < model.getPartCount(); i++) {
130            let mesh = this.partfactory.create(model.getPart(i));
131            partsforjoints.push(mesh.userData);
132            meshes.push(mesh);
133            parts.push(mesh);
134            creature.add(mesh);
135        }
136
137        for (let i = 0; i < model.getJointCount(); i++) {
138            let mesh = this.jointfactory.create(model.getJoint(i), partsforjoints);
139            meshes.push(mesh);
140            joints.push(mesh);
141            creature.add(mesh);
142        }
143    }
144
145    /**
146     * Selects object from scene according to current mouse position. The position
147     * of mouse should be transformed to screen space, which has values from -1 to 1
148     * for each dimension.
149     * @param {{x: number, y: number}} position position of mouse in canvas normalized to [-1,1]x[-1,1] space
150     */
151    handleMouseDown(position) {
152      this.raycaster.setFromCamera( position, this.camera.getPerspectiveCamera() ); 
153      let intersectsC1 = this.raycaster.intersectObjects( this.creature1.children );
154            let intersectsC2 = this.raycaster.intersectObjects( this.creature2.children );
155
156                                if ( intersectsC1.length > 0 || intersectsC2.length > 0 ) {
157                   
158                                        this.controls.enabled = false;
159                    let obj = intersectsC1.length > 0 ? this.creature1 : this.creature2;
160                                        this.SELECTED = obj;
161                                        let intersects = this.raycaster.intersectObject( this.plane );
162                                        this.offset.copy( intersects[ 0 ].point ).sub( this.plane.position );
163                                }
164    }
165
166    handleMouseUp() {
167        this.controls.enabled = true;
168        this.SELECTED = null;
169
170        if (this.INTERSECTED)
171        {
172            this.plane.position.copy(this.INTERSECTED.position);
173            this.SELECTED = null;
174        }
175    }
176
177    handleMouseMove(position) {
178        this.raycaster.setFromCamera( position, this.camera.getPerspectiveCamera() );
179        if ( this.SELECTED ) {
180            let intersects = this.raycaster.intersectObject( this.plane );
181            if (intersects[ 0 ])
182                {this.SELECTED.position.copy( intersects[ 0 ].point.sub( this.offset ) );}
183            return;
184        }
185
186            let intersectsC1 = this.raycaster.intersectObjects(this.creature1.children);
187            let intersectsC2 = this.raycaster.intersectObjects(this.creature2.children);
188            if (intersectsC1.length > 0 || intersectsC2.length > 0)
189            {
190                let obj = intersectsC1.length > 0 ? this.creature1 : this.creature2;
191                this.INTERSECTED = obj;
192                this.plane.position.copy( obj.position );
193                this.plane.lookAt( this.camera.getPerspectiveCamera().position );
194            }
195            else
196            {
197                this.INTERSECTED = null;
198            }
199       
200    }
201
202    /**
203     * Method uses ready Model to create and add meshes to the scene. If model is
204     * undefined, an empty scene will be rendered.
205     * @param {Module.Model} model input model 1
206     * @param {Module.Model} model input model 2
207     */
208    loadMeshesFromModel(model, model2) {
209        this.clearView();
210        if (typeof model !== 'undefined') {
211
212            this.addMesh(this.meshes, this.parts, this.joints, model, this.creature1);
213            this.addMesh(this.meshes2, this.parts2, this.joints2, model2, this.creature2);
214            this.creature2.position.set( 0, 0, 0 );
215            this.creature2.position.set( 0, -1, 0 );
216            this.scene.add(this.creature1);
217            this.scene.add(this.creature2);
218            if (this.rezoom) {
219                this.camera.zoomAll(this.meshes);
220            }
221        }
222    }
223
224    /**
225     * Method for rendering Scene. It should be passed as drawing element in
226     * owners method.
227     *
228     * Example of usage is visible in GenoViewer Component.
229     * @returns {number} frameId of animation
230     */
231    renderScene() {
232        this.camera.getCameraControl().update();
233
234        let frameId = window.requestAnimationFrame(this.renderScene);
235        this.scene.render(this.renderer, this.camera.getPerspectiveCamera());
236        return frameId;
237    }
238
239    /**
240     * Resizes scene. Use with caution - this is performance-consuming function,
241     * so it should only be used when necessary. To perform resize, the canvas
242     * should have absolute position within parent, otherwise it
243     * will change size on its own.
244     * @param {number} width new width of canvas
245     * @param {number} height new height of canvas
246     */
247    resizeView(width, height) {
248        this.renderer.context.canvas.width = width;
249        this.renderer.context.canvas.height = height;
250        this.camera.getPerspectiveCamera().aspect = width / height;
251        this.camera.getPerspectiveCamera().updateProjectionMatrix();
252        this.renderer.setSize(width, height);
253        //console.log(this.renderer.getSize());
254        this.renderer.setPixelRatio(window.devicePixelRatio || 1);
255    }
256
257    /**
258     * Determines whether camera should change position in order to show all body
259     * or not.
260     * @param {boolean} rezoom true if should rezoom, false otherwise
261     */
262    setRezoom(rezoom) {
263        this.rezoom = rezoom;
264        this.camera.zoomAll(this.meshes);
265    }
266
267    /**
268     * Determines whether camera should rotate automatically.
269     * @param {boolean} autorotate true if should autorotate, false otherwise
270     */
271    setAutoRotate(autorotate) {
272        this.camera.setAutoRotate(autorotate);
273    }
274
275    /**
276     * Clears scene from meshes.
277     */
278    clearView() {
279        this.scene.clear();
280    }
281
282    /**
283     * Returns reference to renderer DOM Element.
284     * @returns {Element} DOM Element of Viewer.
285     */
286    getDOM() {
287        return this.renderer.domElement;
288    }
289
290    /**
291     * Highlights all parts and joints presented in selectedelements argument.
292     * @param {SelectedElements} selectedelements object of class SelectedElements defined in mappingresolver.js, determines which parts and joints should be highlighted
293     */
294    highlightElements(selectedelements) {
295        this.selectedelements = [];
296        for (let i = 0; i < selectedelements.parts.length; i++) {
297            this.parts[selectedelements.parts[i]].material.transparent = false;
298            this.parts[selectedelements.parts[i]].material.emissive.set('green');
299            this.selectedelements.push(this.parts[selectedelements.parts[i]]);
300        }
301        for (let i = 0; i < selectedelements.joints.length; i++) {
302            this.joints[selectedelements.joints[i]].material.transparent = false;
303            this.joints[selectedelements.joints[i]].material.emissive.set('green');
304            this.selectedelements.push(this.joints[selectedelements.joints[i]]);
305        }
306    }
307
308    /**
309     * Removes all highlights from objects.
310     */
311    clearHighlights() {
312        for (let i = 0; i < this.selectedelements.length; i++) {
313            if (this.selectedelements[i] && this.selectedelements[i].material) {
314                this.selectedelements[i].material.transparent = this.selectedelements[i].userData.showTransparent;
315                this.selectedelements[i].material.emissive.set('black');
316            }
317        }
318        this.selectedelements = [];
319    }
320}
321
322export default Viewer;
Note: See TracBrowser for help on using the repository browser.