diff --git a/CMakeLists.txt b/CMakeLists.txt index 7f9b8e5..a80f56a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,8 @@ cmake_minimum_required(VERSION 3.10) project(OpenGLUtility) +# set(CMAKE_CXX_STANDARD 17) + set(build_examples ON CACHE BOOL "Build examples") set(build_documentation ON CACHE BOOL "Generate documentation") @@ -26,7 +28,7 @@ add_library(openglu SHARED ${include_files} ${source_files} "cpp.hint" -) + "src/transformable.cpp") target_compile_definitions(openglu PRIVATE OGLU_BUILD_DLL) diff --git a/examples/debug/main.cpp b/examples/debug/main.cpp index 411924a..94c3625 100644 --- a/examples/debug/main.cpp +++ b/examples/debug/main.cpp @@ -1,8 +1,12 @@ #include #include "openglu.hpp" +#include "transformable.hpp" #include #include +#include +#include +#include void framebuffer_size_callback(GLFWwindow* window, int width, int height) { @@ -79,16 +83,32 @@ int main(int argc, char** argv) oglu::Texture crate = oglu::MakeTexture("assets/crate.jpg"); oglu::Texture opengl = oglu::MakeTexture("assets/opengl.png"); + oglu::Transformable model; + //model.SetRotation(-55.0f, 0.0f, 0.0f); + + glm::mat4 view = glm::mat4(1.0f); + view = glm::translate(view, glm::vec3(0.0f, 0.0f, -3.0f)); + + glm::mat4 projection; + projection = glm::perspective(glm::radians(45.f), 1.0f, 0.1f, 100.0f); + + glm::mat4 test = glm::make_mat4(model.GetMatrix()); + // Window loop while (!glfwWindowShouldClose(window)) { processInput(window); - oglu::ClearScreen(GL_COLOR_BUFFER_BIT, oglu::Color(0.29f, 0.13f, 0.23f)); + oglu::ClearScreen(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, oglu::Color(0.29f, 0.13f, 0.23f)); + + model.Rotate(6.0f, 0.0f, 0.0f); shader->Use(); shader->SetUniform("texture1", crate, 0); shader->SetUniform("texture2", opengl, 1); + shader->SetUniformMatrix4fv("model", 1, GL_FALSE, model.GetMatrix()); + shader->SetUniformMatrix4fv("view", 1, GL_FALSE, glm::value_ptr(view)); + shader->SetUniformMatrix4fv("projection", 1, GL_FALSE, glm::value_ptr(projection)); square->BindAndDraw(); diff --git a/examples/debug/shaders/vertexShader.vert b/examples/debug/shaders/vertexShader.vert index 208257b..8b2b3d3 100644 --- a/examples/debug/shaders/vertexShader.vert +++ b/examples/debug/shaders/vertexShader.vert @@ -6,9 +6,13 @@ layout (location = 2) in vec2 aUV; out vec3 oCol; out vec2 oUV; +uniform mat4 model; +uniform mat4 view; +uniform mat4 projection; + void main() { oCol = aCol; oUV = aUV; - gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0); + gl_Position = projection * view * model * vec4(aPos, 1.0); } \ No newline at end of file diff --git a/include/transformable.hpp b/include/transformable.hpp new file mode 100644 index 0000000..c02f4ac --- /dev/null +++ b/include/transformable.hpp @@ -0,0 +1,248 @@ +/*****************************************************************//** + * \file transformable.hpp + * \brief Contains utility for anything that has a geometry + * + * \author Lauchmelder + * \date January 2021 + *********************************************************************/ + +#ifndef TRANSFORMABLE_HPP +#define TRANSFORMABLE_HPP + +#include + +namespace oglu +{ + /** + * @brief Defines position, rotation and scale. + * + * This class wraps a 4x4 transformation matrix and provides useful + * helper functions to perform translations, rotations and scalings. + */ + class OGLU_API Transformable + { + public: + /** + * @brief Create new identity transformation. + * + * An identity transformation is a matrix with all 1's on the diagonal. + * When applied to a vector it leaves it unchanged. + */ + Transformable(); + + /** + * @brief Copies another transformation. + * + * The transformation matrix of @p other is copied. + */ + Transformable(const Transformable& other); + ~Transformable(); + + /** + * @brief Sets the position. + * + * This sets an absolute position, means that it resets any previous + * translations. + * + * @param[in] x New x position + * @param[in] y New y position + * @param[in] z New z position + */ + void SetPosition(float x, float y, float z); + + /** + * @brief Sets the position. + * + * This sets an absolute position, means that it resets any previous + * translations. + * + * @param[in] position An array of floats containing three scalars + */ + void SetPosition(const float* translation); + + /** + * @brief Sets the rotation. + * + * This sets an absolute rotation, means that it resets any previous + * rotations. + * + * @param[in] rotX New rotation around x axis + * @param[in] rotY New rotation around y axis + * @param[in] rotZ New rotation around z axis + */ + void SetRotation(float rotX, float rotY, float rotZ); + + /** + * @brief Sets the rotation. + * + * This sets an absolute rotation, means that it resets any previous + * rotations. + * + * @param[in] rotation An array of floats containing three scalars + */ + void SetRotation(const float* rotation); + + /** + * @brief Sets the rotation. + * + * This sets an absolute rotation, means that it resets any previous + * rotations. + * + * @param[in] angle The angle to rotate by + * @param[in] xAxis The x component of the rotation axis + * @param[in] yAxis The y component of the rotation axis + * @param[in] zAxis The z component of the rotation axis + */ + void SetRotation(float angle, float xAxis, float yAxis, float zAxis); + + /** + * @brief Sets the rotation. + * + * This sets an absolute rotation, means that it resets any previous + * rotations. + * + * @param[in] angle The angle to rotate by + * @param[in] axis An array of floats containing the three rotation axis components + */ + void SetRotation(float angle, const float* axis); + + /** + * @brief Sets the scaling. + * + * This sets an absolute scaling, means that it resets any previous + * scaling. + * + * @param[in] scaleX The scaling in x direction + * @param[in] scaleY The scaling in y direction + * @param[in] scaleZ The scaling in z direction + */ + void SetScale(float scaleX, float scaleY, float scaleZ); + + /** + * @brief Sets the scaling. + * + * This sets an absolute scaling, means that it resets any previous + * scaling. + * + * @param[in] scale An array of floats containing three scalars + */ + void SetScale(const float* scale); + + /** + * @brief Performs a translation. + * + * This function applies a translation to the object, it operates + * operates on the current position. + * + * @param[in] x Offset along the x axis + * @param[in] y Offset along the y axis + * @param[in] z Offset along the z axis + */ + void Move(float x, float y, float z); + + /** + * @brief Performs a translation. + * + * This function applies a translation to the object, it operates + * operates on the current position. + * + * @param[in] position An array of floats containing the offset values + */ + void Move(const float* translation); + + /** + * @brief Performs a rotation. + * + * This function applies a rotation to the object, it operates + * operates on the current rotation. + * + * @param[in] rotX Rotation around the x axis + * @param[in] rotY Rotation around the y axis + * @param[in] rotZ Rotation around the z axis + */ + void Rotate(float rotX, float rotY, float rotZ); + + /** + * @brief Performs a rotation. + * + * This function applies a rotation to the object, it operates + * operates on the current rotation. + * + * @param[in] rotation An array of floats containing the rotation values + */ + void Rotate(const float* rotation); + + /** + * @brief Performs a rotation. + * + * This function applies a rotation to the object, it operates + * operates on the current rotation. + * + * @param[in] angle The angle to rotate by + * @param[in] xAxis x component of the rotation axis + * @param[in] yAxis y component of the rotation axis + * @param[in] zAxis z component of the rotation axis + */ + void Rotate(float angle, float xAxis, float yAxis, float zAxis); + + /** + * @brief Performs a rotation. + * + * This function applies a rotation to the object, it operates + * operates on the current rotation. + * + * @param[in] angle The angle to rotate by + * @param[in] An array of floats containing the components of the rotation axis + */ + void Rotate(float angle, const float* axis); + + /** + * @brief Performs scaling. + * + * This function applies scaling to the object, it operates + * operates on the current scaling. + * + * @param[in] scaleX Scaling in x direction + * @param[in] scaleX Scaling in y direction + * @param[in] scaleX Scaling in z direction + */ + void Scale(float scaleX, float scaleY, float scaleZ); + + /** + * @brief Performs scaling. + * + * This function applies scaling to the object, it operates + * operates on the current scaling. + * + * @param[in] scale An array of floats containing three scaling values + */ + void Scale(const float* scale); + + /** + * @brief Returns a transformation matrix. + * + * Internally, this function multiplies the translation, rotation, + * and scaling matrices into one transformation matrix which is + * then returned. + * + * This multiplication is only performed when a change occured to one + * of those matrices (so when there was a translation, rotation or scaling). + * So it is okay to call this function multiple times without huge performance + * loss, as long as no transformations occur inbetween. + * + * @return An array of 16 floats representing the transformation matrix + */ + const float* GetMatrix(); + + private: + // TODO: Separate translation, rotation and scaling matrices. + // Combine them only when the user wants the transformation matrix + float* translation; ///< Translation matrix + float* rotation; ///< Rotation matrix + float* scaling; ///< Scaling matrix + + bool calculateMatrix; ///< Wether GetMatrix() needs to re-calculate the transformation matrix + }; +} + +#endif \ No newline at end of file diff --git a/src/transformable.cpp b/src/transformable.cpp new file mode 100644 index 0000000..6025583 --- /dev/null +++ b/src/transformable.cpp @@ -0,0 +1,267 @@ +#include "transformable.hpp" + +#include + +#include +#include +#include + +namespace oglu +{ + oglu::Transformable::Transformable() : + translation(new float[16]), rotation(new float[16]), scaling(new float[16]), calculateMatrix(false) + { + glm::mat4 identity(1.0f); + memcpy( + translation, + glm::value_ptr(identity), + 16 * sizeof(float) + ); + memcpy( + rotation, + glm::value_ptr(identity), + 16 * sizeof(float) + ); + memcpy( + scaling, + glm::value_ptr(identity), + 16 * sizeof(float) + ); + } + + Transformable::Transformable(const Transformable& other) : + translation(new float[16]), rotation(new float[16]), scaling(new float[16]), calculateMatrix(true) + { + memcpy( + this->translation, + other.translation, + 16 * sizeof(float) + ); + + memcpy( + this->rotation, + other.rotation, + 16 * sizeof(float) + ); + + memcpy( + this->scaling, + other.scaling, + 16 * sizeof(float) + ); + } + + Transformable::~Transformable() + { + delete[] scaling; + delete[] rotation; + delete[] translation; + } + + void Transformable::SetPosition(float x, float y, float z) + { + memcpy( + this->translation, + glm::value_ptr(glm::translate(glm::mat4(1.0f), glm::vec3(x, y, z))), + 16 * sizeof(float) + ); + calculateMatrix = true; + } + + void Transformable::SetPosition(const float* translation) + { + memcpy( + this->translation, + glm::value_ptr(glm::translate(glm::mat4(1.0f), glm::make_vec3(translation))), + 16 * sizeof(float) + ); + calculateMatrix = true; + } + + void Transformable::SetRotation(float rotX, float rotY, float rotZ) + { + // TODO: Using rotation matrices is stupid. Eventually this could (should) be done with quaternions + // For now we'll just risk gimbal locking the model + memcpy( + this->rotation, + glm::value_ptr( + glm::rotate( + glm::rotate( + glm::rotate( + glm::mat4(1.0f), glm::radians(rotX), glm::vec3(1.0f, 0.0f, 0.0f) + ), glm::radians(rotY), glm::vec3(0.0f, 1.0f, 0.0f) + ), glm::radians(rotZ), glm::vec3(0.0f, 0.0f, 1.0f) + ) + ), + 16 * sizeof(float) + ); + calculateMatrix = true; + } + + void Transformable::SetRotation(const float* rotation) + { + memcpy( + this->rotation, + glm::value_ptr( + glm::rotate( + glm::rotate( + glm::rotate( + glm::mat4(1.0f), glm::radians(rotation[0]), glm::vec3(1.0f, 0.0f, 0.0f) + ), glm::radians(rotation[1]), glm::vec3(0.0f, 1.0f, 0.0f) + ), glm::radians(rotation[2]), glm::vec3(0.0f, 0.0f, 1.0f) + ) + ), + 16 * sizeof(float) + ); + calculateMatrix = true; + } + + void Transformable::SetRotation(float angle, float xAxis, float yAxis, float zAxis) + { + memcpy( + this->rotation, + glm::value_ptr(glm::rotate(glm::mat4(1.0f), glm::radians(angle), glm::vec3(xAxis, yAxis, zAxis))), + 16 * sizeof(float) + ); + calculateMatrix = true; + } + + void Transformable::SetRotation(float angle, const float* axis) + { + memcpy( + this->rotation, + glm::value_ptr(glm::rotate(glm::mat4(1.0f), glm::radians(angle), glm::make_vec3(axis))), + 16 * sizeof(float) + ); + calculateMatrix = true; + } + + void Transformable::SetScale(float scaleX, float scaleY, float scaleZ) + { + memcpy( + this->scaling, + glm::value_ptr(glm::scale(glm::mat4(1.0f), glm::vec3(scaleX, scaleY, scaleZ))), + 16 * sizeof(float) + ); + calculateMatrix = true; + } + + void Transformable::SetScale(const float* scale) + { + memcpy( + this->scaling, + glm::value_ptr(glm::scale(glm::mat4(1.0f), glm::make_vec3(scale))), + 16 * sizeof(float) + ); + calculateMatrix = true; + } + + void Transformable::Move(float x, float y, float z) + { + memcpy( + this->translation, + glm::value_ptr(glm::translate(glm::make_mat4(this->translation), glm::vec3(x, y, z))), + 16 * sizeof(float) + ); + calculateMatrix = true; + } + + void Transformable::Move(const float* translation) + { + memcpy( + this->translation, + glm::value_ptr(glm::translate(glm::make_mat4(this->translation), glm::make_vec3(translation))), + 16 * sizeof(float) + ); + calculateMatrix = true; + } + + void Transformable::Rotate(float rotX, float rotY, float rotZ) + { + memcpy( + this->rotation, + glm::value_ptr( + glm::rotate( + glm::rotate( + glm::rotate( + glm::make_mat4(this->rotation), glm::radians(rotX), glm::vec3(1.0f, 0.0f, 0.0f) + ), glm::radians(rotY), glm::vec3(0.0f, 1.0f, 0.0f) + ), glm::radians(rotZ), glm::vec3(0.0f, 0.0f, 1.0f) + ) + ), + 16 * sizeof(float) + ); + calculateMatrix = true; + } + + void Transformable::Rotate(const float* rotation) + { + memcpy( + this->rotation, + glm::value_ptr( + glm::rotate( + glm::rotate( + glm::rotate( + glm::make_mat4(this->rotation), glm::radians(rotation[0]), glm::vec3(1.0f, 0.0f, 0.0f) + ), glm::radians(rotation[1]), glm::vec3(0.0f, 1.0f, 0.0f) + ), glm::radians(rotation[2]), glm::vec3(0.0f, 0.0f, 1.0f) + ) + ), + 16 * sizeof(float) + ); + calculateMatrix = true; + } + + void Transformable::Rotate(float angle, float xAxis, float yAxis, float zAxis) + { + memcpy( + this->rotation, + glm::value_ptr(glm::rotate(glm::make_mat4(this->rotation), glm::radians(angle), glm::vec3(xAxis, yAxis, zAxis))), + 16 * sizeof(float) + ); + calculateMatrix = true; + } + + void Transformable::Rotate(float angle, const float* axis) + { + memcpy( + this->rotation, + glm::value_ptr(glm::rotate(glm::make_mat4(this->rotation), glm::radians(angle), glm::make_vec3(axis))), + 16 * sizeof(float) + ); + calculateMatrix = true; + } + + void Transformable::Scale(float scaleX, float scaleY, float scaleZ) + { + memcpy( + this->scaling, + glm::value_ptr(glm::scale(glm::make_mat4(this->scaling), glm::vec3(scaleX, scaleY, scaleZ))), + 16 * sizeof(float) + ); + calculateMatrix = true; + } + + void Transformable::Scale(const float* scale) + { + memcpy( + this->scaling, + glm::value_ptr(glm::translate(glm::make_mat4(this->scaling), glm::make_vec3(scale))), + 16 * sizeof(float) + ); + calculateMatrix = true; + } + + const float* Transformable::GetMatrix() + { + static glm::mat4 transformation(1.0f); + + if (calculateMatrix) + { + transformation = glm::make_mat4(translation) * glm::make_mat4(rotation) * glm::make_mat4(scaling); + calculateMatrix = false; + } + + return glm::value_ptr(transformation); + } +}