Converting Unreal quaternions to Babylon.js

I need to convert some position and rotation data of skeleton nodes (in actor space) from Unreal to Babylon.js.

Unreal Has Z-axis as the UP vector as opposed to Babylon.js, which has Y-axis for the UP vector. I’m not super knowledgeable about quaternion math, so how would I go about converting the quaternion properly, given the quaternion X, Y, Z, and W values from Unreal?

Thanks in advance!

is y forward negative or positive? (right handed?)

In Unreal, Y forward is positive.

if you dont need to switch coordinates system (just z-to-y), switch between y and z, just like you would do for position.

This was the first thing I tried, but the resulting rotation is incorrect, where the rotation is pointed in the wrong direction. After swapping the Y and Z, I also tried multiplying the X and the new Z by -1, which gets me closer, but it still doesn’t look correct when comparing it against Unreal.

Y - positive = forward
Z positive = upwards
X positive = right?

I usually answer longer sentences. No idea why I come to the point so quick now :slight_smile:

In Unreal, X positive is forward, Y positive is right, and Z positive is up.

Ah :slight_smile: so switching them won’t do no good. Need to think a bit

Any new thoughts? I’ve tried everything I can think of but haven’t been able to find any conversion that works.

Hmm, I can only think in euler angles for this one:

Unreal is Z+Up, X+Forward, Y+Right
We want to convert to Y+Up, Z+Forward, X+Right

Isn’t that -90deg Z, then -90degY (in Unreal’s coordinate space)? These are both left handed coordinate spaces, so I don’t know if we should be flipping or swapping anything…

Mind you this was done using my hand in front of me haha, so I might have the directions flipped

To switch position information you simply change X to Z, Y to X and Z to Y, right? If you convert everything to this order, including VertexData, you should be able to change them in the quaternion (or better yet - in the rotation matrix) as well and not have any consequences. But this is an uneducated and untested guess.

Want to share a model in the playground and explain how it needs to be positioned?

Thanks for the help, but I managed to figure it out myself. In my case, the way to do this was to keep the order the same (x, y, z, and w), then multiplying x and w by -1.

3 Likes

hi, guys i am desperately come here to find the same answer the @telarium asked.
it is really important to me do this task have been stuck since many weeks.

what i want to do:
I am creating a replay scene basically it is cricket VR game I want to create a replay web platform for user to watch and share their cricket experience to others. I choose playcanvas because it is easy and multiple platform handling.

how i do that:
i have created a rest api server which fetch the unreal engine coordinates. the data which is json format for “Helmet”, “Bat”, “ball” entities with their “rotation” and “location” with the help of entities value, we will lerp means animate. next the data will be fetch in playcanvas.

status of project:
the project look amazing i am getting the values the x, y and z axis converting it then animating it. its complete 80%.

So what the issue Men?
the issue is with “rotation” only and the main rotation problem is with “cricket bet”. you will better get when you watch the video i have played the 3 shots you can compare the result.

here is video

please guy help me solve the problem it would be great pleasure

here is my code
var Replay = pc.createScript(‘replay’);
Replay.attributes.add(‘tweenRotationScript’, { type: ‘entity’ });

Replay.attributes.add(‘duration’, { type: ‘number’, default: 0.05 }); // Set duration to 5 seconds
Replay.attributes.add(‘decreaseAreaBy’, { type: ‘number’, default: 0.01 }); // Set duration to 5 seconds
Replay.attributes.add(‘locationProperty’, { type: ‘string’ }); // Set duration to 5 seconds
Replay.attributes.add(‘rotation’, { type: ‘boolean’, default: false }); // Set duration to 5 seconds
Replay.attributes.add(‘rotationProperty’, { type: ‘string’ }); // Set duration to 5 seconds
Replay.attributes.add(‘loop’, { type: ‘boolean’, default: false, description: ‘Enable looping’ });

Replay.prototype.initialize = function () {
this.fetchData();
};

let replayNewId = 20;
// Replace with the ID of the replay you want to retrieve

Replay.prototype.fetchData = function () {
// Make a GET request to your server’s API endpoint
fetch(https://finalovers.cricket/api/users/getOneReplay/${replayNewId})
.then(response => response.json())
.then(data => {
console.log(‘Received data:’, data);

        const ballLocations = JSON.parse(data.ballLocations);
        const batLocations = JSON.parse(data.batLocations);
        const batRotations = JSON.parse(data.batRotations);
        const helmetLocations = JSON.parse(data.helmetLocations);
        const helmetRotations = JSON.parse(data.helmetRotations);

        this.ballLocations = ballLocations;
        this.batLocations = batLocations;
        this.batRotations = batRotations;
        this.helmetLocations = helmetLocations;
        this.helmetRotations = helmetRotations;

        if (
            ballLocations.length > 0 &&
            batLocations.length > 0 &&
            batRotations.length > 0 &&
            helmetLocations.length > 0 &&
            helmetRotations.length > 0
        ) {
            this.currentIndex = 0;
            this.moveObject();
        }
    })
    .catch(error => console.error('Error fetching data:', error));

};

Replay.prototype.moveObject = function () {
// if we are already tweening then stop first
if (this.tween) {
this.tween.stop();
}

// reset our rotation
this.entity.setEulerAngles(0, 0, 0);

var entity = this.entity;

var targetPosition = new pc.Vec3(
    this[this.locationProperty][this.currentIndex].x * this.decreaseAreaBy,
    this[this.locationProperty][this.currentIndex].z * this.decreaseAreaBy,
    this[this.locationProperty][this.currentIndex].y * this.decreaseAreaBy
);

if (this.rotation) {
    var rotationData = this[this.rotationProperty][this.currentIndex];
    var targetRotation = new pc.Quat().setFromEulerAngles((rotationData.x), (rotationData.z * -1), (rotationData.y * -1));
    entity.setLocalRotation(targetRotation);
}

var tween = this.entity.tween(this.entity.getLocalPosition())
    .to(targetPosition, this.duration, pc["Linear"])
    .on("complete", this.nextLocation.bind(this))
    .start();

};

Replay.prototype.nextLocation = function () {
this.currentIndex++;

if (this.currentIndex < this[this.locationProperty].length) {
    console.log(this.currentIndex, this[this.locationProperty].length)
    this.moveObject();
} else {
    console.log("completed");
    if (this.loop) {
        this.currentIndex = 0;
        this.moveObject();
    }
}

};

I think you’re in the wrong forum to ask a question about PlayCanvas :slight_smile:

You’ll probably get better support at https://forum.playcanvas.com/, as I don’t think many people know about PlayCanvas on the Babylon.js forums.

@Evgeni_Popov your are right actually the logic is same i dont want to ask about the code. i want to know about the logic as @telarium convert the unreal coordinates to babylonjs.