polyfox/moon

View on GitHub
modules/graphics/src/mrb_vertex_buffer.cxx

Summary

Maintainability
Test Coverage
#include <mruby.h>
#include <mruby/class.h>
#include <mruby/data.h>
#include <mruby/numeric.h>
#include <mruby/string.h>
#include "moon/mrb/rect.hxx"
#include "moon/mrb/vector2.hxx"
#include "moon/mrb/vector3.hxx"
#include "moon/mrb/vector4.hxx"
#include "moon/mrb/vertex_buffer.hxx"
#include "moon/mrb/helpers.hxx"
#include "moon/vertex_buffer.hxx"
#include "moon/api.h"

static void
vbo_free(mrb_state *mrb, void *p)
{
  Moon::VertexBuffer *vbo = (Moon::VertexBuffer*)p;
  if (vbo) {
    delete(vbo);
  }
}

MOON_C_API const struct mrb_data_type vbo_data_type = { "Moon::VertexBuffer", vbo_free };

/* Creates a new buffer to store vertex and index data on the GPU.
 * @param [Integer] usage Intended usage mode that hints to OpenGL on how to
 * best store the data.
 *
 * @return [VertexBuffer]
 */
static mrb_value
vbo_initialize(mrb_state *mrb, mrb_value self)
{
  GLenum usage;
  Moon::VertexBuffer *vbo;
  mrb_get_args(mrb, "i", &usage);

  vbo_free(mrb, DATA_PTR(self));
  DATA_PTR(self) = NULL;

  vbo = new Moon::VertexBuffer(usage);
  mrb_data_init(self, vbo, &vbo_data_type);
  return self;
}

/* Clears the buffer.
 *
 * @return [self]
 */
static mrb_value
vbo_clear(mrb_state *mrb, mrb_value self)
{
  mmrb_vertex_buffer_ptr(mrb, self)->Clear();
  return self;
}

/* Binds the VAO and renders the buffer.
 * We can optionally specify an offset which "is added to each index before
 * pulling from the vertex data". That makes it easy to just use the same IBO,
 * but use it to render different meshes.
 *
 * One usecase for that is in the Spritesheet, we use a 1,3,2,4 IBO with a VBO
 * for all the different tiles. Simply specifying the tile index as the offset
 * will render the correct tile.
 *
 * @param [Integer] mode OpenGL mode to use for rendering.
 * @param [Integer] offset optional offset into the vertex data
 * @return [self]
 */
static mrb_value
vbo_render(mrb_state *mrb, mrb_value self)
{
  GLenum mode;
  GLuint offset;
  offset = 0;
  mrb_get_args(mrb, "i|i", &mode, &offset);

  mmrb_vertex_buffer_ptr(mrb, self)->Render(mode, offset);
  return self;
}

/**
 * Adds a single vertex to the buffer
 *
 * @param [Vector2] pos
 * @param [Vector2] tex_coord
 * @param [Vector4] color
 * @return [self]
 */
static mrb_value
vbo_push_back(mrb_state *mrb, mrb_value self)
{
  Moon::Vector2 *pos;
  Moon::Vector2 *tex_coord;
  Moon::Vector4 *color;
  mrb_get_args(mrb, "ddd",
    &pos, &vbo_data_type,
    &tex_coord, &vbo_data_type,
    &color, &vbo_data_type
  );
  mmrb_vertex_buffer_ptr(mrb, self)->PushBack(Moon::Vertex(*pos, *tex_coord, *color));
  return self;
}

/**
 * Pushes the list of indices into the buffer
 *
 * @param [Array<Integer>] indices
 * @return [self]
 */
static mrb_value
vbo_push_indices(mrb_state *mrb, mrb_value self)
{
  mrb_int length;
  mrb_value *indices;
  mrb_get_args(mrb, "a", &indices, &length);
  // hell no, am I gonna malloc an array for this just to push it at once.
  // its a bit ugly to be resizing it each time though...
  for (int i = 0; i < length; ++i) {
    GLuint index = mrb_int(mrb, indices[i]);
    mmrb_vertex_buffer_ptr(mrb, self)->PushBackIndices(&index, 1);
  }
  return self;
}

/* @return [Integer] Returns the number of VBO vertices stored in the buffer.
*/
static mrb_value
vbo_vertex_count(mrb_state *mrb, mrb_value self)
{
  return mrb_fixnum_value(mmrb_vertex_buffer_ptr(mrb, self)->GetVertexCount());
}

/* @return [Integer] Returns the number of IBO indices stored in the buffer.
*/
static mrb_value
vbo_index_count(mrb_state *mrb, mrb_value self)
{
  return mrb_fixnum_value(mmrb_vertex_buffer_ptr(mrb, self)->GetIndexCount());
}

static void
make_quad(Moon::Vertex vertices[4], Moon::IntRect& quad_rect, Moon::FloatRect& quad_texture_rect, Moon::Vector4& color)
{
  GLfloat x0, x1, y0, y1;
  GLfloat tx0, tx1, ty0, ty1;

  x0 = quad_rect.x;
  y0 = quad_rect.y;
  x1 = quad_rect.x + quad_rect.w;
  y1 = quad_rect.y + quad_rect.h;

  tx0 = quad_texture_rect.x;
  ty0 = quad_texture_rect.y;
  tx1 = quad_texture_rect.x + quad_texture_rect.w;
  ty1 = quad_texture_rect.y + quad_texture_rect.h;

  vertices[0] = (Moon::Vertex){ { x0, y0 }, { tx0, ty0 }, color };
  vertices[1] = (Moon::Vertex){ { x1, y0 }, { tx1, ty0 }, color };
  vertices[2] = (Moon::Vertex){ { x1, y1 }, { tx1, ty1 }, color };
  vertices[3] = (Moon::Vertex){ { x0, y1 }, { tx0, ty1 }, color };
}

static void
vbo_quad_m(mrb_state *mrb, mrb_value self, Moon::Vertex vertices[4])
{
  Moon::IntRect quad_rect;
  Moon::FloatRect quad_texture_rect;
  Moon::Vector4 color;

  mrb_value rect_obj;
  mrb_value tex_rect_obj;
  mrb_value color_obj;

  mrb_get_args(mrb, "ooo", &rect_obj, &tex_rect_obj, &color_obj);
  quad_rect = mmrb_to_rect(mrb, rect_obj);
  quad_texture_rect = mmrb_to_float_rect(mrb, tex_rect_obj);
  color = mmrb_to_vector4(mrb, color_obj);

  make_quad(vertices, quad_rect, quad_texture_rect, color);
}

static mrb_value
vbo_add_quad(mrb_state *mrb, mrb_value self)
{
  Moon::Vertex vertices[4];
  GLuint indices[] = { 0, 1, 3, 2, 3, 1 };
  Moon::VertexBuffer *vbo = NULL;
  vbo_quad_m(mrb, self, vertices);
  vbo = mmrb_vertex_buffer_ptr(mrb, self);
  vbo->PushBack(vertices, 4, indices, 6);
  return self;
}

static mrb_value
vbo_add_quad_vertices(mrb_state *mrb, mrb_value self)
{
  Moon::Vertex vertices[4];
  Moon::VertexBuffer *vbo = NULL;
  vbo_quad_m(mrb, self, vertices);
  vbo = mmrb_vertex_buffer_ptr(mrb, self);
  vbo->PushBackVertices(vertices, 4);
  return self;
}

MOON_C_API void
mmrb_vbo_init(mrb_state *mrb)
{
  struct RClass *mod = mrb_define_module(mrb, "Moon");
  struct RClass *vbo_class = mrb_define_class_under(mrb, mod, "VertexBuffer", mrb->object_class);
  MRB_SET_INSTANCE_TT(vbo_class, MRB_TT_DATA);

  mrb_define_method(mrb, vbo_class, "initialize",        vbo_initialize,        MRB_ARGS_REQ(1));
  mrb_define_method(mrb, vbo_class, "clear",             vbo_clear,             MRB_ARGS_NONE());
  mrb_define_method(mrb, vbo_class, "render",            vbo_render,            MRB_ARGS_REQ(1) | MRB_ARGS_OPT(1));
  mrb_define_method(mrb, vbo_class, "push_back",         vbo_push_back,         MRB_ARGS_REQ(3));
  mrb_define_method(mrb, vbo_class, "push_indices",      vbo_push_indices,      MRB_ARGS_REQ(1));
  mrb_define_method(mrb, vbo_class, "vertex_count",      vbo_vertex_count,      MRB_ARGS_NONE());
  mrb_define_method(mrb, vbo_class, "index_count",       vbo_index_count,       MRB_ARGS_NONE());
  mrb_define_method(mrb, vbo_class, "add_quad",          vbo_add_quad,          MRB_ARGS_REQ(3));
  mrb_define_method(mrb, vbo_class, "add_quad_vertices", vbo_add_quad_vertices, MRB_ARGS_REQ(3));
}