diff options
author | Frederico Linhares <fred@linhares.blue> | 2024-12-31 12:32:36 -0300 |
---|---|---|
committer | Frederico Linhares <fred@linhares.blue> | 2024-12-31 19:03:51 -0300 |
commit | 736637680ac7b2cd0d0b878401a7e044fde0ee6a (patch) | |
tree | bf4feaf3f3f0e48207bf7a31ad8bcbff0f244091 /src/candy_gear/core.cpp | |
parent | 083e64da1d4b5b68579288bc1690ca90d3f0a2c0 (diff) |
Diffstat (limited to 'src/candy_gear/core.cpp')
-rw-r--r-- | src/candy_gear/core.cpp | 398 |
1 files changed, 398 insertions, 0 deletions
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} +}; |