holywyvern/carbuncle

View on GitHub
gems/carbuncle-scene/src/camera.c

Summary

Maintainability
Test Coverage
#include <carbuncle/core.h>
#include <carbuncle/point.h>
#include <carbuncle/camera.h>

#include <mruby/data.h>
#include <mruby/error.h>
#include <mruby/class.h>
#include <mruby/variable.h>

#define OFFSET_SYMBOL   mrb_intern_cstr(mrb, "#offset")
#define TARGET_SYMBOL   mrb_intern_cstr(mrb, "#target")

static const struct mrb_data_type camera_data_type = {
  "Carbuncle::Camera", mrb_free
};

static mrb_value
mrb_camera_initialize(mrb_state *mrb, mrb_value self)
{
  mrb_value offset, target;
  mrb_int arena;
  struct mrb_Camera *cam = mrb_malloc(mrb, sizeof *cam);
  DATA_PTR(self) = cam;
  DATA_TYPE(self) = &camera_data_type;
  arena = mrb_gc_arena_save(mrb);
  offset = mrb_carbuncle_point_new(mrb, 0, 0);
  target = mrb_carbuncle_point_new(mrb, 0, 0);
  mrb_iv_set(mrb, self, OFFSET_SYMBOL, offset);
  mrb_iv_set(mrb, self, TARGET_SYMBOL, target);
  cam->offset = mrb_carbuncle_get_point(mrb, offset);
  cam->target = mrb_carbuncle_get_point(mrb, target);
  cam->angle = 0;
  cam->scale = 1;
  mrb_gc_arena_restore(mrb, arena);
  return self;
}

static mrb_value
mrb_camera_get_target(mrb_state *mrb, mrb_value self)
{
  return mrb_iv_get(mrb, self, TARGET_SYMBOL);
}

static mrb_value
mrb_camera_get_offset(mrb_state *mrb, mrb_value self)
{
  return mrb_iv_get(mrb, self, OFFSET_SYMBOL);
}

static mrb_value
mrb_camera_get_angle(mrb_state *mrb, mrb_value self)
{
  struct mrb_Camera *cam = mrb_carbuncle_get_camera(mrb, self);
  return mrb_float_value(mrb, cam->angle);
}

static mrb_value
mrb_camera_get_scale(mrb_state *mrb, mrb_value self)
{
  struct mrb_Camera *cam = mrb_carbuncle_get_camera(mrb, self);
  return mrb_float_value(mrb, cam->scale);
}

static mrb_value
mrb_camera_set_target(mrb_state *mrb, mrb_value self)
{
  struct mrb_Camera *cam;
  Vector2 *point;
  mrb_value obj;
  mrb_get_args(mrb, "o", &obj);
  point = mrb_carbuncle_get_point(mrb, obj);
  cam = mrb_carbuncle_get_camera(mrb, self);
  cam->target = point;
  mrb_iv_set(mrb, self, TARGET_SYMBOL, obj);
  return obj;
}

static mrb_value
mrb_camera_set_offset(mrb_state *mrb, mrb_value self)
{
  struct mrb_Camera *cam;
  Vector2 *point;
  mrb_value obj;
  mrb_get_args(mrb, "o", &obj);
  point = mrb_carbuncle_get_point(mrb, obj);
  cam = mrb_carbuncle_get_camera(mrb, self);
  cam->offset = point;
  mrb_iv_set(mrb, self, OFFSET_SYMBOL, obj);
  return obj;
}

static mrb_value
mrb_camera_set_angle(mrb_state *mrb, mrb_value self)
{
  mrb_float value;
  struct mrb_Camera *cam = mrb_carbuncle_get_camera(mrb, self);
  mrb_get_args(mrb, "f", &value);
  cam->angle = value;
  return mrb_float_value(mrb, value);
}

static mrb_value
mrb_camera_set_scale(mrb_state *mrb, mrb_value self)
{
  mrb_float value;
  struct mrb_Camera *cam = mrb_carbuncle_get_camera(mrb, self);
  mrb_get_args(mrb, "f", &value);
  cam->scale = value;
  return mrb_float_value(mrb, value);
}

static mrb_value
mrb_camera_begin_draw(mrb_state *mrb, mrb_value self)
{
  struct mrb_Camera *data = mrb_carbuncle_get_camera(mrb, self);
  Camera2D camera = (Camera2D){
    .offset = *(data->offset),
    .target = *(data->target),
    .rotation = data->angle,
    .zoom = data->scale
  };
  mrb_value block = mrb_nil_value();
  mrb_get_args(mrb, "|&", &block);
  BeginMode2D(camera);
  if (!mrb_nil_p(block))
  {
    mrb_bool raised = FALSE;
    mrb_value result = mrb_protect(mrb, mrb_carbuncle_call_block, block, &raised);
    EndMode2D();
    if (raised) { mrb_exc_raise(mrb, result); }
  }
  return self;
}

static mrb_value
mrb_camera_end_draw(mrb_state *mrb, mrb_value self)
{
  EndMode2D();
  return self;
}

void
mrb_init_carbuncle_camera(mrb_state *mrb)
{
  struct RClass *carbuncle = mrb_carbuncle_get(mrb);
  struct RClass *camera = mrb_define_class_under(mrb, carbuncle, "Camera", mrb->object_class);
  MRB_SET_INSTANCE_TT(camera, MRB_TT_DATA);

  mrb_define_method(mrb, camera, "initialize", mrb_camera_initialize, MRB_ARGS_NONE());

  mrb_define_method(mrb, camera, "target", mrb_camera_get_target, MRB_ARGS_NONE());
  mrb_define_method(mrb, camera, "offset", mrb_camera_get_offset, MRB_ARGS_NONE());
  mrb_define_method(mrb, camera, "angle",  mrb_camera_get_angle, MRB_ARGS_NONE());
  mrb_define_method(mrb, camera, "scale",  mrb_camera_get_scale, MRB_ARGS_NONE());

  mrb_define_method(mrb, camera, "target=", mrb_camera_set_target, MRB_ARGS_REQ(1));
  mrb_define_method(mrb, camera, "offset=", mrb_camera_set_offset, MRB_ARGS_REQ(1));
  mrb_define_method(mrb, camera, "angle=",  mrb_camera_set_angle, MRB_ARGS_REQ(1));
  mrb_define_method(mrb, camera, "scale=",  mrb_camera_set_scale, MRB_ARGS_REQ(1));

  mrb_define_method(mrb, camera, "draw",  mrb_camera_begin_draw, MRB_ARGS_BLOCK());
  mrb_define_method(mrb, camera, "begin_draw",  mrb_camera_begin_draw, MRB_ARGS_BLOCK());
  mrb_define_method(mrb, camera, "end_draw",  mrb_camera_end_draw, MRB_ARGS_NONE());
}

struct mrb_Camera *
mrb_carbuncle_get_camera(mrb_state *mrb, mrb_value obj)
{
  return DATA_GET_PTR(mrb, obj, &camera_data_type, struct mrb_Camera);
}

mrb_bool
mrb_carbuncle_camera_p(mrb_value obj)
{
  return mrb_data_p(obj) && (DATA_TYPE(obj) == &camera_data_type);
}