Fix VECTOR/LOCAL transitions in Node3D

Fixes #62225, supersedes #62227
This commit is contained in:
reduz
2022-06-25 11:23:24 +02:00
parent c4f0062f15
commit 7acf697479
4 changed files with 143 additions and 77 deletions

View File

@@ -52,6 +52,9 @@ class Node3D : public Node {
GDCLASS(Node3D, Node);
public:
// Edit mode for the rotation.
// THIS MODE ONLY AFFECTS HOW DATA IS EDITED AND SAVED
// IT DOES _NOT_ AFFECT THE TRANSFORM LOGIC (see comment in TransformDirty).
enum RotationEditMode {
ROTATION_EDIT_MODE_EULER,
ROTATION_EDIT_MODE_QUATERNION,
@@ -68,11 +71,27 @@ public:
};
private:
// For the sake of ease of use, Node3D can operate with Transforms (Basis+Origin), Quaterinon/Scale and Euler Rotation/Scale.
// Transform and Quaterinon are stored in data.local_transform Basis (so quaternion is not really stored, but converted back/forth from 3x3 matrix on demand).
// Euler needs to be kept separate because converting to Basis and back may result in a different vector (which is troublesome for users
// editing in the inspector, not only because of the numerical precision loss but because they expect these rotations to be consistent, or support
// "redundant" rotations for animation interpolation, like going from 0 to 720 degrees).
//
// As such, the system works in a way where if the local transform is set (via transform/basis/quaternion), the EULER rotation and scale becomes dirty.
// It will remain dirty until reading back is attempted (for performance reasons). Likewise, if the Euler rotation scale are set, the local transform
// will become dirty (and again, will not become valid again until read).
//
// All this is transparent from outside the Node3D API, which allows everything to works by calling these functions in exchange.
//
// Additionally, setting either transform, quaternion, Euler rotation or scale makes the global transform dirty, which will be updated when read again.
//
// NOTE: Again, RotationEditMode is _independent_ of this mechanism, it is only meant to expose the right set of properties for editing (editor) and saving
// (to scene, in order to keep the same values and avoid data loss on conversions). It has zero influence in the logic described above.
enum TransformDirty {
DIRTY_NONE = 0,
DIRTY_VECTORS = 1,
DIRTY_LOCAL = 2,
DIRTY_GLOBAL = 4
DIRTY_EULER_ROTATION_AND_SCALE = 1,
DIRTY_LOCAL_TRANSFORM = 2,
DIRTY_GLOBAL_TRANSFORM = 4
};
mutable SelfList<Node> xform_change;
@@ -80,8 +99,8 @@ private:
struct Data {
mutable Transform3D global_transform;
mutable Transform3D local_transform;
mutable Basis::EulerOrder rotation_order = Basis::EULER_ORDER_YXZ;
mutable Vector3 rotation;
mutable Basis::EulerOrder euler_rotation_order = Basis::EULER_ORDER_YXZ;
mutable Vector3 euler_rotation;
mutable Vector3 scale = Vector3(1, 1, 1);
mutable RotationEditMode rotation_edit_mode = ROTATION_EDIT_MODE_EULER;
@@ -131,6 +150,7 @@ protected:
_FORCE_INLINE_ void set_ignore_transform_notification(bool p_ignore) { data.ignore_notification = p_ignore; }
_FORCE_INLINE_ void _update_local_transform() const;
_FORCE_INLINE_ void _update_rotation_and_scale() const;
void _notification(int p_what);
static void _bind_methods();