summaryrefslogtreecommitdiff
path: root/src/candy_gear
diff options
context:
space:
mode:
Diffstat (limited to 'src/candy_gear')
-rw-r--r--src/candy_gear/candy_gear.cpp146
-rw-r--r--src/candy_gear/candy_gear.hpp32
-rw-r--r--src/candy_gear/core.cpp398
-rw-r--r--src/candy_gear/core.hpp66
-rw-r--r--src/candy_gear/font.cpp68
-rw-r--r--src/candy_gear/font.hpp28
-rw-r--r--src/candy_gear/graphic.cpp83
-rw-r--r--src/candy_gear/graphic.hpp29
-rw-r--r--src/candy_gear/key.cpp71
-rw-r--r--src/candy_gear/key.hpp25
-rw-r--r--src/candy_gear/main.cpp115
-rw-r--r--src/candy_gear/orientation_3d.cpp135
-rw-r--r--src/candy_gear/orientation_3d.hpp27
-rw-r--r--src/candy_gear/skeletal_mesh.cpp67
-rw-r--r--src/candy_gear/skeletal_mesh.hpp27
-rw-r--r--src/candy_gear/skeletal_model.cpp134
-rw-r--r--src/candy_gear/skeletal_model.hpp25
-rw-r--r--src/candy_gear/sound.cpp73
-rw-r--r--src/candy_gear/sound.hpp32
-rw-r--r--src/candy_gear/sprite.cpp93
-rw-r--r--src/candy_gear/sprite.hpp27
-rw-r--r--src/candy_gear/sprite_3d.cpp82
-rw-r--r--src/candy_gear/sprite_3d.hpp25
-rw-r--r--src/candy_gear/static_mesh.cpp67
-rw-r--r--src/candy_gear/static_mesh.hpp27
-rw-r--r--src/candy_gear/static_model.cpp134
-rw-r--r--src/candy_gear/static_model.hpp25
-rw-r--r--src/candy_gear/texture.cpp121
-rw-r--r--src/candy_gear/texture.hpp28
-rw-r--r--src/candy_gear/vector_3d.cpp302
-rw-r--r--src/candy_gear/vector_3d.hpp34
-rw-r--r--src/candy_gear/vector_4d.cpp479
-rw-r--r--src/candy_gear/vector_4d.hpp27
-rw-r--r--src/candy_gear/view_2d.cpp90
-rw-r--r--src/candy_gear/view_2d.hpp33
-rw-r--r--src/candy_gear/view_3d.cpp127
-rw-r--r--src/candy_gear/view_3d.hpp27
37 files changed, 3329 insertions, 0 deletions
diff --git a/src/candy_gear/candy_gear.cpp b/src/candy_gear/candy_gear.cpp
new file mode 100644
index 0000000..b1758a4
--- /dev/null
+++ b/src/candy_gear/candy_gear.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2022-2024 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "candy_gear.hpp"
+
+#include <mruby/array.h>
+#include <mruby/hash.h>
+#include <mruby/string.h>
+
+#include "core.hpp"
+#include "view_2d.hpp"
+#include "view_3d.hpp"
+
+static mrb_value
+cg_mCandyGear_set_game_name(mrb_state *mrb, mrb_value self)
+{
+ mrb_value name;
+
+ mrb_get_args(mrb, "S", &name);
+ BluCat::INT::core.game_name = RSTRING_PTR(name);
+
+ return self;
+}
+
+static mrb_value
+cg_mCandyGear_set_views(mrb_state *mrb, mrb_value self)
+{
+ struct RClass *cg_m, *cg_cView2D, *cg_cView3D;
+ mrb_value *array;
+ mrb_int array_len;
+
+ std::vector<std::shared_ptr<BluCat::GRA::View2D>> views_2d;
+ std::vector<std::shared_ptr<BluCat::GRA::View3D>> views_3d;
+
+ cg_m = mrb_module_get(mrb, "CandyGear");
+ cg_cView2D = mrb_class_get_under(mrb, cg_m, "View2D");
+ cg_cView3D = mrb_class_get_under(mrb, cg_m, "View3D");
+
+ mrb_get_args(mrb, "a", &array, &array_len);
+ for(mrb_int i{0}; i < array_len; i++)
+ {
+ if(mrb_obj_is_kind_of(mrb, array[i], cg_cView2D))
+ {
+ auto v = (std::shared_ptr<BluCat::GRA::View2D>*)DATA_PTR(array[i]);
+ views_2d.push_back(*v);
+ }
+ else if(mrb_obj_is_kind_of(mrb, array[i], cg_cView3D))
+ {
+ auto v = (std::shared_ptr<BluCat::GRA::View3D>*)DATA_PTR(array[i]);
+ views_3d.push_back(*v);
+ }
+ }
+
+ // A Renderer need at least one view to work.
+ if(views_2d.size() > 0 || views_3d.size() > 0)
+ {
+ delete BluCat::INT::core.vk_renderer;
+ BluCat::INT::core.vk_renderer = new BluCat::GRA::Renderer({views_2d, views_3d});
+ }
+
+ return self;
+}
+
+static mrb_value
+cg_mCandyGear_log(mrb_state *mrb, mrb_value self)
+{
+ const char *message;
+ mrb_sym sym_log_level;
+ Log::Level log_lvl;
+
+ mrb_get_args(mrb, "nz", &sym_log_level, &message);
+
+ if(sym_log_level == cg_core.sym_trace)
+ log_lvl = Log::Level::Trace;
+ else if(sym_log_level == cg_core.sym_debug)
+ log_lvl = Log::Level::Debug;
+ else if(sym_log_level == cg_core.sym_information)
+ log_lvl = Log::Level::Information;
+ else if(sym_log_level == cg_core.sym_warning)
+ log_lvl = Log::Level::Warning;
+ else if(sym_log_level == cg_core.sym_error)
+ log_lvl = Log::Level::Error;
+ else
+ log_lvl = Log::Level::Fatal;
+
+ BluCat::INT::core.log.message(log_lvl, message);
+
+ return self;
+}
+
+static mrb_value
+cg_mCandyGear_quit(mrb_state *mrb, mrb_value self)
+{
+ cg_core.quit_game = true;
+
+ return self;
+}
+
+void
+cg_candy_gear_init_config(mrb_state *mrb)
+{
+ struct RClass *cg_m;
+
+ cg_m = mrb_module_get(mrb, "CandyGear");
+
+ mrb_define_class_method(
+ mrb, cg_m, "game_name=", cg_mCandyGear_set_game_name, MRB_ARGS_REQ(1));
+}
+
+void
+cg_candy_gear_finish_config(mrb_state *mrb)
+{
+ struct RClass *cg_m;
+
+ cg_m = mrb_module_get(mrb, "CandyGear");
+
+ mrb_undef_class_method(mrb, cg_m, "game_name=");
+}
+
+void
+cg_candy_gear_init(mrb_state *mrb)
+{
+ struct RClass *cg_m;
+
+ cg_m = mrb_module_get(mrb, "CandyGear");
+
+ mrb_define_class_method(
+ mrb, cg_m, "views=", cg_mCandyGear_set_views, MRB_ARGS_REQ(1));
+ mrb_define_class_method(
+ mrb, cg_m, "log", cg_mCandyGear_log, MRB_ARGS_REQ(2));
+ mrb_define_class_method(
+ mrb, cg_m, "quit", cg_mCandyGear_quit, MRB_ARGS_NONE());
+}
diff --git a/src/candy_gear/candy_gear.hpp b/src/candy_gear/candy_gear.hpp
new file mode 100644
index 0000000..3ec92b3
--- /dev/null
+++ b/src/candy_gear/candy_gear.hpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CANDY_GEAR_CANDY_GEAR_H
+#define CANDY_GEAR_CANDY_GEAR_H 1
+
+#include "core.hpp"
+
+// Provides the basic interface the game needs to configure the engine's
+// initialization.
+void
+cg_candy_gear_init_config(mrb_state *mrb);
+void
+cg_candy_gear_finish_config(mrb_state *mrb);
+
+void
+cg_candy_gear_init(mrb_state *mrb);
+
+#endif /* CANDY_GEAR_CANDY_GEAR_H */
diff --git a/src/candy_gear/core.cpp b/src/candy_gear/core.cpp
new file mode 100644
index 0000000..28f5a15
--- /dev/null
+++ b/src/candy_gear/core.cpp
@@ -0,0 +1,398 @@
+/*
+ * Copyright 2022-2024 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "core.hpp"
+
+#include "candy_gear.hpp"
+#include "font.hpp"
+#include "graphic.hpp"
+#include "key.hpp"
+#include "orientation_3d.hpp"
+#include "skeletal_model.hpp"
+#include "skeletal_mesh.hpp"
+#include "static_model.hpp"
+#include "static_mesh.hpp"
+#include "sound.hpp"
+#include "sprite.hpp"
+#include "sprite_3d.hpp"
+#include "texture.hpp"
+#include "vector_3d.hpp"
+#include "vector_4d.hpp"
+#include "view_2d.hpp"
+#include "view_3d.hpp"
+
+#ifdef DEBUG
+#include <sstream>
+#endif
+
+namespace
+{
+
+void
+load_mruby_symbols(void *obj)
+{
+ cg_core.sym_config = mrb_intern_cstr(cg_core.mrb, "config");
+ cg_core.sym_debug = mrb_intern_cstr(cg_core.mrb, "debug");
+ cg_core.sym_error = mrb_intern_cstr(cg_core.mrb, "error");
+ cg_core.sym_fatal = mrb_intern_cstr(cg_core.mrb, "fatal");
+ cg_core.sym_information = mrb_intern_cstr(cg_core.mrb, "information");
+ cg_core.sym_init = mrb_intern_cstr(cg_core.mrb, "init");
+ cg_core.sym_key_down = mrb_intern_cstr(cg_core.mrb, "key_down");
+ cg_core.sym_key_up = mrb_intern_cstr(cg_core.mrb, "key_up");
+ cg_core.sym_quit = mrb_intern_cstr(cg_core.mrb, "quit");
+ cg_core.sym_tick = mrb_intern_cstr(cg_core.mrb, "tick");
+ cg_core.sym_trace = mrb_intern_cstr(cg_core.mrb, "trace");
+ cg_core.sym_warning = mrb_intern_cstr(cg_core.mrb, "warning");
+}
+
+void
+load_game(void *obj)
+{
+ FILE *fp;
+ mrb_value main_obj{mrb_top_self(cg_core.mrb)};
+
+ BluCat::INT::core.game_name = "CandyGear Game";
+
+ BluCat::INT::core.display_width = 800;
+ BluCat::INT::core.display_height = 600;
+
+ BluCat::INT::core.game_version_major = 0;
+ BluCat::INT::core.game_version_minor = 1;
+ BluCat::INT::core.game_version_patch = 0;
+
+ BluCat::INT::core.fps = 30;
+
+ mrb_define_module(cg_core.mrb, "CandyGear");
+ cg_candy_gear_init_config(cg_core.mrb);
+ cg_graphic_init_config(cg_core.mrb);
+
+ fp = fopen(cg_core.game_file.c_str(), "rb");
+ mrb_load_irep_file(cg_core.mrb, fp);
+ fclose(fp);
+ if (cg_core.mrb->exc)
+ {
+ mrb_print_error(cg_core.mrb);
+ throw CommandError{"Error loading game."};
+ }
+
+ mrb_funcall_id(cg_core.mrb, main_obj, cg_core.sym_config, 0);
+ if (cg_core.mrb->exc)
+ {
+ mrb_print_error(cg_core.mrb);
+ throw CommandError{"Error configuring game."};
+ }
+
+ cg_candy_gear_finish_config(cg_core.mrb);
+ cg_graphic_finish_config(cg_core.mrb);
+}
+
+void
+load_sdl(void *obj)
+{
+ if(SDL_Init(SDL_INIT_EVERYTHING) < 0)
+ {
+ std::string error{"SDL could not initialize! SDL Error → "};
+ error += SDL_GetError();
+ throw error;
+ }
+
+ if(SDL_Vulkan_LoadLibrary(nullptr) != 0)
+ {
+ SDL_Quit();
+ std::string error{"SDL could not initialize Vulkan! SDL_Error → "};
+ error += SDL_GetError();
+ throw CommandError{error};
+ }
+}
+
+void
+unload_sdl(void *obj)
+{
+ SDL_Vulkan_UnloadLibrary();
+ SDL_Quit();
+}
+
+void
+load_sdl_mixer(void *obj)
+{
+ int flags = MIX_INIT_OGG|MIX_INIT_MOD;
+ int initted = Mix_Init(flags);
+ if(initted&flags != flags)
+ {
+ std::string error{"Could not initialize SDL mixer → "};
+ error += Mix_GetError();
+ throw CommandError{error};
+ }
+}
+
+void
+unload_sdl_mixer(void *obj)
+{
+ while(Mix_Init(0)) Mix_Quit();
+}
+
+void
+load_sdl_open_audio(void *obj)
+{
+ if(Mix_OpenAudio(22050, MIX_DEFAULT_FORMAT, 2, 1024) == -1)
+ {
+ std::string error{"Could not open SDL mixer audio → "};
+ error += Mix_GetError();
+ throw CommandError{error};
+ }
+}
+
+void
+unload_sdl_open_audio(void *obj)
+{
+ Mix_CloseAudio();
+}
+
+void
+load_window(void *obj)
+{
+ cg_core.window = NULL;
+ cg_core.window = SDL_CreateWindow(
+ BluCat::INT::core.game_name.c_str(),
+ SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
+ BluCat::INT::core.display_width, BluCat::INT::core.display_height,
+ SDL_WINDOW_VULKAN);
+ if(cg_core.window == NULL)
+ {
+ std::string error{"Window could not be created! SDL_Error → "};
+ error += SDL_GetError();
+ throw CommandError{error};
+ }
+}
+
+void
+unload_window(void *obj)
+{
+ SDL_DestroyWindow(cg_core.window);
+}
+
+void
+load_vk_instance(void *obj)
+{
+ std::vector<const char*> vk_extensions;
+ std::vector<const char*> vk_required_layers_names;
+
+ // Get extensions.
+ {
+ uint32_t vk_extensions_count;
+ std::vector<const char*> vk_required_extensions;
+
+ // Load debuging layers.
+#ifdef DEBUG
+ vk_required_extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
+ vk_required_layers_names.push_back("VK_LAYER_KHRONOS_validation");
+#endif
+
+ // Get extensions for SDL.
+ {
+ uint32_t vk_sdl_extension_count;
+ std::vector<const char*> vk_sdl_extensions;
+
+ if(!SDL_Vulkan_GetInstanceExtensions(
+ cg_core.window, &vk_sdl_extension_count, nullptr))
+ {
+ std::string error{
+ "Vulkan extensions could not be loaded by SDL! SDL_Error: "};
+ error += SDL_GetError();
+ throw CommandError{error};
+ }
+
+ vk_sdl_extensions.resize(vk_sdl_extension_count);
+ SDL_Vulkan_GetInstanceExtensions(
+ cg_core.window, &vk_sdl_extension_count, vk_sdl_extensions.data());
+
+ // Combine all extensions.
+ vk_extensions_count = vk_sdl_extension_count +
+ vk_required_extensions.size();
+ vk_extensions.resize(vk_extensions_count);
+ for(auto i{0}; i < vk_sdl_extension_count; i++)
+ vk_extensions[i] = vk_sdl_extensions[i];
+ for(auto i{0}; i < vk_required_extensions.size(); i++)
+ vk_extensions[i + vk_sdl_extension_count] = vk_required_extensions[i];
+ }
+
+#ifdef DEBUG
+ BluCat::INT::core.log.message(Log::Level::Trace, "Enabled VK extensions.");
+ for(auto vk_extension: vk_extensions)
+ {
+ std::string message{"Extension name: "};
+ message += vk_extension;
+ BluCat::INT::core.log.message(Log::Level::Trace, message);
+ }
+#endif
+ }
+
+ // Get available instance layers.
+ {
+ uint32_t vk_available_layers_count;
+ std::vector<VkLayerProperties> vk_available_layers;
+ std::vector<const char*> vk_available_layers_names;
+
+ vkEnumerateInstanceLayerProperties(&vk_available_layers_count, nullptr);
+ vk_available_layers.resize(vk_available_layers_count);
+ vkEnumerateInstanceLayerProperties(&vk_available_layers_count,
+ vk_available_layers.data());
+ vk_available_layers_names.resize(vk_available_layers_count);
+#ifdef DEBUG
+ BluCat::INT::core.log.message(
+ Log::Level::Trace, "Available VK instance layers.");
+#endif
+ for(uint32_t i = 0; i < vk_available_layers_count; i++)
+ {
+#ifdef DEBUG
+ std::stringstream message{};
+ message << "\nname: " << vk_available_layers[i].layerName << std::endl;
+ message << "Description: " << vk_available_layers[i].description <<
+ std::endl;
+ message << "Spec version: " << vk_available_layers[i].specVersion <<
+ std::endl;
+ message << "Implementation version: " <<
+ vk_available_layers[i].implementationVersion << std::endl;
+ BluCat::INT::core.log.message(Log::Level::Trace, message.str());
+#endif
+
+ vk_available_layers_names[i] = vk_available_layers[i].layerName;
+ }
+
+ // If required layers are not all available.
+ if(!std::includes(
+ vk_available_layers_names.begin(), vk_available_layers_names.end(),
+ vk_required_layers_names.begin(), vk_required_layers_names.begin()))
+ throw CommandError{"Some required Vulkan layers are not available."};
+ }
+
+ {
+ VkApplicationInfo app_info;
+ app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
+ app_info.pNext = nullptr;
+ app_info.pApplicationName = BluCat::INT::core.game_name.c_str();
+ app_info.applicationVersion = VK_MAKE_VERSION(
+ BluCat::INT::core.game_version_major,
+ BluCat::INT::core.game_version_minor,
+ BluCat::INT::core.game_version_patch);
+ app_info.pEngineName = "BluCat::GRA";
+ app_info.engineVersion = VK_MAKE_VERSION(
+ BLU_CAT_VERSION_MAJOR,
+ BLU_CAT_VERSION_MINOR,
+ BLU_CAT_VERSION_PATCH);
+ app_info.apiVersion = VK_API_VERSION_1_0;
+
+ VkInstanceCreateInfo create_info;
+ create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
+ create_info.pNext = nullptr;
+ create_info.flags = 0;
+ create_info.pApplicationInfo = &app_info;
+ create_info.enabledExtensionCount = vk_extensions.size();
+ create_info.ppEnabledExtensionNames = vk_extensions.data();
+ create_info.enabledLayerCount = vk_required_layers_names.size();
+ create_info.ppEnabledLayerNames = vk_required_layers_names.data();
+
+ VkResult result =
+ vkCreateInstance(&create_info, nullptr, &BluCat::INT::core.vk_instance);
+ if(result != VK_SUCCESS)
+ {
+ std::string error{""};
+ switch(result)
+ {
+ case VK_ERROR_LAYER_NOT_PRESENT:
+ error = " Layer not present.";
+ break;
+ case VK_ERROR_EXTENSION_NOT_PRESENT:
+ error = " Extension not present.";
+ }
+ error = "Failed to create Vulkan instance." + error;
+ throw CommandError{error};
+ }
+ }
+}
+
+void
+unload_vk_instance(void *obj)
+{
+ vkDestroyInstance(BluCat::INT::core.vk_instance, nullptr);
+}
+
+void
+load_window_surface(void *obj)
+{
+ if(!SDL_Vulkan_CreateSurface(
+ cg_core.window, BluCat::INT::core.vk_instance, &BluCat::INT::core.window_surface))
+ {
+ std::string error{"Failed to create a window surface → "};
+ error += SDL_GetError();
+ throw CommandError{error};
+ }
+}
+
+void
+unload_window_surface(void *obj)
+{
+ vkDestroySurfaceKHR(
+ BluCat::INT::core.vk_instance, BluCat::INT::core.window_surface, nullptr);
+}
+
+void
+load_blucat(void *obj)
+{
+ BluCat::INT::core.loader.execute(nullptr);
+}
+
+void
+unload_blucat(void *obj)
+{
+ BluCat::INT::core.loader.revert(nullptr);
+}
+
+void
+load_mruby_interface(void *obj)
+{
+ cg_candy_gear_init(cg_core.mrb);
+ cg_font_init(cg_core.mrb);
+ cg_key_init(cg_core.mrb);
+ cg_orientation_3d_init(cg_core.mrb);
+ cg_skeletal_model_init(cg_core.mrb);
+ cg_skeletal_mesh_init(cg_core.mrb);
+ cg_static_model_init(cg_core.mrb);
+ cg_static_mesh_init(cg_core.mrb);
+ cg_sound_init(cg_core.mrb);
+ cg_sprite_init(cg_core.mrb);
+ cg_sprite_3d_init(cg_core.mrb);
+ cg_texture_init(cg_core.mrb);
+ cg_vector_3d_init(cg_core.mrb);
+ cg_vector_4d_init(cg_core.mrb);
+ cg_view_2d_init(cg_core.mrb);
+ cg_view_3d_init(cg_core.mrb);
+}
+
+}
+
+const CommandChain cg_sCore::loader{
+ {&load_mruby_symbols, nullptr},
+ {&load_game, nullptr},
+ {&load_sdl, &unload_sdl},
+ {&load_sdl_mixer, &unload_sdl_mixer},
+ {&load_sdl_open_audio, &unload_sdl_open_audio},
+ {&load_window, &unload_window},
+ {&load_vk_instance, &unload_vk_instance},
+ {&load_window_surface, &unload_window_surface},
+ {&load_blucat, &unload_blucat},
+ {&load_mruby_interface, nullptr}
+};
diff --git a/src/candy_gear/core.hpp b/src/candy_gear/core.hpp
new file mode 100644
index 0000000..60d8339
--- /dev/null
+++ b/src/candy_gear/core.hpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2022-2024 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CANDY_GEAR_CORE_H
+#define CANDY_GEAR_CORE_H 1
+
+#include <mruby.h>
+#include <mruby/class.h>
+#include <mruby/compile.h>
+#include <mruby/data.h>
+#include <mruby/dump.h>
+#include <mruby/variable.h>
+
+#define SDL_MAIN_HANDLED
+
+#ifdef _WIN64
+#include <Windows.h>
+#endif
+
+#include <SDL2/SDL.h>
+#include <SDL2/SDL_vulkan.h>
+#include <SDL2/SDL_mixer.h>
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+#include "../blu_cat/int/core.hpp"
+
+/**
+ * The Core class stores all global states that the engine needs to work.
+ * Global variables are not evil if you use them carefully.
+ */
+struct cg_sCore
+{
+ static const CommandChain loader;
+
+ mrb_state *mrb;
+
+ std::string game_file;
+
+ /// mruby symbols
+ mrb_sym sym_config, sym_debug, sym_error, sym_fatal, sym_information,
+ sym_init, sym_key_down, sym_key_up, sym_quit, sym_tick, sym_trace,
+ sym_warning;
+
+ bool quit_game;
+
+ SDL_Window *window;
+};
+
+extern cg_sCore cg_core;
+
+#endif /* CANDY_GEAR_CORE_H */
diff --git a/src/candy_gear/font.cpp b/src/candy_gear/font.cpp
new file mode 100644
index 0000000..f9cbf44
--- /dev/null
+++ b/src/candy_gear/font.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2022-2024 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "font.hpp"
+
+#include "../blu_cat/gra/font.hpp"
+
+void
+cg_free_font(mrb_state *mrb, void *obj)
+{
+ auto ptr = static_cast<BluCat::GRA::Font*>(obj);
+
+ ptr->~Font();
+ mrb_free(mrb, ptr);
+}
+
+const struct mrb_data_type
+cg_font_type = {"CG_Font", cg_free_font};
+
+static mrb_value
+cg_cFont_initialize(mrb_state *mrb, mrb_value self)
+{
+ BluCat::GRA::Font *ptr;
+ const char *font_path;
+ mrb_int font_size;
+
+ mrb_get_args(mrb, "zi", &font_path, &font_size);
+ ptr = (BluCat::GRA::Font*)DATA_PTR(self);
+ if(ptr) mrb_free(mrb, ptr);
+ ptr = (BluCat::GRA::Font*)mrb_malloc(mrb, sizeof(BluCat::GRA::Font));
+
+ try
+ {
+ new(ptr)BluCat::GRA::Font(font_path, font_size);
+ }
+ catch(const std::invalid_argument &e)
+ {
+ mrb_raise(mrb, E_RUNTIME_ERROR, e.what());
+ }
+
+ mrb_data_init(self, ptr, &cg_font_type);
+ return self;
+}
+
+void
+cg_font_init(mrb_state *mrb)
+{
+ struct RClass *cg_m, *cg_cFont;
+
+ cg_m = mrb_module_get(mrb, "CandyGear");
+ cg_cFont = mrb_define_class_under(mrb, cg_m, "Font", mrb->object_class);
+ MRB_SET_INSTANCE_TT(cg_cFont, MRB_TT_DATA);
+ mrb_define_method(
+ mrb, cg_cFont, "initialize", cg_cFont_initialize, MRB_ARGS_REQ(2));
+}
diff --git a/src/candy_gear/font.hpp b/src/candy_gear/font.hpp
new file mode 100644
index 0000000..0128ea0
--- /dev/null
+++ b/src/candy_gear/font.hpp
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2022-2023 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CANDY_GEAR_FONT_H
+#define CANDY_GEAR_FONT_H 1
+
+#include "core.hpp"
+
+extern const struct mrb_data_type
+cg_font_type;
+
+void
+cg_font_init(mrb_state *mrb);
+
+#endif /* CANDY_GEAR_FONT_H */
diff --git a/src/candy_gear/graphic.cpp b/src/candy_gear/graphic.cpp
new file mode 100644
index 0000000..ad701d1
--- /dev/null
+++ b/src/candy_gear/graphic.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "graphic.hpp"
+
+#include "core.hpp"
+
+static mrb_value
+cg_mCandyGear_set_display_width(mrb_state *mrb, mrb_value self)
+{
+ mrb_int width;
+
+ mrb_get_args(mrb, "i", &width);
+ BluCat::INT::core.display_width = width;
+
+ return self;
+}
+
+static mrb_value
+cg_mCandyGear_set_display_height(mrb_state *mrb, mrb_value self)
+{
+ mrb_int height;
+
+ mrb_get_args(mrb, "i", &height);
+ BluCat::INT::core.display_height = height;
+
+ return self;
+}
+
+static mrb_value
+cg_mCandyGear_set_fps(mrb_state *mrb, mrb_value self)
+{
+ mrb_int fps;
+
+ mrb_get_args(mrb, "i", &fps);
+ BluCat::INT::core.fps = fps;
+
+ return self;
+}
+
+void
+cg_graphic_init_config(mrb_state *mrb)
+{
+ struct RClass *cg_m, *cg_mGraphic;
+
+ cg_m = mrb_module_get(mrb, "CandyGear");
+ cg_mGraphic = mrb_define_module_under(mrb, cg_m, "Graphic");
+
+ mrb_define_class_method(
+ mrb, cg_mGraphic, "display_width=", cg_mCandyGear_set_display_width,
+ MRB_ARGS_REQ(1));
+ mrb_define_class_method(
+ mrb, cg_mGraphic, "display_height=", cg_mCandyGear_set_display_height,
+ MRB_ARGS_REQ(1));
+ mrb_define_class_method(
+ mrb, cg_mGraphic, "fps=", cg_mCandyGear_set_fps, MRB_ARGS_REQ(1));
+}
+
+void
+cg_graphic_finish_config(mrb_state *mrb)
+{
+ struct RClass *cg_m, *cg_mGraphic;
+
+ cg_m = mrb_module_get(mrb, "CandyGear");
+ cg_mGraphic = mrb_module_get_under(mrb, cg_m, "Graphic");
+
+ mrb_undef_class_method(mrb, cg_mGraphic, "display_width=");
+ mrb_undef_class_method(mrb, cg_mGraphic, "display_height=");
+ mrb_undef_class_method(mrb, cg_mGraphic, "fps=");
+}
diff --git a/src/candy_gear/graphic.hpp b/src/candy_gear/graphic.hpp
new file mode 100644
index 0000000..fbd9df8
--- /dev/null
+++ b/src/candy_gear/graphic.hpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CANDY_GEAR_GRAPHIC_H
+#define CANDY_GEAR_GRAPHIC_H 1
+
+#include "core.hpp"
+
+// Provides the basic interface the game needs to configure the engine's
+// initialization.
+void
+cg_graphic_init_config(mrb_state *mrb);
+void
+cg_graphic_finish_config(mrb_state *mrb);
+
+#endif /* CANDY_GEAR_GRAPHIC_H */
diff --git a/src/candy_gear/key.cpp b/src/candy_gear/key.cpp
new file mode 100644
index 0000000..615119c
--- /dev/null
+++ b/src/candy_gear/key.cpp
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "key.hpp"
+
+void
+cg_key_init(mrb_state *mrb)
+{
+ struct RClass *cg_m, *cg_mKey;
+
+ cg_m = mrb_module_get(mrb, "CandyGear");
+ cg_mKey = mrb_define_module_under(mrb, cg_m, "Key");
+
+ mrb_define_const(mrb, cg_mKey, "A", mrb_int_value(mrb, SDLK_a));
+ mrb_define_const(mrb, cg_mKey, "B", mrb_int_value(mrb, SDLK_b));
+ mrb_define_const(mrb, cg_mKey, "C", mrb_int_value(mrb, SDLK_c));
+ mrb_define_const(mrb, cg_mKey, "D", mrb_int_value(mrb, SDLK_d));
+ mrb_define_const(mrb, cg_mKey, "E", mrb_int_value(mrb, SDLK_e));
+ mrb_define_const(mrb, cg_mKey, "F", mrb_int_value(mrb, SDLK_f));
+ mrb_define_const(mrb, cg_mKey, "G", mrb_int_value(mrb, SDLK_g));
+ mrb_define_const(mrb, cg_mKey, "H", mrb_int_value(mrb, SDLK_h));
+ mrb_define_const(mrb, cg_mKey, "I", mrb_int_value(mrb, SDLK_i));
+ mrb_define_const(mrb, cg_mKey, "J", mrb_int_value(mrb, SDLK_j));
+ mrb_define_const(mrb, cg_mKey, "K", mrb_int_value(mrb, SDLK_k));
+ mrb_define_const(mrb, cg_mKey, "L", mrb_int_value(mrb, SDLK_l));
+ mrb_define_const(mrb, cg_mKey, "M", mrb_int_value(mrb, SDLK_m));
+ mrb_define_const(mrb, cg_mKey, "N", mrb_int_value(mrb, SDLK_n));
+ mrb_define_const(mrb, cg_mKey, "O", mrb_int_value(mrb, SDLK_o));
+ mrb_define_const(mrb, cg_mKey, "P", mrb_int_value(mrb, SDLK_p));
+ mrb_define_const(mrb, cg_mKey, "Q", mrb_int_value(mrb, SDLK_q));
+ mrb_define_const(mrb, cg_mKey, "R", mrb_int_value(mrb, SDLK_r));
+ mrb_define_const(mrb, cg_mKey, "S", mrb_int_value(mrb, SDLK_s));
+ mrb_define_const(mrb, cg_mKey, "T", mrb_int_value(mrb, SDLK_t));
+ mrb_define_const(mrb, cg_mKey, "U", mrb_int_value(mrb, SDLK_u));
+ mrb_define_const(mrb, cg_mKey, "V", mrb_int_value(mrb, SDLK_v));
+ mrb_define_const(mrb, cg_mKey, "W", mrb_int_value(mrb, SDLK_w));
+ mrb_define_const(mrb, cg_mKey, "X", mrb_int_value(mrb, SDLK_x));
+ mrb_define_const(mrb, cg_mKey, "Y", mrb_int_value(mrb, SDLK_y));
+ mrb_define_const(mrb, cg_mKey, "Z", mrb_int_value(mrb, SDLK_z));
+
+ mrb_define_const(mrb, cg_mKey, "UP", mrb_int_value(mrb, SDLK_UP));
+ mrb_define_const(mrb, cg_mKey, "DOWN", mrb_int_value(mrb, SDLK_DOWN));
+ mrb_define_const(mrb, cg_mKey, "LEFT", mrb_int_value(mrb, SDLK_LEFT));
+ mrb_define_const(mrb, cg_mKey, "RIGHT", mrb_int_value(mrb, SDLK_RIGHT));
+
+ mrb_define_const(
+ mrb, cg_mKey, "BACKSPACE", mrb_int_value(mrb, SDLK_BACKSPACE));
+ mrb_define_const(mrb, cg_mKey, "TAB", mrb_int_value(mrb, SDLK_TAB));
+ mrb_define_const(
+ mrb, cg_mKey, "LEFT_SHIFT", mrb_int_value(mrb, SDLK_LSHIFT));
+ mrb_define_const(
+ mrb, cg_mKey, "RIGHT_SHIFT", mrb_int_value(mrb, SDLK_RSHIFT));
+ mrb_define_const(mrb, cg_mKey, "SPACE", mrb_int_value(mrb, SDLK_SPACE));
+ mrb_define_const(mrb, cg_mKey, "LEFT_ALT", mrb_int_value(mrb, SDLK_LALT));
+ mrb_define_const(mrb, cg_mKey, "RIGHT_ALT", mrb_int_value(mrb, SDLK_RALT));
+ mrb_define_const(mrb, cg_mKey, "LEFT_CTRL", mrb_int_value(mrb, SDLK_LCTRL));
+ mrb_define_const(mrb, cg_mKey, "RIGHT_CTRL", mrb_int_value(mrb, SDLK_RCTRL));
+}
diff --git a/src/candy_gear/key.hpp b/src/candy_gear/key.hpp
new file mode 100644
index 0000000..d3f4f5f
--- /dev/null
+++ b/src/candy_gear/key.hpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CANDY_GEAR_KEY_H
+#define CANDY_GEAR_KEY_H 1
+
+#include "core.hpp"
+
+void
+cg_key_init(mrb_state *mrb);
+
+#endif /* CANDY_GEAR_KEY_H */
diff --git a/src/candy_gear/main.cpp b/src/candy_gear/main.cpp
new file mode 100644
index 0000000..353b6bf
--- /dev/null
+++ b/src/candy_gear/main.cpp
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2022-2024 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <chrono>
+#include <thread>
+
+#include "core.hpp"
+
+cg_sCore cg_core;
+
+int main(int argc, char *argv[])
+{
+ using namespace std::chrono;
+ using namespace std::this_thread;
+
+ SDL_Event event;
+
+ // Random numbers
+ BluCat::INT::random_number_generator.seed(BluCat::INT::random_seed());
+
+ cg_core.game_file = argv[1];
+ cg_core.mrb = mrb_open();
+ if (!cg_core.mrb) throw CommandError{"Failed to initialize mruby."};
+ try{ cg_sCore::loader.execute(nullptr); }
+ catch(const CommandError &error)
+ {
+ BluCat::INT::core.log.message(Log::Level::Fatal, error.what());
+ mrb_close(cg_core.mrb);
+ return 1;
+ }
+
+ mrb_value main_obj{mrb_top_self(cg_core.mrb)};
+
+ mrb_funcall_id(cg_core.mrb, main_obj, cg_core.sym_init, 0);
+ if (cg_core.mrb->exc)
+ {
+ mrb_print_error(cg_core.mrb);
+ mrb_close(cg_core.mrb);
+ cg_sCore::loader.revert(nullptr);
+ return 1;
+ }
+
+ auto frame_start = steady_clock::now();
+
+ // Game main loop.
+ while(!cg_core.quit_game)
+ {
+ // Get input.
+ while(SDL_PollEvent(&event) != 0)
+ {
+ switch(event.type)
+ {
+ case SDL_KEYDOWN:
+ mrb_funcall_id(
+ cg_core.mrb, main_obj, cg_core.sym_key_down, 1,
+ mrb_int_value(cg_core.mrb, event.key.keysym.sym));
+ break;
+ case SDL_KEYUP:
+ mrb_funcall_id(
+ cg_core.mrb, main_obj, cg_core.sym_key_up, 1,
+ mrb_int_value(cg_core.mrb, event.key.keysym.sym));
+ break;
+ case SDL_MOUSEMOTION:
+ break;
+ case SDL_MOUSEBUTTONDOWN:
+ break;
+ case SDL_MOUSEBUTTONUP:
+ break;
+ case SDL_QUIT:
+ mrb_funcall_id(cg_core.mrb, main_obj, cg_core.sym_quit, 0);
+ break;
+ }
+ }
+
+ mrb_funcall_id(cg_core.mrb, main_obj, cg_core.sym_tick, 0);
+ if (cg_core.mrb->exc)
+ {
+ mrb_print_error(cg_core.mrb);
+ cg_core.quit_game = true;
+ }
+ else
+ {
+ BluCat::INT::core.vk_renderer->draw();
+
+ { // Timer
+ auto frame_stop = steady_clock::now();
+ auto frame_duration = frame_stop - frame_start;
+
+ // If frame take less time than maximum allowed.
+ if(BluCat::INT::core.max_frame_duration > frame_duration)
+ sleep_for(BluCat::INT::core.max_frame_duration - frame_duration);
+
+ frame_start = frame_stop;
+ }
+ }
+ }
+
+ mrb_close(cg_core.mrb);
+ cg_sCore::loader.revert(nullptr);
+
+ return 0;
+}
diff --git a/src/candy_gear/orientation_3d.cpp b/src/candy_gear/orientation_3d.cpp
new file mode 100644
index 0000000..cb42984
--- /dev/null
+++ b/src/candy_gear/orientation_3d.cpp
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2022-2024 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define _USE_MATH_DEFINES
+
+#include "orientation_3d.hpp"
+
+#include "vector_3d.hpp"
+
+void
+cg_free_orientation_3d(mrb_state *mrb, void* obj)
+{
+ auto ptr = static_cast<std::shared_ptr<glm::quat>*>(obj);
+
+ ptr->~shared_ptr();
+ mrb_free(mrb, ptr);
+}
+
+const struct mrb_data_type cg_orientation_3d_type = {
+ "CG_Orientation3D", cg_free_orientation_3d};
+
+static mrb_value
+cg_cOrientation3D_initialize(mrb_state *mrb, mrb_value self)
+{
+ mrb_float x, y, z;
+ std::shared_ptr<glm::quat> *ptr;
+
+ mrb_get_args(mrb, "fff", &x, &y, &z);
+ ptr = (std::shared_ptr<glm::quat>*)DATA_PTR(self);
+ if(ptr) mrb_free(mrb, ptr);
+ ptr = (std::shared_ptr<glm::quat>*)mrb_malloc(
+ mrb, sizeof(std::shared_ptr<glm::quat>));
+
+ glm::vec3 angles(x, y, z);
+ new(ptr)std::shared_ptr<glm::quat>(
+ std::make_shared<glm::quat>(angles));
+ (**ptr) = glm::normalize(**ptr);
+
+ mrb_data_init(self, ptr, &cg_orientation_3d_type);
+ return self;
+}
+
+static mrb_value
+cg_cOrientation3D_get_w(mrb_state *mrb, mrb_value self)
+{
+ std::shared_ptr<glm::quat> *ptr =
+ (std::shared_ptr<glm::quat>*)DATA_PTR(self);
+
+ return mrb_float_value(mrb, (*ptr)->w);
+}
+
+static mrb_value
+cg_cOrientation3D_get_x(mrb_state *mrb, mrb_value self)
+{
+ std::shared_ptr<glm::quat> *ptr =
+ (std::shared_ptr<glm::quat>*)DATA_PTR(self);
+
+ return mrb_float_value(mrb, (*ptr)->x);
+}
+
+static mrb_value
+cg_cOrientation3D_get_y(mrb_state *mrb, mrb_value self)
+{
+ std::shared_ptr<glm::quat> *ptr =
+ (std::shared_ptr<glm::quat>*)DATA_PTR(self);
+
+ return mrb_float_value(mrb, (*ptr)->y);
+}
+
+static mrb_value
+cg_cOrientation3D_get_z(mrb_state *mrb, mrb_value self)
+{
+ std::shared_ptr<glm::quat> *ptr =
+ (std::shared_ptr<glm::quat>*)DATA_PTR(self);
+
+ return mrb_float_value(mrb, (*ptr)->z);
+}
+
+static mrb_value
+cg_cOrientation3D_rotate(mrb_state *mrb, mrb_value self)
+{
+ mrb_float x, y, z;
+ auto *ptr = (std::shared_ptr<glm::quat>*)DATA_PTR(self);
+
+ mrb_get_args(mrb, "fff", &x, &y, &z);
+
+ glm::vec3 angles(x, y, z);
+ glm::quat rot(angles);
+ (**ptr) *= rot;
+
+ // TODO: calling normalize for every rotation is expensive.
+ (**ptr) = glm::normalize(**ptr);
+
+ return self;
+}
+
+void
+cg_orientation_3d_init(mrb_state *mrb)
+{
+ struct RClass *cg_m, *cg_cOrientation3D;
+
+ cg_m = mrb_module_get(mrb, "CandyGear");
+ cg_cOrientation3D = mrb_define_class_under(
+ mrb, cg_m, "Orientation3D", mrb->object_class);
+ MRB_SET_INSTANCE_TT(cg_cOrientation3D, MRB_TT_DATA);
+ mrb_define_method(
+ mrb, cg_cOrientation3D, "initialize", cg_cOrientation3D_initialize,
+ MRB_ARGS_REQ(3));
+
+ mrb_define_method(
+ mrb, cg_cOrientation3D, "w", cg_cOrientation3D_get_w, MRB_ARGS_NONE());
+ mrb_define_method(
+ mrb, cg_cOrientation3D, "x", cg_cOrientation3D_get_x, MRB_ARGS_NONE());
+ mrb_define_method(
+ mrb, cg_cOrientation3D, "y", cg_cOrientation3D_get_y, MRB_ARGS_NONE());
+ mrb_define_method(
+ mrb, cg_cOrientation3D, "z", cg_cOrientation3D_get_z, MRB_ARGS_NONE());
+
+ mrb_define_method(
+ mrb, cg_cOrientation3D, "rotate", cg_cOrientation3D_rotate,
+ MRB_ARGS_REQ(3));
+}
diff --git a/src/candy_gear/orientation_3d.hpp b/src/candy_gear/orientation_3d.hpp
new file mode 100644
index 0000000..45a8ab2
--- /dev/null
+++ b/src/candy_gear/orientation_3d.hpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2022-2024 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CANDY_GEAR_ORIENTATION_3D_H
+#define CANDY_GEAR_ORIENTATION_3D_H 1
+
+#include "core.hpp"
+
+extern const struct mrb_data_type cg_orientation_3d_type;
+
+void
+cg_orientation_3d_init(mrb_state *mrb);
+
+#endif /* CANDY_GEAR_ORIENTATION_3D_H */
diff --git a/src/candy_gear/skeletal_mesh.cpp b/src/candy_gear/skeletal_mesh.cpp
new file mode 100644
index 0000000..4af4419
--- /dev/null
+++ b/src/candy_gear/skeletal_mesh.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2022-2024 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "skeletal_mesh.hpp"
+
+#include "orientation_3d.hpp"
+#include "vector_3d.hpp"
+#include "../blu_cat/gra/skeletal_mesh.hpp"
+
+void
+cg_free_skeletal_mesh(mrb_state *mrb, void* obj)
+{
+ auto ptr = static_cast<std::shared_ptr<BluCat::GRA::SkeletalMesh>*>(obj);
+
+ ptr->~shared_ptr();
+ mrb_free(mrb, ptr);
+}
+
+const struct mrb_data_type cg_skeletal_mesh_type = {
+ "CG_SkeletalMesh", cg_free_skeletal_mesh };
+
+static mrb_value
+cg_cSkeletalMesh_initialize(mrb_state *mrb, mrb_value self)
+{
+ const char *file_path;
+
+ std::shared_ptr<BluCat::GRA::SkeletalMesh> *ptr;
+
+ mrb_get_args(mrb, "z", &file_path);
+ ptr = (std::shared_ptr<BluCat::GRA::SkeletalMesh>*)DATA_PTR(self);
+ if(ptr) mrb_free(mrb, ptr);
+ ptr = (std::shared_ptr<BluCat::GRA::SkeletalMesh>*)mrb_malloc(
+ mrb, sizeof(std::shared_ptr<BluCat::GRA::SkeletalMesh>));
+
+ new(ptr)std::shared_ptr<BluCat::GRA::SkeletalMesh>(
+ std::make_shared<BluCat::GRA::SkeletalMesh>(file_path));
+
+ mrb_data_init(self, ptr, &cg_skeletal_mesh_type);
+ return self;
+}
+
+void
+cg_skeletal_mesh_init(mrb_state *mrb)
+{
+ struct RClass *cg_m, *cg_cSkeletalMesh;
+
+ cg_m = mrb_module_get(mrb, "CandyGear");
+ cg_cSkeletalMesh = mrb_define_class_under(
+ mrb, cg_m, "SkeletalMesh", mrb->object_class);
+ MRB_SET_INSTANCE_TT(cg_cSkeletalMesh, MRB_TT_DATA);
+ mrb_define_method(
+ mrb, cg_cSkeletalMesh, "initialize", cg_cSkeletalMesh_initialize,
+ MRB_ARGS_REQ(1));
+}
diff --git a/src/candy_gear/skeletal_mesh.hpp b/src/candy_gear/skeletal_mesh.hpp
new file mode 100644
index 0000000..476aa9b
--- /dev/null
+++ b/src/candy_gear/skeletal_mesh.hpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2022-2023 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CANDY_GEAR_SKELETAL_MESH_H
+#define CANDY_GEAR_SKELETAL_MESH_H 1
+
+#include "core.hpp"
+
+extern const struct mrb_data_type cg_skeletal_mesh_type;
+
+void
+cg_skeletal_mesh_init(mrb_state *mrb);
+
+#endif /* CANDY_GEAR_SKELETAL_MESH_H */
diff --git a/src/candy_gear/skeletal_model.cpp b/src/candy_gear/skeletal_model.cpp
new file mode 100644
index 0000000..41d0c9a
--- /dev/null
+++ b/src/candy_gear/skeletal_model.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2022-2024 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "skeletal_model.hpp"
+
+#include "orientation_3d.hpp"
+#include "vector_3d.hpp"
+#include "skeletal_mesh.hpp"
+#include "texture.hpp"
+#include "../blu_cat/gra/skeletal_model.hpp"
+
+void
+cg_free_skeletal_model(mrb_state *mrb, void *obj)
+{
+ auto ptr = static_cast<std::shared_ptr<BluCat::GRA::SkeletalModel>*>(obj);
+
+ ptr->~shared_ptr();
+ mrb_free(mrb, ptr);
+}
+
+const struct mrb_data_type cg_skeletal_model_type = {
+ "CG_SkeletalModel", cg_free_skeletal_model };
+
+static mrb_value
+cg_cSkeletalModel_initialize(mrb_state *mrb, mrb_value self)
+{
+ std::shared_ptr<BluCat::GRA::SkeletalMesh> *skeletal_mesh;
+ std::shared_ptr<BluCat::GRA::Texture> *texture;
+ std::shared_ptr<glm::vec3> *position;
+ std::shared_ptr<glm::quat> *orientation;
+ std::shared_ptr<BluCat::GRA::SkeletalModel> *ptr;
+
+ mrb_get_args(
+ mrb, "dddd", &skeletal_mesh, &cg_skeletal_mesh_type, &texture,
+ &cg_texture_type, &position, &cg_vector_3d_type, &orientation,
+ &cg_orientation_3d_type);
+ ptr = (std::shared_ptr<BluCat::GRA::SkeletalModel>*)DATA_PTR(self);
+ if(ptr) mrb_free(mrb, ptr);
+ ptr = (std::shared_ptr<BluCat::GRA::SkeletalModel>*)mrb_malloc(
+ mrb, sizeof(std::shared_ptr<BluCat::GRA::SkeletalModel>));
+
+ new(ptr)std::shared_ptr<BluCat::GRA::SkeletalModel>(
+ std::make_shared<BluCat::GRA::SkeletalModel>(
+ *skeletal_mesh, *texture, *position, *orientation));
+
+ mrb_data_init(self, ptr, &cg_skeletal_model_type);
+ return self;
+}
+
+static mrb_value
+cg_cSkeletalModel_set_orientation(mrb_state *mrb, mrb_value self)
+{
+ auto ptr = (std::shared_ptr<BluCat::GRA::SkeletalModel>*)DATA_PTR(self);
+ std::shared_ptr<glm::quat> *orientation;
+
+ mrb_get_args(mrb, "d", &orientation, &cg_orientation_3d_type);
+ (*ptr)->orientation = *orientation;
+
+ return self;
+}
+
+static mrb_value
+cg_cSkeletalModel_set_position(mrb_state *mrb, mrb_value self)
+{
+ auto ptr = (std::shared_ptr<BluCat::GRA::SkeletalModel>*)DATA_PTR(self);
+ std::shared_ptr<glm::vec3> *position;
+
+ mrb_get_args(mrb, "d", &position, &cg_vector_3d_type);
+ (*ptr)->position = *position;
+
+ return self;
+}
+
+static mrb_value
+cg_cSkeletalModel_set_animation(mrb_state *mrb, mrb_value self)
+{
+ auto ptr = (std::shared_ptr<BluCat::GRA::SkeletalModel>*)DATA_PTR(self);
+ mrb_int animation_index;
+
+ mrb_get_args(mrb, "i", &animation_index);
+ (*ptr)->animation_index = animation_index;
+
+ return self;
+}
+
+static mrb_value
+cg_cSkeletalModel_draw(mrb_state *mrb, mrb_value self)
+{
+ auto ptr = (std::shared_ptr<BluCat::GRA::SkeletalModel>*)DATA_PTR(self);
+
+ auto &instances = BluCat::INT::core.vk_renderer->skeletal_models_to_draw[
+ BluCat::INT::core.vk_swapchain->current_frame][(*ptr)->skeletal_mesh];
+ instances.push_back(*ptr);
+
+ return self;
+}
+
+void
+cg_skeletal_model_init(mrb_state *mrb)
+{
+ struct RClass *cg_m, *cg_cSkeletalModel;
+
+ cg_m = mrb_module_get(mrb, "CandyGear");
+ cg_cSkeletalModel = mrb_define_class_under(
+ mrb, cg_m, "SkeletalModel", mrb->object_class);
+ MRB_SET_INSTANCE_TT(cg_cSkeletalModel, MRB_TT_DATA);
+ mrb_define_method(
+ mrb, cg_cSkeletalModel, "initialize", cg_cSkeletalModel_initialize,
+ MRB_ARGS_REQ(4));
+ mrb_define_method(
+ mrb, cg_cSkeletalModel, "position=", cg_cSkeletalModel_set_position,
+ MRB_ARGS_REQ(1));
+ mrb_define_method(
+ mrb, cg_cSkeletalModel, "orientation=", cg_cSkeletalModel_set_orientation,
+ MRB_ARGS_REQ(1));
+ mrb_define_method(
+ mrb, cg_cSkeletalModel, "animation=", cg_cSkeletalModel_set_animation,
+ MRB_ARGS_REQ(1));
+ mrb_define_method(
+ mrb, cg_cSkeletalModel, "draw", cg_cSkeletalModel_draw, MRB_ARGS_NONE());
+}
diff --git a/src/candy_gear/skeletal_model.hpp b/src/candy_gear/skeletal_model.hpp
new file mode 100644
index 0000000..05f9c2e
--- /dev/null
+++ b/src/candy_gear/skeletal_model.hpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2022-2023 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CANDY_GEAR_SKELETAL_MODEL_H
+#define CANDY_GEAR_SKELETAL_MODEL_H 1
+
+#include "core.hpp"
+
+void
+cg_skeletal_model_init(mrb_state *mrb);
+
+#endif /* CANDY_GEAR_SKELETAL_MODEL_H */
diff --git a/src/candy_gear/sound.cpp b/src/candy_gear/sound.cpp
new file mode 100644
index 0000000..0b3518f
--- /dev/null
+++ b/src/candy_gear/sound.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "sound.hpp"
+
+void
+cg_free_sound(mrb_state *mrb, void* obj)
+{
+ struct cg_sound *ptr = static_cast<cg_sound*>(obj);
+
+ Mix_FreeChunk(ptr->chunk);
+ mrb_free(mrb, ptr);
+}
+
+const struct mrb_data_type cg_sound_type = {
+ "CG_Sound", cg_free_sound };
+
+static mrb_value
+cg_cSound_initialize(mrb_state *mrb, mrb_value self)
+{
+ const char *file_path;
+
+ struct cg_sound *ptr;
+
+ mrb_get_args(mrb, "z", &file_path);
+ ptr = (struct cg_sound *)DATA_PTR(self);
+ if(ptr) mrb_free(mrb, ptr);
+ ptr = (struct cg_sound *)mrb_malloc(mrb, sizeof(struct cg_sound));
+
+ ptr->chunk = Mix_LoadWAV(file_path);
+ if(ptr->chunk == NULL) mrb_raise(mrb, E_RUNTIME_ERROR, Mix_GetError());
+
+ mrb_data_init(self, ptr, &cg_sound_type);
+ return self;
+}
+
+static mrb_value
+cg_cSound_play(mrb_state *mrb, mrb_value self)
+{
+ struct cg_sound *ptr;
+
+ ptr = (struct cg_sound *)DATA_PTR(self);
+
+ Mix_PlayChannel(-1, ptr->chunk, 0);
+
+ return self;
+}
+
+void
+cg_sound_init(mrb_state *mrb)
+{
+ struct RClass *cg_m, *cg_cSound;
+
+ cg_m = mrb_module_get(mrb, "CandyGear");
+ cg_cSound = mrb_define_class_under(mrb, cg_m, "Sound", mrb->object_class);
+ MRB_SET_INSTANCE_TT(cg_cSound, MRB_TT_DATA);
+ mrb_define_method(
+ mrb, cg_cSound, "initialize", cg_cSound_initialize, MRB_ARGS_REQ(1));
+ mrb_define_method(mrb, cg_cSound, "play", cg_cSound_play, MRB_ARGS_NONE());
+}
diff --git a/src/candy_gear/sound.hpp b/src/candy_gear/sound.hpp
new file mode 100644
index 0000000..2f483e3
--- /dev/null
+++ b/src/candy_gear/sound.hpp
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CANDY_GEAR_SOUND_H
+#define CANDY_GEAR_SOUND_H 1
+
+#include "core.hpp"
+
+struct cg_sound
+{
+ Mix_Chunk *chunk;
+};
+
+extern const struct mrb_data_type cg_sound_type;
+
+void
+cg_sound_init(mrb_state *mrb);
+
+#endif /* CANDY_GEAR_SOUND_H */
diff --git a/src/candy_gear/sprite.cpp b/src/candy_gear/sprite.cpp
new file mode 100644
index 0000000..daf96a8
--- /dev/null
+++ b/src/candy_gear/sprite.cpp
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2022-2024 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "sprite.hpp"
+
+#include "texture.hpp"
+#include "vector_4d.hpp"
+#include "view_2d.hpp"
+#include "../blu_cat/gra/sprite.hpp"
+
+void
+cg_free_sprite(mrb_state *mrb, void* obj)
+{
+ auto ptr = static_cast<std::shared_ptr<BluCat::GRA::Sprite>*>(obj);
+
+ ptr->~shared_ptr();
+ mrb_free(mrb, ptr);
+}
+
+const struct mrb_data_type cg_sprite_type = { "CG_Sprite", cg_free_sprite };
+
+static mrb_value
+cg_cSprite_initialize(mrb_state *mrb, mrb_value self)
+{
+ mrb_float x, y, w, h;
+ glm::vec4 vector_4d;
+ std::shared_ptr<BluCat::GRA::Texture> *texture;
+ std::shared_ptr<BluCat::GRA::Sprite> *ptr;
+
+ mrb_get_args(
+ mrb, "dffff", &texture, &cg_texture_type, &x, &y, &w, &h);
+ ptr = (std::shared_ptr<BluCat::GRA::Sprite>*)DATA_PTR(self);
+ if(ptr) mrb_free(mrb, ptr);
+ ptr = (std::shared_ptr<BluCat::GRA::Sprite>*)mrb_malloc(
+ mrb, sizeof(std::shared_ptr<BluCat::GRA::Sprite>));
+
+ vector_4d.x = x;
+ vector_4d.y = y;
+ vector_4d.z = w;
+ vector_4d.w = h;
+ new(ptr)std::shared_ptr<BluCat::GRA::Sprite>(
+ std::make_shared<BluCat::GRA::Sprite>(*texture, vector_4d));
+
+ mrb_data_init(self, ptr, &cg_sprite_type);
+ return self;
+}
+
+static mrb_value
+cg_cSprite_draw(mrb_state *mrb, mrb_value self)
+{
+ mrb_value view_value;
+ BluCat::GRA::View2D *view_2d;
+ mrb_float x, y, w, h, z_index{0.0};
+ auto ptr = (std::shared_ptr<BluCat::GRA::Sprite>*)DATA_PTR(self);
+
+ mrb_get_args(mrb, "offff|f", &view_value, &x, &y, &w, &h, &z_index);
+
+ view_2d = cg_cView_to_view_2d(mrb, view_value);
+
+ glm::vec4 rect(x, y, x + w, y + h);
+ auto &sprites_to_draw = view_2d->sprites_to_draw[
+ BluCat::INT::core.vk_swapchain->current_frame];
+ sprites_to_draw.emplace_back(*ptr, rect, z_index);
+
+ return self;
+}
+
+void
+cg_sprite_init(mrb_state *mrb)
+{
+ struct RClass *cg_m, *cg_cSprite;
+
+ cg_m = mrb_module_get(mrb, "CandyGear");
+ cg_cSprite = mrb_define_class_under(mrb, cg_m, "Sprite", mrb->object_class);
+ MRB_SET_INSTANCE_TT(cg_cSprite, MRB_TT_DATA);
+ mrb_define_method(
+ mrb, cg_cSprite, "initialize", cg_cSprite_initialize, MRB_ARGS_REQ(5));
+ mrb_define_method(mrb, cg_cSprite, "draw", cg_cSprite_draw, MRB_ARGS_REQ(5)|
+ MRB_ARGS_OPT(1));
+}
diff --git a/src/candy_gear/sprite.hpp b/src/candy_gear/sprite.hpp
new file mode 100644
index 0000000..528f2f7
--- /dev/null
+++ b/src/candy_gear/sprite.hpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CANDY_GEAR_SPRITE_H
+#define CANDY_GEAR_SPRITE_H 1
+
+#include "core.hpp"
+
+extern const struct mrb_data_type cg_sprite_type;
+
+void
+cg_sprite_init(mrb_state *mrb);
+
+#endif /* CANDY_GEAR_SPRITE_H */
diff --git a/src/candy_gear/sprite_3d.cpp b/src/candy_gear/sprite_3d.cpp
new file mode 100644
index 0000000..d03b585
--- /dev/null
+++ b/src/candy_gear/sprite_3d.cpp
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2022-2024 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "sprite_3d.hpp"
+
+#include "sprite.hpp"
+#include "vector_3d.hpp"
+
+void
+cg_free_sprite_3d(mrb_state *mrb, void *obj)
+{
+ auto ptr = static_cast<std::shared_ptr<BluCat::GRA::Sprite3D>*>(obj);
+
+ ptr->~shared_ptr();
+ mrb_free(mrb, ptr);
+}
+
+const struct mrb_data_type cg_sprite_3d_type = {
+ "CG_Sprite3D", cg_free_sprite_3d };
+
+static mrb_value
+cg_cSprite3D_initialize(mrb_state *mrb, mrb_value self)
+{
+ std::shared_ptr<BluCat::GRA::Sprite> *sprite;
+ std::shared_ptr<glm::vec3> *position;
+ mrb_float w, h;
+ std::shared_ptr<BluCat::GRA::Sprite3D> *ptr;
+
+ mrb_get_args(mrb, "ddff", &sprite, &cg_sprite_type,
+ &position, &cg_vector_3d_type, &w, &h);
+ ptr = (std::shared_ptr<BluCat::GRA::Sprite3D>*)DATA_PTR(self);
+ if(ptr) mrb_free(mrb, ptr);
+ ptr = (std::shared_ptr<BluCat::GRA::Sprite3D>*)mrb_malloc(
+ mrb, sizeof(std::shared_ptr<BluCat::GRA::Sprite3D>));
+
+ glm::vec3 size{w, h, 0.0};
+ new(ptr)std::shared_ptr<BluCat::GRA::Sprite3D>(
+ std::make_shared<BluCat::GRA::Sprite3D>(*sprite, *position, size));
+
+ mrb_data_init(self, ptr, &cg_sprite_3d_type);
+ return self;
+}
+
+static mrb_value
+cg_cSprite3D_draw(mrb_state *mrb, mrb_value self)
+{
+ auto ptr = (std::shared_ptr<BluCat::GRA::Sprite3D>*)DATA_PTR(self);
+
+ auto &sprites_3d_to_draw = BluCat::INT::core.vk_renderer->sprites_3d_to_draw[
+ BluCat::INT::core.vk_swapchain->current_frame];
+ sprites_3d_to_draw.emplace_back(*ptr);
+
+ return self;
+}
+
+void
+cg_sprite_3d_init(mrb_state *mrb)
+{
+ struct RClass *cg_m, *cg_cSprite3D;
+
+ cg_m = mrb_module_get(mrb, "CandyGear");
+ cg_cSprite3D = mrb_define_class_under(
+ mrb, cg_m, "Sprite3D", mrb->object_class);
+ MRB_SET_INSTANCE_TT(cg_cSprite3D, MRB_TT_DATA);
+ mrb_define_method(
+ mrb, cg_cSprite3D, "initialize", cg_cSprite3D_initialize, MRB_ARGS_REQ(3));
+ mrb_define_method(
+ mrb, cg_cSprite3D, "draw", cg_cSprite3D_draw, MRB_ARGS_NONE());
+}
diff --git a/src/candy_gear/sprite_3d.hpp b/src/candy_gear/sprite_3d.hpp
new file mode 100644
index 0000000..526c23a
--- /dev/null
+++ b/src/candy_gear/sprite_3d.hpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2022-2023 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CANDY_GEAR_SPRITE_3D_H
+#define CANDY_GEAR_SPRITE_3D_H 1
+
+#include "core.hpp"
+
+void
+cg_sprite_3d_init(mrb_state *mrb);
+
+#endif /* CANDY_GEAR_SPRITE_3D_H */
diff --git a/src/candy_gear/static_mesh.cpp b/src/candy_gear/static_mesh.cpp
new file mode 100644
index 0000000..9cb79ef
--- /dev/null
+++ b/src/candy_gear/static_mesh.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2022-2024 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "static_mesh.hpp"
+
+#include "orientation_3d.hpp"
+#include "vector_3d.hpp"
+#include "../blu_cat/gra/static_mesh.hpp"
+
+void
+cg_free_static_mesh(mrb_state *mrb, void* obj)
+{
+ auto ptr = static_cast<std::shared_ptr<BluCat::GRA::StaticMesh>*>(obj);
+
+ ptr->~shared_ptr();
+ mrb_free(mrb, ptr);
+}
+
+const struct mrb_data_type cg_static_mesh_type = {
+ "CG_StaticMesh", cg_free_static_mesh };
+
+static mrb_value
+cg_cStaticMesh_initialize(mrb_state *mrb, mrb_value self)
+{
+ const char *file_path;
+
+ std::shared_ptr<BluCat::GRA::StaticMesh> *ptr;
+
+ mrb_get_args(mrb, "z", &file_path);
+ ptr = (std::shared_ptr<BluCat::GRA::StaticMesh>*)DATA_PTR(self);
+ if(ptr) mrb_free(mrb, ptr);
+ ptr = (std::shared_ptr<BluCat::GRA::StaticMesh>*)mrb_malloc(
+ mrb, sizeof(std::shared_ptr<BluCat::GRA::StaticMesh>));
+
+ new(ptr)std::shared_ptr<BluCat::GRA::StaticMesh>(
+ std::make_shared<BluCat::GRA::StaticMesh>(file_path));
+
+ mrb_data_init(self, ptr, &cg_static_mesh_type);
+ return self;
+}
+
+void
+cg_static_mesh_init(mrb_state *mrb)
+{
+ struct RClass *cg_m, *cg_cStaticMesh;
+
+ cg_m = mrb_module_get(mrb, "CandyGear");
+ cg_cStaticMesh = mrb_define_class_under(
+ mrb, cg_m, "StaticMesh", mrb->object_class);
+ MRB_SET_INSTANCE_TT(cg_cStaticMesh, MRB_TT_DATA);
+ mrb_define_method(
+ mrb, cg_cStaticMesh, "initialize", cg_cStaticMesh_initialize,
+ MRB_ARGS_REQ(1));
+}
diff --git a/src/candy_gear/static_mesh.hpp b/src/candy_gear/static_mesh.hpp
new file mode 100644
index 0000000..8f3b2d6
--- /dev/null
+++ b/src/candy_gear/static_mesh.hpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2022-2023 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CANDY_GEAR_STATIC_MESH_H
+#define CANDY_GEAR_STATIC_MESH_H 1
+
+#include "core.hpp"
+
+extern const struct mrb_data_type cg_static_mesh_type;
+
+void
+cg_static_mesh_init(mrb_state *mrb);
+
+#endif /* CANDY_GEAR_STATIC_MESH_H */
diff --git a/src/candy_gear/static_model.cpp b/src/candy_gear/static_model.cpp
new file mode 100644
index 0000000..1a6eabe
--- /dev/null
+++ b/src/candy_gear/static_model.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2022-2024 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "static_model.hpp"
+
+#include "orientation_3d.hpp"
+#include "static_mesh.hpp"
+#include "texture.hpp"
+#include "vector_3d.hpp"
+#include "../blu_cat/gra/static_model.hpp"
+
+void
+cg_free_static_model(mrb_state *mrb, void *obj)
+{
+ auto ptr = static_cast<std::shared_ptr<BluCat::GRA::StaticModel>*>(obj);
+
+ ptr->~shared_ptr();
+ mrb_free(mrb, ptr);
+}
+
+const struct mrb_data_type cg_static_model_type = {
+ "CG_StaticModel", cg_free_static_model };
+
+static mrb_value
+cg_cStaticModel_initialize(mrb_state *mrb, mrb_value self)
+{
+ std::shared_ptr<BluCat::GRA::StaticMesh> *static_mesh;
+ std::shared_ptr<BluCat::GRA::Texture> *texture;
+ std::shared_ptr<glm::vec3> *position;
+ std::shared_ptr<glm::quat> *orientation;
+ std::shared_ptr<BluCat::GRA::StaticModel> *ptr;
+
+ mrb_get_args(
+ mrb, "dddd", &static_mesh, &cg_static_mesh_type, &texture,
+ &cg_texture_type, &position, &cg_vector_3d_type, &orientation,
+ &cg_orientation_3d_type);
+ ptr = (std::shared_ptr<BluCat::GRA::StaticModel>*)DATA_PTR(self);
+ if(ptr) mrb_free(mrb, ptr);
+ ptr = (std::shared_ptr<BluCat::GRA::StaticModel>*)mrb_malloc(
+ mrb, sizeof(std::shared_ptr<BluCat::GRA::StaticModel>));
+
+ new(ptr)std::shared_ptr<BluCat::GRA::StaticModel>(
+ std::make_shared<BluCat::GRA::StaticModel>(
+ *static_mesh, *texture, *position, *orientation));
+
+ mrb_data_init(self, ptr, &cg_static_model_type);
+ return self;
+}
+
+static mrb_value
+cg_cStaticModel_set_orientation(mrb_state *mrb, mrb_value self)
+{
+ auto ptr = (std::shared_ptr<BluCat::GRA::StaticModel>*)DATA_PTR(self);
+ std::shared_ptr<glm::quat> *orientation;
+
+ mrb_get_args(mrb, "d", &orientation, &cg_orientation_3d_type);
+ (*ptr)->orientation = *orientation;
+
+ return self;
+}
+
+static mrb_value
+cg_cStaticModel_set_position(mrb_state *mrb, mrb_value self)
+{
+ auto ptr = (std::shared_ptr<BluCat::GRA::StaticModel>*)DATA_PTR(self);
+ std::shared_ptr<glm::vec3> *position;
+
+ mrb_get_args(mrb, "d", &position, &cg_vector_3d_type);
+ (*ptr)->position = *position;
+
+ return self;
+}
+
+static mrb_value
+cg_cStaticModel_set_texture(mrb_state *mrb, mrb_value self)
+{
+ auto ptr = (std::shared_ptr<BluCat::GRA::StaticModel>*)DATA_PTR(self);
+ std::shared_ptr<BluCat::GRA::Texture> *texture;
+
+ mrb_get_args(mrb, "d", &texture, &cg_texture_type);
+ (*ptr)->texture = *texture;
+
+ return self;
+}
+
+static mrb_value
+cg_cStaticModel_draw(mrb_state *mrb, mrb_value self)
+{
+ auto ptr = (std::shared_ptr<BluCat::GRA::StaticModel>*)DATA_PTR(self);
+
+ auto &instances = BluCat::INT::core.vk_renderer->static_models_to_draw[
+ BluCat::INT::core.vk_swapchain->current_frame][(*ptr)->static_mesh];
+ instances.push_back(*ptr);
+
+ return self;
+}
+
+void
+cg_static_model_init(mrb_state *mrb)
+{
+ struct RClass *cg_m, *cg_cStaticModel;
+
+ cg_m = mrb_module_get(mrb, "CandyGear");
+ cg_cStaticModel = mrb_define_class_under(
+ mrb, cg_m, "StaticModel", mrb->object_class);
+ MRB_SET_INSTANCE_TT(cg_cStaticModel, MRB_TT_DATA);
+ mrb_define_method(
+ mrb, cg_cStaticModel, "initialize", cg_cStaticModel_initialize,
+ MRB_ARGS_REQ(4));
+ mrb_define_method(
+ mrb, cg_cStaticModel, "position=", cg_cStaticModel_set_position,
+ MRB_ARGS_REQ(1));
+ mrb_define_method(
+ mrb, cg_cStaticModel, "orientation=", cg_cStaticModel_set_orientation,
+ MRB_ARGS_REQ(1));
+ mrb_define_method(
+ mrb, cg_cStaticModel, "texture=", cg_cStaticModel_set_texture,
+ MRB_ARGS_REQ(1));
+ mrb_define_method(
+ mrb, cg_cStaticModel, "draw", cg_cStaticModel_draw, MRB_ARGS_NONE());
+}
diff --git a/src/candy_gear/static_model.hpp b/src/candy_gear/static_model.hpp
new file mode 100644
index 0000000..dfb7054
--- /dev/null
+++ b/src/candy_gear/static_model.hpp
@@ -0,0 +1,25 @@
+/*
+ * Copyright 2022-2023 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CANDY_GEAR_STATIC_MODEL_H
+#define CANDY_GEAR_STATIC_MODEL_H 1
+
+#include "core.hpp"
+
+void
+cg_static_model_init(mrb_state *mrb);
+
+#endif /* CANDY_GEAR_STATIC_MODEL_H */
diff --git a/src/candy_gear/texture.cpp b/src/candy_gear/texture.cpp
new file mode 100644
index 0000000..355a622
--- /dev/null
+++ b/src/candy_gear/texture.cpp
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2022-2024 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "texture.hpp"
+
+#include "core.hpp"
+#include "font.hpp"
+#include "../blu_cat/gra/texture.hpp"
+
+void
+cg_free_texture(mrb_state *mrb, void* obj)
+{
+ auto *ptr = static_cast<std::shared_ptr<BluCat::GRA::Texture>*>(obj);
+
+ ptr->~shared_ptr();
+ mrb_free(mrb, ptr);
+}
+
+const struct mrb_data_type cg_texture_type = { "CG_Texture", cg_free_texture };
+
+static mrb_value
+texture_alloc(mrb_state *mrb, mrb_value klass)
+{
+ struct RClass *cg_cTexture = mrb_class_ptr(klass);
+ enum mrb_vtype ttype = MRB_INSTANCE_TT(cg_cTexture);
+
+ // I do not know if I need this. If things break, try uncomment this line.
+ // if (ttype == 0) ttype = MRB_TT_OBJECT;
+ return mrb_obj_value(
+ (struct RObject*)mrb_obj_alloc(mrb, ttype, cg_cTexture));
+}
+
+static mrb_value
+cg_cTexture_from_image(mrb_state *mrb, mrb_value self)
+{
+ struct mrb_value texture = texture_alloc(mrb, self);
+ const char *file_path;
+ std::shared_ptr<BluCat::GRA::Texture> *ptr;
+
+ mrb_get_args(mrb, "z", &file_path);
+ ptr = (std::shared_ptr<BluCat::GRA::Texture>*)DATA_PTR(texture);
+ if(ptr) mrb_free(mrb, ptr);
+ ptr = (std::shared_ptr<BluCat::GRA::Texture>*)mrb_malloc(
+ mrb, sizeof(std::shared_ptr<BluCat::GRA::Texture>));
+ new(ptr)std::shared_ptr<BluCat::GRA::Texture>(
+ std::make_shared<BluCat::GRA::Texture>(file_path));
+
+ mrb_data_init(texture, ptr, &cg_texture_type);
+ return texture;
+}
+
+mrb_value
+cg_cTexture_from_text(mrb_state *mrb, mrb_value self)
+{
+ const char *text;
+ unsigned int width, height;
+ struct mrb_value texture = texture_alloc(mrb, self);
+ BluCat::GRA::Font *font_ptr;
+ std::shared_ptr<BluCat::GRA::Texture> *ptr;
+
+ mrb_get_args(mrb, "dz", &font_ptr, &cg_font_type, &text);
+
+ ptr = (std::shared_ptr<BluCat::GRA::Texture>*)DATA_PTR(texture);
+ if(ptr) mrb_free(mrb, ptr);
+ ptr = (std::shared_ptr<BluCat::GRA::Texture>*)mrb_malloc(
+ mrb, sizeof(std::shared_ptr<BluCat::GRA::Texture>));
+ new(ptr)std::shared_ptr<BluCat::GRA::Texture>(
+ std::make_shared<BluCat::GRA::Texture>(font_ptr, text));
+
+ mrb_data_init(texture, ptr, &cg_texture_type);
+ return texture;
+}
+
+static mrb_value
+cg_cTexture_width(mrb_state *mrb, mrb_value self)
+{
+ auto ptr = (std::shared_ptr<BluCat::GRA::Texture>*)DATA_PTR(self);
+ return mrb_int_value(mrb, (*ptr)->width);
+}
+
+static mrb_value
+cg_cTexture_height(mrb_state *mrb, mrb_value self)
+{
+ auto ptr = (std::shared_ptr<BluCat::GRA::Texture>*)DATA_PTR(self);
+ return mrb_int_value(mrb, (*ptr)->height);
+}
+
+void
+cg_texture_init(mrb_state *mrb)
+{
+ struct RClass *cg_m, *cg_cTexture;
+
+ cg_m = mrb_module_get(mrb, "CandyGear");
+ cg_cTexture = mrb_define_class_under(
+ mrb, cg_m, "Texture", mrb->object_class);
+ MRB_SET_INSTANCE_TT(cg_cTexture, MRB_TT_DATA);
+
+ mrb_undef_class_method(mrb, cg_cTexture, "new");
+ mrb_define_class_method(
+ mrb, cg_cTexture, "from_image", cg_cTexture_from_image,
+ MRB_ARGS_REQ(1) | MRB_ARGS_OPT(1));
+ mrb_define_class_method(
+ mrb, cg_cTexture, "from_text", cg_cTexture_from_text, MRB_ARGS_REQ(2));
+ mrb_define_method(
+ mrb, cg_cTexture, "width", cg_cTexture_width, MRB_ARGS_NONE());
+ mrb_define_method(
+ mrb, cg_cTexture, "height", cg_cTexture_height, MRB_ARGS_NONE());
+}
diff --git a/src/candy_gear/texture.hpp b/src/candy_gear/texture.hpp
new file mode 100644
index 0000000..cd96026
--- /dev/null
+++ b/src/candy_gear/texture.hpp
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CANDY_GEAR_TEXTURE_H
+#define CANDY_GEAR_TEXTURE_H 1
+
+#include "core.hpp"
+#include "../blu_cat/com/command.hpp"
+
+extern const struct mrb_data_type cg_texture_type;
+
+void
+cg_texture_init(mrb_state *mrb);
+
+#endif /* CANDY_GEAR_TEXTURE_H */
diff --git a/src/candy_gear/vector_3d.cpp b/src/candy_gear/vector_3d.cpp
new file mode 100644
index 0000000..fc5d186
--- /dev/null
+++ b/src/candy_gear/vector_3d.cpp
@@ -0,0 +1,302 @@
+/*
+ * Copyright 2022-2024 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vector_3d.hpp"
+
+#include <memory>
+
+#include <glm/vec3.hpp>
+
+#include <mruby/array.h>
+
+#include "orientation_3d.hpp"
+
+void
+cg_free_vector_3d(mrb_state *mrb, void* obj)
+{
+ auto ptr = static_cast<std::shared_ptr<glm::vec3>*>(obj);
+
+ ptr->~shared_ptr();
+ mrb_free(mrb, ptr);
+}
+
+const struct mrb_data_type cg_vector_3d_type = {
+ "CG_Vector3D", cg_free_vector_3d};
+
+static mrb_value
+cg_cVector3D_initialize(mrb_state *mrb, mrb_value self)
+{
+ mrb_float x = 0.0f;
+ mrb_float y = 0.0f;
+ mrb_float z = 0.0f;
+ std::shared_ptr<glm::vec3> *ptr;
+
+ mrb_get_args(mrb, "|fff", &x, &y, &z);
+ ptr = (std::shared_ptr<glm::vec3>*)DATA_PTR(self);
+ if(ptr) mrb_free(mrb, ptr);
+ ptr = (std::shared_ptr<glm::vec3>*)mrb_malloc(
+ mrb, sizeof(std::shared_ptr<glm::vec3>));
+
+ new(ptr)std::shared_ptr<glm::vec3>(std::make_shared<glm::vec3>(x, y, z));
+
+ mrb_data_init(self, ptr, &cg_vector_3d_type);
+ return self;
+}
+
+mrb_value
+cg_cVector3D_get_x(mrb_state *mrb, mrb_value self)
+{
+ std::shared_ptr<glm::vec3> *ptr =
+ (std::shared_ptr<glm::vec3>*)DATA_PTR(self);
+
+ return mrb_float_value(mrb, (*ptr)->x);
+}
+
+mrb_value
+cg_cVector3D_get_y(mrb_state *mrb, mrb_value self)
+{
+ std::shared_ptr<glm::vec3> *ptr =
+ (std::shared_ptr<glm::vec3>*)DATA_PTR(self);
+
+ return mrb_float_value(mrb, (*ptr)->y);
+}
+
+mrb_value
+cg_cVector3D_get_z(mrb_state *mrb, mrb_value self)
+{
+ std::shared_ptr<glm::vec3> *ptr =
+ (std::shared_ptr<glm::vec3>*)DATA_PTR(self);
+
+ return mrb_float_value(mrb, (*ptr)->z);
+}
+
+mrb_value
+cg_cVector3D_get_xy(mrb_state *mrb, mrb_value self)
+{
+ std::shared_ptr<glm::vec3> *ptr =
+ (std::shared_ptr<glm::vec3>*)DATA_PTR(self);
+
+ mrb_value array = mrb_ary_new_capa(mrb, 2);
+ mrb_ary_push(mrb, array, mrb_float_value(mrb, (*ptr)->x));
+ mrb_ary_push(mrb, array, mrb_float_value(mrb, (*ptr)->y));
+
+ return array;
+}
+
+mrb_value
+cg_cVector3D_get_xz(mrb_state *mrb, mrb_value self)
+{
+ std::shared_ptr<glm::vec3> *ptr =
+ (std::shared_ptr<glm::vec3>*)DATA_PTR(self);
+
+ mrb_value array = mrb_ary_new_capa(mrb, 2);
+ mrb_ary_push(mrb, array, mrb_float_value(mrb, (*ptr)->x));
+ mrb_ary_push(mrb, array, mrb_float_value(mrb, (*ptr)->z));
+
+ return array;
+}
+
+mrb_value
+cg_cVector3D_get_yz(mrb_state *mrb, mrb_value self)
+{
+ std::shared_ptr<glm::vec3> *ptr =
+ (std::shared_ptr<glm::vec3>*)DATA_PTR(self);
+
+ mrb_value array = mrb_ary_new_capa(mrb, 2);
+ mrb_ary_push(mrb, array, mrb_float_value(mrb, (*ptr)->y));
+ mrb_ary_push(mrb, array, mrb_float_value(mrb, (*ptr)->z));
+
+ return array;
+}
+
+mrb_value
+cg_cVector3D_get_xyz(mrb_state *mrb, mrb_value self)
+{
+ std::shared_ptr<glm::vec3> *ptr =
+ (std::shared_ptr<glm::vec3>*)DATA_PTR(self);
+
+ mrb_value array = mrb_ary_new_capa(mrb, 3);
+ mrb_ary_push(mrb, array, mrb_float_value(mrb, (*ptr)->x));
+ mrb_ary_push(mrb, array, mrb_float_value(mrb, (*ptr)->y));
+ mrb_ary_push(mrb, array, mrb_float_value(mrb, (*ptr)->z));
+
+ return array;
+}
+
+static mrb_value
+cg_cVector3D_set_x(mrb_state *mrb, mrb_value self)
+{
+ mrb_float x;
+ std::shared_ptr<glm::vec3> *ptr =
+ (std::shared_ptr<glm::vec3>*)DATA_PTR(self);
+
+ mrb_get_args(mrb, "f", &x);
+ (*ptr)->x = x;
+
+ return self;
+}
+
+static mrb_value
+cg_cVector3D_set_y(mrb_state *mrb, mrb_value self)
+{
+ mrb_float y;
+ std::shared_ptr<glm::vec3> *ptr =
+ (std::shared_ptr<glm::vec3>*)DATA_PTR(self);
+
+ mrb_get_args(mrb, "f", &y);
+ (*ptr)->y = y;
+
+ return self;
+}
+
+static mrb_value
+cg_cVector3D_set_z(mrb_state *mrb, mrb_value self)
+{
+ mrb_float z;
+ std::shared_ptr<glm::vec3> *ptr =
+ (std::shared_ptr<glm::vec3>*)DATA_PTR(self);
+
+ mrb_get_args(mrb, "f", &z);
+ (*ptr)->z = z;
+
+ return self;
+}
+
+static mrb_value
+cg_cVector3D_set_xy(mrb_state *mrb, mrb_value self)
+{
+ mrb_float x, y;
+ std::shared_ptr<glm::vec3> *ptr =
+ (std::shared_ptr<glm::vec3>*)DATA_PTR(self);
+
+ mrb_get_args(mrb, "ff", &x, &y);
+ (*ptr)->x = x;
+ (*ptr)->y = y;
+
+ return self;
+}
+
+static mrb_value
+cg_cVector3D_set_xz(mrb_state *mrb, mrb_value self)
+{
+ mrb_float x, z;
+ std::shared_ptr<glm::vec3> *ptr =
+ (std::shared_ptr<glm::vec3>*)DATA_PTR(self);
+
+ mrb_get_args(mrb, "ff", &x, &z);
+ (*ptr)->x = x;
+ (*ptr)->z = z;
+
+ return self;
+}
+
+static mrb_value
+cg_cVector3D_set_yz(mrb_state *mrb, mrb_value self)
+{
+ mrb_float y, z;
+ std::shared_ptr<glm::vec3> *ptr =
+ (std::shared_ptr<glm::vec3>*)DATA_PTR(self);
+
+ mrb_get_args(mrb, "ff", &y, &z);
+ (*ptr)->y = y;
+ (*ptr)->z = z;
+
+ return self;
+}
+
+static mrb_value
+cg_cVector3D_set_xyz(mrb_state *mrb, mrb_value self)
+{
+ mrb_float x, y, z;
+ std::shared_ptr<glm::vec3> *ptr =
+ (std::shared_ptr<glm::vec3>*)DATA_PTR(self);
+
+ mrb_get_args(mrb, "fff", &x, &y, &z);
+ (*ptr)->x = x;
+ (*ptr)->y = y;
+ (*ptr)->z = z;
+
+ return self;
+}
+
+static mrb_value
+cg_cVector3D_translate(mrb_state *mrb, mrb_value self)
+{
+ auto *ptr = (std::shared_ptr<glm::vec3>*)DATA_PTR(self);
+ std::shared_ptr<glm::vec3> *direction;
+ std::shared_ptr<glm::quat> *orientation;
+
+ mrb_get_args(
+ mrb, "dd", &direction, &cg_vector_3d_type,
+ &orientation, &cg_orientation_3d_type);
+
+ auto rotated_direction = **orientation * **direction;
+
+ **ptr += rotated_direction;
+
+ return self;
+}
+
+void
+cg_vector_3d_init(mrb_state *mrb)
+{
+ struct RClass *cg_m, *cg_cVector3D;
+
+ cg_m = mrb_module_get(mrb, "CandyGear");
+ cg_cVector3D = mrb_define_class_under(
+ mrb, cg_m, "Vector3D", mrb->object_class);
+ MRB_SET_INSTANCE_TT(cg_cVector3D, MRB_TT_DATA);
+ mrb_define_method(
+ mrb, cg_cVector3D, "initialize", cg_cVector3D_initialize,
+ MRB_ARGS_NONE()|MRB_ARGS_OPT(3));
+
+ mrb_define_method(
+ mrb, cg_cVector3D, "x", cg_cVector3D_get_x, MRB_ARGS_NONE());
+ mrb_define_method(
+ mrb, cg_cVector3D, "y", cg_cVector3D_get_y, MRB_ARGS_NONE());
+ mrb_define_method(
+ mrb, cg_cVector3D, "z", cg_cVector3D_get_z, MRB_ARGS_NONE());
+
+ mrb_define_method(
+ mrb, cg_cVector3D, "xy", cg_cVector3D_get_xy, MRB_ARGS_NONE());
+ mrb_define_method(
+ mrb, cg_cVector3D, "xz", cg_cVector3D_get_xz, MRB_ARGS_NONE());
+ mrb_define_method(
+ mrb, cg_cVector3D, "yz", cg_cVector3D_get_yz, MRB_ARGS_NONE());
+
+ mrb_define_method(
+ mrb, cg_cVector3D, "xyz", cg_cVector3D_get_xyz, MRB_ARGS_NONE());
+
+ mrb_define_method(
+ mrb, cg_cVector3D, "x=", cg_cVector3D_set_x, MRB_ARGS_REQ(1));
+ mrb_define_method(
+ mrb, cg_cVector3D, "y=", cg_cVector3D_set_y, MRB_ARGS_REQ(1));
+ mrb_define_method(
+ mrb, cg_cVector3D, "z=", cg_cVector3D_set_z, MRB_ARGS_REQ(1));
+
+ mrb_define_method(
+ mrb, cg_cVector3D, "set_xy", cg_cVector3D_set_xy, MRB_ARGS_REQ(2));
+ mrb_define_method(
+ mrb, cg_cVector3D, "set_xz", cg_cVector3D_set_xz, MRB_ARGS_REQ(2));
+ mrb_define_method(
+ mrb, cg_cVector3D, "set_yz", cg_cVector3D_set_yz, MRB_ARGS_REQ(2));
+
+ mrb_define_method(
+ mrb, cg_cVector3D, "set_xyz", cg_cVector3D_set_xyz, MRB_ARGS_REQ(3));
+
+ mrb_define_method(
+ mrb, cg_cVector3D, "translate", cg_cVector3D_translate, MRB_ARGS_REQ(2));
+}
diff --git a/src/candy_gear/vector_3d.hpp b/src/candy_gear/vector_3d.hpp
new file mode 100644
index 0000000..2827813
--- /dev/null
+++ b/src/candy_gear/vector_3d.hpp
@@ -0,0 +1,34 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CANDY_GEAR_VECTOR_3D_H
+#define CANDY_GEAR_VECTOR_3D_H 1
+
+#include "core.hpp"
+
+extern const struct mrb_data_type cg_vector_3d_type;
+
+mrb_value
+cg_cVector3D_get_x(mrb_state *mrb, mrb_value self);
+mrb_value
+cg_cVector3D_get_y(mrb_state *mrb, mrb_value self);
+mrb_value
+cg_cVector3D_get_z(mrb_state *mrb, mrb_value self);
+
+void
+cg_vector_3d_init(mrb_state *mrb);
+
+#endif /* CANDY_GEAR_VECTOR_3D_H */
diff --git a/src/candy_gear/vector_4d.cpp b/src/candy_gear/vector_4d.cpp
new file mode 100644
index 0000000..b44b612
--- /dev/null
+++ b/src/candy_gear/vector_4d.cpp
@@ -0,0 +1,479 @@
+/*
+ * Copyright 2022-2024 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "vector_4d.hpp"
+
+#include <memory>
+
+#include <glm/vec4.hpp>
+#include <mruby/array.h>
+
+#include "vector_3d.hpp"
+#include "view_2d.hpp"
+
+namespace
+{
+
+constexpr bool
+align_vertically(const float a_x, const float a_width,
+ const float b_x, const float b_width)
+{
+ return a_x <= b_x + b_width && a_x + a_width >= b_x;
+}
+
+constexpr bool
+align_horizontally(const float a_y, const float a_height,
+ const float b_y, const float b_height)
+{
+ return a_y <= b_y + b_height && a_y + a_height >= b_y;
+}
+
+}
+
+void
+cg_free_vector_4d(mrb_state *mrb, void* obj)
+{
+ auto ptr = static_cast<std::shared_ptr<glm::vec4>*>(obj);
+
+ ptr->~shared_ptr();
+ mrb_free(mrb, ptr);
+}
+
+const struct mrb_data_type cg_vector_4d_type = {
+ "CG_Vector4D", cg_free_vector_4d};
+
+static mrb_value
+cg_cVector4D_initialize(mrb_state *mrb, mrb_value self)
+{
+ mrb_float x = 0.0f;
+ mrb_float y = 0.0f;
+ mrb_float w = 0.0f;
+ mrb_float h = 0.0f;
+ std::shared_ptr<glm::vec4> *ptr;
+
+ mrb_get_args(mrb, "|ffff", &x, &y, &w, &h);
+ ptr = (std::shared_ptr<glm::vec4>*)DATA_PTR(self);
+ if(ptr) mrb_free(mrb, ptr);
+ ptr = (std::shared_ptr<glm::vec4>*)mrb_malloc(
+ mrb, sizeof(std::shared_ptr<glm::vec4>));
+
+ new(ptr)std::shared_ptr<glm::vec4>(std::make_shared<glm::vec4>(x, y, w, h));
+
+ mrb_data_init(self, ptr, &cg_vector_4d_type);
+ return self;
+}
+
+mrb_value
+cg_cVector4D_get_x(mrb_state *mrb, mrb_value self)
+{
+ std::shared_ptr<glm::vec4> *ptr =
+ (std::shared_ptr<glm::vec4>*)DATA_PTR(self);
+
+ return mrb_float_value(mrb, (*ptr)->x);
+}
+
+mrb_value
+cg_cVector4D_get_y(mrb_state *mrb, mrb_value self)
+{
+ auto ptr = (std::shared_ptr<glm::vec4>*)DATA_PTR(self);
+
+ return mrb_float_value(mrb, (*ptr)->y);
+}
+
+mrb_value
+cg_cVector4D_get_w(mrb_state *mrb, mrb_value self)
+{
+ auto ptr = (std::shared_ptr<glm::vec4>*)DATA_PTR(self);
+
+ return mrb_float_value(mrb, (*ptr)->z);
+}
+
+mrb_value
+cg_cVector4D_get_h(mrb_state *mrb, mrb_value self)
+{
+ auto ptr = (std::shared_ptr<glm::vec4>*)DATA_PTR(self);
+
+ return mrb_float_value(mrb, (*ptr)->w);
+}
+
+mrb_value
+cg_cVector4D_get_xy(mrb_state *mrb, mrb_value self)
+{
+ auto *ptr = (std::shared_ptr<glm::vec4>*)DATA_PTR(self);
+
+ mrb_value array = mrb_ary_new_capa(mrb, 2);
+ mrb_ary_push(mrb, array, mrb_float_value(mrb, (*ptr)->x));
+ mrb_ary_push(mrb, array, mrb_float_value(mrb, (*ptr)->y));
+
+ return array;
+}
+
+mrb_value
+cg_cVector4D_get_xw(mrb_state *mrb, mrb_value self)
+{
+ auto ptr = (std::shared_ptr<glm::vec4>*)DATA_PTR(self);
+
+ mrb_value array = mrb_ary_new_capa(mrb, 2);
+ mrb_ary_push(mrb, array, mrb_float_value(mrb, (*ptr)->x));
+ mrb_ary_push(mrb, array, mrb_float_value(mrb, (*ptr)->z));
+
+ return array;
+}
+
+mrb_value
+cg_cVector4D_get_xh(mrb_state *mrb, mrb_value self)
+{
+ auto ptr = (std::shared_ptr<glm::vec4>*)DATA_PTR(self);
+
+ mrb_value array = mrb_ary_new_capa(mrb, 2);
+ mrb_ary_push(mrb, array, mrb_float_value(mrb, (*ptr)->x));
+ mrb_ary_push(mrb, array, mrb_float_value(mrb, (*ptr)->w));
+
+ return array;
+}
+
+mrb_value
+cg_cVector4D_get_yw(mrb_state *mrb, mrb_value self)
+{
+ auto ptr = (std::shared_ptr<glm::vec4>*)DATA_PTR(self);
+
+ mrb_value array = mrb_ary_new_capa(mrb, 2);
+ mrb_ary_push(mrb, array, mrb_float_value(mrb, (*ptr)->y));
+ mrb_ary_push(mrb, array, mrb_float_value(mrb, (*ptr)->z));
+
+ return array;
+}
+
+mrb_value
+cg_cVector4D_get_yh(mrb_state *mrb, mrb_value self)
+{
+ auto ptr = (std::shared_ptr<glm::vec4>*)DATA_PTR(self);
+
+ mrb_value array = mrb_ary_new_capa(mrb, 2);
+ mrb_ary_push(mrb, array, mrb_float_value(mrb, (*ptr)->y));
+ mrb_ary_push(mrb, array, mrb_float_value(mrb, (*ptr)->w));
+
+ return array;
+}
+
+mrb_value
+cg_cVector4D_get_wh(mrb_state *mrb, mrb_value self)
+{
+ auto ptr = (std::shared_ptr<glm::vec4>*)DATA_PTR(self);
+
+ mrb_value array = mrb_ary_new_capa(mrb, 2);
+ mrb_ary_push(mrb, array, mrb_float_value(mrb, (*ptr)->z));
+ mrb_ary_push(mrb, array, mrb_float_value(mrb, (*ptr)->w));
+
+ return array;
+}
+
+static mrb_value
+cg_cVector4D_set_x(mrb_state *mrb, mrb_value self)
+{
+ mrb_float x;
+ auto ptr = (std::shared_ptr<glm::vec4>*)DATA_PTR(self);
+
+ mrb_get_args(mrb, "f", &x);
+ (*ptr)->x = x;
+
+ return self;
+}
+
+static mrb_value
+cg_cVector4D_set_y(mrb_state *mrb, mrb_value self)
+{
+ mrb_float y;
+ auto ptr = (std::shared_ptr<glm::vec4>*)DATA_PTR(self);
+
+ mrb_get_args(mrb, "f", &y);
+ (*ptr)->y = y;
+
+ return self;
+}
+
+static mrb_value
+cg_cVector4D_set_w(mrb_state *mrb, mrb_value self)
+{
+ mrb_float w;
+ auto ptr = (std::shared_ptr<glm::vec4>*)DATA_PTR(self);
+
+ mrb_get_args(mrb, "f", &w);
+ (*ptr)->z = w;
+
+ return self;
+}
+
+static mrb_value
+cg_cVector4D_set_h(mrb_state *mrb, mrb_value self)
+{
+ mrb_float h;
+ auto ptr = (std::shared_ptr<glm::vec4>*)DATA_PTR(self);
+
+ mrb_get_args(mrb, "f", &h);
+ (*ptr)->w = h;
+
+ return self;
+}
+
+static mrb_value
+cg_cVector4D_set_xy(mrb_state *mrb, mrb_value self)
+{
+ mrb_float x, y;
+ auto ptr = (std::shared_ptr<glm::vec4>*)DATA_PTR(self);
+
+ mrb_get_args(mrb, "ff", &x, &y);
+ (*ptr)->x = x;
+ (*ptr)->y = y;
+
+ return self;
+}
+
+static mrb_value
+cg_cVector4D_set_xw(mrb_state *mrb, mrb_value self)
+{
+ mrb_float x, w;
+ auto ptr = (std::shared_ptr<glm::vec4>*)DATA_PTR(self);
+
+ mrb_get_args(mrb, "ff", &x, &w);
+ (*ptr)->x = x;
+ (*ptr)->z = w;
+
+ return self;
+}
+
+static mrb_value
+cg_cVector4D_set_xh(mrb_state *mrb, mrb_value self)
+{
+ mrb_float x, h;
+ auto ptr = (std::shared_ptr<glm::vec4>*)DATA_PTR(self);
+
+ mrb_get_args(mrb, "ff", &x, &h);
+ (*ptr)->x = x;
+ (*ptr)->w = h;
+
+ return self;
+}
+
+static mrb_value
+cg_cVector4D_set_yw(mrb_state *mrb, mrb_value self)
+{
+ mrb_float y, w;
+ auto ptr = (std::shared_ptr<glm::vec4>*)DATA_PTR(self);
+
+ mrb_get_args(mrb, "ff", &y, &w);
+ (*ptr)->y = y;
+ (*ptr)->z = w;
+
+ return self;
+}
+
+static mrb_value
+cg_cVector4D_set_yh(mrb_state *mrb, mrb_value self)
+{
+ mrb_float y, h;
+ auto ptr = (std::shared_ptr<glm::vec4>*)DATA_PTR(self);
+
+ mrb_get_args(mrb, "ff", &y, &h);
+ (*ptr)->x = y;
+ (*ptr)->w = h;
+
+ return self;
+}
+
+static mrb_value
+cg_cVector4D_set_wh(mrb_state *mrb, mrb_value self)
+{
+ mrb_float w, h;
+ auto ptr = (std::shared_ptr<glm::vec4>*)DATA_PTR(self);
+
+ mrb_get_args(mrb, "ff", &w, &h);
+ (*ptr)->z = w;
+ (*ptr)->w = h;
+
+ return self;
+}
+
+static mrb_value
+cg_cVector4D_align_vertically(mrb_state *mrb, mrb_value self)
+{
+ std::shared_ptr<glm::vec4> *ptr, *that;
+
+ mrb_get_args(mrb, "d", &that, &cg_vector_4d_type);
+ ptr = (std::shared_ptr<glm::vec4>*)DATA_PTR(self);
+
+ return mrb_bool_value(
+ align_vertically((*ptr)->x, (*ptr)->z, (*that)->x, (*that)->z));
+}
+
+static mrb_value
+cg_cVector4D_align_horizontally(mrb_state *mrb, mrb_value self)
+{
+ std::shared_ptr<glm::vec4> *ptr, *that;
+
+ mrb_get_args(mrb, "d", &that, &cg_vector_4d_type);
+ ptr = (std::shared_ptr<glm::vec4>*)DATA_PTR(self);
+
+ return mrb_bool_value(
+ align_horizontally((*ptr)->y, (*ptr)->w, (*that)->y, (*that)->w));
+}
+
+static mrb_value
+cg_cVector4D_collide(mrb_state *mrb, mrb_value self)
+{
+ std::shared_ptr<glm::vec4> *ptr, *that;
+
+ mrb_get_args(mrb, "d", &that, &cg_vector_4d_type);
+ ptr = (std::shared_ptr<glm::vec4>*)DATA_PTR(self);
+
+ return mrb_bool_value(
+ align_vertically((*ptr)->x, (*ptr)->z, (*that)->x, (*that)->z) &&
+ align_horizontally((*ptr)->y, (*ptr)->w, (*that)->y, (*that)->w));
+}
+
+static mrb_value
+cg_cVector4D_draw_rectangle(mrb_state *mrb, mrb_value self)
+{
+ mrb_value view_value;
+ BluCat::GRA::View2D *view_2d;
+ std::shared_ptr<glm::vec3> *color;
+ auto ptr = (std::shared_ptr<glm::vec4>*)DATA_PTR(self);
+
+ mrb_get_args(mrb, "od", &view_value, &color, &cg_vector_3d_type);
+
+ view_2d = cg_cView_to_view_2d(mrb, view_value);
+
+ glm::vec4 position(
+ (*ptr)->x, (*ptr)->y, (*ptr)->x + (*ptr)->z, (*ptr)->y + (*ptr)->w);
+ BluCat::GRA::Rectangle rect{position, (**color)};
+ auto &rectangles = view_2d->rectangles_to_draw[
+ BluCat::INT::core.vk_swapchain->current_frame];
+ rectangles.push_back(rect);
+
+ return self;
+}
+
+void
+cg_vector_4d_init(mrb_state *mrb)
+{
+ struct RClass *cg_m, *cg_cVector4D;
+
+ cg_m = mrb_module_get(mrb, "CandyGear");
+ cg_cVector4D = mrb_define_class_under(
+ mrb, cg_m, "Vector4D", mrb->object_class);
+ MRB_SET_INSTANCE_TT(cg_cVector4D, MRB_TT_DATA);
+ mrb_define_method(
+ mrb, cg_cVector4D, "initialize", cg_cVector4D_initialize,
+ MRB_ARGS_NONE()|MRB_ARGS_OPT(4));
+
+ mrb_define_method(
+ mrb, cg_cVector4D, "x", cg_cVector4D_get_x, MRB_ARGS_NONE());
+ mrb_define_method(
+ mrb, cg_cVector4D, "y", cg_cVector4D_get_y, MRB_ARGS_NONE());
+ mrb_define_method(
+ mrb, cg_cVector4D, "w", cg_cVector4D_get_w, MRB_ARGS_NONE());
+ mrb_define_method(
+ mrb, cg_cVector4D, "h", cg_cVector4D_get_h, MRB_ARGS_NONE());
+ mrb_define_method(
+ mrb, cg_cVector4D, "r", cg_cVector4D_get_x, MRB_ARGS_NONE());
+ mrb_define_method(
+ mrb, cg_cVector4D, "g", cg_cVector4D_get_y, MRB_ARGS_NONE());
+ mrb_define_method(
+ mrb, cg_cVector4D, "b", cg_cVector4D_get_w, MRB_ARGS_NONE());
+ mrb_define_method(
+ mrb, cg_cVector4D, "a", cg_cVector4D_get_h, MRB_ARGS_NONE());
+
+ mrb_define_method(
+ mrb, cg_cVector4D, "xy", cg_cVector4D_get_xy, MRB_ARGS_NONE());
+ mrb_define_method(
+ mrb, cg_cVector4D, "xw", cg_cVector4D_get_xw, MRB_ARGS_NONE());
+ mrb_define_method(
+ mrb, cg_cVector4D, "xh", cg_cVector4D_get_xh, MRB_ARGS_NONE());
+ mrb_define_method(
+ mrb, cg_cVector4D, "yw", cg_cVector4D_get_yw, MRB_ARGS_NONE());
+ mrb_define_method(
+ mrb, cg_cVector4D, "yh", cg_cVector4D_get_yh, MRB_ARGS_NONE());
+ mrb_define_method(
+ mrb, cg_cVector4D, "wh", cg_cVector4D_get_wh, MRB_ARGS_NONE());
+
+ mrb_define_method(
+ mrb, cg_cVector4D, "rg", cg_cVector4D_get_xy, MRB_ARGS_NONE());
+ mrb_define_method(
+ mrb, cg_cVector4D, "rb", cg_cVector4D_get_xw, MRB_ARGS_NONE());
+ mrb_define_method(
+ mrb, cg_cVector4D, "ra", cg_cVector4D_get_xh, MRB_ARGS_NONE());
+ mrb_define_method(
+ mrb, cg_cVector4D, "gb", cg_cVector4D_get_yw, MRB_ARGS_NONE());
+ mrb_define_method(
+ mrb, cg_cVector4D, "ga", cg_cVector4D_get_yh, MRB_ARGS_NONE());
+ mrb_define_method(
+ mrb, cg_cVector4D, "ba", cg_cVector4D_get_wh, MRB_ARGS_NONE());
+
+ mrb_define_method(
+ mrb, cg_cVector4D, "x=", cg_cVector4D_set_x, MRB_ARGS_REQ(1));
+ mrb_define_method(
+ mrb, cg_cVector4D, "y=", cg_cVector4D_set_y, MRB_ARGS_REQ(1));
+ mrb_define_method(
+ mrb, cg_cVector4D, "w=", cg_cVector4D_set_w, MRB_ARGS_REQ(1));
+ mrb_define_method(
+ mrb, cg_cVector4D, "h=", cg_cVector4D_set_h, MRB_ARGS_REQ(1));
+ mrb_define_method(
+ mrb, cg_cVector4D, "r=", cg_cVector4D_set_x, MRB_ARGS_REQ(1));
+ mrb_define_method(
+ mrb, cg_cVector4D, "g=", cg_cVector4D_set_y, MRB_ARGS_REQ(1));
+ mrb_define_method(
+ mrb, cg_cVector4D, "b=", cg_cVector4D_set_w, MRB_ARGS_REQ(1));
+ mrb_define_method(
+ mrb, cg_cVector4D, "a=", cg_cVector4D_set_h, MRB_ARGS_REQ(1));
+
+ mrb_define_method(
+ mrb, cg_cVector4D, "set_xy", cg_cVector4D_set_xy, MRB_ARGS_REQ(2));
+ mrb_define_method(
+ mrb, cg_cVector4D, "set_xw", cg_cVector4D_set_xw, MRB_ARGS_REQ(2));
+ mrb_define_method(
+ mrb, cg_cVector4D, "set_xh", cg_cVector4D_set_xh, MRB_ARGS_REQ(2));
+ mrb_define_method(
+ mrb, cg_cVector4D, "set_yw", cg_cVector4D_set_yw, MRB_ARGS_REQ(2));
+ mrb_define_method(
+ mrb, cg_cVector4D, "set_yh", cg_cVector4D_set_yh, MRB_ARGS_REQ(2));
+ mrb_define_method(
+ mrb, cg_cVector4D, "set_wh", cg_cVector4D_set_wh, MRB_ARGS_REQ(2));
+
+ mrb_define_method(
+ mrb, cg_cVector4D, "set_rg", cg_cVector4D_set_xy, MRB_ARGS_REQ(2));
+ mrb_define_method(
+ mrb, cg_cVector4D, "set_rb", cg_cVector4D_set_xw, MRB_ARGS_REQ(2));
+ mrb_define_method(
+ mrb, cg_cVector4D, "set_ra", cg_cVector4D_set_xh, MRB_ARGS_REQ(2));
+ mrb_define_method(
+ mrb, cg_cVector4D, "set_gb", cg_cVector4D_set_yw, MRB_ARGS_REQ(2));
+ mrb_define_method(
+ mrb, cg_cVector4D, "set_ga", cg_cVector4D_set_yh, MRB_ARGS_REQ(2));
+ mrb_define_method(
+ mrb, cg_cVector4D, "set_ba", cg_cVector4D_set_wh, MRB_ARGS_REQ(2));
+
+ mrb_define_method(
+ mrb, cg_cVector4D, "align_vertically?", cg_cVector4D_align_vertically,
+ MRB_ARGS_REQ(1));
+ mrb_define_method(
+ mrb, cg_cVector4D, "align_horizontally?",
+ cg_cVector4D_align_horizontally, MRB_ARGS_REQ(1));
+ mrb_define_method(
+ mrb, cg_cVector4D, "collide?", cg_cVector4D_collide, MRB_ARGS_REQ(1));
+ mrb_define_method(
+ mrb, cg_cVector4D, "draw_rectangle", cg_cVector4D_draw_rectangle,
+ MRB_ARGS_REQ(2));
+}
diff --git a/src/candy_gear/vector_4d.hpp b/src/candy_gear/vector_4d.hpp
new file mode 100644
index 0000000..2797d53
--- /dev/null
+++ b/src/candy_gear/vector_4d.hpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CANDY_GEAR_VECTOR_4D_H
+#define CANDY_GEAR_VECTOR_4D_H 1
+
+#include "core.hpp"
+
+extern const struct mrb_data_type cg_vector_4d_type;
+
+void
+cg_vector_4d_init(mrb_state *mrb);
+
+#endif /* CANDY_GEAR_VECTOR_4D_H */
diff --git a/src/candy_gear/view_2d.cpp b/src/candy_gear/view_2d.cpp
new file mode 100644
index 0000000..e0dc8a1
--- /dev/null
+++ b/src/candy_gear/view_2d.cpp
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2022-2024 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "view_2d.hpp"
+
+#include "sprite.hpp"
+#include "vector_4d.hpp"
+#include "view_3d.hpp"
+#include "../blu_cat/gra/sprite.hpp"
+#include "../blu_cat/gra/view_2d.hpp"
+
+void
+cg_free_view_2d(mrb_state *mrb, void* obj)
+{
+ auto ptr = static_cast<std::shared_ptr<BluCat::GRA::View2D>*>(obj);
+
+ ptr->~shared_ptr();
+ mrb_free(mrb, ptr);
+}
+
+const struct mrb_data_type cg_view_2d_type = { "CG_View2D", cg_free_view_2d };
+
+static mrb_value
+cg_cView2D_initialize(mrb_state *mrb, mrb_value self)
+{
+ std::shared_ptr<glm::vec4> *region;
+ mrb_float projection_width, projection_height;
+ std::shared_ptr<BluCat::GRA::View2D> *ptr;
+
+ mrb_get_args(mrb, "dff", &region, &cg_vector_4d_type,
+ &projection_width, &projection_height);
+ ptr = (std::shared_ptr<BluCat::GRA::View2D>*)DATA_PTR(self);
+ if(ptr) mrb_free(mrb, ptr);
+ ptr = (std::shared_ptr<BluCat::GRA::View2D>*)mrb_malloc(
+ mrb, sizeof(std::shared_ptr<BluCat::GRA::View2D>));
+
+ new(ptr)std::shared_ptr<BluCat::GRA::View2D>(
+ std::make_shared<BluCat::GRA::View2D>(
+ *region->get(), projection_width, projection_height));
+
+ mrb_data_init(self, ptr, &cg_view_2d_type);
+ return self;
+}
+
+BluCat::GRA::View2D*
+cg_cView_to_view_2d(mrb_state *mrb, mrb_value view_value)
+{
+ BluCat::GRA::View2D* view_2d;
+ const mrb_data_type *type = DATA_TYPE(view_value);
+
+ if(type == &cg_view_2d_type)
+ view_2d = static_cast<std::shared_ptr<BluCat::GRA::View2D>*>(
+ DATA_PTR(view_value))->get();
+ else if (type == &cg_view_3d_type)
+ view_2d = static_cast<BluCat::GRA::View2D*>(
+ static_cast<std::shared_ptr<BluCat::GRA::View3D>*>(
+ DATA_PTR(view_value))->get());
+ else
+ mrb_raisef(
+ mrb, E_TYPE_ERROR, "wrong argument type %s (expected %s or %s)",
+ type->struct_name, cg_view_2d_type.struct_name,
+ cg_view_3d_type.struct_name);
+
+ return view_2d;
+}
+
+void
+cg_view_2d_init(mrb_state *mrb)
+{
+ struct RClass *cg_m, *cg_cView2D;
+
+ cg_m = mrb_module_get(mrb, "CandyGear");
+ cg_cView2D = mrb_define_class_under(mrb, cg_m, "View2D", mrb->object_class);
+ MRB_SET_INSTANCE_TT(cg_cView2D, MRB_TT_DATA);
+ mrb_define_method(
+ mrb, cg_cView2D, "initialize", cg_cView2D_initialize, MRB_ARGS_REQ(3));
+}
diff --git a/src/candy_gear/view_2d.hpp b/src/candy_gear/view_2d.hpp
new file mode 100644
index 0000000..9b742a0
--- /dev/null
+++ b/src/candy_gear/view_2d.hpp
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2022-2024 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CANDY_GEAR_VIEW_2D_H
+#define CANDY_GEAR_VIEW_2D_H 1
+
+#include "core.hpp"
+
+extern const struct mrb_data_type cg_view_2d_type;
+
+// Receives a mrb_value that points to a View2D or a View3D and returns a
+// pointer to View2D. If the mrb_value points to a View3D, the view will be
+// cast to a BluCat::GRA::View2D.
+BluCat::GRA::View2D*
+cg_cView_to_view_2d(mrb_state *mrb, mrb_value view_value);
+
+void
+cg_view_2d_init(mrb_state *mrb);
+
+#endif /* CANDY_GEAR_VIEW_2D_H */
diff --git a/src/candy_gear/view_3d.cpp b/src/candy_gear/view_3d.cpp
new file mode 100644
index 0000000..b829cca
--- /dev/null
+++ b/src/candy_gear/view_3d.cpp
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2022-2024 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "view_3d.hpp"
+
+#include "sprite.hpp"
+#include "orientation_3d.hpp"
+#include "vector_3d.hpp"
+#include "vector_4d.hpp"
+#include "../blu_cat/gra/sprite.hpp"
+#include "../blu_cat/gra/view_3d.hpp"
+
+void
+cg_free_view_3d(mrb_state *mrb, void* obj)
+{
+ auto ptr = static_cast<std::shared_ptr<BluCat::GRA::View3D>*>(obj);
+
+ ptr->~shared_ptr();
+ mrb_free(mrb, ptr);
+}
+
+const struct mrb_data_type cg_view_3d_type = { "CG_View3D", cg_free_view_3d };
+
+static mrb_value
+cg_cView3D_initialize(mrb_state *mrb, mrb_value self)
+{
+ std::shared_ptr<glm::vec4> *region;
+ mrb_float projection_width, projection_height;
+ std::shared_ptr<BluCat::GRA::View3D> *ptr;
+
+ mrb_get_args(mrb, "dff", &region, &cg_vector_4d_type,
+ &projection_width, &projection_height);
+ ptr = (std::shared_ptr<BluCat::GRA::View3D>*)DATA_PTR(self);
+ if(ptr) mrb_free(mrb, ptr);
+ ptr = (std::shared_ptr<BluCat::GRA::View3D>*)mrb_malloc(
+ mrb, sizeof(std::shared_ptr<BluCat::GRA::View3D>));
+
+ new(ptr)std::shared_ptr<BluCat::GRA::View3D>(
+ std::make_shared<BluCat::GRA::View3D>(
+ *region->get(), projection_width, projection_height));
+
+ mrb_data_init(self, ptr, &cg_view_3d_type);
+ return self;
+}
+
+static mrb_value
+cg_cView3D_set_camera_position(mrb_state *mrb, mrb_value self)
+{
+ std::shared_ptr<glm::vec3> *camera_position;
+ auto ptr = (std::shared_ptr<BluCat::GRA::View3D>*)DATA_PTR(self);
+
+ mrb_get_args(mrb, "d", &camera_position, &cg_vector_3d_type);
+ (*ptr)->camera_position = (*camera_position);
+
+ return self;
+}
+
+static mrb_value
+cg_cView3D_set_camera_orientation(mrb_state *mrb, mrb_value self)
+{
+ std::shared_ptr<glm::quat> *camera_orientation;
+ auto ptr = (std::shared_ptr<BluCat::GRA::View3D>*)DATA_PTR(self);
+
+ mrb_get_args(mrb, "d", &camera_orientation, &cg_orientation_3d_type);
+ (*ptr)->camera_orientation = (*camera_orientation);
+
+ return self;
+}
+
+static mrb_value
+cg_cView3D_get_field_of_view(mrb_state *mrb, mrb_value self)
+{
+ auto ptr = (std::shared_ptr<BluCat::GRA::View3D>*)DATA_PTR(self);
+
+ return mrb_float_value(mrb, (*ptr)->field_of_view);
+
+ return self;
+}
+
+static mrb_value
+cg_cView3D_set_field_of_view(mrb_state *mrb, mrb_value self)
+{
+ mrb_float fov;
+ auto ptr = (std::shared_ptr<BluCat::GRA::View3D>*)DATA_PTR(self);
+
+ mrb_get_args(mrb, "f", &fov);
+ (*ptr)->field_of_view = fov;
+
+ return self;
+}
+
+void
+cg_view_3d_init(mrb_state *mrb)
+{
+ struct RClass *cg_m, *cg_cView3D;
+
+ cg_m = mrb_module_get(mrb, "CandyGear");
+ cg_cView3D = mrb_define_class_under(mrb, cg_m, "View3D", mrb->object_class);
+ MRB_SET_INSTANCE_TT(cg_cView3D, MRB_TT_DATA);
+ mrb_define_method(
+ mrb, cg_cView3D, "initialize", cg_cView3D_initialize, MRB_ARGS_REQ(3));
+ mrb_define_method(
+ mrb, cg_cView3D, "camera_position=", cg_cView3D_set_camera_position,
+ MRB_ARGS_REQ(1));
+ mrb_define_method(
+ mrb, cg_cView3D, "camera_orientation=", cg_cView3D_set_camera_orientation,
+ MRB_ARGS_REQ(1));
+ mrb_define_method(
+ mrb, cg_cView3D, "field_of_view", cg_cView3D_get_field_of_view,
+ MRB_ARGS_NONE());
+ mrb_define_method(
+ mrb, cg_cView3D, "field_of_view=", cg_cView3D_set_field_of_view,
+ MRB_ARGS_REQ(1));
+}
diff --git a/src/candy_gear/view_3d.hpp b/src/candy_gear/view_3d.hpp
new file mode 100644
index 0000000..22aa084
--- /dev/null
+++ b/src/candy_gear/view_3d.hpp
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2022 Frederico de Oliveira Linhares
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef CANDY_GEAR_VIEW_3D_H
+#define CANDY_GEAR_VIEW_3D_H 1
+
+#include "core.hpp"
+
+extern const struct mrb_data_type cg_view_3d_type;
+
+void
+cg_view_3d_init(mrb_state *mrb);
+
+#endif /* CANDY_GEAR_VIEW_3D_H */