Having trouble rotating with Quaternions in THREE.js? Let's lend you a hand!

I've been trying to grasp the concept of quaternions for three.js, but have struggled to apply them in the specific way I need for my project. Here's my dilemma:

Imagine a sphere with its center at (0,0,0), and I want to position an object on the surface of the sphere as a focal point for the camera. This focal point should be able to move and rotate on the surface based on keyboard input. While setting it into orbit is straightforward, maintaining the correct rotation perpendicular to the surface has proven challenging. I understand that quaternions are essential for achieving smooth movement and arbitrary axis rotation, but I'm unsure how to begin.

The next challenge is rotating the camera offset along with the focal point. The code snippet I tried doesn't seem to produce the desired outcome, as the cameraOffset fails to inherit the rotation properly:

var cameraOffset = relativeCameraOffset.clone().applyMatrix4( focalPoint.matrixWorld );
camera.position.copy( focalPoint.position.clone().add(cameraOffset) );
camera.lookAt( focalPoint.position );

Update 1: I attempted fixing the camera on the pole and rotating the planet instead. However, this approach also encounters issues when moving towards the equator, where the directional orientation becomes skewed (i.e., left becomes forward). The updated code snippet is as follows:

        acceleration.set(0,0,0);

        if (keyboard.pressed("w")) acceleration.x = 1 * accelerationSpeed;
        if (keyboard.pressed("s")) acceleration.x = -1 * accelerationSpeed;
        if (keyboard.pressed("a")) acceleration.z = 1 * accelerationSpeed;
        if (keyboard.pressed("d")) acceleration.z = -1 * accelerationSpeed;
        if (keyboard.pressed("q")) acceleration.y = 1 * accelerationSpeed;
        if (keyboard.pressed("e")) acceleration.y = -1 * accelerationSpeed;

        velocity.add(acceleration);
        velocity.multiplyScalar(dropOff);
        velocity.max(minV);
        velocity.min(maxV);

        planet.mesh.rotation.x += velocity.x;
        planet.mesh.rotation.y += velocity.y;
        planet.mesh.rotation.z += velocity.z;

So I'm still searching for alternative solutions.

Answer №1

We've cracked the code by utilizing a combination of matrices and quaternions:

    //Initiating
    var ux = new THREE.Vector3(1,0,0);
    var uy = new THREE.Vector3(0,1,0);
    var uz = new THREE.Vector3(0,0,1);

    var direction = ux.clone();
    var m4 = new THREE.Matrix4();
    var dq = new THREE.Quaternion(); //initial direction quaternion
    var dqq;   //final direction quaternion
    var dq2 = new THREE.Quaternion();
    dq2.setFromAxisAngle(uz,Math.PI/2); //perpendicular rotation

        //Adjusting
        if (velocity.length() < 0.1) return;
        if (velocity.x) { focalPoint.translateY( velocity.x ); }
        if (velocity.y) { focalPoint.translateX( velocity.y ); }
        //create new direction from focalPoint quaternion, but perpendicular
        dqq = dq.clone().multiply(focalPoint.quaternion).multiply(dq2);

        velocity.multiplyScalar(dropOff);
        //forward direction vector          
        direction = ux.clone().applyQuaternion(dqq).normalize();
        //utilize Matrix4.lookAt to align focalPoint with the direction
        m4.lookAt(focalPoint.position, planet.mesh.position, direction);
        focalPoint.quaternion.setFromRotationMatrix(m4);

        var cameraOffset = relativeCameraOffset.clone();
        cameraOffset.z = cameraDistance;

        cameraOffset.applyQuaternion(focalPoint.quaternion);

        camera.position = focalPoint.position.clone().add(cameraOffset) ;
        //use direction for camera rotation as well
        camera.up = direction;
        camera.lookAt( focalPoint.position );

This is the essence of it. It smoothly transitions (and optionally rotates) around the planet without encountering issues at the poles.

Answer №2

I'm having trouble grasping the issue you're experiencing. However, to assist you, I've created a code snippet that draws a boat on a sphere.

var geometry = new THREE.ShapeGeometry(shape);
var translation = new THREE.Matrix4().makeTranslation(boat.position.x, boat.position.y, boat.position.z);
var rotationZ = new THREE.Matrix4().makeRotationZ(-THREE.Math.degToRad(boat.cap));
var rotationX = new THREE.Matrix4().makeRotationX(-THREE.Math.degToRad(boat.latitude));
var rotationY = new THREE.Matrix4().makeRotationY(Math.PI / 2 + THREE.Math.degToRad(boat.longitude));
var roationXY = rotationY.multiply(rotationX);
geometry.applyMatrix(rotationZ);
geometry.applyMatrix(roationXY );
geometry.applyMatrix(translation);
  1. To start, I apply a Z-axis rotation to determine the boat's heading
  2. Next, I perform Y and X-axis rotations to position the boat perpendicular to the sphere's surface
  3. Lastly, a translation is applied to place the boat on the surface of the sphere

The sequence of rotations is crucial for accurate placement

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

two adjacent elements filling the entire height of the window at 100% dimensions

Is there a way to place two elements side by side, with one of them (the textarea) always being 100% height of the window? I've looked at similar questions here but can't figure out how to make it work. Can you help me with this? html: <div ...

Deactivate ngOptions when the value in the array is modified by ngChange

My goal is to disable options in a select menu after they have been selected. This functionality relies on the 'perf_no' associated with each 'customer_no', as shown in the code snippet below. The ngChange directive updates the value of ...

How can a single value (the clicked one) be retrieved from a for loop using addEventListener in a Django project with JavaScript

Struggling to integrate JS into my Django project, I received the following message in the console: HTMLCollection(2) [p.currency-one, p.currency-one] HTMLCollection(2) [input.amount-one, input.amount-one] app.js:21 Uncaught TypeError: c1.addEventListener ...

A white background emerges when I select md-dropdown

Lately, I've been experiencing an issue on my website where a white background pops up whenever I click on a dropdown (md-select) in my form. Initially, it only happened with forms inside a modal, but now it's affecting dropdowns across the entir ...

Bringing back a string from an external Ajax call

Learning something new can be challenging, so please bear with me: I've set out to create an image gallery that features a main index page where users can select various project categories, a sub-index page for selecting specific projects within the ...

update the dropdown values in the database by submitting the form

Members sign up for the website. The administrator will log in and access a list of users. I am attempting to provide an option for the admin to select a checkbox and update the user's status through a dropdown menu submission. When I tested the code ...

Tips for implementing a repeater item to function beyond the ng-repeat directive

Recently, I decided to play around with AngularJS and now I seem to be facing a small issue with ng-class. Specifically, I'm attempting to change the color of an awesome font icon. <div ng-controller="MyCtrl"> <i ng-class="{'test': ...

In production mode, the Angular/JS Express app is stuck in an endless routing loop, while it functions perfectly in development mode

My webapp is constructed using generator-angular-fullstack. While it functions properly in development mode (grunt serve), I encounter a problem with what appears to be an endless routing loop when switching to production mode (grunt serve:dist). The outp ...

JavaScript: changing text to numbers within an array

Looking at this array of strings: ["5:17", "8:22", "3:34", "5:23", "7:12", "7:24", "6:46", "4:45", "4:40", "7:58", "11:51", "9:13", "5:50", "5:52", "5:49", "8:57", "11:29", "3:07", "5:59", "3:31"] I'm curious, how do you suggest converting the ...

Error retrieving Facebook access token using Node.js https.get()

I've been working on setting up a Facebook login flow in my node.js app, but I'm facing an issue where the access token isn't returning from the Facebook API when using node's https.get() method. Interestingly, I am able to retrieve the ...

retrieving data from Redis with the help of Node.js

I'm having trouble with the code snippet below. Despite setting values for the left_label and right_label variables in Redis, they always seem to return as "true". I suspect this is due to the client.get function returning true when successful. How ca ...

Guide on how to optimize an ajax call in the browser to prefetch JSON data

One scenario I am dealing with involves initiating multiple ajax calls upon page load. The first call fetches data in JSON format from the server, and subsequent user interactions trigger further ajax requests that require database queries to process the J ...

Unlocking access: Bearer Authentication for AmCharts JS

I am currently working with a .NET Core server that has an API from which I need to request data using the default loading method in AmCharts. The endpoint on the server side is structured like this: [Authorize] public class StocksController : ApiCont ...

Save the HTML elements in an object and then use the ng-include directive to include the template

Currently experimenting with this code snippet: var CustomTable = function($elem) { this.properties.element = $elem; this.initialize(); }; CustomTable.prototype.PROPERTIE = { secondElement: $('.second') // this element ...

Mastering the Art of Dynamically Passing Data-IDs with JQuery

My custom context menu should inherit the data-id value of the list item that was right-clicked. Specifically, I have an unordered list of countries. When a user right-clicks on any of these countries (list items), a context menu pops up. The goal is to p ...

Utilize Node.js to encode images in base64 before sending them through a Post Form

I am trying to retrieve an image from a post form submitted by users on my website and encode it in base64 before storing it in my database. However, I am facing an issue where only the file name ("5165151.jpg") is being captured instead of the actual imag ...

Can anyone recommend a reliable JavaScript library for creating resizable elements?

I am looking to incorporate resizable functionality for a textarea. While I have experimented with jQuery UI's "resizable" feature, it doesn't quite meet my needs. I appreciate jQuery, but the resizable option falls short in allowing me to resize ...

Forcing UTF-8 Encoding in JavaScript

I came across this helpful article: While following the advice of many others suggesting to use unescape(encodeURIComponent(srt)), I encountered issues. Attempting to convert the file obtained through XMLHttpRequest did not yield the desired results. Pri ...

Guide on updating PHP queries in real-time based on form selections using a combination of JQuery and PHP

My form consists of four inputs: $query_sort, $query_time , $query_date , $query_rows. All these are displayed as combo boxes with options, except for $query_rows which is a text box. Upon receiving this input, PHP processes it and generates a query that ...

Conceal the message using star symbols

I need help figuring out how to hide a certain type of string input from the user, and then use Angular data binding to display it in another component with part of the data masked with asterisks. I'm not very skilled in JavaScript, so I'm wonder ...