one OrbitControls with two scenes

Viewed 54

I want to sync the mouse control of two .dae files (in two scenes side-by-side) with OrbitControls. I can control any one object individually with any one of my scenes but any attempt to control both objects in sync fails. It seems like I can only have one OrbitControls instance. Any one of the following 'controls#' lines works on its own but as soon as I have both, only the first one is operative: controls1 = new OrbitControls( camera1, renderer1.domElement ); controls2 = new OrbitControls( camera2, renderer2.domElement );

I am a chemistry prof, not a programmer.Any help is gratefully appreciated. Thanks!

<html lang="en">
    <head>
        <title>GD - 2 mols</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
        <link type="text/css" rel="stylesheet" href="main.css">
    </head>
    <body>
        <h3> VB orbital visualizer: 2 molecules</h3>
        

<div id="molcontainer1"></div>
<div id="molcontainer2"></div>
        
    <!-- Remove this when import maps will be widely supported -->
        <script async src="https://unpkg.com/es-module-shims@1.3.6/dist/es-module-shims.js"></script>

        <script type="importmap">
            {
                "imports": {
                    "three": "../three/build/three.module.js"
                }
            }
        </script>

        <script type="module">

            import * as THREE from 'three';
            import { ColladaLoader } from '../three/examples/jsm/loaders/ColladaLoader.js';         
            import { OrbitControls } from '../three/examples/jsm/controls/OrbitControls.js';

            let molcontainer1, molcontainer2, camera1, camera2, scene1, scene2, renderer1, renderer2, mol1, mol2;

            init();
            //animate();

            function init() {

                molcontainer1 = document.getElementById( 'molcontainer1' );
                molcontainer2 = document.getElementById( 'molcontainer2' );
                
                //scene, camera, lighting, etc
                
                scene1 = new THREE.Scene();
                scene1.background = new THREE.Color( 0xbbffff ); 
                scene2 = new THREE.Scene();
                scene2.background = new THREE.Color( 0xffcccc );
                
                camera1 = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.1, 2000 );
                camera1.position.set( 0.4, 0.4, 0.4 );
                camera1.lookAt( 0, 0, 0 );
                camera2 = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.1, 2000 );
                camera2.position.set( 0.4, 0.4, 0.4 );
                camera2.lookAt( 0, 0, 0 );

                const ambientLight1 = new THREE.AmbientLight( 0xffffff, 1 );  // was 0xcccccc
                scene1.add( ambientLight1 ); 
                const ambientLight2 = new THREE.AmbientLight( 0xffffff, 1 );  // was 0xcccccc
                scene2.add( ambientLight2 );

                const directionalLight1 = new THREE.DirectionalLight( 0xffffff, 0.8 );
                directionalLight1.position.set( 1, 1, 1 ).normalize();
                scene1.add( directionalLight1 );
                const directionalLight2 = new THREE.DirectionalLight( 0xffffff, 0.8 );
                directionalLight2.position.set( 1, 1, 1 ).normalize();
                scene2.add( directionalLight2 );

                renderer1 = new THREE.WebGLRenderer();
                renderer1.outputEncoding = THREE.sRGBEncoding;
                renderer1.setPixelRatio( window.devicePixelRatio );
                renderer1.setSize( window.innerWidth/2, window.innerHeight/2 );
                molcontainer1.appendChild( renderer1.domElement );
                
                renderer2 = new THREE.WebGLRenderer();
                renderer2.outputEncoding = THREE.sRGBEncoding;
                renderer2.setPixelRatio( window.devicePixelRatio );
                renderer2.setSize( window.innerWidth/2, window.innerHeight/2 );
                molcontainer2.appendChild( renderer2.domElement );
                
                // loading manager

                const loadingManager = new THREE.LoadingManager( function () {
                    
                    scene1.add( mol1 );
                    scene2.add( mol2 );

                } );

                // assign collada .dae file

                const loader1 = new ColladaLoader( loadingManager );
                loader1.load( './models/molecule.dae', function ( collada ) {

                    mol1 = collada.scene;
                    animate();
                    render();
                } );

                const loader2 = new ColladaLoader( loadingManager );
                loader2.load( './models/CH2CHO.dae', function ( collada ) {

                    mol2 = collada.scene;
                    animate();
                    render();
                } );
                
                // 3d mouse controls    
                
                controls1 = new OrbitControls( camera1, renderer1.domElement );
                controls2 = new OrbitControls( camera2, renderer2.domElement );
                controls1.addEventListener( 'change', render );
                controls2.addEventListener( 'change', render );
                controls1.target.set( 0, 0, 0 ); 
                controls2.target.set( 0, 0, 0 ); 
                //controls.update();
                window.addEventListener( 'resize', onWindowResize ); 
            }
        

            function animate() {
              
                requestAnimationFrame( animate);
                
                window.addEventListener( 'resize', onWindowResize );    // just added this   
                render();
            }

            
            function onWindowResize() {

                camera1.aspect = window.innerWidth / window.innerHeight;
                camera1.updateProjectionMatrix();
                renderer1.setSize( window.innerWidth/2, window.innerHeight/2 );
                camera2.aspect = window.innerWidth / window.innerHeight;
                camera2.updateProjectionMatrix();
                renderer2.setSize( window.innerWidth/2, window.innerHeight/2 );
                
                render();
            }
            
            function render() {

                renderer1.render( scene1, camera1 );
                renderer2.render( scene2, camera2 );
                //controls.update();
            }

        </script>
    </body>
</html>```
1 Answers

You actually only need one camera and one OrbitControls to drive the two renderers and Scenes. The reason for this is that neither is intrinsically tied to the renderer or the Scene; the camera basically just tracks a group of variables that, on each frame, allow the renderer to compute its projection, and the OrbitControls only cares about the user's interaction with (a defined portion of) the page and the camera whose values it needs to mutate. (Note, as well, that neither a camera nor an OrbitControls takes any reference to a renderer or Scene when you initialize it, and most examples you see don't use Scene.Add() to add the camera to the scene graph.)

So, your code needs the following modifications.

First, wrap your molcontainer* divs inside of another div, and give it an ID of, say, molwrapper. This wrapper div will define the area on the page (comprising the two scenes) that will receive the user interaction for the OrbitControls. Use document.getElementById() to store a reference to molwrapper.

Next, remove all references to camera2 and rename camera1 to camera, and do the same with controls2 and controls1.

Finally, initialize controls (which used to be controls1) by passing it references to camera (which used to be camera1) and molwrapper.

I've posted a working example on CodePen.

Related