one OrbitControls with two scenes - three.js

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>```

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

How to move the Sphere using mouse in three.js? (Uncaught ReferenceError: OrbitControls is not defined)

I was making a sphere that can be moved by a mouse using three.js but the output is just a black screen as shown.
The code I used is:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>My first three.js app</title>
<style>
body { margin: 0; }
</style>
</head>
<body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r79/three.min.js"></script>
<script>
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
camera.position.set( 0, 0, 50 );
camera.lookAt( 0, 0, 0 );
const renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
const geometry = new THREE.SphereGeometry( 15, 32, 16 );
const texture = new THREE.TextureLoader().load( 'https://i.imgur.com/kFoWvzw.jpg' );
const material = new THREE.MeshBasicMaterial( { map: texture } );
const sphere = new THREE.Mesh( geometry, material );
const controls = new OrbitControls( camera, renderer.domElement );
controls.mouseButtons = {
LEFT: THREE.MOUSE.ROTATE,
MIDDLE: THREE.MOUSE.DOLLY,
RIGHT: THREE.MOUSE.PAN
}
controls.update();
scene.add( sphere );
function animate()
{
requestAnimationFrame( animate );
sphere.rotation.x += 0.01;
sphere.rotation.y += 0.01;
renderer.render( scene, camera );
};
animate();
</script>
</body>
</html>
I couldn't explain why the code was unable to render the sphere. What went wrong?
Thank you!
Like the error message indicates, you are trying to use OrbitControls but it cannot be found. This is because it is not part of the core THREE.js library, but it is in the THREE.js examples directory so you need to import it separately.
You can import it through an HTML script tag like you do with THREE.js:
<script src="path/to/your/local/file/OrbitControls.js">.
Notice also that if you import it in your HTML, you should call it as
const controls = new THREE.OrbitControls(...);
as the js/ file adds the class to the THREE object.
See the docs or the source.
This is the complete code on how to move the camera on the Sphere.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>My first three.js app</title>
<style>
body { margin: 0; }
</style>
</head>
<body>
<script src="https://cdn.jsdelivr.net/npm/three#0.143/build/three.min.js"></script>
<script src=".\three.js-master\three.js-master\examples\js\controls\OrbitControls.js"></script>
<script>
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
camera.position.set( 0, 0, 50 );
camera.lookAt( 0, 0, 0 );
const renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
const geometry = new THREE.SphereGeometry( 15, 32, 16 );
const texture = new THREE.TextureLoader().load( 'https://i.imgur.com/kFoWvzw.jpg' );
const material = new THREE.MeshBasicMaterial( { map: texture } );
const sphere = new THREE.Mesh( geometry, material );
const controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.update();
scene.add( sphere );
function animate()
{
requestAnimationFrame( animate );
sphere.rotation.x += 0.01;
sphere.rotation.y += 0.01;
renderer.render( scene, camera );
};
animate();
</script>
</body>
</html>
This is the correct code on how to move the camera on the Sphere...:-)

How can I flip some normals of vertex geometry in three.js?

I have tested PCL triangluation introduced here:
And from the experiment I got a .vtk file.
And I tried to render it via web browser whith three.js on windows.
It is like below:
See! There are many holes. These holes are triangles that have opposite normal vectors.
What I need is flipping those normal vectors that are opposite than others.
This way I believe I would get clean mesh image as shown below:
How can I do this?
Do I work with three.js?
Had I better work with pcl library? - the triangluation?
This is the html I tested:
<!DOCTYPE html>
<html lang="en">
<head>
<!--title>four.js webgl - loaders - vtk loader</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>
<script type="module">
var console = (window.console = window.console || {});
import * as THREE from '../build/three.module.js';
import { TrackballControls } from './jsm/controls/TrackballControls.js';
import { VTKLoader } from './jsm/loaders/VTKLoader.js';
import { VertexNormalsHelper } from './jsm/helpers/VertexNormalsHelper.js';
let container, camera, controls, scene, renderer, vnh;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 0.01, 1e10);
camera.position.set( -1, 0, 0 );
scene = new THREE.Scene();
scene.add( camera );
// light
const hemiLight = new THREE.HemisphereLight( 0xffffff, 0x000000, 1 );
scene.add( hemiLight );
const dirLight = new THREE.DirectionalLight( 0xffffff, 0.5 );
dirLight.position.set( 2, 2, 2 );
scene.add( dirLight );
const material = new THREE.MeshLambertMaterial( { color: 0xffffff } );
const loader = new VTKLoader();
loader.load( "models/vtk/point_cloud_mesh.vtk", function ( geometry )
//loader.load( "models/vtk/point_cloud_mesh_not_smoothed.vtk", function ( geometry )
{
//geometry.applyMatrix(new THREE.Matrix4().makeScale(-1, -1,-1));
//geometry.computeFaceNormals();
//var tmp;
//console.log(geometry.index.count);
//console.log(geometry.userData);
geometry.computeVertexNormals();
const mesh = new THREE.Mesh( geometry, material );
mesh.position.set( - 0.075, 0.005, 0 );
mesh.scale.multiplyScalar( 0.2 );
scene.add( mesh );
//vnh = new VertexNormalsHelper(mesh, 0.001, 0x00ff00, 1);
//scene.add( vnh );
} );
// renderer
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container = document.createElement( 'div' );
document.body.appendChild( container );
container.appendChild( renderer.domElement );
// controls
controls = new TrackballControls( camera, renderer.domElement );
controls.minDistance = .1;
controls.maxDistance = 0.5;
controls.rotateSpeed = 5.0;
window.addEventListener( 'resize', onWindowResize );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
controls.handleResize();
}
function animate() {
requestAnimationFrame( animate );
controls.update();
//if (vnh) vnh.update();
renderer.render( scene, camera );
}
</script>
</body>
</html>
Thanks to WestLangley , I finally got a expected result as shown below:
Thank you WestLangley and Stackoverflow!!
But the question still remains. WestLangley's suggestion helps but it wouldn't be the only one solution. As you see below, some of the normal vectors are facing into the body of my hand. I believe there would be a way to make all the normal vectors face out of the body of my hand. Answers to this question still wanted...

three.js gltf model becomes grey and textureless, after changing texture

hey guys i am new to three.js and i am trying to change my texture using Meshmatcap material. however, i am facing an issue where i change the texture, my model texture and color would disappear after adding skinning: true , which is necessary for my model to retain size . is there any way to solve this issue? thanks in advance. currently using the model from https://sketchfab.com/3d-models/tyrannosaurus-rex-9d3a3e42c0054c35aa39c3ee07388d16
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>3d model</title>
</head>
<body>
<script type="module">
import * as THREE from 'https://cdn.jsdelivr.net/npm/three#0.114/build/three.module.js';
import { OrbitControls } from 'https://cdn.jsdelivr.net/npm/three#0.114/examples/jsm/controls/OrbitControls.js';
import { GLTFLoader } from 'https://cdn.jsdelivr.net/npm/three#0.114/examples/jsm/loaders/GLTFLoader.js';
import { RGBELoader } from 'https://cdn.jsdelivr.net/npm/three#0.114/examples/jsm/loaders/RGBELoader.js';
var container, controls;
var camera, scene, renderer, mixer, clock;
var obj , material , texture , mesh
init();
animate();
function init() {
container = document.getElementById( 'test' );
document.body.appendChild( container );
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.01, 1000 );
camera.position.x = 0
camera.position.y = 2
camera.position.z = 10
scene = new THREE.Scene();
// scene.background = new THREE.Color(0xffffff);
var light = new THREE.HemisphereLight(0xffffff,0x000000,10);
scene.add(light);
clock = new THREE.Clock();
var loader = new GLTFLoader();
// Load a glTF resource
loader.load('scene.gltf', function ( gltf ) {
var textureLoader = new THREE.TextureLoader();
mesh = gltf.scene.children[0]
console.log(mesh)
var texture = textureLoader.load('blue1.jpg');
// texture.minFilter = THREE.LinearFilter;
var matcapMaterial = new THREE.MeshMatcapMaterial({ skinning: true ,normalMap: texture })
obj = scene.add( mesh );
obj.traverse((o) => {
if (o.isMesh) o.material = matcapMaterial
;
})
;
mixer = new THREE.AnimationMixer( mesh );
gltf.animations.forEach( ( clip ) => {
mixer.clipAction( clip ).play();
} );
} );
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.toneMapping = THREE.ACESFilmicToneMapping;
renderer.toneMappingExposure = 0.8;
renderer.outputEncoding = THREE.sRGBEncoding;
container.appendChild( renderer.domElement );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
//
function animate() {
requestAnimationFrame( animate );
var delta = clock.getDelta();
if ( mixer ) mixer.update( delta );
renderer.render( scene, camera );
}
</script>
<div id="test">
</div>
</body>
</html>
When replacing textures of glTF asset with manually loaded textures, you have to set the Texture.flipY property to false in order to accommodate the uv convention of glTF.
This circumstance is also mentioned in the documentation page of THREE.GLTFLoader.

The marker is not showing

I'm trying to add markers to rotating globe object with Three.js - the globe is showing, but the markers is not displayed. I don't know why this happens, the console.log dont throw any errors to debug anything.
Can someone help me find the root to this issue and correct it?
The code inside the index.html file is down below:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<script src="js/three.min.js"></script>
<script src="js/controls/OrbitControls.js"></script>
</head>
<body>
<div id="threejs" style="position: absolute; left:0px; top:0px"></div>
<script>
var boards = [];
var texture = [];
var matTexture = [];
var camera, scene, renderer, controls, container, materialBoard, boardGeo,materialSph, sphGeo, sphere,picGeo;
init();
animate();
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 300000 );
scene.add( camera );
camera.position.set( 0, 1500, 5000);
renderer = new THREE.WebGLRenderer( { antialias:true } );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setClearColor( 0xeeeeee );
container = document.getElementById( 'threejs' );
container.appendChild( renderer.domElement );
controls = new THREE.OrbitControls( camera, renderer.domElement );
var loader = new THREE.TextureLoader();
texture[0] = loader.load( "test.png" );
matTexture[0] = new THREE.MeshBasicMaterial( { color:0xffffff, map: texture[0], transparent:true, opacity:1, wireframe:false} );
// ball
materialSph = new THREE.MeshBasicMaterial( { color: 0x0099dd, transparent: true, opacity: 0.8, wireframe:false } );
sphGeo = new THREE.SphereGeometry(1200, 8, 8);
sphere = new THREE.Mesh( sphGeo, materialSph );
scene.add( sphere );
// board for some things
materialBoard = new THREE.MeshBasicMaterial({color:0xdddddd, transparent:true, opacity:0.4, wireframe: true });
boardGeo = new THREE.PlaneGeometry(500,500);
picGeo = new THREE.PlaneGeometry( 500, 500 );
const vertices = sphGeo.attributes.position.array;
console.log(vertices);
var board = new THREE.Mesh( boardGeo, materialBoard ); // board for pictures etc.
var pic = new THREE.Mesh( picGeo, matTexture[ 0 ] );
board.add( pic ) ; // add picture to the board
boards.push( board );
scene.add( boards[ 0 ] );
boards[ 0 ].position.set( 1 * vertices[ 10 ].x , 0.5 * vertices[ 2 ].y, 1.8 * vertices[ 10 ].z );
}
function animate() {
requestAnimationFrame( animate );
for( var n = 0; n < boards.length; n ++ ) {
//boards[ n ].lookAt( camera.position );
boards[ n ].quaternion.copy( camera.quaternion );
}
renderer.render( scene, camera );
controls.update();
}
</script>
</body>
</html>

GLTFLoader global variable undefined

i'm learning three js and try to animate an gltf object rotation in three.js r98. But I get a Console Error: Variable obj undefined, but the variable is declared on top before the init script, so it should be global right. I don't see why this shouldn't work. If I use scene.rotation.y += 0.01; it works. But this is not usefull if there are other object which should not rotate^^. the variable model has the same error. With MLTD Loader this works: Loop Rotation on any axis for a 3D obj Three js. Instead of gltf.scene i tried gltf.asset but with same error.
Many Thanks for help.
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8>
<title>My first three.js app</title>
<style>
body { margin: 0; }
canvas { width: 100%; height: 100% }
</style>
</head>
<body>
<script src="js/three.js"></script>
<script src="js/GLTFLoader.js"></script>
<script src="js/WebGL.js"></script>
<script src="js/stats.min.js"></script>
<script src="js/dat.gui.min.js"></script>
<script>
if ( WEBGL.isWebGLAvailable() === false ) {
document.body.appendChild( WEBGL.getWebGLErrorMessage() );
}
var stats, clock, mixer;
var camera, scene, renderer, model;
var obj;
init();
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.25, 100 );
camera.position.set( 0, 3, 10);
camera.lookAt( new THREE.Vector3( 0, 0, 0 ) );
scene = new THREE.Scene();
scene.background = new THREE.Color( 0xf0f0f0f );
scene.fog = new THREE.Fog( 0xe0e0e0, 20, 100 );
clock = new THREE.Clock();
// lights
var light = new THREE.HemisphereLight( 0xffffff, 0x444444 );
light.position.set( 0, 20, 0 );
scene.add( light );
light = new THREE.DirectionalLight( 0xffffff );
light.position.set( 0, 20, 10 );
scene.add( light );
// model
var loader = new THREE.GLTFLoader();
loader.load( 'logo1.gltf', function( gltf ) {
model = gltf.scene;
model.scale.set(1,1,1);
obj = model;
scene.add( model );
//createGUI( model, gltf.animations );
}, undefined, function( e ) {
console.error( e );
} );
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.gammaOutput = true;
renderer.gammaFactor = 2.2;
container.appendChild( renderer.domElement );
window.addEventListener( 'resize', onWindowResize, false );
// stats
// stats = new Stats();
//container.appendChild( stats.dom );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
//
function animate() {
obj.rotation.y += 0.01;
requestAnimationFrame( animate );
renderer.render( scene, camera );
//stats.update();
}
</script>
</body>
</html>
The problem is that obj is only set when the onLoad() callback of GLTFLoader.load() fires. Since this happens in an asynchronous way, you should put the following line of code in your animate() function in order to solve the problem.
if ( obj ) obj.rotation.y += 0.01;

Resources