/* * 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 #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::core.game_name = "CandyGear Game"; BluCat::core.display_width = 800; BluCat::core.display_height = 600; BluCat::core.game_version_major = 0; BluCat::core.game_version_minor = 1; BluCat::core.game_version_patch = 0; BluCat::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::core.game_name.c_str(), SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, BluCat::core.display_width, BluCat::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 vk_extensions; std::vector vk_required_layers_names; // Get extensions. { uint32_t vk_extensions_count; std::vector 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 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::core.log.message(Log::Level::Trace, "Enabled VK extensions."); for(auto vk_extension: vk_extensions) { std::string message{"Extension name: "}; message += vk_extension; BluCat::core.log.message(Log::Level::Trace, message); } #endif } // Get available instance layers. { uint32_t vk_available_layers_count; std::vector vk_available_layers; std::vector 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::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::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::core.game_name.c_str(); app_info.applicationVersion = VK_MAKE_VERSION( BluCat::core.game_version_major, BluCat::core.game_version_minor, BluCat::core.game_version_patch); app_info.pEngineName = "CandyGear"; app_info.engineVersion = VK_MAKE_VERSION( CANDY_GEAR_VERSION_MAJOR, CANDY_GEAR_VERSION_MINOR, CANDY_GEAR_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::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::core.vk_instance, nullptr); } void load_window_surface(void *obj) { if(!SDL_Vulkan_CreateSurface( cg_core.window, BluCat::core.vk_instance, &BluCat::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::core.vk_instance, BluCat::core.window_surface, nullptr); } void load_blucat(void *obj) { BluCat::core.loader.execute(nullptr); } void unload_blucat(void *obj) { BluCat::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} };