예제 #1
0
/**
 * graphene_matrix_project_point:
 * @m: ...
 * @p: ...
 * @res: (out caller-allocates): ...
 *
 * ...
 *
 * Since: 1.0
 */
void
graphene_matrix_project_point (const graphene_matrix_t *m,
                               const graphene_point_t  *p,
                               graphene_point_t        *res)
{
  graphene_vec3_t pa, qa;
  graphene_vec3_t pback, qback, uback;
  float p_z, u_z, t;

  g_return_if_fail (m != NULL);
  g_return_if_fail (p != NULL);
  g_return_if_fail (res != NULL);

  graphene_vec3_init (&pa, p->x, p->y, 0.0f);
  graphene_vec3_init (&qa, p->x, p->y, 1.0f);

  graphene_matrix_transform_vec3 (m, &pa, &pback);
  graphene_matrix_transform_vec3 (m, &qa, &qback);

  graphene_vec3_subtract (&qback, &pback, &uback);

  p_z = graphene_vec3_get_z (&pback);
  u_z = graphene_vec3_get_z (&uback);
  t = -p_z / u_z;

  graphene_point_init (res,
                       graphene_vec3_get_x (&pback) + t * graphene_vec3_get_x (&uback),
                       graphene_vec3_get_y (&pback) + t * graphene_vec3_get_y (&uback));
}
예제 #2
0
static void
gst_gl_transformation_build_mvp (GstGLTransformation * transformation)
{
  graphene_point3d_t translation_vector =
      GRAPHENE_POINT3D_INIT (transformation->xtranslation * 2.0 *
      transformation->aspect,
      transformation->ytranslation * 2.0,
      transformation->ztranslation * 2.0);

  graphene_matrix_t model_matrix;
  graphene_matrix_t projection_matrix;
  graphene_matrix_t view_matrix;
  graphene_matrix_t vp_matrix;

  graphene_vec3_t eye;
  graphene_vec3_t center;
  graphene_vec3_t up;

  graphene_vec3_init (&eye, 0.f, 0.f, 1.f);
  graphene_vec3_init (&center, 0.f, 0.f, 0.f);
  graphene_vec3_init (&up, 0.f, 1.f, 0.f);

  graphene_matrix_init_scale (&model_matrix,
      transformation->xscale, transformation->yscale, 1.0f);

  graphene_matrix_rotate (&model_matrix,
      transformation->xrotation, graphene_vec3_x_axis ());
  graphene_matrix_rotate (&model_matrix,
      transformation->yrotation, graphene_vec3_y_axis ());
  graphene_matrix_rotate (&model_matrix,
      transformation->zrotation, graphene_vec3_z_axis ());

  graphene_matrix_translate (&model_matrix, &translation_vector);

  if (transformation->ortho) {
    graphene_matrix_init_ortho (&projection_matrix,
        -transformation->aspect, transformation->aspect,
        -1, 1, transformation->znear, transformation->zfar);
  } else {
    graphene_matrix_init_perspective (&projection_matrix,
        transformation->fov,
        transformation->aspect, transformation->znear, transformation->zfar);
  }

  graphene_matrix_init_look_at (&view_matrix, &eye, &center, &up);

  graphene_matrix_multiply (&view_matrix, &projection_matrix, &vp_matrix);
  graphene_matrix_multiply (&model_matrix, &vp_matrix,
      &transformation->mvp_matrix);
}
예제 #3
0
파일: gskglrenderops.c 프로젝트: GNOME/gtk
static void
extract_matrix_metadata (const graphene_matrix_t *m,
                         OpsMatrixMetadata       *md)
{
  switch (md->category)
    {
    case GSK_TRANSFORM_CATEGORY_IDENTITY:
      md->scale_x = 1;
      md->scale_y = 1;
    break;

    case GSK_TRANSFORM_CATEGORY_2D_TRANSLATE:
      md->translate_x = graphene_matrix_get_value (m, 3, 0);
      md->translate_y = graphene_matrix_get_value (m, 3, 1);
      md->scale_x = 1;
      md->scale_y = 1;
    break;

    case GSK_TRANSFORM_CATEGORY_UNKNOWN:
    case GSK_TRANSFORM_CATEGORY_ANY:
    case GSK_TRANSFORM_CATEGORY_3D:
    case GSK_TRANSFORM_CATEGORY_2D:
    case GSK_TRANSFORM_CATEGORY_2D_AFFINE:
      {
        graphene_vec3_t col1;
        graphene_vec3_t col2;

        md->translate_x = graphene_matrix_get_value (m, 3, 0);
        md->translate_y = graphene_matrix_get_value (m, 3, 1);

        graphene_vec3_init (&col1,
                            graphene_matrix_get_value (m, 0, 0),
                            graphene_matrix_get_value (m, 1, 0),
                            graphene_matrix_get_value (m, 2, 0));

        graphene_vec3_init (&col2,
                            graphene_matrix_get_value (m, 0, 1),
                            graphene_matrix_get_value (m, 1, 1),
                            graphene_matrix_get_value (m, 2, 1));

        md->scale_x = graphene_vec3_length (&col1);
        md->scale_y = graphene_vec3_length (&col2);
      }
    break;
    default:
      {}
    }
}
예제 #4
0
/**
 * graphene_quaternion_to_angle_vec3:
 * @q: a #graphene_quaternion_t
 * @angle: (out): return location for the angle, in degrees
 * @axis: (out caller-allocates): return location for the rotation axis
 *
 * Converts a quaternion into an @angle, @axis pair.
 *
 * Since: 1.0
 */
void
graphene_quaternion_to_angle_vec3 (const graphene_quaternion_t *q,
                                   float                       *angle,
                                   graphene_vec3_t             *axis)
{
  graphene_quaternion_t q_n;
  float cos_a;

  graphene_quaternion_normalize (q, &q_n);

  cos_a = q_n.w;

  if (angle != NULL)
    *angle = GRAPHENE_RAD_TO_DEG (acosf (cos_a) * 2.f);

  if (axis != NULL)
    {
      float sin_a = sqrtf (1.f - cos_a * cos_a);

      if (fabsf (sin_a) < 0.00005)
       sin_a = 1.f;

      graphene_vec3_init (axis, q_n.x / sin_a, q_n.y / sin_a, q_n.z / sin_a);
    }
}
예제 #5
0
void
gst_3d_camera_init (Gst3DCamera * self)
{
  self->fov = 45.0;
  //self->aspect = 4.0 / 3.0;
  self->aspect = 16.0 / 9.0;
  self->znear = 0.01;
  self->zfar = 1000.0;

  graphene_vec3_init (&self->eye, 0.f, 0.f, 1.f);
  graphene_vec3_init (&self->center, 0.f, 0.f, 0.f);
  graphene_vec3_init (&self->up, 0.f, 1.f, 0.f);

  self->cursor_last_x = 0;
  self->cursor_last_y = 0;
  self->pressed_mouse_button = 0;
}
예제 #6
0
/*< private >
 * graphene_euler_init_internal:
 * @e: the #graphene_euler_t to initialize
 * @x: rotation angle on the X axis, in radians
 * @y: rotation angle on the Y axis, in radians
 * @z: rotation angle on the Z axis, in radians
 * @order: order of rotations
 *
 * Initializes a #graphene_euler_t using the given angles
 * and order of rotation.
 *
 * Returns: (transfer none): the initialized #graphene_euler_t
 */
static graphene_euler_t *
graphene_euler_init_internal (graphene_euler_t       *e,
                              float                   rad_x,
                              float                   rad_y,
                              float                   rad_z,
                              graphene_euler_order_t  order)
{
  graphene_vec3_init (&e->angles, rad_x, rad_y, rad_z);
  e->order = order;

  return e;
}
예제 #7
0
/**
 * graphene_euler_init_from_quaternion:
 * @e: a #graphene_euler_t
 * @q: (nullable): a normalized #graphene_quaternion_t
 * @order: the order used to apply the rotations
 *
 * Initializes a #graphene_euler_t using the given normalized quaternion.
 *
 * If the #graphene_quaternion_t @q is %NULL, the #graphene_euler_t will
 * be initialized with all angles set to 0.
 *
 * Returns: (transfer none): the initialized #graphene_euler_t
 *
 * Since: 1.2
 */
graphene_euler_t *
graphene_euler_init_from_quaternion (graphene_euler_t            *e,
                                     const graphene_quaternion_t *q,
                                     graphene_euler_order_t       order)
{
  float sqx, sqy, sqz, sqw;
  float x, y, z;

  if (q == NULL)
    return graphene_euler_init_with_order (e, 0.f, 0.f, 0.f, order);

  sqx = q->x * q->x;
  sqy = q->y * q->y;
  sqz = q->z * q->z;
  sqw = q->w * q->w;

  x = y = z = 0.f;

  e->order = order;
  switch (graphene_euler_get_order (e))
    {
    case GRAPHENE_EULER_ORDER_XYZ:
      x = atan2f (2.f * (q->x * q->w - q->y * q->z), (sqw - sqx - sqy + sqz));
      y = asinf (CLAMP (2.f * (q->x * q->z + q->y * q->w ), -1.f, 1.f));
      z = atan2f (2.f * (q->z * q->w - q->x * q->y), (sqw + sqx - sqy - sqz));
      break;

    case GRAPHENE_EULER_ORDER_YXZ:
      x = asinf (CLAMP (2.f * (q->x * q->w - q->y * q->z), -1.f, 1.f));
      y = atan2f (2.f * (q->x * q->z + q->y * q->w), (sqw - sqx - sqy + sqz));
      z = atan2f (2.f * (q->x * q->y + q->z * q->w), (sqw - sqx + sqy - sqz));
      break;

    case GRAPHENE_EULER_ORDER_ZXY:
      x = asinf (CLAMP (2.f * (q->x * q->w + q->y * q->z), -1.f, 1.f));
      y = atan2f (2.f * (q->y * q->w - q->z * q->x), (sqw - sqx - sqy + sqz));
      z = atan2f (2.f * (q->z * q->w - q->x * q->y), (sqw - sqx + sqy - sqz));
      break;

    case GRAPHENE_EULER_ORDER_ZYX:
      x = atan2f (2.f * (q->x * q->w + q->z * q->y), (sqw - sqx - sqy + sqz));
      y = asinf (CLAMP (2.f * (q->y * q->w - q->x * q->z), -1.f, 1.f));
      z = atan2f (2.f * (q->x * q->y + q->z * q->w), (sqw + sqx - sqy - sqz));
      break;

    case GRAPHENE_EULER_ORDER_YZX:
      x = atan2f (2.f * (q->x * q->w - q->z * q->y), (sqw - sqx + sqy - sqz));
      y = atan2f (2.f * (q->y * q->w - q->x * q->z), (sqw + sqx - sqy - sqz));
      z = asinf (CLAMP (2.f * (q->x * q->y + q->z * q->w ), -1.f, 1.f));
      break;

    case GRAPHENE_EULER_ORDER_XZY:
      x = atan2f (2.f * (q->x * q->w + q->y * q->z), (sqw - sqx + sqy - sqz));
      y = atan2f (2.f * (q->x * q->z + q->y * q->w), (sqw + sqx - sqy - sqz));
      z = asinf (CLAMP (2.f * (q->z * q->w - q->x * q->y), -1.f, 1.f));
      break;

    case GRAPHENE_EULER_ORDER_DEFAULT:
      break;
    }

  graphene_vec3_init (&e->angles, x, y, z);

  return e;
}
예제 #8
0
/**
 * graphene_euler_init_from_matrix:
 * @e: the #graphene_euler_t to initialize
 * @m: (nullable): a rotation matrix
 * @order: the order used to apply the rotations
 *
 * Initializes a #graphene_euler_t using the given rotation matrix.
 *
 * If the #graphene_matrix_t @m is %NULL, the #graphene_euler_t will
 * be initialized with all angles set to 0.
 *
 * Returns: (transfer none): the initialized #graphene_euler_t
 *
 * Since: 1.2
 */
graphene_euler_t *
graphene_euler_init_from_matrix (graphene_euler_t        *e,
                                 const graphene_matrix_t *m,
                                 graphene_euler_order_t   order)
{
  float me[16];
  float m11, m12, m13;
  float m21, m22, m23;
  float m31, m32, m33;
  float x, y, z;

  if (m == NULL)
    return graphene_euler_init_with_order (e, 0.f, 0.f, 0.f, order);

  graphene_matrix_to_float (m, me);

  /* isolate the rotation components */
  m11 = me[0]; m21 = me[4]; m31 = me[8];
  m12 = me[1]; m22 = me[5]; m32 = me[9];
  m13 = me[2]; m23 = me[6]; m33 = me[10];

  x = y = z = 0.f;

  e->order = order;
  switch (graphene_euler_get_order (e))
    {
    case GRAPHENE_EULER_ORDER_XYZ:
      y = asinf (CLAMP (m13, -1.f, 1.f));
      if (fabsf (m13) < 1.f)
        {
          x = atan2f (-1.f * m23, m33);
          z = atan2f (-1.f * m12, m11);
        }
      else
        {
          x = atan2f (m32, m22);
          z = 0.f;
        }
      break;

    case GRAPHENE_EULER_ORDER_YXZ:
      x = asinf (-1.f * CLAMP (m23, -1, 1));
      if (fabsf (m23) < 1.f)
        {
          y = atan2f (m13, m33);
          z = atan2f (m21, m22);
        }
      else
        {
          y = atan2f (-1.f * m31, m11);
          z = 0.f;
        }
      break;

    case GRAPHENE_EULER_ORDER_ZXY:
      x = asinf (CLAMP (m32, -1.f, 1.f));

      if (fabsf (m32) < 1.f)
        {
          y = atan2f (-1.f * m31, m33);
          z = atan2f (-1.f * m12, m22);
        }
      else
        {
          y = 0.f;
          z = atan2f (m21, m11);
        }
      break;

    case GRAPHENE_EULER_ORDER_ZYX:
      y = asinf (-1.f * CLAMP (m31, -1.f, 1.f));

      if (fabsf (m31) < 1.f)
        {
          x = atan2f (m32, m33);
          z = atan2f (m21, m11);
        }
      else
        {
          x = 0.f;
          z = atan2f (-1.f * m12, m22);
        }
      break;

    case GRAPHENE_EULER_ORDER_YZX:
      z = asinf (CLAMP (m21, -1.f, 1.f));

      if (fabsf (m21) < 1.f)
        {
          x = atan2f (-1.f * m23, m22);
          y = atan2f (-1.f * m31, m11);
        }
      else
        {
          x = 0.f;
          y = atan2f (m13, m33);
        }
      break;

    case GRAPHENE_EULER_ORDER_XZY:
      z = asinf (-1.f * CLAMP (m12, -1.f, 1.f));

      if (fabsf (m12) < 1.f)
        {
          x = atan2f (m32, m22);
          y = atan2f (m13, m11);
        }
      else
        {
          x = atan2f (-1.f * m23, m33);
          y = 0.f;
        }
      break;

    case GRAPHENE_EULER_ORDER_DEFAULT:
      break;
    }

  graphene_vec3_init (&e->angles, x, y, z);

  return e;
}
예제 #9
0
파일: main.c 프로젝트: zakkor/renderer
// The MAIN function, from here we start the application and run the game loop
int main()
{
    // Init GLFW
    glfwInit();
    // Set all the required options for GLFW
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

    // Create a GLFWwindow object that we can use for GLFW's functions
    GLFWwindow* window = glfwCreateWindow(WIDTH, HEIGHT, "LearnOpenGL", NULL, NULL);
    glfwMakeContextCurrent(window);

    // Set the required callback functions
    glfwSetKeyCallback(window, key_callback);

    // Set this to true so GLEW knows to use a modern approach to retrieving function pointers and extensions
    glewExperimental = GL_TRUE;
    // Initialize GLEW to setup the OpenGL Function pointers
    glewInit();

    // Define the viewport dimensions
    glViewport(0, 0, WIDTH, HEIGHT);

    // Setup OpenGL options
    glEnable(GL_DEPTH_TEST);


    // Build and compile our shader program
    shader our_shader = shader_new("shader.vs", "shader.frag");


    // Set up vertex data (and buffer(s)) and attribute pointers
    GLfloat vertices[] = {
        -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,
         0.5f, -0.5f, -0.5f,  1.0f, 0.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 0.0f,

        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
         0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 1.0f,
        -0.5f,  0.5f,  0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,

        -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
        -0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
        -0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
         0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
         0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
         0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,

        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,
         0.5f, -0.5f, -0.5f,  1.0f, 1.0f,
         0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
         0.5f, -0.5f,  0.5f,  1.0f, 0.0f,
        -0.5f, -0.5f,  0.5f,  0.0f, 0.0f,
        -0.5f, -0.5f, -0.5f,  0.0f, 1.0f,

        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f,
         0.5f,  0.5f, -0.5f,  1.0f, 1.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
         0.5f,  0.5f,  0.5f,  1.0f, 0.0f,
        -0.5f,  0.5f,  0.5f,  0.0f, 0.0f,
        -0.5f,  0.5f, -0.5f,  0.0f, 1.0f
    };
    // World space positions of our cubes
    graphene_point3d_t cubePositions[] = {
        GRAPHENE_POINT3D_INIT(0.0f,  0.0f,  0.0f),
        GRAPHENE_POINT3D_INIT( 2.0f,  5.0f, -15.0f),
        GRAPHENE_POINT3D_INIT(-1.5f, -2.2f, -2.5f),
        GRAPHENE_POINT3D_INIT(-3.8f, -2.0f, -12.3f),
        GRAPHENE_POINT3D_INIT( 2.4f, -0.4f, -3.5f),
        GRAPHENE_POINT3D_INIT(-1.7f,  3.0f, -7.5f),
        GRAPHENE_POINT3D_INIT( 1.3f, -2.0f, -2.5f),
        GRAPHENE_POINT3D_INIT( 1.5f,  2.0f, -2.5f),
        GRAPHENE_POINT3D_INIT( 1.5f,  0.2f, -1.5f),
        GRAPHENE_POINT3D_INIT(-1.3f,  1.0f, -1.5f)
    };

    GLuint VBO, VAO;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);

    glBindVertexArray(VAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    // Position attribute
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    // TexCoord attribute
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(2);

    glBindVertexArray(0); // Unbind VAO


    // Load and create a texture
    GLuint texture1;
    GLuint texture2;
    // ====================
    // Texture 1
    // ====================
    glGenTextures(1, &texture1);
    glBindTexture(GL_TEXTURE_2D, texture1); // All upcoming GL_TEXTURE_2D operations now have effect on our texture object
    // Set our texture parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);	// Set texture wrapping to GL_REPEAT
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    // Set texture filtering
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    // Load, create texture and generate mipmaps
    int width, height;
    unsigned char* image = SOIL_load_image("container.jpg", &width, &height, 0, SOIL_LOAD_RGB);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
    glGenerateMipmap(GL_TEXTURE_2D);
    SOIL_free_image_data(image);
    glBindTexture(GL_TEXTURE_2D, 0); // Unbind texture when done, so we won't accidentily mess up our texture.
    // ===================
    // Texture 2
    // ===================
    glGenTextures(1, &texture2);
    glBindTexture(GL_TEXTURE_2D, texture2);
    // Set our texture parameters
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    // Set texture filtering
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    // Load, create texture and generate mipmaps
    image = SOIL_load_image("awesomeface.png", &width, &height, 0, SOIL_LOAD_RGB);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
    glGenerateMipmap(GL_TEXTURE_2D);
    SOIL_free_image_data(image);
    glBindTexture(GL_TEXTURE_2D, 0);


    // Game loop
    while (!glfwWindowShouldClose(window))
    {
        // Check if any events have been activiated (key pressed, mouse moved etc.) and call corresponding response functions
        glfwPollEvents();

        // Render
        // Clear the colorbuffer
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);


        // Bind Textures using texture units
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, texture1);
        glUniform1i(glGetUniformLocation(our_shader.program, "ourTexture1"), 0);
        glActiveTexture(GL_TEXTURE1);
        glBindTexture(GL_TEXTURE_2D, texture2);
        glUniform1i(glGetUniformLocation(our_shader.program, "ourTexture2"), 1);

        // Activate shader
        shader_use(&our_shader);

        // Create transformations
        graphene_matrix_t view;
        graphene_point3d_t view_point;
        graphene_matrix_init_translate(&view, graphene_point3d_init(&view_point, 0.0f, 0.0f, -3.0f));

        graphene_matrix_t projection;
        graphene_matrix_init_perspective(&projection, 45.0f, (GLfloat) WIDTH / (GLfloat) HEIGHT, 0.1f, 100.0f);

        // Get their uniform location
        GLint modelLoc = glGetUniformLocation(our_shader.program, "model");
        GLint viewLoc = glGetUniformLocation(our_shader.program, "view");
        GLint projLoc = glGetUniformLocation(our_shader.program, "projection");

        // Pass the matrices to the shader
        GLfloat view_f[16] = {};
        graphene_matrix_to_float(&view, view_f);
        GLfloat proj_f[16] = {};
        graphene_matrix_to_float(&projection, proj_f);

        glUniformMatrix4fv(viewLoc, 1, GL_FALSE, view_f);
        // Note: currently we set the projection matrix each frame, but since the projection matrix rarely changes it's often best practice to set it outside the main loop only once.
        glUniformMatrix4fv(projLoc, 1, GL_FALSE, proj_f);

        glBindVertexArray(VAO);
        for (GLuint i = 0; i < 10; i++)
        {
            // Calculate the model matrix for each object and pass it to shader before drawing
            graphene_matrix_t model;
            graphene_matrix_init_translate(&model, &cubePositions[i]);
            GLfloat angle = 5.0f * i;

            graphene_vec3_t axis;
            graphene_vec3_init(&axis, -1.3f,  1.0f, -1.5f);
            graphene_matrix_rotate(&model, angle, &axis);

            GLfloat model_f[16] = {};
            graphene_matrix_to_float(&model, model_f);

            glUniformMatrix4fv(modelLoc, 1, GL_FALSE, model_f);

            glDrawArrays(GL_TRIANGLES, 0, 36);
        }
        glBindVertexArray(0);

        // Swap the screen buffers
        glfwSwapBuffers(window);
    }
    // Properly de-allocate all resources once they've outlived their purpose
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    // Terminate GLFW, clearing any resources allocated by GLFW.
    glfwTerminate();
    return 0;
}