https://github.com/nurunue/3dcg-learnings

 

//! @file transformations.hpp
//! @brief OpenGLの関数の実装例
//! @author suzulang.com
//! @date 令和三年六月二十五日

#pragma once

#include "vmmath.hpp"

namespace szl {
  namespace cg3 {

    //! @brief 回転行列作成
    //! @param [out] m 結果を格納する要素数16の配列
    //! @param [in] angle_degree 回転角を度で指定
    //! @param [in] axis_x 回転軸のX成分
    //! @param [in] axis_y 回転軸のY成分
    //! @param [in] axis_z 回転軸のZ成分
    //! @return なし
    template<typename Matrix>
    void rotate_matrix(
      Matrix& m,
      const double angle_degree,
      const double axis_x,
      const double axis_y,
      const double axis_z) {

      using elem_t = typename std::remove_reference<decltype(m[0])>::type;

      double x = axis_x;
      double y = axis_y;
      double z = axis_z;

      //len(x y z) != 0ならnormalizeする
      double len = sqrt(x * x + y * y + z * z);

      if (maths::is_error(len) == false) {
        x = x / len;
        y = y / len;
        z = z / len;
      }
      else {
        mathm::load_identity4(m);
      }


      auto angle_rad = maths::to_radian(angle_degree);

      auto c = std::cos(angle_rad);
      auto s = std::sin(angle_rad);

      m[0] = static_cast<elem_t>(x * x * (1 - c) + c);
      m[1] = static_cast<elem_t>(y * x * (1 - c) + z * s);
      m[2] = static_cast<elem_t>(x * z * (1 - c) - y * s);
      m[3] = static_cast<elem_t>(0.0);

      m[4] = static_cast<elem_t>(x * y * (1 - c) - z * s);
      m[5] = static_cast<elem_t>(y * y * (1 - c) + c);
      m[6] = static_cast<elem_t>(y * z * (1 - c) + x * s);
      m[7] = static_cast<elem_t>(0.0);


      m[8] = static_cast<elem_t>(x * z * (1 - c) + y * s);
      m[9] = static_cast<elem_t>(y * z * (1 - c) - x * s);
      m[10] = static_cast<elem_t>(z * z * (1 - c) + c);
      m[11] = static_cast<elem_t>(0.0);


      m[12] = static_cast<elem_t>(0.0);
      m[13] = static_cast<elem_t>(0.0);
      m[14] = static_cast<elem_t>(0.0);
      m[15] = static_cast<elem_t>(1.0);

    }


    //! @brief 回転行列作成
    //! @param [out] m 結果を格納する要素数16の配列
    //! @param [in] angle_degree 回転角を度で指定
    //! @param [in] axis3 回転軸のX成分
    //! @return なし
    template<typename Matrix, typename Axis, typename ScalarT = double>
    void rotate_matrix(
      Matrix& m,
      const ScalarT angle_degree,
      const Axis& axis3) {

      rotate_matrix(m, angle_degree, axis3[0], axis3[1], axis3[2]);

    }

    //! @brief 移動行列作成
    //! @param [out] m 結果を格納する要素数16の配列
    //! @param [in] x 移動量X成分
    //! @param [in] y 移動量Y成分
    //! @param [in] z 移動量Z成分
    //! @return なし
    template<typename Matrix>
    void translate_matrix(
      Matrix& m,
      const double x,
      const double y,
      const double z
    ) {
      using elem_t = typename std::remove_reference<decltype(m[0])>::type;

      m[0] = static_cast<elem_t>(1.0);
      m[1] = static_cast<elem_t>(0.0);
      m[2] = static_cast<elem_t>(0.0);
      m[3] = static_cast<elem_t>(0.0);

      m[4] = static_cast<elem_t>(0.0);
      m[5] = static_cast<elem_t>(1.0);
      m[6] = static_cast<elem_t>(0.0);
      m[7] = static_cast<elem_t>(0.0);

      m[8] = static_cast<elem_t>(0.0);
      m[9] = static_cast<elem_t>(0.0);
      m[10] = static_cast<elem_t>(1.0);
      m[11] = static_cast<elem_t>(0.0);

      m[12] = static_cast<elem_t>(x);
      m[13] = static_cast<elem_t>(y);
      m[14] = static_cast<elem_t>(z);
      m[15] = static_cast<elem_t>(1.0);

    }

    //! @brief 移動行列作成
    //! @param [out] m 結果を格納する要素数16の配列
    //! @param [in] vec3 移動量
    //! @return なし
    template<typename Matrix, typename VectorT>
    void translate_matrix(
      Matrix& m,
      const VectorT& vec3
    ) {
      translate_matrix(m, vec3[0], vec3[1], vec3[2]);
    }

    //! @brief 拡大・縮小行列作成
    //! @param [out] m 結果を格納する要素数16の配列
    //! @param [in] sx スケーリング量X
    //! @param [in] sy スケーリング量Y
    //! @param [in] sz スケーリング量Z
    //! @return なし
    template<typename Matrix>
    void scale_matrix(
      Matrix& m,
      const double sx,
      const double sy,
      const double sz
    ) {
      using elem_t = typename std::remove_reference<decltype(m[0])>::type;

      m[0] = static_cast<elem_t>(sx);
      m[1] = static_cast<elem_t>(0.0);
      m[2] = static_cast<elem_t>(0.0);
      m[3] = static_cast<elem_t>(0.0);

      m[4] = static_cast<elem_t>(0.0);
      m[5] = static_cast<elem_t>(sy);
      m[6] = static_cast<elem_t>(0.0);
      m[7] = static_cast<elem_t>(0.0);

      m[8] = static_cast<elem_t>(0.0);
      m[9] = static_cast<elem_t>(0.0);
      m[10] = static_cast<elem_t>(sz);
      m[11] = static_cast<elem_t>(0.0);

      m[12] = static_cast<elem_t>(0.0);
      m[13] = static_cast<elem_t>(0.0);
      m[14] = static_cast<elem_t>(0.0);
      m[15] = static_cast<elem_t>(1.0);

    }
    //! @brief 拡大・縮小行列作成
    //! @param [out] m 結果を格納する要素数16の配列
    //! @param [in] vec3 スケーリング量
    //! @return なし
    template<typename Matrix, typename VectorT>
    void scale_matrix(
      Matrix& m,
      const VectorT& vec3
    ) {
      scale_matrix(m, vec3[0], vec3[1], vec3[2]);
    }





    //////////////////////////////////////////////////

    //! @brief 透視投影行列の作成 (視野角で指定)
    //! @param [out] m 結果を格納する要素数16の配列
    //! @param [in] fovy_degree 視野角
    //! @param [in] aspect アスペクト比
    //! @param [in] zNear 一番近いz位置
    //! @param [in] zFar 一番遠いz位置
    //! @return なし
    template<typename Matrix>
    void perspective_matrix(
      Matrix& m,
      double fovy_degree,
      double aspect,
      double zNear,
      double zFar) {

      using elem_t = typename std::remove_reference<decltype(m[0])>::type;


      double fovy_rad = maths::to_radian(fovy_degree);


      double f = maths::cot(fovy_rad / 2.0);

      m[0] = static_cast<elem_t>(f / aspect);
      m[1] = static_cast<elem_t>(0.0);
      m[2] = static_cast<elem_t>(0.0);
      m[3] = static_cast<elem_t>(0.0);

      m[4] = static_cast<elem_t>(0.0);
      m[5] = static_cast<elem_t>(f);
      m[6] = static_cast<elem_t>(0.0);
      m[7] = static_cast<elem_t>(0.0);

      m[8] = static_cast<elem_t>(0.0);
      m[9] = static_cast<elem_t>(0.0);
      m[10] = static_cast<elem_t>((zFar + zNear) / (zNear - zFar));
      m[11] = static_cast<elem_t>(-1.0);

      m[12] = static_cast<elem_t>(0.0);
      m[13] = static_cast<elem_t>(0.0);
      m[14] = static_cast<elem_t>((2 * zFar * zNear) / (zNear - zFar));
      m[15] = static_cast<elem_t>(0.0);

    }

    //! @brief 平行投影行列作成
    //! @param [out] m 結果を格納する要素数16の配列
    //! @param [in] left 左の垂直方向のクリッピング平面の座標
    //! @param [in] right 垂直方向のクリッピング平面の座標
    //! @param [in] bottom 下方向のクリッピング平面の座標
    //! @param [in] top 上部の水平方向のクリッピング計画の座標
    //! @param [in] znear 最も近い深度のクリッピング平面への距離
    //! @param [in] zfar より深い深度のクリッピング平面への距離
    //! @return
    template<typename Matrix>
    void ortho_matrix(
      Matrix& m,
      const double left,
      const double right,
      const double bottom,
      const double top,
      const double znear,
      const double zfar) {

      using elem_t = typename std::remove_reference<decltype(m[0])>::type;

      double tx = -(right + left) / (right - left);
      double ty = -(top + bottom) / (top - bottom);
      double tz = -(zfar + znear) / (zfar - znear);

      //infが出たときは単位行列を返す模様
      if (std::isinf(tx) || std::isinf(ty) || std::isinf(tz)) {
        mathm::load_identity4(m);

        return;
      }



      m[0] = static_cast<elem_t>(2.0 / (right - left));
      m[1] = static_cast<elem_t>(0.0);
      m[2] = static_cast<elem_t>(0.0);
      m[3] = static_cast<elem_t>(0.0);

      m[4] = static_cast<elem_t>(0.0);
      m[5] = static_cast<elem_t>(2.0 / (top - bottom));
      m[6] = static_cast<elem_t>(0.0);
      m[7] = static_cast<elem_t>(0.0);

      m[8] = static_cast<elem_t>(0.0);
      m[9] = static_cast<elem_t>(0.0);
      m[10] = static_cast<elem_t>(-2.0 / (zfar - znear));
      m[11] = static_cast<elem_t>(0.0);

      m[12] = static_cast<elem_t>(tx);
      m[13] = static_cast<elem_t>(ty);
      m[14] = static_cast<elem_t>(tz);
      m[15] = static_cast<elem_t>(1.0);

    }


    //! @brief gluLookAtの自前実装版
    //! @param [out] M 結果の格納先
    //! @param [in] eyeX カメラ位置X
    //! @param [in] eyeY カメラ位置Y
    //! @param [in] eyeZ カメラ位置Z
    //! @param [in] centerX 注視点X
    //! @param [in] centerY 注視点Y
    //! @param [in] centerZ 注視点Z
    //! @param [in] upX 上方向X
    //! @param [in] upY 上方向Y
    //! @param [in] upZ 上方向Z
    //! @return なし
    template<typename Matrix>
    void lookat_matrix(
      Matrix& M,
      double eyeX,
      double eyeY,
      double eyeZ,
      double centerX,
      double centerY,
      double centerZ,
      double upX,
      double upY,
      double upZ)
    {
      double f[3] = {
        centerX - eyeX,
        centerY - eyeY,
        centerZ - eyeZ
      };
      mathv::normalize3(f);

      double UP[3] = { upX,upY,upZ };
      mathv::normalize3(UP);

      double s[3];
      mathv::outer3(s, f, UP);
      mathv::normalize3(s);

      double u[3];
      mathv::outer3(u, s, f);

      double M0[16];
      M0[0] = s[0];
      M0[1] = u[0];
      M0[2] = -f[0];
      M0[3] = 0;

      M0[4] = s[1];
      M0[5] = u[1];
      M0[6] = -f[1];
      M0[7] = 0;

      M0[8] = s[2];
      M0[9] = u[2];
      M0[10] = -f[2];
      M0[11] = 0;

      M0[12] = 0;
      M0[13] = 0;
      M0[14] = 0;
      M0[15] = 1;


      /////////////////////////////
      /////////////////////////////
      double E[16];

      translate_matrix(E, -eyeX, -eyeY, -eyeZ);

      mathm::mult_matrix44(M, M0, E);

      if (mathv::chek_array_nan(M, 16) == true) {
        mathm::load_identity4(M);
      }

    }

    //! @brief gluLookAtの自前実装版
    //! @param [out] M 結果の格納先
    //! @param [in] eye3 カメラ位置
    //! @param [in] center3 注視点
    //! @param [in] up3 上方向
    //! @return なし
    template<typename Matrix, typename VectorE, typename VectorC, typename VectorU>
    void lookat_matrix(
      Matrix& M,
      const VectorE& eye3,
      const VectorC& center3,
      const VectorU& up3
    )
    {
      lookat_matrix(M,
        eye3[0], eye3[1], eye3[2],
        center3[0], center3[1], center3[2],
        up3[0], up3[1], up3[2]
      );
    }

  }
}