Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

is there a way of rotating a model around a point instead of around itself? #1783

Open
hamza-hajji opened this issue Jan 22, 2025 · 3 comments
Labels
feature-suggestion Suggestion for how to extend xeokit

Comments

@hamza-hajji
Copy link
Contributor

Is your feature request related to a problem? Please describe.
I want a way to rotate an object based on a different origin, not the default one. If I load a model , I can specify its origin in the load params

let model = xktLoader.load({
  id: "mymodel",
  src: "./models/whatever.xkt",
  saoEnabled: true,
  edges: true,
  dtxEnabled: true,
  origin: [1, 0, 1],
});

If I rotate it like this, it's correctly rotated according to that local origin, but I wish there was a way that while rotating using model.rotation = [x, y, z] we also provide an global origin for that rotation

Context

I want to rotate a group of individual models around the center point which is the average of all their positions, so I need to set each of their global origins as that center

Describe the solution you'd like
This could be a function called like this

const newRotation = [0, 90, 0];
// global center
const center = [0, 0, 0];
model.rotateAroundPoint(newRotation, center)

This would rotate a model around the point (0, 0, 0) in the world, not the origin (0, 0, 0) we provide when loading the model

Alternative solutions I tried
I tried to group the models under one Node, like with Meshes but that apparenly only works with Meshes, I wonder if it's possible?

Thanks for consideration

@hamza-hajji hamza-hajji added the feature-suggestion Suggestion for how to extend xeokit label Jan 22, 2025
@MichalDybizbanskiCreoox
Copy link
Collaborator

Hi @hamza-hajji ,

This is an interesting problem that can be solved with a following approach:
Aside from the position and rotation properties, the SceneModel also exposes a matrix property.
They are related in a way that a change in either of the first two affects the third one, and vice versa.
The matrix property represents a transformation matrix of a SceneModel, and assigning to it can be used to perform any kind of 3D transformation.

A 3D transformation matrix that represents the case you described can be created as follows:

  1. Translate the coordinate system so that the pivot point moves to its origin.
  2. Rotate the coordinate system (around its origin).
  3. Translate the pivot point back to its position.

There are many libraries that abstract such operations, and xeokit SDK exposes such a library as well, as the math package.
An eulerRotationAroundPoint function that returns a transformation matrix could be implemented as this:

function eulerRotationAroundPoint(eulerRotation, center) {
    // Matrix that translates `center` to origin.
    const t1  = math.translationMat4v(math.subVec3([0,0,0], center, math.vec3()), math.identityMat4());
    // Quaternion and matrix representing the rotation
    const q2 = math.eulerToQuaternion(eulerRotation, "XYZ", math.vec4());
    const r2 = math.quaternionToMat4(q2, math.identityMat4());
    // Matrix that translates `center` back to its place.
    const t3  = math.translationMat4v(center, math.identityMat4());
    // Combine the transformations (order is important)
    const m = math.mat4();
    math.mulMat4(t3, math.mulMat4(r2, t1, m), m);
    return m;
}

and then used as:

const newRotation = [0, 90, 0];
// global center
const center = [0, 0, 0];
model.matrix = eulerRotationAroundPoint(newRotation, center);

Hope this helps!
Michał

@hamza-hajji
Copy link
Contributor Author

@MichalDybizbanskiCreoox Thanks for your response, but for some reason after applying that algo, the model moves to position [0, 0, 0]

function eulerRotationAroundPoint(eulerRotation, center) {
    // Matrix that translates `center` to origin.
    const t1  = math.translationMat4v(math.subVec3([0,0,0], center, math.vec3()), math.identityMat4());
    // Quaternion and matrix representing the rotation
    const q2 = math.eulerToQuaternion(eulerRotation, "XYZ", math.vec4());
    const r2 = math.quaternionToMat4(q2, math.identityMat4());
    // Matrix that translates `center` back to its place.
    const t3  = math.translationMat4v(center, math.identityMat4());
    // Combine the transformations (order is important)
    const m = math.mat4();
    math.mulMat4(t3, math.mulMat4(r2, t1, m), m);
    return m;
}

function rotateAroundPoint(object, center, rotation) {
  object.matrix = eulerRotationAroundPoint(rotation, center);
}

window.rotateAroundPoint = rotateAroundPoint;

I call it then in the console
rotateAroundPoint(viewer.scene.models.libModel, [0, 0, 0], [0, 30, 0])

Image

@MichalDybizbanskiCreoox
Copy link
Collaborator

Hi @hamza-hajji ,
Yes, the reason the position is set to zero is because the matrix returned by the eulerRotationAroundPoint function doesn't contain a translation component, so after it's being assigned to object.matrix, the model's original translation is overwritten.
If the model is to be translated, then the rotation matrix needs to be multiplied by a translation matrix.
In your case that'd be

object.matrix = math.mulMat4(
    eulerRotationAroundPoint(rotation, center), 
    math.translationMat4v(position, math.identityMat4()), 
    math.mat4());

That represents a sequence of transformations:

  1. Translate an object by position from the coordinate system's origin
  2. Rotate the coordinate system around a center pivot point, by rotation euler angle.
    Please note that the original model's position, before the model is rotated, is also represented by the model's matrix property.

BTW if you wanted to rotate a model around a pivot while preserving existing transformation, you could achieve it by

object.matrix = math.mulMat4(
    eulerRotationAroundPoint(rotation, center), 
    object.matrix, 
    math.mat4());

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature-suggestion Suggestion for how to extend xeokit
Projects
None yet
Development

No branches or pull requests

2 participants