Adjust camera angle to directly face object while ensuring latitude and longitude coordinates remain accurate

My current camera control code involves moving a vector (camera.target) and then setting the camera to look at that vector using camera.lookAt( camera.target ). The onDocumentMouseMove function calculates longitude and latitude values which are used to adjust the camera.target's x, y, and z coordinates. However, I am facing an issue where after a key press event causes the camera to rotate towards an object, dragging the mouse subsequently results in the camera jumping back to its position before the key press. This is because the mouse movement relies on tracking lat and lon values, which need to be recalculated after the key press. I have attempted to reverse calculate the lat and long from the x, y, z values but my lack of mathematical expertise has proved to be a hindrance. Therefore, I have added a bounty for this problem and any assistance would be greatly appreciated. View the example here.

        var spriteImg, material, geometry;
        var camera, scene, renderer;
        var keyboard = new THREEx.KeyboardState();
        var fov = 70,
        texture_placeholder,
        isUserInteracting = false,
        onMouseDownMouseX = 0, onMouseDownMouseY = 0,
        lon = 0, onMouseDownLon = 0,
        lat = 0, onMouseDownLat = 0,
        phi = 0, theta = 0;

        init();
        animate();

        function init() {

            var container;

            container = document.getElementById( 'container' );

            camera = new THREE.PerspectiveCamera( fov, window.innerWidth / window.innerHeight, 1, 1100 );
            camera.target = new THREE.Vector3( 0, 0, 0 );

            scene = new THREE.Scene();

            renderer = new THREE.WebGLRenderer();
            renderer.setClearColor(0xffffff, 1);
            renderer.setSize( window.innerWidth, window.innerHeight );
            container.appendChild( renderer.domElement );

            document.addEventListener( 'mousedown', onDocumentMouseDown, false );
            document.addEventListener( 'mousemove', onDocumentMouseMove, false );
            document.addEventListener( 'mouseup', onDocumentMouseUp, false );
            window.addEventListener( 'resize', onWindowResize, false );
            stats = new Stats();
            stats.domElement.style.position = 'absolute';
            stats.domElement.style.bottom = '0px';
            stats.domElement.style.left = '0px';
            stats.domElement.style.zIndex = 100;
            container.appendChild( stats.domElement );

            material = new THREE.MeshBasicMaterial( { color: 0x0125fd} );
            geometry = new THREE.PlaneGeometry(50, 50, 3, 3);
            blue1 = new THREE.Mesh(geometry, material);
            blue1.position.set(200,100,200);
            scene.add(blue1);
            blue1.lookAt( camera.position );

            blue2 = new THREE.Mesh(geometry, material);
            blue2.position.set(-200,-100,-200);
            blue2.lookAt( camera.position );
            scene.add(blue2);

        }

        function onWindowResize() {

            camera.aspect = window.innerWidth / window.innerHeight;
            camera.updateProjectionMatrix();

            renderer.setSize( window.innerWidth, window.innerHeight );

        }

        function onDocumentMouseDown( event ) {

            isUserInteracting = true;

            onPointerDownPointerX = event.clientX;
            onPointerDownPointerY = event.clientY;

            onPointerDownLon = lon;
            onPointerDownLat = lat;

        }

        function onDocumentMouseMove( event ) {
            if ( isUserInteracting ) {
                lon = ( event.clientX - onPointerDownPointerX ) * 0.3 + onPointerDownLon;
                lat = ( onPointerDownPointerY - event.clientY ) * 0.3 + onPointerDownLat;
            }
        }

        function onDocumentMouseUp( event ) {
            isUserInteracting = false;
        }

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

        function update()
        {           
            stats.update();
        }

        function render() {

            lat = Math.max( - 85, Math.min( 85, lat ) );
            phi = THREE.Math.degToRad( 90 - lat );
            theta = THREE.Math.degToRad( lon );

            camera.target.x = 500 * Math.sin( phi ) * Math.cos( theta );
            camera.target.y = 500 * Math.cos( phi );
            camera.target.z = 500 * Math.sin( phi ) * Math.sin( theta );

            camera.lookAt( camera.target );

            renderer.render( scene, camera );
        }

Answer №1

To determine the latitude and longitude based on the x, y, z coordinates, you need to use inverse methods. Check out the updated demo on code-pen for more information.

Original:

lat = Math.max( - 85, Math.min( 85, lat ) );
phi = THREE.Math.degToRad( 90 - lat );
theta = THREE.Math.degToRad( lon );

camera.target.x = 500 * Math.sin( phi ) * Math.cos( theta );
camera.target.y = 500 * Math.cos( phi );
camera.target.z = 500 * Math.sin( phi ) * Math.sin( theta );

Inverse:

This snippet is extracted from the original code-pen, located in the update() method within the document.body.onkeydown action event listener -- I modified this method in the demo with the provided code

//By focusing on a specific shape upon keypress, the inversion was performed from that
//shape's location.
camera.lookAt( blue1.position );
//I included these additional four lines of code
phi = Math.acos((blue1.position.y)/500);
theta = Math.acos((blue1.position.x)/(500 * Math.sin(phi)))
lon = THREE.Math.radToDeg(theta);
lat = 90-THREE.Math.radToDeg(phi);

Meticulous algebraic insights

As I always received deductions at school for only presenting solutions, here is the journey to the inverse/solution for determining latitude and longitude from x, y, and z

Consider x, y, and z as your viewpoint variables (camera.target.x or <object>.position.x, etc).

Let phi = Φ, theta = Θ

Solving for Θ

  1. x = 500 * sin (Φ) * cos(Θ)
  2. x/(500 * sin(Φ)) = 500 * sin (Φ) * cos(Θ) / (500 * sin(Φ))
  3. x/(500 * sin(Φ)) = cos(Θ)
  4. arccos(x/(500 * sin(Φ))) = arccos(cos(Θ))
  5. arccos(x/(500 * sin(Φ))) = Θ

Solving for Φ

  1. y = 500 * cos(Φ)
  2. y/500 = 500 * cos(Φ) / 500
  3. y/500 = cos(Φ)
  4. arccos(y/500) = arccos(cos(Φ))
  5. arccos(y/500) = Φ

Solving for lon

  1. Θ = degToRad(lon)
  2. radToDeg(Θ) = degToRad(degToRad(lon))
  3. radToDeg(Θ) = lon

Solving for lat

  1. Φ = degToRad( 90 - lat );
  2. radToDeg(Φ) = radToDeg(degToRad( 90 - lat ))
  3. radToDeg(Φ) = 90 - lat
  4. radToDeg(Φ) - 90 = 90 - lat - 90
  5. radToDeg(Φ) - 90 = -lat
  6. -1*(radToDeg(Φ) - 90) = -1*(-lat)
  7. -radToDeg(Φ + 90 = lat
  8. 90 - radToDeg(Φ) = lat
  9. 90 - radToDeg(Φ) = lat

Answer №2

camera.watch(yourObject.location);
//?

If my understanding is correct, the query pertains to how to orient the camera towards an object.

In your script, it seems a bit convoluted that you are adjusting both camera.target and using camera.lookAt. It might be simpler if you only use lookAt without modifying the target separately, or vice versa.

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

Reference now inactive in an array object no longer exhibiting reactivity

After implementing the following code successfully, we noticed that changing the language updates the text correctly thanks to the ref: const mainNavigationLinks = computed(() => [ { label: context.root.$t('navigationMenu.home') }, { labe ...

AngularFire - Structuring item references for child templates using ng-repeat

I have been struggling with this issue for hours and can't seem to find a solution. In my dashboard, all data from my Firebase database is visible using Ng-repeat. However, I am trying to select a specific item and view its details on another page. H ...

Error message HMR Webpack was found to be invalid

When using Webpack, I keep encountering the following error message: Invalid HMR message, along with a lengthy JSON string. Unfortunately, I couldn't find any helpful resources to assist me in debugging this issue. Do you have any suggestions? https ...

Having trouble loading select2 using PHP/Ajax/JSON even though I can see the data in the inspector

I encountered a puzzling issue after deploying my web application from a Windows (XAMPP environment) to a Linux Server. Despite everything working perfectly on Windows, I am now facing a frustrating problem that has left me stumped. I have scoured through ...

Step-by-step guide for integrating a Twig file in Symfony using Angular's ng-include feature

Seeking guidance in Angular, as a newcomer to the platform. I attempted to load a template within an ng-repeat loop like so, but encountered an error. I received the following error message: "Cross origin requests are only supported for protocol schemes ...

Express.js: Request body with an undefined base64 encoding

I'm currently working on transforming a table in .xls format to individual rows in .csv format. I came across a helpful library for this task called XLSX My initial step involves encoding the .xls table into base64 format. Next, I'm attempting ...

What is the best method to retrieve the title from an array using javascript?

I am working with a JSON object containing information on thousands of students. I have converted this JSON object into an array, and below is an example of how one array looks: [ 'Alex', { id: '0.0010733333111112', grade: &apos ...

Error: React Select input control is throwing a TypeError related to event.target

Having trouble changing the state on change using a React Select node module package. It works with regular text input, but I can't quite get it to work with this specific component. The error message "TypeError: event.target is undefined" keeps poppi ...

Activate Bootstrap datetimepicker by using the enter key to automatically populate the initial date

Check out the Bootstrap datetimepicker on this page: I'm trying to make it so that when the datetimepicker is first shown, pressing the enter key will hide the widget and insert the current date into the input field. I've experimented with a few ...

After the ajax call has finished loading the page, I would like to toggle a particular div

When I trigger a click event on an element, it calls a function with Ajax calls. How can I toggle a div (which will be loaded after the Ajax call) once the page is loaded? function triggerFunction(){ $("#trigger_div").trigger("click"); $("#to ...

Dealt with and dismissed commitments in a personalized Jasmine Matcher

The Tale: A unique jasmine matcher has been crafted by us, with the ability to perform 2 key tasks: hover over a specified element verify the presence of a tooltip displaying the expected text Execution: toHaveTooltip: function() { return { ...

How to retrieve the value from an editable td within a table using Jquery

I am working with a dynamic table that looks like this: <table> <tbody> <tr> <td>1</td> <td contenteditable='true'>Value1</td> </tr> <tr> ...

Tips on incorporating the vue package

I recently started using Vue and decided to incorporate a plugin called vue-toastr. I'm attempting to integrate it with Laravel, which already has Vue set up with the plugin. Unfortunately, I keep encountering an error that says "TypeError: Cannot rea ...

Tips for displaying a loading spinner each time the material table is loading

Hey there, I'm currently working on an Angular project where I need to display a table using Material Table. To indicate that the table is loading, I've defined a variable called isLoading. Here's how it works: In my TypeScript file: @Com ...

Problem with selecting odd and even div elements

I have a div populated with a list of rows, and I want to alternate the row colors. To achieve this, I am using the following code: $('#PlatformErrorsTableData').html(table1Html); $('#PlatformErrorsTableData div:nth-child(even)').css(" ...

Tips for accessing and modifying local JSON data in a Chrome Extension

I am working on an extension and need to access and modify a local JSON file within the extension's directory. How can I accomplish this task? ...

Guide to Automatically Transform Markdown (.md) to HTML and Display it within a Designated <div> Container using Node.js alongside markdown-it Library

I am currently working on a unique project where my client specifically requires the ability to input Markdown (MD) content into a .md file and have it dynamically converted into HTML. The resulting HTML must then be displayed within a designated element i ...

RS256 requires that the secretOrPrivateKey is an asymmetric key

Utilizing the jsonwebtoken library to create a bearer token. Following the guidelines from the official documentation, my implementation code appears as below: var privateKey = fs.readFileSync('src\\private.key'); //returns Buffer let ...

Can text be replaced without using a selector?

I am utilizing a JavaScript library to change markdown into HTML code. If I insert <span id="printHello></span> within the markdown content, I can still access it using getElementById() after conversion. However, there is one scenario where th ...

Refresh data with Axios using the PUT method

I have a query regarding the use of the HTTP PUT method with Axios. I am developing a task scheduling application using React, Express, and MySQL. My goal is to implement the functionality to update task data. Currently, my project displays a modal window ...