From bf240d7eeaa89657462b705849fde56e54e237db Mon Sep 17 00:00:00 2001
From: Frederico Linhares <fred@linhares.blue>
Date: Tue, 6 Feb 2024 17:37:20 -0300
Subject: refa Use quaternion for orientation

---
 src/core.cpp                             |   4 +-
 src/orientation_3d.cpp                   | 135 +++++++++++++++++++++++++++++++
 src/orientation_3d.hpp                   |  27 +++++++
 src/rotation_3d.cpp                      | 115 --------------------------
 src/rotation_3d.hpp                      |  27 -------
 src/skeletal_mesh.cpp                    |   2 +-
 src/skeletal_model.cpp                   |  22 ++---
 src/static_mesh.cpp                      |   4 +-
 src/static_model.cpp                     |  24 +++---
 src/vector_3d.cpp                        |  27 +++----
 src/view_3d.cpp                          |  14 ++--
 src/vk/core.hpp                          |   1 +
 src/vk/graphics_pipeline_3d.cpp          |  31 +++----
 src/vk/graphics_pipeline_3d_skeletal.cpp |  31 +++----
 src/vk/skeletal_model.cpp                |   4 +-
 src/vk/skeletal_model.hpp                |   5 +-
 src/vk/sprite_to_draw.hpp                |   8 +-
 src/vk/static_model.cpp                  |   6 +-
 src/vk/static_model.hpp                  |   5 +-
 src/vk/view_3d.cpp                       |   2 +-
 src/vk/view_3d.hpp                       |   2 +-
 test/src/mode/demo.rb                    |  36 ++++-----
 22 files changed, 265 insertions(+), 267 deletions(-)
 create mode 100644 src/orientation_3d.cpp
 create mode 100644 src/orientation_3d.hpp
 delete mode 100644 src/rotation_3d.cpp
 delete mode 100644 src/rotation_3d.hpp

diff --git a/src/core.cpp b/src/core.cpp
index ec79fa0..3de9da5 100644
--- a/src/core.cpp
+++ b/src/core.cpp
@@ -20,7 +20,7 @@
 #include "font.hpp"
 #include "graphic.hpp"
 #include "key.hpp"
-#include "rotation_3d.hpp"
+#include "orientation_3d.hpp"
 #include "skeletal_model.hpp"
 #include "skeletal_mesh.hpp"
 #include "static_model.hpp"
@@ -792,7 +792,7 @@ load_mruby_interface(void *obj)
   cg_candy_gear_init(cg_core.mrb);
   cg_font_init(cg_core.mrb);
   cg_key_init(cg_core.mrb);
-  cg_rotation_3d_init(cg_core.mrb);
+  cg_orientation_3d_init(cg_core.mrb);
   cg_skeletal_model_init(cg_core.mrb);
   cg_skeletal_mesh_init(cg_core.mrb);
   cg_static_model_init(cg_core.mrb);
diff --git a/src/orientation_3d.cpp b/src/orientation_3d.cpp
new file mode 100644
index 0000000..cb42984
--- /dev/null
+++ b/src/orientation_3d.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2022-2024 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _USE_MATH_DEFINES
+
+#include "orientation_3d.hpp"
+
+#include "vector_3d.hpp"
+
+void
+cg_free_orientation_3d(mrb_state *mrb, void* obj)
+{
+  auto ptr = static_cast<std::shared_ptr<glm::quat>*>(obj);
+
+  ptr->~shared_ptr();
+  mrb_free(mrb, ptr);
+}
+
+const struct mrb_data_type cg_orientation_3d_type = {
+  "CG_Orientation3D", cg_free_orientation_3d};
+
+static mrb_value
+cg_cOrientation3D_initialize(mrb_state *mrb, mrb_value self)
+{
+  mrb_float x, y, z;
+  std::shared_ptr<glm::quat> *ptr;
+
+  mrb_get_args(mrb, "fff", &x, &y, &z);
+  ptr = (std::shared_ptr<glm::quat>*)DATA_PTR(self);
+  if(ptr) mrb_free(mrb, ptr);
+  ptr = (std::shared_ptr<glm::quat>*)mrb_malloc(
+    mrb, sizeof(std::shared_ptr<glm::quat>));
+
+	glm::vec3 angles(x, y, z);
+  new(ptr)std::shared_ptr<glm::quat>(
+    std::make_shared<glm::quat>(angles));
+  (**ptr) = glm::normalize(**ptr);
+
+  mrb_data_init(self, ptr, &cg_orientation_3d_type);
+  return self;
+}
+
+static mrb_value
+cg_cOrientation3D_get_w(mrb_state *mrb, mrb_value self)
+{
+  std::shared_ptr<glm::quat> *ptr =
+    (std::shared_ptr<glm::quat>*)DATA_PTR(self);
+
+  return mrb_float_value(mrb, (*ptr)->w);
+}
+
+static mrb_value
+cg_cOrientation3D_get_x(mrb_state *mrb, mrb_value self)
+{
+  std::shared_ptr<glm::quat> *ptr =
+    (std::shared_ptr<glm::quat>*)DATA_PTR(self);
+
+  return mrb_float_value(mrb, (*ptr)->x);
+}
+
+static mrb_value
+cg_cOrientation3D_get_y(mrb_state *mrb, mrb_value self)
+{
+  std::shared_ptr<glm::quat> *ptr =
+    (std::shared_ptr<glm::quat>*)DATA_PTR(self);
+
+  return mrb_float_value(mrb, (*ptr)->y);
+}
+
+static mrb_value
+cg_cOrientation3D_get_z(mrb_state *mrb, mrb_value self)
+{
+  std::shared_ptr<glm::quat> *ptr =
+    (std::shared_ptr<glm::quat>*)DATA_PTR(self);
+
+  return mrb_float_value(mrb, (*ptr)->z);
+}
+
+static mrb_value
+cg_cOrientation3D_rotate(mrb_state *mrb, mrb_value self)
+{
+  mrb_float x, y, z;
+  auto *ptr = (std::shared_ptr<glm::quat>*)DATA_PTR(self);
+
+  mrb_get_args(mrb, "fff", &x, &y, &z);
+
+  glm::vec3 angles(x, y, z);
+  glm::quat rot(angles);
+  (**ptr) *= rot;
+
+  // TODO: calling normalize for every rotation is expensive.
+  (**ptr) = glm::normalize(**ptr);
+
+  return self;
+}
+
+void
+cg_orientation_3d_init(mrb_state *mrb)
+{
+  struct RClass *cg_m, *cg_cOrientation3D;
+
+  cg_m = mrb_module_get(mrb, "CandyGear");
+  cg_cOrientation3D = mrb_define_class_under(
+    mrb, cg_m, "Orientation3D", mrb->object_class);
+  MRB_SET_INSTANCE_TT(cg_cOrientation3D, MRB_TT_DATA);
+  mrb_define_method(
+    mrb, cg_cOrientation3D, "initialize", cg_cOrientation3D_initialize,
+    MRB_ARGS_REQ(3));
+
+  mrb_define_method(
+    mrb, cg_cOrientation3D, "w", cg_cOrientation3D_get_w, MRB_ARGS_NONE());
+  mrb_define_method(
+    mrb, cg_cOrientation3D, "x", cg_cOrientation3D_get_x, MRB_ARGS_NONE());
+  mrb_define_method(
+    mrb, cg_cOrientation3D, "y", cg_cOrientation3D_get_y, MRB_ARGS_NONE());
+  mrb_define_method(
+    mrb, cg_cOrientation3D, "z", cg_cOrientation3D_get_z, MRB_ARGS_NONE());
+
+  mrb_define_method(
+    mrb, cg_cOrientation3D, "rotate", cg_cOrientation3D_rotate,
+    MRB_ARGS_REQ(3));
+}
diff --git a/src/orientation_3d.hpp b/src/orientation_3d.hpp
new file mode 100644
index 0000000..45a8ab2
--- /dev/null
+++ b/src/orientation_3d.hpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2022-2024 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CANDY_GEAR_ORIENTATION_3D_H
+#define CANDY_GEAR_ORIENTATION_3D_H 1
+
+#include "core.hpp"
+
+extern const struct mrb_data_type cg_orientation_3d_type;
+
+void
+cg_orientation_3d_init(mrb_state *mrb);
+
+#endif /* CANDY_GEAR_ORIENTATION_3D_H */
diff --git a/src/rotation_3d.cpp b/src/rotation_3d.cpp
deleted file mode 100644
index 0795859..0000000
--- a/src/rotation_3d.cpp
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright 2022 Frederico de Oliveira Linhares
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define _USE_MATH_DEFINES
-
-#include "rotation_3d.hpp"
-
-#include "vector_3d.hpp"
-
-namespace
-{
-inline float
-pitch_limit(float pitch)
-{
-  if(pitch > M_PI/2) pitch = M_PI/2;
-  else if (pitch < -(M_PI/2)) pitch = -(M_PI/2);
-
-  return pitch;
-}
-
-}
-
-void
-cg_free_rotation_3d(mrb_state *mrb, void* obj)
-{
-  auto ptr = static_cast<std::shared_ptr<glm::vec3>*>(obj);
-
-  ptr->~shared_ptr();
-  mrb_free(mrb, ptr);
-}
-
-const struct mrb_data_type cg_rotation_3d_type = {
-  "CG_Rotation3D", cg_free_rotation_3d};
-
-static mrb_value
-cg_cRotation3D_initialize(mrb_state *mrb, mrb_value self)
-{
-  mrb_float pitch, yaw, roll;
-  std::shared_ptr<glm::vec3> *ptr;
-
-  mrb_get_args(mrb, "fff", &pitch, &yaw, &roll);
-  ptr = (std::shared_ptr<glm::vec3>*)DATA_PTR(self);
-  if(ptr) mrb_free(mrb, ptr);
-  ptr = (std::shared_ptr<glm::vec3>*)mrb_malloc(
-    mrb, sizeof(std::shared_ptr<glm::vec3>));
-
-  new(ptr)std::shared_ptr<glm::vec3>(
-    std::make_shared<glm::vec3>(pitch_limit(pitch), yaw, roll));
-
-  mrb_data_init(self, ptr, &cg_rotation_3d_type);
-  return self;
-}
-
-// Rotation when there is a up direction (ex.: Earth).
-static mrb_value
-cg_cRotation3D_rotate(mrb_state *mrb, mrb_value self)
-{
-  mrb_float pitch, yaw;
-  auto *ptr = (std::shared_ptr<glm::vec3>*)DATA_PTR(self);
-
-  mrb_get_args(mrb, "ff", &pitch, &yaw);
-
-  (*ptr)->x = pitch_limit((*ptr)->x + pitch);
-  (*ptr)->y += yaw;
-
-  return self;
-}
-
-// Rotation when there is a up direction (ex.: space without gravity).
-static mrb_value
-cg_cRotation3D_baseless_rotation(mrb_state *mrb, mrb_value self)
-{
-  mrb_float x, y;
-
-  // TODO
-
-  return self;
-}
-
-void
-cg_rotation_3d_init(mrb_state *mrb)
-{
-  struct RClass *cg_m, *cg_cRotation3D;
-
-  cg_m = mrb_module_get(mrb, "CandyGear");
-  cg_cRotation3D = mrb_define_class_under(
-    mrb, cg_m, "Rotation3D", mrb->object_class);
-  MRB_SET_INSTANCE_TT(cg_cRotation3D, MRB_TT_DATA);
-  mrb_define_method(
-    mrb, cg_cRotation3D, "initialize", cg_cRotation3D_initialize,
-    MRB_ARGS_REQ(3));
-
-  mrb_define_method(
-    mrb, cg_cRotation3D, "pitch", cg_cVector3D_get_x, MRB_ARGS_NONE());
-  mrb_define_method(
-    mrb, cg_cRotation3D, "yaw", cg_cVector3D_get_y, MRB_ARGS_NONE());
-  mrb_define_method(
-    mrb, cg_cRotation3D, "roll", cg_cVector3D_get_z, MRB_ARGS_NONE());
-
-  mrb_define_method(
-    mrb, cg_cRotation3D, "rotate", cg_cRotation3D_rotate, MRB_ARGS_REQ(2));
-}
diff --git a/src/rotation_3d.hpp b/src/rotation_3d.hpp
deleted file mode 100644
index bba4bec..0000000
--- a/src/rotation_3d.hpp
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2022 Frederico de Oliveira Linhares
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef CANDY_GEAR_ROTATION_3D_H
-#define CANDY_GEAR_ROTATION_3D_H 1
-
-#include "core.hpp"
-
-extern const struct mrb_data_type cg_rotation_3d_type;
-
-void
-cg_rotation_3d_init(mrb_state *mrb);
-
-#endif /* CANDY_GEAR_ROTATION_3D_H */
diff --git a/src/skeletal_mesh.cpp b/src/skeletal_mesh.cpp
index 767ef5a..b9d4b0e 100644
--- a/src/skeletal_mesh.cpp
+++ b/src/skeletal_mesh.cpp
@@ -16,7 +16,7 @@
 
 #include "skeletal_mesh.hpp"
 
-#include "rotation_3d.hpp"
+#include "orientation_3d.hpp"
 #include "vector_3d.hpp"
 #include "vk/skeletal_mesh.hpp"
 
diff --git a/src/skeletal_model.cpp b/src/skeletal_model.cpp
index 0165e91..a8dc62a 100644
--- a/src/skeletal_model.cpp
+++ b/src/skeletal_model.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright 2022-2023 Frederico de Oliveira Linhares
+ * Copyright 2022-2024 Frederico de Oliveira Linhares
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,8 +16,8 @@
 
 #include "skeletal_model.hpp"
 
+#include "orientation_3d.hpp"
 #include "vector_3d.hpp"
-#include "rotation_3d.hpp"
 #include "skeletal_mesh.hpp"
 #include "texture.hpp"
 #include "vk/skeletal_model.hpp"
@@ -40,13 +40,13 @@ cg_cSkeletalModel_initialize(mrb_state *mrb, mrb_value self)
   std::shared_ptr<VK::SkeletalMesh> *skeletal_mesh;
   std::shared_ptr<VK::Texture> *texture;
   std::shared_ptr<glm::vec3> *position;
-  std::shared_ptr<glm::vec3> *rotation;
+  std::shared_ptr<glm::quat> *orientation;
   std::shared_ptr<VK::SkeletalModel> *ptr;
 
   mrb_get_args(
     mrb, "dddd", &skeletal_mesh, &cg_skeletal_mesh_type, &texture,
-    &cg_texture_type, &position, &cg_vector_3d_type, &rotation,
-    &cg_rotation_3d_type);
+    &cg_texture_type, &position, &cg_vector_3d_type, &orientation,
+    &cg_orientation_3d_type);
   ptr = (std::shared_ptr<VK::SkeletalModel>*)DATA_PTR(self);
   if(ptr) mrb_free(mrb, ptr);
   ptr = (std::shared_ptr<VK::SkeletalModel>*)mrb_malloc(
@@ -54,20 +54,20 @@ cg_cSkeletalModel_initialize(mrb_state *mrb, mrb_value self)
 
   new(ptr)std::shared_ptr<VK::SkeletalModel>(
     std::make_shared<VK::SkeletalModel>(
-      *skeletal_mesh, *texture, *position, *rotation));
+      *skeletal_mesh, *texture, *position, *orientation));
 
   mrb_data_init(self, ptr, &cg_skeletal_model_type);
   return self;
 }
 
 static mrb_value
-cg_cSkeletalModel_set_rotation(mrb_state *mrb, mrb_value self)
+cg_cSkeletalModel_set_orientation(mrb_state *mrb, mrb_value self)
 {
   auto ptr = (std::shared_ptr<VK::SkeletalModel>*)DATA_PTR(self);
-  std::shared_ptr<glm::vec3> *rotation;
+  std::shared_ptr<glm::quat> *orientation;
 
-  mrb_get_args(mrb, "d", &rotation, &cg_rotation_3d_type);
-  (*ptr)->rotation = *rotation;
+  mrb_get_args(mrb, "d", &orientation, &cg_orientation_3d_type);
+  (*ptr)->orientation = *orientation;
 
   return self;
 }
@@ -124,7 +124,7 @@ cg_skeletal_model_init(mrb_state *mrb)
     mrb, cg_cSkeletalModel, "position=", cg_cSkeletalModel_set_position,
     MRB_ARGS_REQ(1));
   mrb_define_method(
-    mrb, cg_cSkeletalModel, "rotation=", cg_cSkeletalModel_set_rotation,
+    mrb, cg_cSkeletalModel, "orientation=", cg_cSkeletalModel_set_orientation,
     MRB_ARGS_REQ(1));
   mrb_define_method(
     mrb, cg_cSkeletalModel, "animation=", cg_cSkeletalModel_set_animation,
diff --git a/src/static_mesh.cpp b/src/static_mesh.cpp
index e1e8776..17d7be3 100644
--- a/src/static_mesh.cpp
+++ b/src/static_mesh.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright 2022-2023 Frederico de Oliveira Linhares
+ * Copyright 2022-2024 Frederico de Oliveira Linhares
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,7 +16,7 @@
 
 #include "static_mesh.hpp"
 
-#include "rotation_3d.hpp"
+#include "orientation_3d.hpp"
 #include "vector_3d.hpp"
 #include "vk/static_mesh.hpp"
 
diff --git a/src/static_model.cpp b/src/static_model.cpp
index 5873792..eb9e4c9 100644
--- a/src/static_model.cpp
+++ b/src/static_model.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright 2022-2023 Frederico de Oliveira Linhares
+ * Copyright 2022-2024 Frederico de Oliveira Linhares
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,10 +16,10 @@
 
 #include "static_model.hpp"
 
-#include "vector_3d.hpp"
-#include "rotation_3d.hpp"
+#include "orientation_3d.hpp"
 #include "static_mesh.hpp"
 #include "texture.hpp"
+#include "vector_3d.hpp"
 #include "vk/static_model.hpp"
 
 void
@@ -40,13 +40,13 @@ cg_cStaticModel_initialize(mrb_state *mrb, mrb_value self)
   std::shared_ptr<VK::StaticMesh> *static_mesh;
   std::shared_ptr<VK::Texture> *texture;
   std::shared_ptr<glm::vec3> *position;
-  std::shared_ptr<glm::vec3> *rotation;
+  std::shared_ptr<glm::quat> *orientation;
   std::shared_ptr<VK::StaticModel> *ptr;
 
   mrb_get_args(
     mrb, "dddd", &static_mesh, &cg_static_mesh_type, &texture,
-    &cg_texture_type, &position, &cg_vector_3d_type, &rotation,
-    &cg_rotation_3d_type);
+    &cg_texture_type, &position, &cg_vector_3d_type, &orientation,
+    &cg_orientation_3d_type);
   ptr = (std::shared_ptr<VK::StaticModel>*)DATA_PTR(self);
   if(ptr) mrb_free(mrb, ptr);
   ptr = (std::shared_ptr<VK::StaticModel>*)mrb_malloc(
@@ -54,20 +54,20 @@ cg_cStaticModel_initialize(mrb_state *mrb, mrb_value self)
 
   new(ptr)std::shared_ptr<VK::StaticModel>(
     std::make_shared<VK::StaticModel>(
-      *static_mesh, *texture, *position, *rotation));
+      *static_mesh, *texture, *position, *orientation));
 
   mrb_data_init(self, ptr, &cg_static_model_type);
   return self;
 }
 
 static mrb_value
-cg_cStaticModel_set_rotation(mrb_state *mrb, mrb_value self)
+cg_cStaticModel_set_orientation(mrb_state *mrb, mrb_value self)
 {
   auto ptr = (std::shared_ptr<VK::StaticModel>*)DATA_PTR(self);
-  std::shared_ptr<glm::vec3> *rotation;
+  std::shared_ptr<glm::quat> *orientation;
 
-  mrb_get_args(mrb, "d", &rotation, &cg_rotation_3d_type);
-  (*ptr)->rotation = *rotation;
+  mrb_get_args(mrb, "d", &orientation, &cg_orientation_3d_type);
+  (*ptr)->orientation = *orientation;
 
   return self;
 }
@@ -124,7 +124,7 @@ cg_static_model_init(mrb_state *mrb)
     mrb, cg_cStaticModel, "position=", cg_cStaticModel_set_position,
     MRB_ARGS_REQ(1));
   mrb_define_method(
-    mrb, cg_cStaticModel, "rotation=", cg_cStaticModel_set_rotation,
+    mrb, cg_cStaticModel, "orientation=", cg_cStaticModel_set_orientation,
     MRB_ARGS_REQ(1));
   mrb_define_method(
     mrb, cg_cStaticModel, "texture=", cg_cStaticModel_set_texture,
diff --git a/src/vector_3d.cpp b/src/vector_3d.cpp
index c0a9a3e..fc5d186 100644
--- a/src/vector_3d.cpp
+++ b/src/vector_3d.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright 2022 Frederico de Oliveira Linhares
+ * Copyright 2022-2024 Frederico de Oliveira Linhares
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -22,7 +22,7 @@
 
 #include <mruby/array.h>
 
-#include "rotation_3d.hpp"
+#include "orientation_3d.hpp"
 
 void
 cg_free_vector_3d(mrb_state *mrb, void* obj)
@@ -235,24 +235,17 @@ cg_cVector3D_set_xyz(mrb_state *mrb, mrb_value self)
 static mrb_value
 cg_cVector3D_translate(mrb_state *mrb, mrb_value self)
 {
-  std::shared_ptr<glm::vec3> *ptr =
-    (std::shared_ptr<glm::vec3>*)DATA_PTR(self);
-  std::shared_ptr<glm::vec3> *direction, *rotation;
+	auto *ptr = (std::shared_ptr<glm::vec3>*)DATA_PTR(self);
+	std::shared_ptr<glm::vec3> *direction;
+	std::shared_ptr<glm::quat> *orientation;
 
   mrb_get_args(
     mrb, "dd", &direction, &cg_vector_3d_type,
-    &rotation, &cg_rotation_3d_type);
-
-  glm::mat4 matrix{1.0f};
-  glm::vec4 vec{(**direction), 1.0f};
-  matrix = glm::rotate(matrix, (*rotation)->y, glm::vec3{0.0f, 1.0f, 0.0f});
-  matrix = glm::rotate(matrix, (*rotation)->x, glm::vec3{1.0f, 0.0f, 0.0f});
-  matrix = glm::rotate(matrix, (*rotation)->z, glm::vec3{0.0f, 0.0f, 1.0f});
-  vec = matrix * vec;
-
-  (*ptr)->x += vec.x;
-  (*ptr)->y += vec.y;
-  (*ptr)->z += vec.z;
+    &orientation, &cg_orientation_3d_type);
+
+	auto rotated_direction = **orientation * **direction;
+
+	**ptr += rotated_direction;
 
   return self;
 }
diff --git a/src/view_3d.cpp b/src/view_3d.cpp
index 8f4f14c..7db0015 100644
--- a/src/view_3d.cpp
+++ b/src/view_3d.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright 2022 Frederico de Oliveira Linhares
+ * Copyright 2022-2024 Frederico de Oliveira Linhares
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -17,7 +17,7 @@
 #include "view_3d.hpp"
 
 #include "sprite.hpp"
-#include "rotation_3d.hpp"
+#include "orientation_3d.hpp"
 #include "vector_3d.hpp"
 #include "vector_4d.hpp"
 #include "vk/sprite.hpp"
@@ -69,13 +69,13 @@ cg_cView3D_set_camera_position(mrb_state *mrb, mrb_value self)
 }
 
 static mrb_value
-cg_cView3D_set_camera_rotation(mrb_state *mrb, mrb_value self)
+cg_cView3D_set_camera_orientation(mrb_state *mrb, mrb_value self)
 {
-  std::shared_ptr<glm::vec3> *camera_rotation;
+  std::shared_ptr<glm::quat> *camera_orientation;
   auto ptr = (std::shared_ptr<VK::View3D>*)DATA_PTR(self);
 
-  mrb_get_args(mrb, "d", &camera_rotation, &cg_rotation_3d_type);
-  (*ptr)->camera_rotation = (*camera_rotation);
+  mrb_get_args(mrb, "d", &camera_orientation, &cg_orientation_3d_type);
+  (*ptr)->camera_orientation = (*camera_orientation);
 
   return self;
 }
@@ -116,7 +116,7 @@ cg_view_3d_init(mrb_state *mrb)
     mrb, cg_cView3D, "camera_position=", cg_cView3D_set_camera_position,
     MRB_ARGS_REQ(1));
   mrb_define_method(
-    mrb, cg_cView3D, "camera_rotation=", cg_cView3D_set_camera_rotation,
+    mrb, cg_cView3D, "camera_orientation=", cg_cView3D_set_camera_orientation,
     MRB_ARGS_REQ(1));
   mrb_define_method(
     mrb, cg_cView3D, "field_of_view", cg_cView3D_get_field_of_view,
diff --git a/src/vk/core.hpp b/src/vk/core.hpp
index 64d1431..736b8bb 100644
--- a/src/vk/core.hpp
+++ b/src/vk/core.hpp
@@ -19,6 +19,7 @@
 
 // GLM uses some definitions to control their behavior, so you should not
 // include it directly. Instead, use this header.
+#define GLM_ENABLE_EXPERIMENTAL
 #define GLM_FORCE_RADIANS
 #define GLM_FORCE_DEPTH_ZERO_TO_ONE
 
diff --git a/src/vk/graphics_pipeline_3d.cpp b/src/vk/graphics_pipeline_3d.cpp
index f657a54..2b6812d 100644
--- a/src/vk/graphics_pipeline_3d.cpp
+++ b/src/vk/graphics_pipeline_3d.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright 2022-2023 Frederico de Oliveira Linhares
+ * Copyright 2022-2024 Frederico de Oliveira Linhares
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -296,14 +296,10 @@ GraphicsPipeline3D::draw(
 
       for(auto &instance: instances)
       { // Object matrix.
-        glm::mat4 base_matrix{1.0f};
-        base_matrix = glm::translate(base_matrix, *instance->position);
-        base_matrix = glm::rotate(
-          base_matrix, instance->rotation->x, glm::vec3{1.0, 0.0, 0.0});
-        base_matrix = glm::rotate(
-          base_matrix, instance->rotation->y, glm::vec3{0.0, 1.0, 0.0});
-        base_matrix = glm::rotate(
-          base_matrix, instance->rotation->z, glm::vec3{0.0, 0.0, 1.0});
+        glm::mat4 translation_matrix{1.0f};
+        translation_matrix = glm::translate(
+	  translation_matrix, *instance->position);
+	glm::mat4 rotation_matrix{glm::toMat4(*instance->orientation)};
 
 	std::array<VkDescriptorSet, 4> vk_descriptor_sets{
 	  cg_core.vk_light->descriptor_sets_world[image_index],
@@ -320,7 +316,7 @@ GraphicsPipeline3D::draw(
           draw_command_buffer, static_mesh->index_count, 1, 0, 0, 0);
 
 	VK::UDOStaticModel udo_static_model{};
-	udo_static_model.base_matrix = base_matrix;
+	udo_static_model.base_matrix = translation_matrix * rotation_matrix;
 	instance->uniform_buffers[image_index].copy_data(&udo_static_model);
       }
   }
@@ -329,16 +325,11 @@ GraphicsPipeline3D::draw(
     VK::UDOView3D ubo_view_3d{};
 
     // View matrix.
-    ubo_view_3d.view = glm::mat4{1.0f};
-    ubo_view_3d.view = glm::translate(
-      ubo_view_3d.view, *view->camera_position);
-    ubo_view_3d.view = glm::rotate(
-      ubo_view_3d.view, view->camera_rotation->y, glm::vec3{0.0, 1.0, 0.0});
-    ubo_view_3d.view = glm::rotate(
-      ubo_view_3d.view, view->camera_rotation->x, glm::vec3{1.0, 0.0, 0.0});
-    ubo_view_3d.view = glm::rotate(
-      ubo_view_3d.view, view->camera_rotation->z, glm::vec3{0.0, 0.0, 1.0});
-    ubo_view_3d.view = glm::inverse(ubo_view_3d.view);
+    glm::mat4 translation_matrix{1.0f};
+    translation_matrix = glm::translate(
+	  translation_matrix, *view->camera_position);
+    glm::mat4 rotation_matrix{glm::toMat4(*view->camera_orientation)};
+    ubo_view_3d.view = glm::inverse(translation_matrix * rotation_matrix);
 
     // Projection matrix.
     ubo_view_3d.proj = glm::perspective(
diff --git a/src/vk/graphics_pipeline_3d_skeletal.cpp b/src/vk/graphics_pipeline_3d_skeletal.cpp
index c4dabdf..364b502 100644
--- a/src/vk/graphics_pipeline_3d_skeletal.cpp
+++ b/src/vk/graphics_pipeline_3d_skeletal.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright 2022-2023 Frederico de Oliveira Linhares
+ * Copyright 2022-2024 Frederico de Oliveira Linhares
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -305,14 +305,10 @@ GraphicsPipeline3DSkeletal::draw(
 
       for(auto &instance: instances)
       { // Object matrix.
-        glm::mat4 base_matrix{1.0f};
-        base_matrix = glm::translate(base_matrix, *instance->position);
-        base_matrix = glm::rotate(
-          base_matrix, instance->rotation->x, glm::vec3{1.0, 0.0, 0.0});
-        base_matrix = glm::rotate(
-          base_matrix, instance->rotation->y, glm::vec3{0.0, 1.0, 0.0});
-        base_matrix = glm::rotate(
-          base_matrix, instance->rotation->z, glm::vec3{0.0, 0.0, 1.0});
+        glm::mat4 translation_matrix{1.0f};
+        translation_matrix = glm::translate(
+	  translation_matrix, *instance->position);
+	glm::mat4 rotation_matrix{glm::toMat4(*instance->orientation)};
 
 	std::array<VkDescriptorSet, 4> vk_descriptor_sets{
 	  cg_core.vk_light->descriptor_sets_world[image_index],
@@ -330,7 +326,7 @@ GraphicsPipeline3DSkeletal::draw(
 
 	VK::UDOSkeletalModel udo_skeletal_model{};
 	instance->tick(cg_core.delta_time);
-	udo_skeletal_model.base_matrix = base_matrix;
+	udo_skeletal_model.base_matrix = translation_matrix * rotation_matrix;
 	std::copy(instance->bone_transforms.begin(),
 		  instance->bone_transforms.end(),
 		  udo_skeletal_model.bone_matrices);
@@ -342,16 +338,11 @@ GraphicsPipeline3DSkeletal::draw(
     VK::UDOView3D ubo_view_3d{};
 
     // View matrix.
-    ubo_view_3d.view = glm::mat4{1.0f};
-    ubo_view_3d.view = glm::translate(
-      ubo_view_3d.view, *view->camera_position);
-    ubo_view_3d.view = glm::rotate(
-      ubo_view_3d.view, view->camera_rotation->y, glm::vec3{0.0, 1.0, 0.0});
-    ubo_view_3d.view = glm::rotate(
-      ubo_view_3d.view, view->camera_rotation->x, glm::vec3{1.0, 0.0, 0.0});
-    ubo_view_3d.view = glm::rotate(
-      ubo_view_3d.view, view->camera_rotation->z, glm::vec3{0.0, 0.0, 1.0});
-    ubo_view_3d.view = glm::inverse(ubo_view_3d.view);
+    glm::mat4 translation_matrix{1.0f};
+    translation_matrix = glm::translate(
+	  translation_matrix, *view->camera_position);
+    glm::mat4 rotation_matrix{glm::toMat4(*view->camera_orientation)};
+    ubo_view_3d.view = glm::inverse(translation_matrix * rotation_matrix);
 
     // Projection matrix.
     ubo_view_3d.proj = glm::perspective(
diff --git a/src/vk/skeletal_model.cpp b/src/vk/skeletal_model.cpp
index b2d5fc3..d2739c4 100644
--- a/src/vk/skeletal_model.cpp
+++ b/src/vk/skeletal_model.cpp
@@ -148,11 +148,11 @@ namespace VK
 SkeletalModel::SkeletalModel(
   std::shared_ptr<SkeletalMesh> skeletal_mesh,
   std::shared_ptr<Texture> texture, std::shared_ptr<glm::vec3> position,
-  std::shared_ptr<glm::vec3> rotation):
+  std::shared_ptr<glm::quat> orientation):
   skeletal_mesh{skeletal_mesh},
   texture{texture},
   position{position},
-  rotation{rotation},
+  orientation{orientation},
   animation_index{0},
   animation_time{0.0f},
   bone_transforms(SKELETAL_MESH_MAX_NUM_OF_BONES)
diff --git a/src/vk/skeletal_model.hpp b/src/vk/skeletal_model.hpp
index 1043e0f..db54ac9 100644
--- a/src/vk/skeletal_model.hpp
+++ b/src/vk/skeletal_model.hpp
@@ -31,7 +31,8 @@ struct SkeletalModel
   std::shared_ptr<SkeletalMesh> skeletal_mesh;
   std::shared_ptr<Texture> texture;
   std::vector<UniformBuffer> uniform_buffers;
-  std::shared_ptr<glm::vec3> position, rotation;
+  std::shared_ptr<glm::vec3> position;
+  std::shared_ptr<glm::quat> orientation;
   int animation_index;
   float animation_time;
   std::vector<glm::mat4> bone_transforms;
@@ -42,7 +43,7 @@ struct SkeletalModel
   SkeletalModel(
     std::shared_ptr<SkeletalMesh> skeletal_mesh,
     std::shared_ptr<Texture> texture, std::shared_ptr<glm::vec3> position,
-    std::shared_ptr<glm::vec3> rotation);
+    std::shared_ptr<glm::quat> orientation);
   ~SkeletalModel();
 
   void
diff --git a/src/vk/sprite_to_draw.hpp b/src/vk/sprite_to_draw.hpp
index 3bd6af3..84effff 100644
--- a/src/vk/sprite_to_draw.hpp
+++ b/src/vk/sprite_to_draw.hpp
@@ -14,8 +14,8 @@
  * limitations under the License.
  */
 
-#ifndef CANDY_GEAR_VK_SPRITES_TO_DRAW_2D_H
-#define CANDY_GEAR_VK_SPRITES_TO_DRAW_2D_H 1
+#ifndef CANDY_GEAR_VK_SPRITES_TO_DRAW_H
+#define CANDY_GEAR_VK_SPRITES_TO_DRAW_H 1
 
 #include <memory>
 
@@ -38,7 +38,7 @@ namespace VK
   operator<(const SpriteToDraw &a, const SpriteToDraw &b);
 
   bool
-  operator<(const SpriteToDraw &a, const SpriteToDraw &b);
+  operator>(const SpriteToDraw &a, const SpriteToDraw &b);
 }
 
-#endif /* CANDY_GEAR_VK_SPRITES_TO_DRAW_2D_H */
+#endif /* CANDY_GEAR_VK_SPRITES_TO_DRAW_H */
diff --git a/src/vk/static_model.cpp b/src/vk/static_model.cpp
index a89d79a..ef53155 100644
--- a/src/vk/static_model.cpp
+++ b/src/vk/static_model.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright 2022-2023 Frederico de Oliveira Linhares
+ * Copyright 2022-2024 Frederico de Oliveira Linhares
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -148,11 +148,11 @@ namespace VK
 StaticModel::StaticModel(
   std::shared_ptr<StaticMesh> static_mesh,
   std::shared_ptr<Texture> texture, std::shared_ptr<glm::vec3> position,
-  std::shared_ptr<glm::vec3> rotation):
+  std::shared_ptr<glm::quat> orientation):
   static_mesh{static_mesh},
   texture{texture},
   position{position},
-  rotation{rotation}
+  orientation{orientation}
 {
   loader.execute(this);
 }
diff --git a/src/vk/static_model.hpp b/src/vk/static_model.hpp
index 08c68c8..72f4fac 100644
--- a/src/vk/static_model.hpp
+++ b/src/vk/static_model.hpp
@@ -31,7 +31,8 @@ struct StaticModel
   std::shared_ptr<StaticMesh> static_mesh;
   std::shared_ptr<Texture> texture;
   std::vector<UniformBuffer> uniform_buffers;
-  std::shared_ptr<glm::vec3> position, rotation;
+  std::shared_ptr<glm::vec3> position;
+  std::shared_ptr<glm::quat> orientation;
 
   VkDescriptorPool descriptor_pool;
   std::vector<VkDescriptorSet> descriptor_sets;
@@ -39,7 +40,7 @@ struct StaticModel
   StaticModel(
     std::shared_ptr<StaticMesh> static_mesh,
     std::shared_ptr<Texture> texture, std::shared_ptr<glm::vec3> position,
-    std::shared_ptr<glm::vec3> rotation);
+    std::shared_ptr<glm::quat> orientation);
   ~StaticModel();
 };
 
diff --git a/src/vk/view_3d.cpp b/src/vk/view_3d.cpp
index f9cfe5a..273874c 100644
--- a/src/vk/view_3d.cpp
+++ b/src/vk/view_3d.cpp
@@ -120,7 +120,7 @@ View3D::View3D(
   View2D{region, projection_width, projection_height},
   field_of_view{45.0f},
   camera_position{std::make_shared<glm::vec3>(0.0f, 0.0f, 0.0f)},
-  camera_rotation{std::make_shared<glm::vec3>(0.0f, 0.0f, 0.0f)}
+  camera_orientation{std::make_shared<glm::quat>(0.0f, 0.0f, 0.0f, 0.0f)}
 {
   ::loader.execute(this);
 }
diff --git a/src/vk/view_3d.hpp b/src/vk/view_3d.hpp
index 52146df..c5a803b 100644
--- a/src/vk/view_3d.hpp
+++ b/src/vk/view_3d.hpp
@@ -31,7 +31,7 @@ struct View3D: public View2D
   std::vector<VkDescriptorSet> descriptor_sets_3d;
 
   std::shared_ptr<glm::vec3> camera_position;
-  std::shared_ptr<glm::vec3> camera_rotation;
+  std::shared_ptr<glm::quat> camera_orientation;
 
   View3D(glm::vec4 region, float projection_width, float projection_height);
   ~View3D();
diff --git a/test/src/mode/demo.rb b/test/src/mode/demo.rb
index 67fa805..a821d65 100644
--- a/test/src/mode/demo.rb
+++ b/test/src/mode/demo.rb
@@ -49,21 +49,21 @@ module Mode
         CandyGear::Vector3D.new(0.0, 0.0, 5.0),
         CandyGear::Vector3D.new(0.0, 0.0, -5.0)
       ];
-      @instances_rotation = CandyGear::Rotation3D.new(0.0, 0.0, 0.0);
+      @instances_orientation = CandyGear::Orientation3D.new(0.0, 0.0, 0.0);
 
       @instances = [
         CandyGear::StaticModel.new(
-          mesh, texture, instance_positions[0], @instances_rotation),
+          mesh, texture, instance_positions[0], @instances_orientation),
         CandyGear::StaticModel.new(
-          mesh, texture, instance_positions[1], @instances_rotation),
+          mesh, texture, instance_positions[1], @instances_orientation),
         CandyGear::StaticModel.new(
-          mesh, texture, instance_positions[2], @instances_rotation),
+          mesh, texture, instance_positions[2], @instances_orientation),
         CandyGear::StaticModel.new(
-          mesh, texture, instance_positions[3], @instances_rotation),
+          mesh, texture, instance_positions[3], @instances_orientation),
         CandyGear::StaticModel.new(
-          mesh, texture, instance_positions[4], @instances_rotation),
+          mesh, texture, instance_positions[4], @instances_orientation),
         CandyGear::StaticModel.new(
-          mesh, texture, instance_positions[5], @instances_rotation)
+          mesh, texture, instance_positions[5], @instances_orientation)
       ]
 
       sprite_3d_position = CandyGear::Vector3D.new(0.0, 0.0, 0.0);
@@ -71,7 +71,7 @@ module Mode
         @sprite, sprite_3d_position, 1.0, 1.0);
 
       @camera_position = CandyGear::Vector3D.new(0.0, 0.0, 0.0);
-      @camera_rotation = CandyGear::Rotation3D.new(0.0, 0.0, 0.0);
+      @camera_orientation = CandyGear::Orientation3D.new(0.0, 0.0, 0.0);
 
       color = CandyGear::Vector3D.new(0.12, 0.12, 0.18);
       @view1 = CandyGear::View2D.new(
@@ -81,35 +81,35 @@ module Mode
       CandyGear.views = [@view1, @view2];
 
       @view2.camera_position = @camera_position;
-      @view2.camera_rotation = @camera_rotation;
+      @view2.camera_orientation = @camera_orientation;
     end
 
     def key_down(key)
       case key
       when CandyGear::Key::I
-        @camera_rotation.rotate(-CAMERA_ROTATION_SPEED, 0.0);
+        @camera_orientation.rotate(-CAMERA_ROTATION_SPEED, 0.0, 0.0);
       when CandyGear::Key::K
-        @camera_rotation.rotate(CAMERA_ROTATION_SPEED, 0.0);
+        @camera_orientation.rotate(CAMERA_ROTATION_SPEED, 0.0, 0.0);
       when CandyGear::Key::J
-        @camera_rotation.rotate(0.0, CAMERA_ROTATION_SPEED);
+        @camera_orientation.rotate(0.0, CAMERA_ROTATION_SPEED, 0.0);
       when CandyGear::Key::L
-        @camera_rotation.rotate(0.0, -CAMERA_ROTATION_SPEED);
+        @camera_orientation.rotate(0.0, -CAMERA_ROTATION_SPEED, 0.0);
       when CandyGear::Key::E
         @camera_position.translate(
           CandyGear::Vector3D.new(
-            0.0, 0.0, -TRANSLATION_SPEED), @camera_rotation);
+            0.0, 0.0, -TRANSLATION_SPEED), @camera_orientation);
       when CandyGear::Key::D
         @camera_position.translate(
           CandyGear::Vector3D.new(
-            0.0, 0.0, TRANSLATION_SPEED), @camera_rotation);
+            0.0, 0.0, TRANSLATION_SPEED), @camera_orientation);
       when CandyGear::Key::S
         @camera_position.translate(
           CandyGear::Vector3D.new(
-            -TRANSLATION_SPEED, 0.0, 0.0), @camera_rotation);
+            -TRANSLATION_SPEED, 0.0, 0.0), @camera_orientation);
       when CandyGear::Key::F
         @camera_position.translate(
           CandyGear::Vector3D.new(
-            TRANSLATION_SPEED, 0.0, 0.0), @camera_rotation);
+            TRANSLATION_SPEED, 0.0, 0.0), @camera_orientation);
       end
     end
 
@@ -126,7 +126,7 @@ module Mode
       @english_text_sprite.draw(
         @view1, @english_text_position.x, @english_text_position.y,
         @english_text_position.w, @english_text_position.h);
-      @instances_rotation.rotate(0.0, BOX_ROTATION_SPEED);
+      @instances_orientation.rotate(0.0, BOX_ROTATION_SPEED, 0.0);
       @rectangle.draw_rectangle(@view1, @color);
       @instances.each {_1.draw()};
       @sprite_3d.draw();
-- 
cgit v1.2.3