modules/graphics/src/mrb_spritesheet.cxx
#include <mruby.h>
#include <mruby/array.h>
#include <mruby/class.h>
#include <mruby/data.h>
#include <mruby/error.h>
#include <mruby/hash.h>
#include <mruby/numeric.h>
#include <mruby/string.h>
#include "moon/glm.h"
#include "moon/api.h"
#include "moon/mrb/matrix4.hxx"
#include "moon/mrb/shader.hxx"
#include "moon/mrb/texture.hxx"
#include "moon/mrb/vector3.hxx"
#include "moon/mrb/vector4.hxx"
#include "moon/mrb/vertex_buffer.hxx"
#include "moon/mrb_err.hxx"
#include "moon/mrb/helpers.hxx"
static mrb_sym id_opacity;
static mrb_sym id_tone;
static mrb_sym id_color;
static mrb_sym id_ox;
static mrb_sym id_oy;
static mrb_sym id_angle;
static mrb_sym id_transform;
struct RenderState {
GLfloat opacity;
GLfloat angle;
Moon::Vector2 origin;
Moon::Vector4 color;
Moon::Vector4 tone;
Moon::Matrix4 transform;
RenderState() :
opacity(1.0),
angle(0.0),
origin(0.0, 0.0),
color(1.0, 1.0, 1.0, 1.0),
tone(0.0, 0.0, 0.0, 1.0) {};
};
static mrb_value
spritesheet_generate_buffers(mrb_state *mrb, mrb_value self)
{
Moon::Texture *texture = mmrb_valid_texture_ptr(mrb, moon_iv_get(mrb, self, KEY_TEXTURE));
Moon::VertexBuffer *vbo = mmrb_vertex_buffer_ptr(mrb, moon_iv_get(mrb, self, KEY_VBO));
const GLuint tile_width = mrb_int(mrb, moon_iv_get(mrb, self, "@w"));
const GLuint tile_height = mrb_int(mrb, moon_iv_get(mrb, self, "@h"));
const GLfloat tiles_per_row = texture->GetWidth() / tile_width;
const GLfloat tiles_per_column = texture->GetHeight() / tile_height;
const GLuint total_sprites = tiles_per_row * tiles_per_column;
// set @cell_count
IVset("@cell_count", mrb_fixnum_value(total_sprites));
// TODO: clear out VBO if we're calling generate_buffers again
// note: this is only called once in initialize, so it never needs clearing
// right now
for (GLuint i = 0; i < total_sprites; ++i) {
GLfloat ox = (float)(i % (int)tiles_per_row);
GLfloat oy = (float)(i / (int)tiles_per_row);
float s0 = (ox) / tiles_per_row;
float s1 = (ox + 1.0) / tiles_per_row;
float t0 = (oy) / tiles_per_column;
float t1 = (oy + 1.0) / tiles_per_column;
Moon::Vertex vertices[4] = {
{ {0.f, 0.f}, {s0, t0}, Moon::Vector4(1, 1, 1, 1) },
{ {tile_width, 0.f}, {s1, t0}, Moon::Vector4(1, 1, 1, 1) },
{ {tile_width, tile_height}, {s1, t1}, Moon::Vector4(1, 1, 1, 1) },
{ {0.f, tile_height}, {s0, t1}, Moon::Vector4(1, 1, 1, 1) }
};
vbo->PushBackVertices(vertices, 4);
}
GLuint indices[4] = {0, 1, 3, 2};
vbo->PushBackIndices(indices, 4);
return self;
}
static void
set_render_options(mrb_state *mrb, mrb_value options, RenderState *render_state)
{
mrb_value keys = mrb_hash_keys(mrb, options);
int len = mrb_ary_len(mrb, keys);
const mrb_value *keys_ary = RARRAY_PTR(keys);
for (int i = 0; i < len; ++i) {
mrb_value key = keys_ary[i];
if (!mrb_symbol_p(key)) continue;
mrb_value val = mrb_hash_get(mrb, options, key);
// :opacity
if (mrb_symbol(key) == id_opacity) {
render_state->opacity = mrb_to_flo(mrb, val);
// :color
} else if (mrb_symbol(key) == id_color) {
render_state->color = mmrb_to_vector4(mrb, val);
// :tone
} else if (mrb_symbol(key) == id_tone) {
render_state->tone = mmrb_to_vector4(mrb, val);
// :ox
} else if (mrb_symbol(key) == id_ox) {
render_state->origin.x = mrb_to_flo(mrb, val);
// :oy
} else if (mrb_symbol(key) == id_oy) {
render_state->origin.y = mrb_to_flo(mrb, val);
// :angle
} else if (mrb_symbol(key) == id_angle) {
render_state->angle = mrb_to_flo(mrb, val);
// :transform
} else if (mrb_symbol(key) == id_transform) {
render_state->transform = mmrb_to_matrix4(mrb, val);
}
}
}
static mrb_value
spritesheet_copy_quad_to(mrb_state *mrb, mrb_value self)
{
RenderState render_state;
Moon::VertexBuffer *dest_vbo;
Moon::VertexBuffer *vbo;
mrb_float x, y, z;
mrb_value options = mrb_nil_value();
mrb_int offset = 0;
GLuint index;
Moon::Vertex vertices[4];
GLuint indices[] = { 0, 1, 3, 2, 3, 1 };
mrb_get_args(mrb, "dfffi|H",
&dest_vbo, &vbo_data_type,
&x, &y, &z, &offset,
&options
);
if (!mrb_nil_p(options)) {
set_render_options(mrb, options, &render_state);
}
vbo = mmrb_vertex_buffer_ptr(mrb, moon_iv_get(mrb, self, KEY_VBO));
Moon::Vector2 pos(x, y);
index = offset * 4;
// now apply render state changes
for (int i = 0; i < 4; ++i) {
vertices[i] = vbo->GetVertex(index + i);
vertices[i].pos += pos;
vertices[i].color *= render_state.color;
vertices[i].color.a *= render_state.opacity;
}
dest_vbo->PushBack(vertices, 4, indices, 6);
return self;
}
MOON_C_API void
mmrb_spritesheet_init(mrb_state *mrb)
{
struct RClass* mod = mrb_define_module(mrb, "Moon");
struct RClass *ss_cls = mrb_define_class_under(mrb, mod, "Spritesheet", mrb->object_class);
mrb_define_method(mrb, ss_cls, "generate_buffers", spritesheet_generate_buffers, MRB_ARGS_NONE());
mrb_define_method(mrb, ss_cls, "copy_quad_to", spritesheet_copy_quad_to, MRB_ARGS_ARG(5,1));
id_opacity = mrb_intern_cstr(mrb, "opacity");
id_tone = mrb_intern_cstr(mrb, "tone");
id_color = mrb_intern_cstr(mrb, "color");
id_ox = mrb_intern_cstr(mrb, "ox");
id_oy = mrb_intern_cstr(mrb, "oy");
id_angle = mrb_intern_cstr(mrb, "angle");
id_transform = mrb_intern_cstr(mrb, "transform");
}