Wrong orientation of mesh when performing undo

Hi all, sometimes when i try to redo the transformation change the orientation of the mesh is wrong, what i mean by that is if the model is facing me originally, and i do some transformations on the model, sometimes it happens that the wrong orientations is applied?

in undoing im just reading previous position, cloning it and then applying to the model, also that issue occurs more often when i doupletap in between the transformations, the double tap works as a reset, so anything selected becomes unselected, the code is here:

 case BABYLON.PointerEventTypes.POINTERDOUBLETAP:
                let pickedMesh2 = pointerInfo.pickInfo?.pickedMesh;
                let activeCamera = scene.activeCamera as BABYLON.ArcRotateCamera;

                if (pickedMesh2) {
                    activeCamera.setTarget(
                        pointerInfo.pickInfo?.pickedPoint as BABYLON.Vector3
                    );
                }
                else {
                    for (let mesh of scene.meshes) {
                        if (meshState.get(mesh.name)) {
                            mesh.showBoundingBox = false;
                            //hl.removeMesh(mesh as BABYLON.Mesh);
                            meshState.set(mesh.name, false);

                            if (mesh.name === "grid") {
                                mesh.disableEdgesRendering();
                            }
                        }
                    }


                    for (let nodes of scene.transformNodes) {
                        if (meshState.get(nodes.name)) {
                            meshState.set(nodes.name, false);
                        }
                    }

                    for (let i = 0; i < paragraphs.length; i++) {
                        Array.from(paragraphs).forEach((paragraph, index) => {
                            if (index !== i && paragraph.classList.contains("clicked")) {
                                paragraph.classList.remove("clicked");
                            }
                        });

                    }

                    gizmoPosition.attachedNode = null;
                    gizmoRotation.attachedNode = null;
                    gizmoScale.attachedNode = null;

                    for (let i = 0; i < paragraphs.length; i++) {
                        if (paragraphs[i].classList.contains("clicked")) {
                            paragraphs[i].classList.remove("clicked");
                        }
                    }

                    if (pickedMesh) {
                        removeDragBehavior(pickedMesh);
                    }

                    scene.getNodes().forEach(node => {
                        if (node.getClassName() === "Mesh") {
                            (node as BABYLON.AbstractMesh).showBoundingBox = false;
                        }

                        //if (node.getClassName() === "TransformNode") {
                        //    removeGizmos(node as BABYLON.TransformNode);
                        //}

                        //removeDragBehavior(node as BABYLON.TransformNode, node.parent);
                    })

                }
function performUndo() {
    let undoBtn = document.getElementById("undo");

    let keyRedo = 0;
    undoBtn!.addEventListener("click", () => {
        let entries = [...undoMap.entries()];


        if (entries.length > 0) {
            let lastEntry = entries[entries.length - 1];

            for (const [key, value] of lastEntry.entries()) {

                if (value.changeType === "rotation") {
                    let mesh = scene.getNodeByName(value.mesh)
                    let quaternionToApply;

                    if (value.rotationsPerformed === 1) {
                        if ((mesh as BABYLON.TransformNode).rotationQuaternion !== null) {
                            let eulsRotationVector = value.originalRotation.clone();
                            quaternionToApply = BABYLON.Quaternion.FromEulerAngles(eulsRotationVector.x, eulsRotationVector.y, eulsRotationVector.z);

                            (mesh as BABYLON.TransformNode).rotationQuaternion = quaternionToApply;
                        }
                        else {
                            (mesh as BABYLON.TransformNode).rotation = value.originalRotation.clone();
                        }
                    }
                    else if (value.rotationsPerformed !== 1) {
                        if ((mesh as BABYLON.TransformNode).rotationQuaternion !== null) {
                            let eulsRotationVector = value.lastVector.clone();
                            quaternionToApply = BABYLON.Quaternion.FromEulerAngles(eulsRotationVector.x, eulsRotationVector.y, eulsRotationVector.z);

                            (mesh as BABYLON.TransformNode).rotationQuaternion = quaternionToApply;
                        }
                        else {
                            (mesh as BABYLON.TransformNode).rotation = value.lastVector.clone();
                        }


                    }

                    let lastKey = entries[entries.length - 1][0]; //The last key in the map
                    redoMap.set(keyRedo, value);
                    keyRedo++;
                    console.log("Setting redo map here");
                    console.log("with values => ", keyRedo, value);
                    undoMap.delete(lastKey);
                }

                if (value.changeType === "position") {
                    let mesh = scene.getNodeByName(value.mesh);

                    if (value.positionsPerformed === 1) {
                        (mesh as BABYLON.TransformNode).position = value.originalPosition.clone();
                    }
                    else if (value.positionsPerformed !== 1) {
                        (mesh as BABYLON.TransformNode).position = value.lastVector.clone();
                    }

                    let lastKey = entries[entries.length - 1][0]; //The last key in the map
                    redoMap.set(key, value);
                    undoMap.delete(lastKey); // will go into redo map instead of being deleted
                }

                if (value.changeType === "scaling") {
                    let mesh = scene.getNodeByName(value.mesh);

                    if (value.scalingPerformed === 1) {
                        (mesh as BABYLON.TransformNode).scaling = value.originalScale.clone();
                    }
                    else if (value.scalingPerformed !== 1) {
                        (mesh as BABYLON.TransformNode).scaling = value.lastVector.clone();
                    }

                    let lastKey = entries[entries.length - 1][0]; //The last key in the map
                    redoMap.set(key, value);
                    undoMap.delete(lastKey); // will go into redo map instead of being deleted
                }

                if (value.changeType === "dragPosition") {
                    let mesh = scene.getNodeByName(value.mesh);

                    if (value.dragPositionPerformed === 1) {
                        (mesh as BABYLON.AbstractMesh).position = value.originalPosition.clone();
                    }
                    else if (value.dragPositionPerformed !== 1) {
                        (mesh as BABYLON.AbstractMesh).position = value.lastVector.clone();
                        console.log("IM HERE NOW TEST TEST THE MESH SHOULD BE AT => ", value.lastVector);
                    }
                    console.log("DOING DRAG POSITION UNDO NOW");

                    let lastKey = entries[entries.length - 1][0]; //The last key in the map
                    redoMap.set(key, value);
                    undoMap.delete(lastKey); // will go into redo map instead of being deleted

                    //console.log(undoMap);
                }

            }

            //console.log(lastEntry);
        } else {
            console.log("No entries to undo");
        }
    });
}

what im hoping for is that i just forgot to take into account some minor thing, i cannot provide a full playground right now, but if needed i will later, as always thank you for your help

I can see you’re storing a rotation (Euler angles) in your entry stack, and then converting it to quaternion,
just stick with rotationQuaternion and avoid the conversion.

2 Likes

Thank you I will try that.

Quick question, can i just set the Quaternion to null for every mesh? Or could that lead to strange behaviors?

I did avoid the conversion, now the problem is occurring every time compared to before, when i get to the original rotation. the model will be facing a different rotation

can you share a playground ? it would be easier for us to help you.

yes i will, working on it right now

1 Like

Click on the Car mesh to select it
Change it’s rotation, even once is enough
Double click somewhere on the canvas to deselect everything
Undo

If the mesh is selected the whole time it will work as expected.
I believe the problem occurs because of drag behavior, i tested with multiple models and there are bugs present because of detaching from the parent and the reattaching later

Also im not setting rotationQuaternion on the parentNode when importing the model

I couldn’t figure out what caused the issue within your playground, as you are handling a lot of variables during the process and changing on the model hierarchy.

I made a quick playground using your models and everything works as expected on my end, maybe this could help ?

2 Likes

It did help, thank you very much

1 Like