polyfox/moon

View on GitHub
modules/system/src/mrb_matrix4.cxx

Summary

Maintainability
Test Coverage
/*
 * Moon Matrix4, a wrapper around glm::mat4
 */
#define GLM_FORCE_RADIANS

#include <mruby.h>
#include <mruby/array.h>
#include <mruby/class.h>
#include <mruby/numeric.h>
#include "moon/matrix4.hxx"
#include "moon/mrb/matrix4.hxx"
#include "moon/mrb/vector1.hxx"
#include "moon/mrb/vector2.hxx"
#include "moon/mrb/vector3.hxx"
#include "moon/mrb/vector4.hxx"

#define math_op_base(__op__, __target_mat4__)                                  \
  const mrb_vtype t = mrb_type(rother);                                        \
  if (t == MRB_TT_DATA) {                                                      \
    const mrb_data_type *dt = DATA_TYPE(rother);                               \
    if (dt == &matrix4_data_type) { /* Matrix4 */                              \
      Moon::Matrix4 *source_mat4 = mmrb_matrix4_ptr(mrb, rother);              \
      *__target_mat4__ __op__ ## = *source_mat4;                               \
    } else if (dt == &vector4_data_type) { /* Vector4 */                       \
      *__target_mat4__ __op__ ## = mmrb_to_vector4(mrb, rother);               \
    }                                                                          \
                                                                               \
  } else if (t == MRB_TT_FIXNUM || t == MRB_TT_FLOAT) { /* Scalar */           \
                                                                               \
    *__target_mat4__ __op__ ## = mrb_to_flo(mrb, rother);                      \
  } else {                                                                     \
    mrb_raisef(mrb, E_TYPE_ERROR,                                              \
               "wrong argument type %s (expected Matrix4, Vector4 or Numeric)",\
               mrb_obj_classname(mrb, rother));                                \
  }

#define math_op_inline(__op__)                                            \
  Moon::Matrix4 *target_mat4;                                             \
  mrb_value rother;                                                       \
  mrb_get_args(mrb, "o", &rother);                                        \
  target_mat4 = mmrb_matrix4_ptr(mrb, self);                              \
  math_op_base(__op__, target_mat4)                                       \
  return self;

#define math_op(__op__)                                                   \
  mrb_value rother;                                                       \
  mrb_get_args(mrb, "o", &rother);                                        \
  mrb_value rtarget = mrb_obj_dup(mrb, self);                             \
  Moon::Matrix4 *target_mat4 = mmrb_matrix4_ptr(mrb, rtarget);            \
  math_op_base(__op__, target_mat4)                                       \
  return rtarget; /* */

static void
matrix4_free(mrb_state *mrb, void *p)
{
  Moon::Matrix4 *matrix4 = (Moon::Matrix4*)p;
  if (matrix4) {
    delete(matrix4);
  }
}

MOON_C_API const struct mrb_data_type matrix4_data_type = { "Moon::Matrix4", matrix4_free };

MOON_C_API Moon::Matrix4
mmrb_to_matrix4(mrb_state *mrb, mrb_value self)
{
  return *mmrb_matrix4_ptr(mrb, self);
}

MOON_C_API mrb_value
mmrb_matrix4_value(mrb_state *mrb, Moon::Matrix4 mat)
{
  mrb_value rsult = mrb_obj_new(mrb, mmrb_get_matrix4_class(mrb), 0, NULL);
  Moon::Matrix4 *trns = mmrb_matrix4_ptr(mrb, rsult);
  *trns = mat;
  return rsult;
}

static void
matrix4_set_m(mrb_state *mrb, Moon::Matrix4 *mat, mrb_int argc, mrb_value *args)
{
  if (argc == 1) {
    mrb_value val = args[0];
    if (mrb_type(val) == MRB_TT_DATA) {
      if (DATA_TYPE(val) == &matrix4_data_type) { /* Matrix4 */
        Moon::Matrix4 *source_mat4;
        source_mat4 = (Moon::Matrix4*)mrb_data_get_ptr(mrb, val, &matrix4_data_type);
        *mat = *source_mat4;
      } else {
        mrb_raisef(mrb, E_TYPE_ERROR,
                   "wrong argument type %s (expected Matrix4)",
                   mrb_obj_classname(mrb, args[0]));
      }
    } else {
      double value = mrb_to_flo(mrb, args[0]);
      *mat = Moon::Matrix4(value);
    }
  } else if (argc == 4) {
    glm::vec4 row1 = mmrb_to_vector4(mrb, args[0]);
    glm::vec4 row2 = mmrb_to_vector4(mrb, args[1]);
    glm::vec4 row3 = mmrb_to_vector4(mrb, args[2]);
    glm::vec4 row4 = mmrb_to_vector4(mrb, args[3]);

    *mat = Moon::Matrix4(row1, row2, row3, row4);
  } else if (argc == 16) {
    *mat = Moon::Matrix4(
      mrb_to_flo(mrb, args[0]),
      mrb_to_flo(mrb, args[1]),
      mrb_to_flo(mrb, args[2]),
      mrb_to_flo(mrb, args[3]),

      mrb_to_flo(mrb, args[4]),
      mrb_to_flo(mrb, args[5]),
      mrb_to_flo(mrb, args[6]),
      mrb_to_flo(mrb, args[7]),

      mrb_to_flo(mrb, args[8]),
      mrb_to_flo(mrb, args[9]),
      mrb_to_flo(mrb, args[10]),
      mrb_to_flo(mrb, args[11]),

      mrb_to_flo(mrb, args[12]),
      mrb_to_flo(mrb, args[13]),
      mrb_to_flo(mrb, args[14]),
      mrb_to_flo(mrb, args[15])
    );
  } else {
    mrb_raisef(mrb, E_ARGUMENT_ERROR,
               "wrong argument count %d (expected 0, 1, 4, or 16)",
               argc);
  }
}

static mrb_value
matrix4_set(mrb_state *mrb, mrb_value self)
{
  mrb_value *args;
  mrb_int argc;
  mrb_get_args(mrb, "*", &args, &argc);
  matrix4_set_m(mrb, mmrb_matrix4_ptr(mrb, self), argc, args);
  return self;
}

static mrb_value
matrix4_initialize(mrb_state *mrb, mrb_value self)
{
  mrb_value *args;
  mrb_int argc;
  mrb_get_args(mrb, "*", &args, &argc);

  Moon::Matrix4 *mat;

  mat = (Moon::Matrix4*)DATA_PTR(self);
  if (mat) {
    matrix4_free(mrb, (void*)mat);
  }
  mat = new Moon::Matrix4();
  mrb_data_init(self, mat, &matrix4_data_type);

  if (argc == 0) {
  } else {
    matrix4_set_m(mrb, mat, argc, args);
  }

  return self;
}

/*
 * @overload initialize_copy(Matrix4 other)
 * @return [self]
 */
static mrb_value
matrix4_initialize_copy(mrb_state *mrb, mrb_value self)
{
  Moon::Matrix4 *source_mat;
  mrb_get_args(mrb, "d", &source_mat, &matrix4_data_type);
  matrix4_free(mrb, DATA_PTR(self));
  mrb_data_init(self, new Moon::Matrix4(*source_mat), &matrix4_data_type);
  return self;
}

/*
 * @return [Array<Object>]
 * @api private
 */
static mrb_value
matrix4_coerce(mrb_state *mrb, mrb_value self)
{
  mrb_value other;
  mrb_get_args(mrb, "o", &other);
  mrb_value argv[2] = { self, other };
  return mrb_ary_new_from_values(mrb, 2, argv);
}

/*
 * @return [Boolean]
 */
static mrb_value
matrix4_eq(mrb_state *mrb, mrb_value self)
{
  mrb_value other;
  mrb_get_args(mrb, "o", &other);
  if (mrb_obj_is_kind_of(mrb, other, mmrb_get_matrix4_class(mrb))) {
    return mrb_bool_value((*mmrb_matrix4_ptr(mrb, self)) == (*mmrb_matrix4_ptr(mrb, other)));
  }
  return mrb_bool_value(false);
}

/*
 * @overload Matrix4#[int]
 *   @return [Vector4]
 * @overload Matrix4#[int, int]
 *   @return [Numeric]
 */
static mrb_value
matrix4_entry_get(mrb_state *mrb, mrb_value self)
{
  mrb_value *vals;
  int len;
  mrb_get_args(mrb, "*", &vals, &len);

  Moon::Matrix4 *mat4;
  mat4 = (Moon::Matrix4*)mrb_data_get_ptr(mrb, self, &matrix4_data_type);

  if (len == 1) {
    mrb_int x;

    mrb_get_args(mrb, "i", &x);

    if (x < 0 || x >= 4) {
      mrb_raisef(mrb, E_INDEX_ERROR,
                 "x %d is out of range (expected 0...4)", x);
      return mrb_nil_value();
    }

    return mmrb_vector4_value(mrb, (*mat4)[x]);
  } else if (len == 2) {
    mrb_int x, y;

    mrb_get_args(mrb, "ii", &x, &y);

    if (x < 0 || x >= 4) {
      mrb_raisef(mrb, E_INDEX_ERROR,
                 "x %d is out of range (expected 0...4)", x);
      return mrb_nil_value();
    }
    if (y < 0 || y >= 4) {
      mrb_raisef(mrb, E_INDEX_ERROR,
                 "y %d is out of range (expected 0...4)", y);
      return mrb_nil_value();
    }

    return mrb_float_value(mrb, (*mat4)[x][y]);
  } else {
    mrb_raisef(mrb, E_ARGUMENT_ERROR,
               "wrong number of arguments (%d for 1, or 2)", len);
  }
  return mrb_nil_value();
}

static mrb_value
matrix4_entry_set(mrb_state *mrb, mrb_value self)
{
  mrb_value *vals;
  int len;
  mrb_get_args(mrb, "*", &vals, &len);

  Moon::Matrix4 *mat4;
  mat4 = (Moon::Matrix4*)mrb_data_get_ptr(mrb, self, &matrix4_data_type);

  if (len == 2) {
    mrb_int x;
    mrb_value rvec4;

    mrb_get_args(mrb, "io", &x, &rvec4);

    if (x < 0 || x >= 4) {
      mrb_raisef(mrb, E_INDEX_ERROR,
                 "x %d is out of range (expected 0...4)", x);
      return mrb_nil_value();
    }

    (*mat4)[x] = mmrb_to_vector4(mrb, rvec4);
  } else if (len == 3) {
    mrb_int x, y;
    mrb_float v;

    mrb_get_args(mrb, "iif", &x, &y, &v);

    if (x < 0 || x >= 4) {
      mrb_raisef(mrb, E_INDEX_ERROR,
                 "x %d is out of range (expected 0...4)", x);
      return mrb_nil_value();
    }
    if (y < 0 || y >= 4) {
      mrb_raisef(mrb, E_INDEX_ERROR,
                 "y %d is out of range (expected 0...4)", y);
      return mrb_nil_value();
    }

    (*mat4)[x][y] = (double)v;
  } else {
    mrb_raisef(mrb, E_ARGUMENT_ERROR,
               "wrong number of arguments (%d for 2, or 3)", len);
  }
  return mrb_nil_value();
}

/* Returns the inverse of the matrix
 *
 * @return [Matrix4]
 */
static mrb_value
matrix4_op_negate(mrb_state *mrb, mrb_value self)
{
  mrb_value dest_mat4 = mrb_obj_dup(mrb, self);

  Moon::Matrix4* dmat4;
  dmat4 = (Moon::Matrix4*)mrb_data_get_ptr(mrb, dest_mat4, &matrix4_data_type);

  Moon::Matrix4* smat4;
  smat4 = (Moon::Matrix4*)mrb_data_get_ptr(mrb, self, &matrix4_data_type);

  *dmat4 = -(*smat4);

  return dest_mat4;
}

/* Returns self
 *
 * @return [Matrix4]
 */
static mrb_value
matrix4_op_identity(mrb_state *mrb, mrb_value self)
{
  return self;
}

/*
 * @return [Matrix4]
 */
static mrb_value
matrix4_op_add(mrb_state *mrb, mrb_value self)
{
  math_op(+)
}

static mrb_value
matrix4_add(mrb_state *mrb, mrb_value self)
{
  math_op_inline(+)
}

/*
 * @return [Matrix4]
 */
static mrb_value
matrix4_op_sub(mrb_state *mrb, mrb_value self)
{
  math_op(-)
}

static mrb_value
matrix4_sub(mrb_state *mrb, mrb_value self)
{
  math_op_inline(-)
}

/*
 * @return [Matrix4]
 */
static mrb_value
matrix4_op_mul(mrb_state *mrb, mrb_value self)
{
  math_op(*)
}

static mrb_value
matrix4_mul(mrb_state *mrb, mrb_value self)
{
  math_op_inline(*)
}

/*
 * @return [Matrix4]
 */
static mrb_value
matrix4_op_div(mrb_state *mrb, mrb_value self)
{
  math_op(/)
};

static mrb_value
matrix4_div(mrb_state *mrb, mrb_value self)
{
  math_op_inline(/)
}

/*
 * @return [Matrix4]
 */
//static mrb_value
//matrix4_op_mod(mrb_state *mrb, mrb_value self)
//{
//  math_op(%)
//}

/**
 * Sets all the matrix components to 1.0f
 *
 * @return [self]
 */
static mrb_value
matrix4_clear(mrb_state *mrb, mrb_value self)
{
  Moon::Matrix4 *mat4 = mmrb_matrix4_ptr(mrb, self);
  *mat4 = Moon::Matrix4(1.0f);
  return self;
}

/*
 * @return [Matrix4]
 */
static Moon::Matrix4
matrix4_translate_m(mrb_state *mrb, mrb_value self)
{
  mrb_value *vals;
  int len;
  mrb_get_args(mrb, "*", &vals, &len);
  Moon::Matrix4 *mat4 = mmrb_matrix4_ptr(mrb, self);
  Moon::Matrix4 target_mat4;
  if (len == 1) {
    glm::vec3 v3 = mmrb_to_vector3(mrb, vals[0]);
    target_mat4 = glm::translate(*mat4, v3);
  } else if (len == 3) {
    target_mat4 = glm::translate(*mat4, glm::vec3(
      mrb_to_flo(mrb, vals[0]),
      mrb_to_flo(mrb, vals[1]),
      mrb_to_flo(mrb, vals[2])));
  } else {
    mrb_raisef(mrb, E_ARGUMENT_ERROR,
               "wrong argument count %d (expected 1 or 3)",
               len);
  }
  return target_mat4;
};

/*
 * @return [Matrix4]
 */
static mrb_value
matrix4_translate(mrb_state *mrb, mrb_value self)
{
  Moon::Matrix4 *target_mat4;
  Moon::Matrix4 mat4 = matrix4_translate_m(mrb, self);
  mrb_value rtarget = mrb_obj_dup(mrb, self);
  target_mat4 = mmrb_matrix4_ptr(mrb, rtarget);
  *target_mat4 = mat4;
  return rtarget;
};

static mrb_value
matrix4_translate_bang(mrb_state *mrb, mrb_value self)
{
  Moon::Matrix4 *target_mat4;
  Moon::Matrix4 mat4 = matrix4_translate_m(mrb, self);
  target_mat4 = mmrb_matrix4_ptr(mrb, self);
  *target_mat4 = mat4;
  return self;
};

/*
 * @return [Matrix4]
 */
static Moon::Matrix4
matrix4_rotate_m(mrb_state *mrb, mrb_value self)
{
  mrb_value *vals;
  int len;
  mrb_get_args(mrb, "*", &vals, &len);
  Moon::Matrix4 *mat4 = mmrb_matrix4_ptr(mrb, self);
  Moon::Matrix4 target_mat4;

  if (len == 2) {
    mrb_float angle = mrb_to_flo(mrb, vals[0]);
    glm::vec3 rotate_v3 = mmrb_to_vector3(mrb, vals[1]);

    target_mat4 = glm::rotate(*mat4, glm::radians((float)angle), rotate_v3);
  } else if (len == 4) {
    target_mat4 = glm::rotate(*mat4,
      glm::radians((float)mrb_to_flo(mrb, vals[0])),
      glm::vec3(mrb_to_flo(mrb, vals[1]),
                mrb_to_flo(mrb, vals[2]),
                mrb_to_flo(mrb, vals[3])));
  } else {
    mrb_raisef(mrb, E_ARGUMENT_ERROR,
               "wrong argument count %d (expected 2 or 4)",
               len);
  }
  return target_mat4;
}

/*
 * @return [Matrix4]
 */
static mrb_value
matrix4_rotate(mrb_state *mrb, mrb_value self)
{
  Moon::Matrix4 *target_mat4;
  Moon::Matrix4 mat4 = matrix4_rotate_m(mrb, self);
  mrb_value rtarget = mrb_obj_dup(mrb, self);
  target_mat4 = mmrb_matrix4_ptr(mrb, rtarget);
  *target_mat4 = mat4;
  return rtarget;
};

static mrb_value
matrix4_rotate_bang(mrb_state *mrb, mrb_value self)
{
  Moon::Matrix4 *target_mat4;
  Moon::Matrix4 mat4 = matrix4_rotate_m(mrb, self);
  target_mat4 = mmrb_matrix4_ptr(mrb, self);
  *target_mat4 = mat4;
  return self;
};

static Moon::Matrix4
matrix4_scale_m(mrb_state *mrb, mrb_value self)
{
  mrb_value *vals;
  int len;
  mrb_get_args(mrb, "*", &vals, &len);

  Moon::Matrix4 *mat4 = mmrb_matrix4_ptr(mrb, self);
  Moon::Matrix4 target_mat4;

  if (len == 1) {
    glm::vec3 v3 = mmrb_to_vector3(mrb, vals[0]);

    target_mat4 = glm::scale(*mat4, v3);
  } else if (len == 3) {
    target_mat4 = glm::scale(*mat4, glm::vec3(
      mrb_to_flo(mrb, vals[0]),
      mrb_to_flo(mrb, vals[1]),
      mrb_to_flo(mrb, vals[2])));
  } else {
    mrb_raisef(mrb, E_ARGUMENT_ERROR,
               "wrong argument count %d (expected 1 or 3)",
               len);
  }
  return target_mat4;
}

static mrb_value
matrix4_scale(mrb_state *mrb, mrb_value self)
{
  Moon::Matrix4 *target_mat4;
  Moon::Matrix4 mat4 = matrix4_scale_m(mrb, self);
  mrb_value rtarget = mrb_obj_dup(mrb, self);
  target_mat4 = mmrb_matrix4_ptr(mrb, rtarget);
  *target_mat4 = mat4;
  return rtarget;
};

static mrb_value
matrix4_scale_bang(mrb_state *mrb, mrb_value self)
{
  Moon::Matrix4 *target_mat4;
  Moon::Matrix4 mat4 = matrix4_scale_m(mrb, self);
  target_mat4 = mmrb_matrix4_ptr(mrb, self);
  *target_mat4 = mat4;
  return self;
};

/* Converts the matrix to an Array of Floats
 * @return [Array<Numeric>]
 */
static mrb_value
matrix4_to_a16(mrb_state *mrb, mrb_value self)
{
  Moon::Matrix4 *mat4;
  mat4 = (Moon::Matrix4*)mrb_data_get_ptr(mrb, self, &matrix4_data_type);

  glm::vec4 row1 = (*mat4)[0];
  glm::vec4 row2 = (*mat4)[1];
  glm::vec4 row3 = (*mat4)[2];
  glm::vec4 row4 = (*mat4)[3];

  mrb_value argv[16] = {
    mrb_float_value(mrb, row1[0]),
    mrb_float_value(mrb, row1[1]),
    mrb_float_value(mrb, row1[2]),
    mrb_float_value(mrb, row1[3]),

    mrb_float_value(mrb, row2[0]),
    mrb_float_value(mrb, row2[1]),
    mrb_float_value(mrb, row2[2]),
    mrb_float_value(mrb, row2[3]),

    mrb_float_value(mrb, row3[0]),
    mrb_float_value(mrb, row3[1]),
    mrb_float_value(mrb, row3[2]),
    mrb_float_value(mrb, row3[3]),

    mrb_float_value(mrb, row4[0]),
    mrb_float_value(mrb, row4[1]),
    mrb_float_value(mrb, row4[2]),
    mrb_float_value(mrb, row4[3])
  };

  return mrb_ary_new_from_values(mrb, 16, argv);
}

/* Converts the matrix to an Array of Vector4s
 * @return [Array<Vector4>]
 */
static mrb_value
matrix4_to_a(mrb_state *mrb, mrb_value self)
{
  Moon::Matrix4 *mat4;
  mat4 = (Moon::Matrix4*)mrb_data_get_ptr(mrb, self, &matrix4_data_type);
  mrb_value argv[4] = { mmrb_vector4_value(mrb, (*mat4)[0]),
                        mmrb_vector4_value(mrb, (*mat4)[1]),
                        mmrb_vector4_value(mrb, (*mat4)[2]),
                        mmrb_vector4_value(mrb, (*mat4)[3]) };
  return mrb_ary_new_from_values(mrb, 4, argv);
}

/*
 * @overload Matrix4[obj]
 * @return [Matrix4]
 */
static mrb_value
matrix4_s_cast(mrb_state *mrb, mrb_value self)
{
  mrb_value *vals;
  int len;
  mrb_get_args(mrb, "*", &vals, &len);

  return mrb_obj_new(mrb, mmrb_get_matrix4_class(mrb), len, vals);
}

/**
 * Creates an Orthographic projection matrix
 *
 * @param [Float] a
 * @param [Float] b
 * @param [Float] c
 * @param [Float] d
 * @param [Float] e
 * @param [Float] f
 * @return [Moon::Matrix4] orthographic matrix
 */
static mrb_value
matrix4_s_ortho(mrb_state *mrb, mrb_value self)
{
  mrb_float a, b, c, d, e, f;
  mrb_get_args(mrb, "ffffff", &a, &b, &c, &d, &e, &f);
  return mmrb_matrix4_value(mrb, glm::ortho(a, b, c, d, e, f));
}

//static mrb_value s_extract(mrb_state *mrb, mrb_value self) {
//  return mrb_nil_value();
//};

MOON_C_API void
mmrb_matrix4_init(mrb_state *mrb)
{
  struct RClass *mod = mrb_define_module(mrb, "Moon");
  struct RClass *matrix4_class = mrb_define_class_under(mrb, mod, "Matrix4", mrb->object_class);
  MRB_SET_INSTANCE_TT(matrix4_class, MRB_TT_DATA);

  mrb_define_method(mrb, matrix4_class, "initialize",      matrix4_initialize,      MRB_ARGS_ANY());
  mrb_define_method(mrb, matrix4_class, "initialize_copy", matrix4_initialize_copy, MRB_ARGS_REQ(1));

  mrb_define_method(mrb, matrix4_class, "coerce",          matrix4_coerce,          MRB_ARGS_REQ(1));

  mrb_define_method(mrb, matrix4_class, "==",              matrix4_eq,              MRB_ARGS_REQ(1));

  mrb_define_method(mrb, matrix4_class, "[]",              matrix4_entry_get,       MRB_ARGS_ARG(1,1));
  mrb_define_method(mrb, matrix4_class, "[]=",             matrix4_entry_set,       MRB_ARGS_ARG(2,1));

  mrb_define_method(mrb, matrix4_class, "-@",              matrix4_op_negate,       MRB_ARGS_NONE());
  mrb_define_method(mrb, matrix4_class, "+@",              matrix4_op_identity,     MRB_ARGS_NONE());

  mrb_define_method(mrb, matrix4_class, "+",               matrix4_op_add,          MRB_ARGS_REQ(1));
  mrb_define_method(mrb, matrix4_class, "-",               matrix4_op_sub,          MRB_ARGS_REQ(1));
  mrb_define_method(mrb, matrix4_class, "*",               matrix4_op_mul,          MRB_ARGS_REQ(1));
  mrb_define_method(mrb, matrix4_class, "/",               matrix4_op_div,          MRB_ARGS_REQ(1));
  //mrb_define_method(mrb, matrix4_class, "%",               op_mod,          MRB_ARGS_REQ(1));

  mrb_define_method(mrb, matrix4_class, "add",             matrix4_add,             MRB_ARGS_REQ(1));
  mrb_define_method(mrb, matrix4_class, "sub",             matrix4_sub,             MRB_ARGS_REQ(1));
  mrb_define_method(mrb, matrix4_class, "mul",             matrix4_mul,             MRB_ARGS_REQ(1));
  mrb_define_method(mrb, matrix4_class, "div",             matrix4_div,             MRB_ARGS_REQ(1));

  mrb_define_method(mrb, matrix4_class, "set",             matrix4_set,             MRB_ARGS_ANY());
  mrb_define_method(mrb, matrix4_class, "clear",           matrix4_clear,           MRB_ARGS_NONE());

  mrb_define_method(mrb, matrix4_class, "translate",       matrix4_translate,       MRB_ARGS_ANY());
  mrb_define_method(mrb, matrix4_class, "rotate",          matrix4_rotate,          MRB_ARGS_ANY());
  mrb_define_method(mrb, matrix4_class, "scale",           matrix4_scale,           MRB_ARGS_ANY());

  mrb_define_method(mrb, matrix4_class, "translate!",      matrix4_translate_bang,  MRB_ARGS_ANY());
  mrb_define_method(mrb, matrix4_class, "rotate!",         matrix4_rotate_bang,     MRB_ARGS_ANY());
  mrb_define_method(mrb, matrix4_class, "scale!",          matrix4_scale_bang,      MRB_ARGS_ANY());

  mrb_define_method(mrb, matrix4_class, "to_a16",          matrix4_to_a16,          MRB_ARGS_NONE());
  mrb_define_method(mrb, matrix4_class, "to_a",            matrix4_to_a,            MRB_ARGS_NONE());

  mrb_define_class_method(mrb, matrix4_class, "[]",        matrix4_s_cast,          MRB_ARGS_ANY());
  mrb_define_class_method(mrb, matrix4_class, "ortho",     matrix4_s_ortho,         MRB_ARGS_REQ(6));
  //mrb_define_class_method(mrb, matrix4_class, "extract",   matrix4_s_extract,       MRB_ARGS_REQ(1));
}