Guide on implementing OrbitControls to a camera imported from a glTF file

I am facing an issue with OrbitControls.js when trying to apply it to a loaded camera in a three.js scene. While the camera and model load successfully, the OrbitControls.js seem to work only with cameras created directly in the three.js using new THREE.PerspectiveCamera(). Below is the code snippet, where I have commented out the play() function for testing purposes.

        var container, stats, clock, controls;
        var camera, scene, renderer, mixer;

        init();
        animate();              


        controls.update();


        function init() {
            container = document.getElementById( 'container' );

            scene = new THREE.Scene();
            clock = new THREE.Clock();
            var loader = new THREE.GLTFLoader();

            var ruta = 'file.gltf';

            loader.load( ruta, function ( data ) {


                handleSceneLoaded(data);

                var animations = data.animations;
                var avatar = data.scene;
                mixer = new THREE.AnimationMixer( avatar );
                NumberOfAnimations = animations.length;
                for ( var i=0; i<NumberOfAnimations; i++){
                    //mixer.clipAction( animations[ i ] ).play();
                }

                scene.add( avatar );
            } );
            //
            scene.background = new THREE.Color( 0xefe3a7 );

            renderer = new THREE.WebGLRenderer( { antialias: true } );
            renderer.setPixelRatio( window.devicePixelRatio );
            renderer.setSize( window.innerWidth, window.innerHeight );
            container.appendChild( renderer.domElement );
        }

        function handleSceneLoaded(data){
            scene.add(data.scene);
            var result={};
            data.scene.traverse(function(n){traverseScene(n,result);});

            if (result.cameras && result.cameras.length){

                camera = result.cameras[0];

                controls = new THREE.OrbitControls(camera);
                controls.screenSpacePanning = true;
            }else{
                camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
                camera.position.set( 15, 10, - 15 );
            }

            if(result.lights && result.lights.length){
                for (i=0;i<result.lights.length;i++){
                    var lamp = result.lights[i];
                    scene.add(lamp);
                }
            }else {
                var ambientLight = new THREE.AmbientLight( 0xffffff, 0.2 );
                scene.add( ambientLight );
                var directionalLight = new THREE.DirectionalLight( 0xffffff, .8 );
                directionalLight.position.set( 1, 1, - 1 );
                scene.add( directionalLight );
                var directionalLight2 = new THREE.DirectionalLight( 0xffffff, .8 );
                directionalLight2.position.set( -5, -5,  10 );
                scene.add( directionalLight2 );
            }

        }

        function traverseScene(n, result){
            if (n instanceof THREE.Camera){
                if(!result.cameras)
                    result.cameras = [];

                result.cameras.push(n);
            }
        }

        function animate() {
            requestAnimationFrame( animate );
            render();

        }
        function render() {
            var delta = clock.getDelta();
            if ( mixer !== undefined ) {
                mixer.update( delta );
            }
            renderer.render( scene, camera );
        }

Edit1: I eventually resolved the issue by assigning the loaded camera to a variable blenderCamera and then creating a real camera where I copied the values from the loaded camera and its parent:

        camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );

            blenderCamera = result.cameras[0];
            camera.position.x = blenderCamera.parent.position.x;
            camera.position.y = blenderCamera.parent.position.y;
            camera.position.z = blenderCamera.parent.position.z;

            camera.aspect = blenderCamera.aspect;
            camera.fov = blenderCamera.fov;
            camera.far = blenderCamera.far;
            camera.near = blenderCamera.near;

Answer №1

It appears that when exporting from Blender using the GLTF exporter, a parent object is created for exported cameras, as you have encountered in your workaround.

Creating consistent behavior for an orbital camera that is being moved around the scene by its parent's transformation can be challenging. This seems to be causing OrbitControls to fail when applied to a camera with a parent object.


To resolve this issue, try detaching the camera from its parent object:

scene.attach(blenderCamera)

This will remove the parent attribute of the imported camera while preserving its position and orientation in the scene by applying the inverse transformation matrices of its parent. You can verify that blenderCamera.parent == null afterwards, similar to the working hard-coded cameras.


It seems that scene.attach is now the recommended way to detach an object from its parent, replacing the deprecated SceneUtils.detach. Alternatively, you could set .parent = null directly, but this may result in losing the parent's transformation. In more complex scenarios, you may need to manually apply transformation matrices to maintain the correct position and orientation.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Combine an array of objects using the main key in each object

I have an array of objects with different years and details var worksSummaryDetailsArr = [ { year: 2020, worksSummaryDetailsObj: [ [Object], [Object], [Object], [Object] ] }, { year: 2021, worksSummaryDetailsObj: [ [Object], [Object], ...

Is there a more concise method for accepting a collection of interfaces in TypeScript?

Issue I am facing a simplified version of a problem with my model: Here is how my model currently looks: interface Instrument { name: string; // ...more properties shared by all instruments... } interface Guitar extends Instrument { type: &q ...

Unable to display image using EJS and Multer

While working on my node.js application, I encountered an issue with rendering the uploaded image file. I have successfully integrated multer to handle file uploads, and the images are being stored in the correct folder. However, when trying to display the ...

JavaScript: Use onclick events to change the class of a div on multiple divs

I have a jQuery script that allows for toggling of the parent class when #icon is clicked. Additionally, when the body is clicked, it reverts back to the original class. Now, I'm looking to achieve the same behavior when clicking on #item5 or #item4 a ...

What is the process for generating an API endpoint in Next.js that includes a query string?

I am currently working on setting up an API endpoint in Next.js, specifically the following one: /api/products/[name]?keyword=${anykeyword}. To accomplish this, I understand that I have to create it within the pages/api/products/[name] directory in index ...

Ways to preserve decal placement using Three.js

I am searching for a solution to store and reload the decals generated in this example (source code). The goal is to allow users to save a new splatter they paint and then reload it at a later time. I have attempted various methods, but none have been succ ...

Identifying browsers with Zend Framework versus JavaScript

Currently, I am working on developing an application that demands the capability to upload large files. After much consideration, I have opted to utilize the FormData object as it allows me to provide progress updates to the user. Sadly, Internet Explorer ...

Is there a way to dynamically change the height in jQuery/JavaScript?

I am encountering an issue with my embed code where the height changes randomly after a few seconds, depending on certain parameters. Here is an example of the code I am using: $( <embed></embed>) .attr("src", "http://www.bbs.com") ...

How to Utilize Output() and EventEmitter() for Value Transmission in Angular Application

Last week I was successfully able to implement Output() and EventEmitter() in my Angular app. However, today I am facing a new challenge while trying to apply the same concept in a different scenario. I'm not sure what I might be overlooking. Firstly ...

Warning: An unhandled promise error occurred due to a validation error

I've been facing an issue for the past few days. Currently, I'm diving into learning the MEAN stack, but while trying to create a user on MongoDB using Mongoose schema, I encountered this problem: (node:93337) UnhandledPromiseRejectionWarning: ...

Guide on how to address the problem of the @tawk.to/tawk-messenger-react module's absence of TypeScript definitions

Is there a way to fix the issue of missing TypeScript definitions for the @tawk.to/tawk-messenger-react module? The module '@tawk.to/tawk-messenger-react' does not have a declaration file. 'c:/develop/eachblock/aquatrack/management-tool-app ...

What other ways can websockets be utilized besides comet?

Websockets offer a more efficient solution for comet (reverse Ajax, often achieved through long-polling). However, are there other ways we can utilize websockets? For instance: - Can websockets be used to facilitate communication between different bro ...

Is there a way to create a JavaScript script that automatically updates a webpage for all users simultaneously?

I have developed a web application that enables users to modify content using JavaScript. Currently, these changes are only visible on the local browser and do not involve AJAX. However, I am wondering how I can ensure that these DOM alterations are refle ...

Tips for utilizing ng-checked and ng-disabled in conjunction with ng-repeat

I encountered a challenge with ng-repeat while incorporating ng-checked and ng-disable alongside it. <div ng-repeat="module in modulelist"> <input id="switch{{$index}}" ng-init="setState($index);" type="checkbox" ng-checked="switch.checked" ng-di ...

"Step-by-step guide to updating user information in MongoDB with the help of node.js

I have been working on a basic express app with MongoDB integration, allowing users to register, log in, and log out successfully. However, I am facing an issue when attempting to implement the functionality to edit user data. The code runs without any err ...

JavaScript list in Google Docs

I am a beginner in the world of the Google Docs API and I am interested in retrieving a list of all documents that are not spreadsheets using the Google Docs API. I have found an API that allows me to access a specific document when I provide its documentI ...

When using a callback function to update the state in React, the child component is not refreshing with the most recent properties

Lately, I've come across a peculiar issue involving the React state setter and component re-rendering. In my parent component, I have an object whose value I update using an input field. I then pass this updated state to a child component to display t ...

Error in Firebase Cloud Function: Function did not return the expected Promise or value and instead returned undefined

I've been encountering an issue with my cloud function that triggers on specific database writes (onCreate). It seems to be working fine, but I keep getting an error message stating "Function returned undefined, expected Promise or value" even though ...

Symfony2: Making AJAX request that unexpectedly returns complete html page

I am facing an issue with setting up a basic AJAX request in Symfony2. It appears that the controller is not receiving the request as expected. Instead of displaying '123', the AJAX response shows the HTML content of the current page (index.html. ...

Verify if there is a date present within the JSON entity

I have a PHP array containing date strings and I want to validate them using a native HTML5 date input element. To achieve this, I have converted the date array into a JSON object for use with JavaScript: <script> var date_array = <?php echo json ...