refactored MainWindow and Engine, changed project structure

This commit is contained in:
Timerix 2025-04-26 00:59:47 +05:00
parent bb00392e3a
commit d659dcde10
54 changed files with 1662 additions and 339 deletions

114
src/Engine.cpp Normal file
View File

@ -0,0 +1,114 @@
#include "Engine.hpp"
#include "gui/gui.hpp"
namespace ougge {
Engine::Engine()
: gameObjectPool(GAMEOBJECTPOOL_SIZE)
{
}
void Engine::init(){
engineManagedAssembly = mono.loadAssembly("Ougge.dll");
gameObjectClass = engineManagedAssembly->getClass("Ougge", "GameObject");
gameObjectCtor = Mono::Method<void(u64, u32)>(gameObjectClass, ".ctor");
gameObjectInvokeUpdate = Mono::Method<void(f64)>(gameObjectClass, "InvokeUpdate");
gameObjectTryCreateComponent = Mono::Method<Mono::Bool(Mono::String)>(gameObjectClass, "TryCreateComponent_internal");
}
void Engine::openMainWindow(const std::string& window_title){
mainWindow.open(window_title);
}
void Engine::startLoop(){
if(loop_running)
throw UsefulException("loop is running already");
nsec_t prev_update_time_ns = getMonotonicTimeNsec();
loop_running=true;
// main loop
while(loop_running){
mainWindow.pollEvents(&loop_running);
if(!loop_running)
break;
nsec_t update_time_ns = getMonotonicTimeNsec();
if(update_time_ns < prev_update_time_ns)
throw UsefulException("monotonic clock returned unexpected value");
f64 delta_time_s = (f64)(update_time_ns - prev_update_time_ns) / 1e9;
prev_update_time_ns = update_time_ns;
tryDrawFrame(delta_time_s);
nsec_t after_update_time_ns = getMonotonicTimeNsec();
nsec_t frame_delay_ns = (nsec_t)1e9 / mainWindow.fps_max - (after_update_time_ns - update_time_ns);
if(frame_delay_ns > 0){
SDL_Delay(frame_delay_ns / 1e6);
}
}
mainWindow.close();
}
void Engine::stopLoop(){
loop_running = false;
}
void Engine::tryDrawFrame(f64 deltaTime){
static std::string error_message;
static bool draw_error_window = false;
try {
mainWindow.beginFrame();
if(draw_error_window)
gui::drawErrorWindow(error_message, &draw_error_window);
else {
updateGameObjects(deltaTime);
if(!updateCallback.isNull())
updateCallback(deltaTime);
}
mainWindow.endFrame();
}
catch(const std::exception& e){
error_message = "Catched exception: " + std::string(e.what());
draw_error_window = true;
std::cerr<<error_message<<std::endl;
}
catch(const char* cstr){
error_message = "Catched error message (const char*): " + std::string(cstr);
draw_error_window = true;
std::cerr<<error_message<<std::endl;
}
catch(const std::string& str){
error_message = "Catched error message (std::string): " + str;
draw_error_window = true;
std::cerr<<error_message<<std::endl;
}
catch(...){
error_message = "Catched unknown error";
draw_error_window = true;
std::cerr<<error_message<<std::endl;
}
}
void Engine::updateGameObjects(f64 deltaTime){
for(auto pair : gameObjectPool){
gameObjectInvokeUpdate(pair.second.getObjectHandle().getObject(), deltaTime);
}
}
game::GameObject& Engine::createGameObject(){
auto pair = gameObjectPool.emplace(game::GameObject(mono.createObject(gameObjectClass)));
game::GameObject& obj = pair.second;
gameObjectCtor(obj.getObjectHandle().getObject(), obj_id++, pair.first);
return obj;
}
bool Engine::tryCreateComponent(game::GameObject& obj, const std::string& componentClassName){
Mono::String componentClassNameManaged = mono.createString(componentClassName);
Mono::Bool created = gameObjectTryCreateComponent(obj.getObjectHandle().getObject(), componentClassNameManaged);
return created.wide_bool;
}
}

50
src/Engine.hpp Normal file
View File

@ -0,0 +1,50 @@
#pragma once
#include "common/function_shared_ptr.hpp"
#include "mono/mono.hpp"
#include "game/GameObjectPool.hpp"
#include "gui/MainWindow.hpp"
#include "resources/textures.hpp"
namespace ougge {
#define GAMEOBJECTPOOL_SIZE 64*1024
using UpdateFunc_t = function_shared_ptr<void(f64)>;
class Engine {
bool loop_running = false;
game::GameObjectPool gameObjectPool;
u64 obj_id = 0;
MonoClass* gameObjectClass;
Mono::Method<void(u64, u32)> gameObjectCtor;
Mono::Method<void(f64)> gameObjectInvokeUpdate;
Mono::Method<Mono::Bool(Mono::String)> gameObjectTryCreateComponent;
public:
gui::MainWindow mainWindow;
UpdateFunc_t updateCallback;
Mono::RuntimeJIT mono;
std::shared_ptr<Mono::Assembly> engineManagedAssembly;
resources::CacheStorage<resources::Texture> textures;
Engine();
void init();
void openMainWindow(const std::string& window_title);
// start game loop on the current thread
void startLoop();
void stopLoop();
game::GameObject& createGameObject();
bool tryCreateComponent(game::GameObject& obj, const std::string& componentClassName);
private:
void tryDrawFrame(f64 deltaTime);
void updateGameObjects(f64 deltaTime);
};
}

View File

@ -2,18 +2,18 @@
#include <backends/imgui_impl_sdlrenderer2.h>
#include <iostream>
#include "MainWindow.hpp"
#include "exceptions.hpp"
#include "../format.hpp"
#include "../Resources/fonts.hpp"
#include "../Resources/textures.hpp"
#include "../math.hpp"
#include "gui_exceptions.hpp"
#include "../common/ougge_format.hpp"
#include "../resources/fonts.hpp"
#include "../resources/textures.hpp"
#include "../common/math.hpp"
namespace ougge::GUI {
namespace ougge::gui {
f32 MainWindow::getDPI(){
int w=0, h=0;
i32 w=0, h=0;
SDL_GetRendererOutputSize(sdl_renderer, &w, &h);
int sim_w=0, sim_h=0;
i32 sim_w=0, sim_h=0;
SDL_GetWindowSize(sdl_window, &sim_w, &sim_h);
f32 wdpi=(f32)w / sim_w;
f32 hdpi=(f32)h / sim_h;
@ -21,13 +21,11 @@ f32 MainWindow::getDPI(){
return dpi;
}
void MainWindow::open(const char* window_title, UpdateFunc_t _update){
update = _update;
void MainWindow::open(const std::string& window_title){
SDL_TRY(SDL_Init(SDL_INIT_EVERYTHING));
SDL_version v;
SDL_GetVersion(&v);
std::cout<<format("SDL version: %u.%u.%u\n", v.major, v.minor, v.patch);
std::cout<<ougge_format("SDL version: %u.%u.%u\n", v.major, v.minor, v.patch);
// From 2.0.18: Enable native IME.
#ifdef SDL_HINT_IME_SHOW_UI
@ -37,7 +35,7 @@ void MainWindow::open(const char* window_title, UpdateFunc_t _update){
SDL_WindowFlags window_flags = (SDL_WindowFlags)(
SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI
);
sdl_window = SDL_CreateWindow(window_title,
sdl_window = SDL_CreateWindow(window_title.c_str(),
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
1280, 720, window_flags);
if(sdl_window == nullptr)
@ -75,138 +73,10 @@ void MainWindow::open(const char* window_title, UpdateFunc_t _update){
// Setup Dear ImGui style
ImGui::StyleColorsDark();
f32 dpi = getDPI();
io.FontDefault = Resources::ImFont_LoadFromResource(default_font, default_font_size, dpi);
io.FontDefault = resources::ImFont_LoadFromResource(default_font, default_font_size, dpi);
}
// Wait, poll and handle events (inputs, window resize, etc.)
void MainWindow::poll_events(bool waitForEvent){
SDL_Event event;
if(waitForEvent){
// waits for first event in cpu-efficient way
SDL_TRY(SDL_WaitEvent(&event) != 1);
}
// dont wait for event
else if(!SDL_PollEvent(&event))
return;
do {
ImGui_ImplSDL2_ProcessEvent(&event);
switch(event.type){
case SDL_QUIT: {
close();
break;
}
case SDL_WINDOWEVENT: {
if(event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(sdl_window)){
close();
}
break;
}
}
} while (SDL_PollEvent(&event)); // if there are more events, handles them
}
void ImGui_drawErrorWindow(bool* draw_error_window, std::string& msg){
ImGui::Begin("ERROR", draw_error_window);
ImGui::Text("%s", msg.c_str());
ImGui::End();
}
void MainWindow::draw_ui(){
static std::string error_message;
static bool draw_error_window = false;
try {
// Draw UI
if(draw_error_window)
ImGui_drawErrorWindow(&draw_error_window, error_message);
else {
draw_bg_window();
draw_debug_window();
}
}
catch(const std::exception& e){
error_message = "Catched exception: " + std::string(e.what());
draw_error_window = true;
std::cerr<<error_message<<std::endl;
}
catch(const char* cstr){
error_message = "Catched error message (const char*): " + std::string(cstr);
draw_error_window = true;
std::cerr<<error_message<<std::endl;
}
catch(const std::string& str){
error_message = "Catched error message (std::string): " + str;
draw_error_window = true;
std::cerr<<error_message<<std::endl;
}
catch(...){
error_message = "Catched unknown error";
draw_error_window = true;
std::cerr<<error_message<<std::endl;
}
}
void MainWindow::draw_frame(){
// Start the Dear ImGui frame
ImGui_ImplSDLRenderer2_NewFrame();
ImGui_ImplSDL2_NewFrame();
ImGui::NewFrame();
draw_ui();
// Rendering
ImGui::Render();
ImGuiIO& io = ImGui::GetIO();
SDL_RenderSetScale(sdl_renderer, io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y);
SDL_SetRenderDrawColor(sdl_renderer, (Uint8)(clear_color.x * 255), (Uint8)(clear_color.y * 255), (Uint8)(clear_color.z * 255), (Uint8)(clear_color.w * 255));
SDL_RenderClear(sdl_renderer);
// Example sprite
//Resources::Texture tutel(Resources::getResource("tutel.png"), sdl_renderer);
static Resources::CacheStorage<Resources::Texture> textures;
Resources::Texture& tutel = textures.getOrCreate("tutel.png", sdl_renderer);
Resources::SDL_RenderCopyExF_Params rp;
rp.target_section = SDL_FRectConstruct(100, 100, 400, 400);
static int si = 1;
rp.rotation_angle = M_PI_4 * (si++ / fps_max);
tutel.render(rp);
ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData(), sdl_renderer);
// Swap buffers
SDL_RenderPresent(sdl_renderer);
}
void MainWindow::startUpdateLoop(){
if(loop_running)
throw UsefulException("loop is running already");
nsec_t prev_update_time_ns = getMonotonicTimeNsec();
loop_running=true;
// main loop
while(loop_running){
poll_events(false);
nsec_t update_time_ns = getMonotonicTimeNsec();
if(update_time_ns < prev_update_time_ns)
throw UsefulException("monotonic clock returned unexpected value");
f64 delta_time_s = (f64)(update_time_ns - prev_update_time_ns) / 1e9;
prev_update_time_ns = update_time_ns;
update(delta_time_s);
draw_frame();
nsec_t after_update_time_ns = getMonotonicTimeNsec();
nsec_t frame_delay_ns = (nsec_t)1e9 / fps_max - (after_update_time_ns - update_time_ns);
if(frame_delay_ns > 0){
SDL_Delay(frame_delay_ns / 1e6);
}
}
destroy();
}
void MainWindow::destroy(){
void MainWindow::close(){
ImGui_ImplSDLRenderer2_Shutdown();
ImGui_ImplSDL2_Shutdown();
ImGui::DestroyContext();
@ -215,8 +85,52 @@ void MainWindow::destroy(){
SDL_Quit();
}
void MainWindow::close(){
loop_running = false;
void MainWindow::pollEvents(bool* loopRunning){
SDL_Event event;
while (SDL_PollEvent(&event)) {
ImGui_ImplSDL2_ProcessEvent(&event);
switch(event.type){
case SDL_QUIT: {
*loopRunning = false;
break;
}
case SDL_WINDOWEVENT: {
if(event.window.event == SDL_WINDOWEVENT_CLOSE
&& event.window.windowID == SDL_GetWindowID(sdl_window))
{
*loopRunning = false;
}
break;
}
}
}
}
void MainWindow::beginFrame(){
// Start the Dear ImGui frame
ImGui_ImplSDLRenderer2_NewFrame();
ImGui_ImplSDL2_NewFrame();
ImGui::NewFrame();
ImGuiIO& io = ImGui::GetIO();
SDL_RenderSetScale(sdl_renderer, io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y);
SDL_SetRenderDrawColor(sdl_renderer,
(u8)(clear_color.x * 255),
(u8)(clear_color.y * 255),
(u8)(clear_color.z * 255),
(u8)(clear_color.w * 255));
SDL_RenderClear(sdl_renderer);
draw_bg_window();
draw_debug_window();
}
void MainWindow::endFrame(){
ImGui::Render();
ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData(), sdl_renderer);
// Swap buffers
SDL_RenderPresent(sdl_renderer);
}
void MainWindow::draw_bg_window(){
@ -285,13 +199,14 @@ void MainWindow::draw_bg_window(){
void MainWindow::draw_debug_window(){
ImGuiIO& io = ImGui::GetIO();
ImGui::Begin("Debug Options");
if(ImGui::Begin("Debug Options", &show_debug_window)){
ImGui::ColorEdit3("clear_color", (float*)&clear_color);
ImGui::InputInt("fps_max", &fps_max);
ImGui::Text("Application average %.3f ms/frame (%.2f FPS)", 1000.0f / io.Framerate, io.Framerate);
ImGui::Checkbox("Demo Window", &show_demo_window);
ImGui::Checkbox("Metrics/Debug Window", &show_metrics_window);
ImGui::End();
}
if (show_demo_window)
ImGui::ShowDemoWindow(&show_demo_window);

View File

@ -2,47 +2,42 @@
#include <SDL.h>
#include <imgui.h>
#include "../std.hpp"
#include "../time.hpp"
#include <functional>
#include "../common/std.hpp"
#include "../common/time.hpp"
/// converts hex color to float vector
#define RGBAHexToF(R8,G8,B8,A8) ImVec4(((u8)35)/255.0f, ((u8)35)/255.0f, ((u8)50)/255.0f, ((u8)255)/255.0f)
/// converts float vector to hex color
#define RGBAFToHex(VEC4) {(u8)(VEC4.x*255), (u8)(VEC4.y*255), (u8)(VEC4.z*255), (u8)(VEC4.w*255)}
namespace ougge::GUI {
namespace ougge::gui {
#define default_font "DroidSans"
using UpdateFunc_t = std::function<void(f64)>;
class MainWindow {
public:
ImVec4 clear_color = RGBAHexToF(35,35,50,255);
i32 fps_max = 60;
f32 default_font_size = 14.0f;
int fps_max = 60;
// called on each frame
UpdateFunc_t update = nullptr;
private:
bool loop_running = false;
bool show_demo_window = false;
bool show_metrics_window = false;
ImVec4 clear_color = RGBAHexToF(35,35,50,255);
SDL_Window* sdl_window = nullptr;
SDL_Renderer* sdl_renderer = nullptr;
private:
bool show_debug_window = true;
bool show_demo_window = false;
bool show_metrics_window = false;
public:
void open(const char* window_title, UpdateFunc_t update);
void startUpdateLoop();
void open(const std::string& window_title);
void close();
private:
void destroy();
/// process io events happened since previous frame
void pollEvents(bool* loopRunning);
void beginFrame();
void endFrame();
f32 getDPI();
void poll_events(bool waitForEvent);
void draw_frame();
void draw_ui();
private:
void draw_debug_window();
void draw_bg_window();
};

View File

@ -1,37 +0,0 @@
#include "Engine.hpp"
namespace ougge {
Engine::Engine()
: gameObjectPool(GAMEOBJECTPOOL_SIZE)
{
}
void Engine::init(){
engineManagedAssembly = mono.loadAssembly("Ougge.dll");
gameObjectClass = engineManagedAssembly->getClass("Ougge", "GameObject");
gameObjectCtor = Mono::Method<void(u64, u32)>(gameObjectClass, ".ctor");
gameObjectInvokeUpdate = Mono::Method<void(f64)>(gameObjectClass, "InvokeUpdate");
gameObjectTryCreateComponent = Mono::Method<Mono::Bool(Mono::String)>(gameObjectClass, "TryCreateComponent_internal");
}
void Engine::invokeUpdate(f64 deltaTime){
for(auto pair : gameObjectPool){
gameObjectInvokeUpdate(pair.second.getObjectHandle().getObject(), deltaTime);
}
}
GameObject& Engine::createGameObject(){
auto pair = gameObjectPool.emplace(GameObject(mono.createObject(gameObjectClass)));
GameObject& obj = pair.second;
gameObjectCtor(obj.getObjectHandle().getObject(), obj_id++, pair.first);
return obj;
}
bool Engine::tryCreateComponent(GameObject& obj, const std::string& componentClassName){
Mono::String componentClassNameManaged = mono.createString(componentClassName);
Mono::Bool created = gameObjectTryCreateComponent(obj.getObjectHandle().getObject(), componentClassNameManaged);
return created.wide_bool;
}
}

View File

@ -1,30 +0,0 @@
#pragma once
#include "../Mono/Mono.hpp"
#include "GameObjectPool.hpp"
namespace ougge {
#define GAMEOBJECTPOOL_SIZE 64*1024
class Engine {
GameObjectPool gameObjectPool;
u64 obj_id = 0;
MonoClass* gameObjectClass;
Mono::Method<void(u64, u32)> gameObjectCtor;
Mono::Method<void(f64)> gameObjectInvokeUpdate;
Mono::Method<Mono::Bool(Mono::String)> gameObjectTryCreateComponent;
public:
Mono::RuntimeJIT mono;
std::shared_ptr<Mono::Assembly> engineManagedAssembly;
Engine();
void init();
void invokeUpdate(f64 deltaTime);
GameObject& createGameObject();
bool tryCreateComponent(GameObject& obj, const std::string& componentClassName);
};
}

View File

@ -1,6 +1,6 @@
#include "GameObject.hpp"
namespace ougge {
namespace ougge::game {
std::ostream& operator<<(std::ostream& s, Transform& t){
s<<"{ position: {x: "<<t.position.x<<", y: "<<t.position.y;

View File

@ -2,11 +2,11 @@
#include <map>
#include <iostream>
#include "../math.hpp"
#include "../UsefulException.hpp"
#include "../Mono/Mono.hpp"
#include "../common/math.hpp"
#include "../common/UsefulException.hpp"
#include "../mono/mono.hpp"
namespace ougge {
namespace ougge::game {
class GameObject;

View File

@ -2,7 +2,7 @@
#include <bit>
#include <cstring>
namespace ougge {
namespace ougge::game {
GameObjectPool::GameObjectPool(u32 size)
{
@ -75,9 +75,9 @@ u32 GameObjectPool::getNearestUsedIndex(u32 startIndex)
GameObject& GameObjectPool::get(u32 index)
{
if(index >= size)
throw UsefulException(format("index %i is out of size %i", index, size));
throw UsefulException(ougge_format("index %i is out of size %i", index, size));
if(!isIndexUsed(index))
throw UsefulException(format("there is no object at index %i", index));
throw UsefulException(ougge_format("there is no object at index %i", index));
return buffer[index];
}
@ -97,9 +97,9 @@ std::pair<u32, GameObject&> GameObjectPool::emplace(GameObject&& new_obj)
void GameObjectPool::erase(u32 index)
{
if(index >= size)
throw UsefulException(format("index %i is out of size %i", index, size));
throw UsefulException(ougge_format("index %i is out of size %i", index, size));
if(!isIndexUsed(index))
throw UsefulException(format("there is no object at index %i", index));
throw UsefulException(ougge_format("there is no object at index %i", index));
buffer[index] = GameObject();
used_indices[index/64] &= ~(u64(1)<<(index%64)); // mark index bit as unused

View File

@ -1,6 +1,6 @@
#include "GameObject.hpp"
namespace ougge {
namespace ougge::game {
/*
Fixed array that stores deleted elements indices as bits in array of u64.

View File

@ -1,4 +1,4 @@
#include "Mono.hpp"
#include "mono.hpp"
namespace Mono {
@ -10,7 +10,7 @@ Assembly::Assembly(MonoAssembly *ptr)
MonoClass* Assembly::getClass(const std::string &name_space, const std::string &name){
auto c = mono_class_from_name(image, name_space.c_str(), name.c_str());
if(!c)
throw UsefulException(format("can't get class '%s.%s'", name_space.c_str(), name.c_str()));
throw UsefulException(ougge_format("can't get class '%s.%s'", name_space.c_str(), name.c_str()));
return c;
}

View File

@ -1,4 +1,4 @@
#include "Mono.hpp"
#include "mono.hpp"
#include <mono/metadata/appdomain.h>
namespace Mono {

View File

@ -1,8 +1,8 @@
#pragma once
#include "../std.hpp"
#include "../UsefulException.hpp"
#include "../format.hpp"
#include "../common/std.hpp"
#include "../common/UsefulException.hpp"
#include "../common/ougge_format.hpp"
#include <vector>
#include <type_traits>
#include <mono/metadata/class.h>
@ -77,11 +77,12 @@ public:
static MonoClass* return_class { getClass<ReturnT>() };
method_ptr = tryGetMonoMethod(target_class, name, return_class, arg_classes, sizeof...(ArgTypes));
if(method_ptr == nullptr){
throw UsefulException(format("can't get method '%s' from class '%s'",
throw UsefulException(ougge_format("can't get method '%s' from class '%s'",
name.c_str(), mono_class_get_name(target_class)));
}
}
// ReturnT not is void
template<typename RT = ReturnT>
std::enable_if_t<!std::is_void<RT>::value, RT> operator()(MonoObject* class_instance, ArgTypes... args) const {
if(method_ptr == nullptr)
@ -98,6 +99,7 @@ public:
return valueFromMonoObject<ReturnT>(result);
};
// ReturnT is void
template<typename RT = ReturnT>
std::enable_if_t<std::is_void<RT>::value, RT> operator()(MonoObject* class_instance, ArgTypes... args) const {
if(method_ptr == nullptr)

View File

@ -1,4 +1,4 @@
#include "Mono.hpp"
#include "mono.hpp"
#include <mono/jit/jit.h>
#include <mono/metadata/mono-config.h>
#include <mono/metadata/appdomain.h>
@ -23,7 +23,7 @@ RuntimeJIT::~RuntimeJIT(){
std::shared_ptr<Assembly> RuntimeJIT::loadAssembly(const std::string &name){
MonoAssembly* ptr = mono_domain_assembly_open(domain, name.c_str());
if(!ptr)
throw UsefulException(format("can't load assembly '%s'", name.c_str()));
throw UsefulException(ougge_format("can't load assembly '%s'", name.c_str()));
return std::make_shared<Assembly>(ptr);
}

View File

@ -1,12 +1,12 @@
#include <fstream>
#include <sstream>
#include "../UsefulException.hpp"
#include "../format.hpp"
#include "Resources.hpp"
#include "../common/UsefulException.hpp"
#include "../common/ougge_format.hpp"
#include "resources.hpp"
#include "embedded_resources.h"
namespace ougge::Resources {
namespace ougge::resources {
Resource::Resource(const std::string& path, const std::size_t size, StreamFactoryMethod open_read_steam_func)
: path(path), size(size), openStream(open_read_steam_func)
@ -42,7 +42,7 @@ MemoryStreamRead::~MemoryStreamRead(){
static std::unordered_map<std::string, Resource>* _resourceMap = nullptr;
void loadEmbeddedResources(){
void loadEmbeddedresources(){
for(int i = 0; i < EmbeddedResource_table_count; i++){
const EmbeddedResource& e = EmbeddedResource_table[i];
std::cout <<"loading resource '" << e.path << "' "
@ -55,7 +55,7 @@ void loadEmbeddedResources(){
auto r = _resourceMap->emplace(e.path,
Resource(e.path, e.size, embedded_resource_factory));
if(!r.second)
throw UsefulException(format("can't load duplicate resource '%s'", e.path));
throw UsefulException(ougge_format("can't load duplicate resource '%s'", e.path));
}
}
@ -63,13 +63,13 @@ void init(){
if(_resourceMap != nullptr)
throw UsefulException("resource has been initialized already");
_resourceMap = new std::unordered_map<std::string, Resource>();
loadEmbeddedResources();
loadEmbeddedresources();
}
Resource& getResource(const std::string& path){
auto it = _resourceMap->find(path);
if(it == _resourceMap->end())
throw UsefulException(format("can't find resource '%s'", path.c_str()));
throw UsefulException(ougge_format("can't find resource '%s'", path.c_str()));
return it->second;
}

View File

@ -1,11 +1,11 @@
#pragma once
#include "../std.hpp"
#include "../common/std.hpp"
#include <iostream>
#include <functional>
#include <unordered_map>
namespace ougge::Resources {
namespace ougge::resources {
// call this in main()
void init();

View File

@ -1,9 +1,9 @@
#include <imgui.h>
#include "Resources.hpp"
#include "resources.hpp"
#include <cstdio>
#include <cstring>
namespace ougge::Resources {
namespace ougge::resources {
// select all glyphs from font
static const ImWchar glyph_ranges[] = {

View File

@ -1,9 +1,9 @@
#pragma once
#include "imgui.h"
#include "../std.hpp"
#include "../common/std.hpp"
namespace ougge::Resources {
namespace ougge::resources {
ImFont* ImFont_LoadFromFile(const std::string& file_path, f32 font_size, f32 dpi);

View File

@ -1,8 +1,8 @@
#include "textures.hpp"
#include <SDL_image.h>
#include "../GUI/exceptions.hpp"
#include "../gui/gui_exceptions.hpp"
namespace ougge::Resources {
namespace ougge::resources {
Texture::Texture(const Resource& r, SDL_Renderer* renderer)
: Texture(*r.openStream(), r.size, renderer)
@ -13,10 +13,10 @@ Texture::Texture(std::istream& s, size_t size, SDL_Renderer* renderer)
{
SDL_RWops* sdl_stream = SDL_RWFromIStream(s, size);
if(!sdl_stream)
throw SDLException();
throw gui::SDLException();
texture = std::shared_ptr<SDL_Texture>(IMG_LoadTexture_RW(renderer, sdl_stream, 1), SDL_DestroyTexture);
if(!texture)
throw IMGException();
throw gui::IMGException();
SDL_TRY(SDL_QueryTexture(texture.get(), nullptr, nullptr, &w, &h));
}
@ -52,7 +52,7 @@ static Sint64 istream_size(SDL_RWops* context){
return (Sint64)(size_t)context->hidden.unknown.data2;
}
static Sint64 istream_seek(SDL_RWops* context, Sint64 offset, int whence){
static Sint64 istream_seek(SDL_RWops* context, Sint64 offset, i32 whence){
std::istream* stream = (std::istream*)context->hidden.unknown.data1;
switch(whence){
case SEEK_SET: stream->seekg(offset, std::ios::beg); break;
@ -72,7 +72,7 @@ static size_t istream_read(SDL_RWops* context, void *ptr, size_t size, size_t ma
return stream->bad() ? -1 : stream->gcount() / size;
}
static int istream_close(SDL_RWops* context){
static i32 istream_close(SDL_RWops* context){
if (context)
SDL_FreeRW(context);
return 0;

View File

@ -3,10 +3,10 @@
#include <iostream>
#include <optional>
#include <SDL.h>
#include "Resources.hpp"
#include "../math.hpp"
#include "resources.hpp"
#include "../common/math.hpp"
namespace ougge::Resources {
namespace ougge::resources {
#define SDL_RectConstruct(X, Y, W, H) (SDL_Rect){X, Y, W, H}
#define SDL_FRectConstruct(X, Y, W, H) (SDL_FRect){X, Y, W, H}
@ -30,8 +30,8 @@ struct SDL_RenderCopyExF_Params {
struct Texture {
SDL_Renderer* renderer;
std::shared_ptr<SDL_Texture> texture;
int w;
int h;
i32 w;
i32 h;
Texture(const Resource& r, SDL_Renderer* renderer);
Texture(std::istream& s, size_t size, SDL_Renderer* renderer);

View File

@ -0,0 +1,50 @@
#pragma once
#include <functional>
#include <memory>
#include "UsefulException.hpp"
template<typename SignatureT> class function_shared_ptr;
template<typename ReturnT, typename... ArgTypes>
class function_shared_ptr<ReturnT(ArgTypes...)>
{
public:
using func_t = std::function<ReturnT(ArgTypes...)>;
using ptr_t = std::shared_ptr<func_t>;
protected:
ptr_t func_ptr;
public:
function_shared_ptr() = default;
function_shared_ptr(const function_shared_ptr&) = default;
function_shared_ptr(function_shared_ptr&&) = default;
function_shared_ptr(const ptr_t& p) : func_ptr(p) { }
function_shared_ptr(ptr_t&& p) : func_ptr(p) { }
template<typename FunctionT>
function_shared_ptr(FunctionT f){ func_ptr = std::make_shared<func_t>(f); }
function_shared_ptr& operator=(const function_shared_ptr&) = default;
function_shared_ptr& operator=(function_shared_ptr&&) = default;
bool isNull() const { return func_ptr == nullptr; }
//TODO: make inline
// ReturnT is not void
template<typename RT = ReturnT>
std::enable_if_t<!std::is_void<RT>::value, RT> operator()(ArgTypes... args){
if(func_ptr == nullptr)
throw UsefulException("function_shared_ptr is null");
return func_ptr->operator()(args...);
}
// ReturnT is void
template<typename RT = ReturnT>
std::enable_if_t<std::is_void<RT>::value, RT> operator()(ArgTypes... args){
if(func_ptr == nullptr)
throw UsefulException("function_shared_ptr is null");
func_ptr->operator()(args...);
}
};

View File

@ -1,9 +1,9 @@
#include <sstream>
#include <stdarg.h>
#include "format.hpp"
#include "ougge_format.hpp"
#include "UsefulException.hpp"
std::string _format(const std::string& format_str, const size_t args_count, ...){
std::string _ougge_format(const std::string& format_str, const size_t args_count, ...){
va_list vl;
va_start(vl, args_count);
std::stringstream ss;

View File

@ -0,0 +1,6 @@
#pragma once
#include "std.hpp"
std::string _ougge_format(const std::string& format_str, const size_t args_count, ...);
#define ougge_format(FORMAT_STR, ARGS...) _ougge_format(FORMAT_STR, count_args(ARGS) ,##ARGS)

View File

@ -1,6 +0,0 @@
#pragma once
#include "std.hpp"
std::string _format(const std::string& format_str, const size_t args_count, ...);
#define format(FORMAT_STR, ARGS...) _format(FORMAT_STR, count_args(ARGS) ,##ARGS)

View File

@ -1,30 +0,0 @@
#pragma once
#include <functional>
#include <memory>
template <typename SignatureT>
class function_shared_ptr;
template<typename ReturnT, typename... ArgTypes>
class function_shared_ptr<ReturnT(ArgTypes...)> {
public:
using func_t = std::function<ReturnT(ArgTypes...)>;
using ptr_t = std::shared_ptr<func_t>;
protected:
ptr_t func_ptr;
public:
function_shared_ptr() = default;
function_shared_ptr(function_shared_ptr&) = default;
function_shared_ptr(function_shared_ptr&&) = default;
function_shared_ptr(ptr_t& p) { func_ptr = p; }
function_shared_ptr(ptr_t&& p) { func_ptr = p; }
template<typename FunctionT>
function_shared_ptr(FunctionT f){ func_ptr = std::make_shared<func_t>(f); }
ReturnT operator()(ArgTypes... args){ return func_ptr->operator()(args...); }
function_shared_ptr& operator=(function_shared_ptr&) = default;
function_shared_ptr& operator=(function_shared_ptr&&) = default;
};

30
src/game/GameObject.cpp Normal file
View File

@ -0,0 +1,30 @@
#include "GameObject.hpp"
namespace ougge::game {
std::ostream& operator<<(std::ostream& s, Transform& t){
s<<"{ position: {x: "<<t.position.x<<", y: "<<t.position.y;
s<<"}, rotation: "<<t.rotation;
s<<", scale {x: "<<t.scale.x<<", y: "<<t.scale.y<<"} }";
return s;
}
GameObject::GameObject(Mono::Object managed_obj)
: object_handle(managed_obj)
{}
GameObject::GameObject(GameObject &&o) :
object_handle(std::move(o.object_handle)),
parent(o.parent),
transform(o.transform)
{
}
GameObject& GameObject::operator=(GameObject &&o){
object_handle = std::move(o.object_handle);
parent = o.parent;
transform = o.transform;
return *this;
}
}

45
src/game/GameObject.hpp Normal file
View File

@ -0,0 +1,45 @@
#pragma once
#include <map>
#include <iostream>
#include "../common/math.hpp"
#include "../common/UsefulException.hpp"
#include "../mono/mono.hpp"
namespace ougge::game {
class GameObject;
struct Transform {
Vec2 scale = { 1, 1 };
Vec2 position = { 0, 0 };
angle_t rotation = 0;
};
std::ostream& operator<<(std::ostream& s, Transform& t);
class GameObject {
Mono::ObjectHandle object_handle;
GameObject* parent;
Transform transform;
public:
/// @warning Do not use this to create objects.
/// This constructor creates null values for GameObject arrays
/// GameObject* array = new GameObject[10];
/// array[0] = GameObject(initialized_mono_object_ptr)
GameObject() = default;
GameObject(Mono::Object managed_obj);
GameObject(const GameObject& o) = delete;
GameObject(GameObject&& o);
GameObject& operator=(GameObject&& o);
inline Mono::ObjectHandle& getObjectHandle() { return object_handle; }
inline GameObject* getParent() { return parent; }
inline void setParent(GameObject* p) { parent = p; }
inline Transform& getTransform() { return transform; }
};
}

130
src/game/GameObjectPool.cpp Normal file
View File

@ -0,0 +1,130 @@
#include "GameObjectPool.hpp"
#include <bit>
#include <cstring>
namespace ougge::game {
GameObjectPool::GameObjectPool(u32 size)
{
useful_assert(size % 64 == 0, "size of GameObjectPool must be a multiple of 64");
this->size = size;
first_unused_index = 0;
buffer = new GameObject[size];
used_indices = new u64[size/64];
// std::memset(buffer, 0, size*sizeof(GameObject));
std::memset(used_indices, 0, size/8);
}
GameObjectPool::~GameObjectPool()
{
delete[] buffer;
delete[] used_indices;
}
bool GameObjectPool::isIndexUsed(u32 index)
{
return ( used_indices[index/64] & (u64(1)<<(index%64)) ) != 0;
}
u32 GameObjectPool::getNearestUnusedIndex(u32 startIndex)
{
if(startIndex >= size)
return -1;
if(!isIndexUsed(startIndex))
return startIndex;
u32 i = startIndex/64;
// mark previous bits as used
u64 u = used_indices[i] | ( (u64(1)<<startIndex%64) -1 );
while(u == u64(-1)){
i++;
if(i == size/64)
return -1;
u = used_indices[i];
}
u32 bitpos = std::countr_one(u);
if(bitpos == 64)
return -1;
u32 index = i*64 + bitpos;
return index;
}
u32 GameObjectPool::getNearestUsedIndex(u32 startIndex)
{
if(startIndex >= size)
return -1;
if(isIndexUsed(startIndex))
return startIndex;
u32 i = startIndex/64;
// mark previous bits as unused
u64 u = used_indices[i] & !( (u64(1)<<startIndex%64) -1 );
while(u == 0){
i++;
if(i == size/64)
return -1;
u = used_indices[i];
}
u32 bitpos = std::countr_zero(u);
if(bitpos == 64)
return -1;
u32 index = i*64 + bitpos;
return index;
}
GameObject& GameObjectPool::get(u32 index)
{
if(index >= size)
throw UsefulException(ougge_format("index %i is out of size %i", index, size));
if(!isIndexUsed(index))
throw UsefulException(ougge_format("there is no object at index %i", index));
return buffer[index];
}
std::pair<u32, GameObject&> GameObjectPool::emplace(GameObject&& new_obj)
{
u32 i = first_unused_index;
if(i == u32(-1))
throw UsefulException("can't put new GameObject to GameObjectPool because it's full");
buffer[i] = std::move(new_obj);
GameObject& r = buffer[i];
used_indices[i/64] |= u64(1)<<(i%64); // mark index bit as used
first_unused_index = getNearestUnusedIndex(i+1);
return std::pair<u32, GameObject&>(i, r);
}
void GameObjectPool::erase(u32 index)
{
if(index >= size)
throw UsefulException(ougge_format("index %i is out of size %i", index, size));
if(!isIndexUsed(index))
throw UsefulException(ougge_format("there is no object at index %i", index));
buffer[index] = GameObject();
used_indices[index/64] &= ~(u64(1)<<(index%64)); // mark index bit as unused
if(index < first_unused_index)
first_unused_index = index;
}
GameObjectPool::iterator::iterator(GameObjectPool* pool, u32 index)
: pool(pool), index(index)
{
}
std::pair<u32, GameObject&> GameObjectPool::iterator::operator*()
{
if(index >= pool->size)
throw UsefulException("can't get value of end() iterator");
GameObject& r = pool->buffer[index];
return std::pair<u32, GameObject&>(index, r);
}
GameObjectPool::iterator& GameObjectPool::iterator::operator++()
{
index = pool->getNearestUsedIndex(index+1);
return *this;
}
}

View File

@ -0,0 +1,70 @@
#include "GameObject.hpp"
namespace ougge::game {
/*
Fixed array that stores deleted elements indices as bits in array of u64.
Fast emplace, erase and lookup.
------------------------[construct]------------------------
operation 'GameObjectPool::GameObjectPool()' took 1.0549 ms
operation 'other_collections_construct' took 0.0133 ms
-------------------------[emplace]-------------------------
operation 'GameObjectPool::emplace' took 8.0557 ms
operation 'vector::emplace_back' took 11.3735 ms
operation 'set::emplace' took 80.5633 ms
operation 'list::emplace_front' took 18.1442 ms
operation 'forward_list::emplace_front' took 11.5467 ms
--------------------------[erase]--------------------------
operation 'GameObjectPool::erase' took 0.2745 ms
operation 'vector::erase' took 15790.6 ms
operation 'set::erase' took 1.2697 ms
operation 'list::erase_after' took 0.93 ms
operation 'forward_list::erase_after' took 1.1127 ms
-------------------------[iterate]-------------------------
operation 'GameObjectPool::iterate' took 1.1166 ms
operation 'vector::iterate' took 0.8883 ms
operation 'set::iterate' took 2.8011 ms
operation 'list::iterate' took 2.0766 ms
operation 'forward_list::iterate' took 2.0823 ms
*/
class GameObjectPool {
GameObject* buffer;
u64* used_indices;
u32 size;
u32 first_unused_index;
bool isIndexUsed(u32 index);
u32 getNearestUnusedIndex(u32 startIndex);
u32 getNearestUsedIndex(u32 startIndex);
public:
///@param size must be a multiple of 64
GameObjectPool(u32 size);
~GameObjectPool();
GameObject& get(u32 index);
std::pair<u32, GameObject&> emplace(GameObject&& new_obj);
void erase(u32 index);
#pragma region iterator class
class iterator {
GameObjectPool* pool;
u32 index = 0;
public:
iterator(GameObjectPool* pool, u32 index);
std::pair<u32, GameObject&> operator*();
iterator& operator++();
inline bool operator!=(const iterator& o) const { return index != o.index; };
inline bool operator==(const iterator& o) const { return index == o.index; };
};
#pragma endregion
inline iterator begin() { return iterator(this, 0); }
inline iterator end() { return iterator(this, -1); }
friend class iterator;
};
}

11
src/gui/ErrorWindow.cpp Normal file
View File

@ -0,0 +1,11 @@
#include "gui.hpp"
namespace ougge::gui {
void drawErrorWindow(const std::string& msg, bool* draw_error_window){
ImGui::Begin("ERROR", draw_error_window);
ImGui::Text("%s", msg.c_str());
ImGui::End();
}
}

218
src/gui/MainWindow.cpp Normal file
View File

@ -0,0 +1,218 @@
#include <backends/imgui_impl_sdl2.h>
#include <backends/imgui_impl_sdlrenderer2.h>
#include <iostream>
#include "MainWindow.hpp"
#include "gui_exceptions.hpp"
#include "../common/ougge_format.hpp"
#include "../resources/fonts.hpp"
#include "../resources/textures.hpp"
#include "../common/math.hpp"
namespace ougge::gui {
f32 MainWindow::getDPI(){
i32 w=0, h=0;
SDL_GetRendererOutputSize(sdl_renderer, &w, &h);
i32 sim_w=0, sim_h=0;
SDL_GetWindowSize(sdl_window, &sim_w, &sim_h);
f32 wdpi=(f32)w / sim_w;
f32 hdpi=(f32)h / sim_h;
f32 dpi=SDL_sqrtf(wdpi*wdpi + hdpi*hdpi);
return dpi;
}
void MainWindow::open(const std::string& window_title){
SDL_TRY(SDL_Init(SDL_INIT_EVERYTHING));
SDL_version v;
SDL_GetVersion(&v);
std::cout<<ougge_format("SDL version: %u.%u.%u\n", v.major, v.minor, v.patch);
// From 2.0.18: Enable native IME.
#ifdef SDL_HINT_IME_SHOW_UI
SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");
#endif
SDL_WindowFlags window_flags = (SDL_WindowFlags)(
SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI
);
sdl_window = SDL_CreateWindow(window_title.c_str(),
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
1280, 720, window_flags);
if(sdl_window == nullptr)
throw SDLException();
SDL_RendererFlags renderer_flags = (SDL_RendererFlags)(
SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC
);
sdl_renderer = SDL_CreateRenderer(sdl_window, -1, renderer_flags);
if(sdl_renderer == nullptr)
throw SDLException();
SDL_RendererInfo info;
SDL_GetRendererInfo(sdl_renderer, &info);
std::cout<<"Current SDL_Renderer driver: "<<info.name<<std::endl;
// Setup Dear ImGui context
IMGUI_CHECKVERSION();
ImGui::CreateContext();
ImGuiIO& io = ImGui::GetIO();
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking
io.ConfigDockingTransparentPayload = true;
io.ConfigInputTextCursorBlink = false;
// io.ConfigViewportsNoTaskBarIcon = true;
io.ConfigWindowsMoveFromTitleBarOnly = true;
// Setup Platform/Renderer backends
if(!ImGui_ImplSDL2_InitForSDLRenderer(sdl_window, sdl_renderer))
throw SDLException();
if(!ImGui_ImplSDLRenderer2_Init(sdl_renderer))
throw SDLException();
// Setup Dear ImGui style
ImGui::StyleColorsDark();
f32 dpi = getDPI();
io.FontDefault = resources::ImFont_LoadFromResource(default_font, default_font_size, dpi);
}
void MainWindow::close(){
ImGui_ImplSDLRenderer2_Shutdown();
ImGui_ImplSDL2_Shutdown();
ImGui::DestroyContext();
SDL_DestroyRenderer(sdl_renderer);
SDL_DestroyWindow(sdl_window);
SDL_Quit();
}
void MainWindow::pollEvents(bool* loopRunning){
SDL_Event event;
while (SDL_PollEvent(&event)) {
ImGui_ImplSDL2_ProcessEvent(&event);
switch(event.type){
case SDL_QUIT: {
*loopRunning = false;
break;
}
case SDL_WINDOWEVENT: {
if(event.window.event == SDL_WINDOWEVENT_CLOSE
&& event.window.windowID == SDL_GetWindowID(sdl_window))
{
*loopRunning = false;
}
break;
}
}
}
}
void MainWindow::beginFrame(){
// Start the Dear ImGui frame
ImGui_ImplSDLRenderer2_NewFrame();
ImGui_ImplSDL2_NewFrame();
ImGui::NewFrame();
ImGuiIO& io = ImGui::GetIO();
SDL_RenderSetScale(sdl_renderer, io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y);
SDL_SetRenderDrawColor(sdl_renderer,
(u8)(clear_color.x * 255),
(u8)(clear_color.y * 255),
(u8)(clear_color.z * 255),
(u8)(clear_color.w * 255));
SDL_RenderClear(sdl_renderer);
draw_bg_window();
draw_debug_window();
}
void MainWindow::endFrame(){
ImGui::Render();
ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData(), sdl_renderer);
// Swap buffers
SDL_RenderPresent(sdl_renderer);
}
void MainWindow::draw_bg_window(){
const ImGuiDockNodeFlags dockspace_flags =
ImGuiDockNodeFlags_PassthruCentralNode;
const ImGuiWindowFlags window_flags =
ImGuiWindowFlags_MenuBar |
ImGuiWindowFlags_NoDocking |
ImGuiWindowFlags_NoScrollbar |
ImGuiWindowFlags_NoScrollWithMouse |
ImGuiWindowFlags_NoBringToFrontOnFocus |
ImGuiWindowFlags_NoFocusOnAppearing |
ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoCollapse |
ImGuiWindowFlags_NoTitleBar |
ImGuiWindowFlags_NoBackground;
// not dockable window that always bound to viewport
ImGuiViewport* viewport = ImGui::GetWindowViewport();
ImGui::SetNextWindowPos(viewport->Pos, ImGuiCond_Always);
ImGui::SetNextWindowSize(viewport->Size, ImGuiCond_Always);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
ImGui::Begin("bg_window", nullptr, window_flags);
ImGui::PopStyleVar(3);
// DockSpace
ImGuiID dockspace_id = ImGui::GetID("bg_dockspace");
ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags);
// MenuBar
if(ImGui::BeginMainMenuBar()){
if(ImGui::BeginMenu("test")){
if(ImGui::MenuItem("throw exception")){
ImGui::EndMenu();
ImGui::EndMainMenuBar();
ImGui::End();
throw UsefulException("example exception");
}
if(ImGui::MenuItem("throw const char*")){
ImGui::EndMenu();
ImGui::EndMainMenuBar();
ImGui::End();
throw "cptr";
}
if(ImGui::MenuItem("throw std::string")){
ImGui::EndMenu();
ImGui::EndMainMenuBar();
ImGui::End();
throw std::string("str");
}
if(ImGui::MenuItem("throw unknown")){
ImGui::EndMenu();
ImGui::EndMainMenuBar();
ImGui::End();
throw 111;
}
ImGui::EndMenu();
}
ImGui::EndMainMenuBar();
}
ImGui::End();
}
void MainWindow::draw_debug_window(){
ImGuiIO& io = ImGui::GetIO();
if(ImGui::Begin("Debug Options", &show_debug_window)){
ImGui::ColorEdit3("clear_color", (float*)&clear_color);
ImGui::InputInt("fps_max", &fps_max);
ImGui::Text("Application average %.3f ms/frame (%.2f FPS)", 1000.0f / io.Framerate, io.Framerate);
ImGui::Checkbox("Demo Window", &show_demo_window);
ImGui::Checkbox("Metrics/Debug Window", &show_metrics_window);
ImGui::End();
}
if (show_demo_window)
ImGui::ShowDemoWindow(&show_demo_window);
if (show_metrics_window)
ImGui::ShowMetricsWindow(&show_metrics_window);
}
}

45
src/gui/MainWindow.hpp Normal file
View File

@ -0,0 +1,45 @@
#pragma once
#include <SDL.h>
#include <imgui.h>
#include "../common/std.hpp"
#include "../common/time.hpp"
/// converts hex color to float vector
#define RGBAHexToF(R8,G8,B8,A8) ImVec4(((u8)35)/255.0f, ((u8)35)/255.0f, ((u8)50)/255.0f, ((u8)255)/255.0f)
/// converts float vector to hex color
#define RGBAFToHex(VEC4) {(u8)(VEC4.x*255), (u8)(VEC4.y*255), (u8)(VEC4.z*255), (u8)(VEC4.w*255)}
namespace ougge::gui {
#define default_font "DroidSans"
class MainWindow {
public:
i32 fps_max = 60;
f32 default_font_size = 14.0f;
ImVec4 clear_color = RGBAHexToF(35,35,50,255);
SDL_Window* sdl_window = nullptr;
SDL_Renderer* sdl_renderer = nullptr;
private:
bool show_debug_window = true;
bool show_demo_window = false;
bool show_metrics_window = false;
public:
void open(const std::string& window_title);
void close();
/// process io events happened since previous frame
void pollEvents(bool* loopRunning);
void beginFrame();
void endFrame();
f32 getDPI();
private:
void draw_debug_window();
void draw_bg_window();
};
}

0
src/gui/SceneView.hpp Normal file
View File

11
src/gui/gui.hpp Normal file
View File

@ -0,0 +1,11 @@
#pragma once
#include <SDL.h>
#include <imgui.h>
#include "gui_exceptions.hpp"
namespace ougge::gui {
void drawErrorWindow(const std::string& msg, bool* draw_error_window);
}

View File

@ -1,8 +1,8 @@
#include "exceptions.hpp"
#include "gui_exceptions.hpp"
#include <SDL.h>
#include <SDL_image.h>
namespace ougge {
namespace ougge::gui {
SDLException_::SDLException_(const std::string& _file,const std::string& _func, int _line_n)
: UsefulException_(std::string("SDLException: ") + SDL_GetError(), _file, _func, _line_n)
@ -10,10 +10,14 @@ SDLException_::SDLException_(const std::string& _file,const std::string& _func,
SDL_ClearError();
}
#ifndef IMG_ClearError
#define IMG_ClearError SDL_ClearError
#endif
IMGException_::IMGException_(const std::string& _file,const std::string& _func, int _line_n)
: UsefulException_(std::string("IMGException: ") + IMG_GetError(), _file, _func, _line_n)
{
SDL_ClearError();
IMG_ClearError();
}
}

View File

@ -1,8 +1,10 @@
#pragma once
#include "../UsefulException.hpp"
namespace ougge {
#include "../common/std.hpp"
#include "../common/UsefulException.hpp"
namespace ougge::gui {
#define SDLException() SDLException_(__FILE__, __func__, __LINE__)
@ -20,6 +22,6 @@ public:
};
#define SDL_TRY(EXPR) if(EXPR) throw SDLException();
#define SDL_TRY(EXPR) if(EXPR) throw ::ougge::gui::SDLException();
}

View File

@ -1,17 +1,25 @@
#define SDL_MAIN_HANDLED
#include <iostream>
#include <iomanip>
#include "GUI/MainWindow.hpp"
#include "Resources/Resources.hpp"
#include "Game/Engine.hpp"
#include "format.hpp"
#include "UsefulException.hpp"
#include "common/UsefulException.hpp"
#include "common/ougge_format.hpp"
#include "Engine.hpp"
#include "gui/gui.hpp"
using namespace ougge;
void drawTutel(Engine& engine){
resources::Texture& tutel = engine.textures.getOrCreate("tutel.png", engine.mainWindow.sdl_renderer);
resources::SDL_RenderCopyExF_Params params;
params.target_section = SDL_FRectConstruct(100, 100, 400, 400);
static i32 si = 1;
params.rotation_angle = M_PI_4 * (si++ / engine.mainWindow.fps_max);
tutel.render(params);
}
int main(int argc, const char** argv){
try {
Resources::init();
resources::init();
std::cout<<"initialized resource loader"<<std::endl;
Engine engine;
@ -19,21 +27,20 @@ int main(int argc, const char** argv){
engine.init();
std::cout<<"initialized game engine"<<std::endl;
GameObject& exampleObj = engine.createGameObject();
game::GameObject& exampleObj = engine.createGameObject();
std::string componentClassName = "Ougge.ExampleComponent";
if(!engine.tryCreateComponent(exampleObj, componentClassName))
throw UsefulException(format("couldn't create component '%s'", componentClassName.c_str()));
throw UsefulException(ougge_format("couldn't create component '%s'", componentClassName.c_str()));
std::cout<<"created ExampleObject"<<std::endl;
auto updateCallback = [&engine](f64 deltaTime) -> void {
engine.invokeUpdate(deltaTime);
engine.updateCallback = [&engine](f64 deltaTime) -> void {
drawTutel(engine);
};
GUI::MainWindow w;
w.open("ougge", updateCallback);
engine.openMainWindow("ougge");
std::cout<<"created sdl window"<<std::endl;
w.startUpdateLoop();
engine.startLoop();
std::cout<<"sdl window has been cosed"<<std::endl;
}
catch(const std::exception& e){

17
src/mono/Assembly.cpp Normal file
View File

@ -0,0 +1,17 @@
#include "mono.hpp"
namespace Mono {
Assembly::Assembly(MonoAssembly *ptr)
: ptr(ptr), image(mono_assembly_get_image(ptr))
{
}
MonoClass* Assembly::getClass(const std::string &name_space, const std::string &name){
auto c = mono_class_from_name(image, name_space.c_str(), name.c_str());
if(!c)
throw UsefulException(ougge_format("can't get class '%s.%s'", name_space.c_str(), name.c_str()));
return c;
}
}

30
src/mono/Runtime.cpp Normal file
View File

@ -0,0 +1,30 @@
#include "mono.hpp"
#include <mono/jit/jit.h>
#include <mono/metadata/mono-config.h>
#include <mono/metadata/appdomain.h>
namespace Mono {
RuntimeJIT::RuntimeJIT(const std::string& domain_name){
mono_set_dirs("mono-libs", "mono-libs");
mono_set_assemblies_path("mono-libs");
mono_config_parse("mono-libs/config.xml");
domain = mono_jit_init(domain_name.c_str());
if(!domain)
throw UsefulException("can't initialize mono domain");
}
RuntimeJIT::~RuntimeJIT(){
// TODO: fix segfault on cleanup
// mono_jit_cleanup(domain);
}
std::shared_ptr<Assembly> RuntimeJIT::loadAssembly(const std::string &name){
MonoAssembly* ptr = mono_domain_assembly_open(domain, name.c_str());
if(!ptr)
throw UsefulException(ougge_format("can't load assembly '%s'", name.c_str()));
return std::make_shared<Assembly>(ptr);
}
}

81
src/mono/mono.cpp Normal file
View File

@ -0,0 +1,81 @@
#include "mono.hpp"
#include <mono/metadata/appdomain.h>
namespace Mono {
template <> MonoClass* getClass<SByte>(){ return mono_get_sbyte_class(); }
template <> MonoClass* getClass<Byte>(){ return mono_get_byte_class(); }
template <> MonoClass* getClass<Short>(){ return mono_get_int16_class(); }
template <> MonoClass* getClass<UShort>(){ return mono_get_uint16_class(); }
template <> MonoClass* getClass<Int>(){ return mono_get_int32_class(); }
template <> MonoClass* getClass<UInt>(){ return mono_get_uint32_class(); }
template <> MonoClass* getClass<Long>(){ return mono_get_int64_class(); }
template <> MonoClass* getClass<ULong>(){ return mono_get_uint64_class(); }
template <> MonoClass* getClass<Float>(){ return mono_get_single_class(); }
template <> MonoClass* getClass<Double>(){ return mono_get_double_class(); }
template <> MonoClass* getClass<Bool>(){ return mono_get_boolean_class(); }
template <> MonoClass* getClass<Char>(){ return mono_get_char_class(); }
template <> MonoClass* getClass<String>(){ return mono_get_string_class(); }
template <> MonoClass* getClass<Object>(){ return mono_get_object_class(); }
template <> MonoClass* getClass<Void>(){ return mono_get_void_class(); }
void getMethodSignatureTypes(MonoMethod* mono_method,
MonoType** return_type,
std::vector<MonoType*>& argument_types)
{
auto signature = mono_method_signature(mono_method);
void* iter = nullptr;
*return_type = mono_signature_get_return_type(signature);
MonoType* pt = nullptr;
while( (pt = mono_signature_get_params(signature, &iter)) ){
argument_types.push_back(pt);
}
}
MonoMethod* tryGetMonoMethod(MonoClass* target_class, const std::string& name,
MonoClass* return_class, MonoClass* arg_classes[], size_t arg_classes_size)
{
if(target_class == nullptr)
throw UsefulException("target_class is nullptr");
// iterate each method
void* iter = nullptr;
MonoMethod* m = nullptr;
std::vector<MonoType*> argument_types;
while( (m = mono_class_get_methods(target_class, &iter)) ){
// compare name
std::string m_name = mono_method_get_name(m);
if(m_name != name)
continue;
argument_types.clear();
MonoType* return_type = nullptr;
getMethodSignatureTypes(m, &return_type, argument_types);
// compare argument count
if(argument_types.size() != arg_classes_size)
continue;
// compare return type
if(!mono_metadata_type_equal(return_type, mono_class_get_type(return_class)))
continue;
// compare argument types
bool argument_types_mismatch = false;
for(size_t i = 0; i < arg_classes_size; i++){
if(!mono_metadata_type_equal(argument_types[i], mono_class_get_type(arg_classes[i]))){
argument_types_mismatch = true;
break;
}
}
if(argument_types_mismatch)
continue;
// passed all tests successfully
break;
}
return m;
}
}

187
src/mono/mono.hpp Normal file
View File

@ -0,0 +1,187 @@
#pragma once
#include "../common/std.hpp"
#include "../common/UsefulException.hpp"
#include "../common/ougge_format.hpp"
#include <vector>
#include <type_traits>
#include <mono/metadata/class.h>
#include <mono/metadata/assembly.h>
#include <mono/metadata/object.h>
namespace Mono {
typedef i8 SByte;
typedef u8 Byte;
typedef i16 Short;
typedef u16 UShort;
typedef i32 Int;
typedef u32 UInt;
typedef i64 Long;
typedef u64 ULong;
typedef f32 Float;
typedef f64 Double;
typedef union { mono_bool wide_bool; } Bool; //USAGE: Bool t = {true};
typedef char16_t Char;
typedef MonoString* String;
/// @warning MonoObject can be moved in memory by GC in any time and raw pointer will be invalid.
/// Use ObjectHandle where it is possible.
typedef MonoObject* Object;
typedef void Void;
template<typename PrimitiveT>
MonoClass* getClass();
template<typename T>
void* valueToVoidPtr(T& v){ return &v; }
template<>
inline void* valueToVoidPtr<Object>(Object& v){ return v; }
template<>
inline void* valueToVoidPtr<String>(String& v){ return v; }
template<typename T>
T valueFromMonoObject(MonoObject* o){
void* result_value_ptr = mono_object_unbox(o);
if(result_value_ptr == nullptr)
throw UsefulException("can't unbox method value");
return *((T*)result_value_ptr);
}
template<>
inline Object valueFromMonoObject<Object>(MonoObject* o){ return o; }
template<>
inline String valueFromMonoObject<String>(MonoObject* o){ return (String)((void*)o); }
void getMethodSignatureTypes(MonoMethod* mono_method,
MonoType** return_type,
std::vector<MonoType*>& argument_types);
/// searches for method `name` in `target_class` with return type `return_class` and argument types `arg_classes`
/// @return found method or nullptr
MonoMethod* tryGetMonoMethod(MonoClass* target_class, const std::string& name,
MonoClass* return_class, MonoClass* arg_classes[], size_t arg_classes_size);
template<typename SignatureT> class Method;
template<typename ReturnT, typename... ArgTypes>
class Method<ReturnT(ArgTypes...)>
{
MonoMethod* method_ptr;
public:
Method() { method_ptr = nullptr; }
/// all types must implement getClass<T>()
Method(MonoClass* target_class, const std::string& name){
static MonoClass* arg_classes[] { getClass<ArgTypes>()... };
static MonoClass* return_class { getClass<ReturnT>() };
method_ptr = tryGetMonoMethod(target_class, name, return_class, arg_classes, sizeof...(ArgTypes));
if(method_ptr == nullptr){
throw UsefulException(ougge_format("can't get method '%s' from class '%s'",
name.c_str(), mono_class_get_name(target_class)));
}
}
// ReturnT not is void
template<typename RT = ReturnT>
std::enable_if_t<!std::is_void<RT>::value, RT> operator()(MonoObject* class_instance, ArgTypes... args) const {
if(method_ptr == nullptr)
throw UsefulException("method_ptr is null");
void* arg_array[] = { valueToVoidPtr(args)..., nullptr };
MonoObject* ex = nullptr;
MonoObject* result = mono_runtime_invoke(method_ptr, class_instance, arg_array, &ex);
if(ex){
//TODO: call mono_trace_set_printerr_handler from mono/mono/utils/mono-logger.h
mono_print_unhandled_exception(ex);
throw UsefulException("Some C# exception occured");
}
return valueFromMonoObject<ReturnT>(result);
};
// ReturnT is void
template<typename RT = ReturnT>
std::enable_if_t<std::is_void<RT>::value, RT> operator()(MonoObject* class_instance, ArgTypes... args) const {
if(method_ptr == nullptr)
throw UsefulException("method_ptr is null");
void* arg_array[] = { valueToVoidPtr(args)..., nullptr };
MonoObject* ex = nullptr;
mono_runtime_invoke(method_ptr, class_instance, arg_array, &ex);
if(ex){
//TODO: call mono_trace_set_printerr_handler from mono/mono/utils/mono-logger.h
mono_print_unhandled_exception(ex);
throw UsefulException("Some C# exception occured");
}
};
};
class Assembly {
MonoAssembly* ptr;
MonoImage* image;
public:
Assembly(MonoAssembly* ptr);
Assembly(const Assembly&) = delete;
MonoClass* getClass(const std::string& name_space, const std::string& name);
MonoAssembly* getMonoAssembly() const { return ptr; }
MonoImage* getMonoImage() const { return image; }
};
///LINUX: `config.xml`, `mscorelib.dll`, `libmono-native.so` in `mono-libs` directory
///
///WINDOWS: `config.xml`, `mscorelib.dll` in `mono-libs` directory
class RuntimeJIT {
MonoDomain* domain;
public:
RuntimeJIT(const std::string& domain_name = "OuggeDomain");
RuntimeJIT(const RuntimeJIT&) = delete;
~RuntimeJIT();
inline MonoDomain* getDomain() { return domain; }
std::shared_ptr<Assembly> loadAssembly(const std::string& name);
inline Object createObject(MonoClass* klass) { return mono_object_new(domain, klass); }
inline String createString(const std::string& v) { return mono_string_new_len(domain, v.c_str(), v.size()); }
inline String createString(const char* v) { return mono_string_new(domain, v); }
};
/// @brief ObjectHandle can be used to store reliable reference to MonoObject.
/// MonoObject can be moved in memory by GC in any time and raw pointer will be invalid.
struct ObjectHandle {
u32 gc_handle;
inline ObjectHandle() : gc_handle(0) {}
inline ObjectHandle(Object obj) {
gc_handle = mono_gchandle_new(obj, false);
}
/// implicitly create new ObjectHandle instead
inline ObjectHandle(const ObjectHandle& o) = delete;
inline ObjectHandle(ObjectHandle&& o) {
gc_handle = o.gc_handle;
o.gc_handle = 0;
}
inline ObjectHandle& operator=(ObjectHandle&& o) {
gc_handle = o.gc_handle;
o.gc_handle = 0;
return *this;
}
inline ~ObjectHandle() {
if(gc_handle)
mono_gchandle_free(gc_handle);
}
inline Object getObject() const {
return mono_gchandle_get_target(gc_handle);
}
};
}

View File

@ -0,0 +1,64 @@
////////////////////////////////////////////////////////////////
// This file was generated by resource_embedder //
// https://timerix.ddns.net:3322/Timerix/resource_embedder //
////////////////////////////////////////////////////////////////
// USAGE: //
// Put it in a SOURCE file to define variables //
// #define EMBEDDED_RESOURCE_DEFINITION //
// #define EMBEDDED_RESOURCE_POSTFIX your_postfix //
// #include "../../src/Resources/embedded_resources.h" //
// //
// Put it in a HEADER file to declare external variables //
// #define EMBEDDED_RESOURCE_POSTFIX your_postfix //
// #include "../../src/Resources/embedded_resources.h" //
// //
// Then you can access embedded files through //
// EmbeddedResource_table_your_postfix. You can get table //
// content by index and put it into a hashtable or a map. //
////////////////////////////////////////////////////////////////
#pragma once
#if __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stddef.h>
typedef struct {
const char* path;
const char* data;
size_t size;
} EmbeddedResource;
#define RSCAT(A,B,C...) A##B##C
#ifdef EMBEDDED_RESOURCE_POSTFIX
#define _EmbeddedResource_table(P) \
RSCAT(EmbeddedResource_table_, P)
#define _EmbeddedResource_table_count(P) \
RSCAT(EmbeddedResource_table_, P, _count)
#else
#define _EmbeddedResource_table(P) \
EmbeddedResource_table
#define _EmbeddedResource_table_count(P) \
EmbeddedResource_table_count
#endif
extern const EmbeddedResource _EmbeddedResource_table(EMBEDDED_RESOURCE_POSTFIX)[];
extern const int _EmbeddedResource_table_count(EMBEDDED_RESOURCE_POSTFIX);
#ifdef EMBEDDED_RESOURCE_DEFINITION
const EmbeddedResource _EmbeddedResource_table(EMBEDDED_RESOURCE_POSTFIX)[]={
};
const int _EmbeddedResource_table_count(EMBEDDED_RESOURCE_POSTFIX)=0;
#endif // EMBEDDED_RESOURCE_DEFINITION
#undef _EmbeddedResource_table
#undef _EmbeddedResource_table_count
#undef EMBEDDED_RESOURCE_POSTFIX
#undef RSCAT
#if __cplusplus
}
#endif

33
src/resources/fonts.cpp Normal file
View File

@ -0,0 +1,33 @@
#include <imgui.h>
#include "resources.hpp"
#include <cstdio>
#include <cstring>
namespace ougge::resources {
// select all glyphs from font
static const ImWchar glyph_ranges[] = {
0x0020, 0xFFFF, 0
};
ImFont* ImFont_LoadFromFile(const std::string& file_path, f32 font_size, f32 dpi){
ImGuiIO& io = ImGui::GetIO();
font_size *= dpi;
return io.Fonts->AddFontFromFileTTF(file_path.c_str(), font_size, nullptr, glyph_ranges);
}
ImFont* ImFont_LoadFromResource(const std::string& font_name, f32 font_size, f32 dpi){
ImGuiIO& io = ImGui::GetIO();
font_size *= dpi;
ImFontConfig font_cfg = ImFontConfig();
std::sprintf(font_cfg.Name, "%s %ipx", font_name.c_str(), (i32)font_size);
auto& res = getResource("fonts/" + font_name + ".ttf");
char* font_data = new char[res.size];
res.openStream()->read(font_data, res.size);
return io.Fonts->AddFontFromMemoryTTF((void*)(font_data), res.size,
font_size, &font_cfg, glyph_ranges);
}
}

12
src/resources/fonts.hpp Normal file
View File

@ -0,0 +1,12 @@
#pragma once
#include "imgui.h"
#include "../common/std.hpp"
namespace ougge::resources {
ImFont* ImFont_LoadFromFile(const std::string& file_path, f32 font_size, f32 dpi);
ImFont* ImFont_LoadFromResource(const std::string& font_name, f32 font_size, f32 dpi);
}

View File

@ -0,0 +1,94 @@
#include <fstream>
#include <sstream>
#include "../common/UsefulException.hpp"
#include "../common/ougge_format.hpp"
#include "resources.hpp"
#include "embedded_resources.h"
namespace ougge::resources {
Resource::Resource(const std::string& path, const std::size_t size, StreamFactoryMethod open_read_steam_func)
: path(path), size(size), openStream(open_read_steam_func)
{}
MemoryStreamBuf::MemoryStreamBuf(void* _p, const std::size_t n){
char* p=(char*)_p;
setg(p, p, p + n);
setp(p, p + n);
}
std::istream::pos_type MemoryStreamBuf::seekoff(
std::istream::off_type off,
std::ios_base::seekdir dir,
std::ios_base::openmode which)
{
if (dir == std::ios_base::cur)
gbump(off);
else if (dir == std::ios_base::end)
setg(eback(), egptr() + off, egptr());
else if (dir == std::ios_base::beg)
setg(eback(), eback() + off, egptr());
return gptr() - eback();
}
MemoryStreamRead::MemoryStreamRead(const void* p, const std::size_t n)
: std::istream(new MemoryStreamBuf((void*)p, n))
{}
MemoryStreamRead::~MemoryStreamRead(){
delete rdbuf();
}
static std::unordered_map<std::string, Resource>* _resourceMap = nullptr;
void loadEmbeddedresources(){
for(int i = 0; i < EmbeddedResource_table_count; i++){
const EmbeddedResource& e = EmbeddedResource_table[i];
std::cout <<"loading resource '" << e.path << "' "
<< formatSizeHumanReadable(e.size) << std::endl;
auto embedded_resource_factory = [e]() -> std::unique_ptr<std::istream> {
return std::make_unique<MemoryStreamRead>(e.data, e.size);
};
auto r = _resourceMap->emplace(e.path,
Resource(e.path, e.size, embedded_resource_factory));
if(!r.second)
throw UsefulException(ougge_format("can't load duplicate resource '%s'", e.path));
}
}
void init(){
if(_resourceMap != nullptr)
throw UsefulException("resource has been initialized already");
_resourceMap = new std::unordered_map<std::string, Resource>();
loadEmbeddedresources();
}
Resource& getResource(const std::string& path){
auto it = _resourceMap->find(path);
if(it == _resourceMap->end())
throw UsefulException(ougge_format("can't find resource '%s'", path.c_str()));
return it->second;
}
std::string formatSizeHumanReadable(std::size_t b){
std::stringstream ss;
ss.precision(3);
std::size_t k = b / 1024;
std::size_t m = k / 1024;
std::size_t g = m / 1024;
if(g)
ss<<m / 1024.0f<<'G';
else if(m)
ss<<k / 1024.0f<<'M';
else if(k)
ss<<b / 1024.0f<<'K';
else ss<<b;
return ss.str();
}
}

View File

@ -0,0 +1,63 @@
#pragma once
#include "../common/std.hpp"
#include <iostream>
#include <functional>
#include <unordered_map>
namespace ougge::resources {
// call this in main()
void init();
std::string formatSizeHumanReadable(std::size_t byte_n);
class Resource {
public:
const std::string path;
const std::size_t size;
using StreamFactoryMethod = std::function< std::unique_ptr<std::istream> () >;
Resource(const std::string& path, const std::size_t size, StreamFactoryMethod open_read_steam_func);
const StreamFactoryMethod openStream;
};
class MemoryStreamBuf : public std::streambuf {
public:
MemoryStreamBuf(void* p, const std::size_t n);
virtual std::istream::pos_type seekoff(
std::istream::off_type off,
std::ios_base::seekdir dir,
std::ios_base::openmode which);
};
class MemoryStreamRead : public std::istream {
public:
MemoryStreamRead(const void* p, const std::size_t n);
virtual ~MemoryStreamRead();
};
Resource& getResource(const std::string& path);
/// @brief stores requested resources in memory
/// @tparam T must implement constructor `T(const Resource&, ...)`
template<class T>
class CacheStorage {
std::unordered_map<std::string, T> _map;
public:
template<typename... TCtorArgs>
T& getOrCreate(const std::string& name, TCtorArgs&&... ctor_args){
auto it = _map.find(name);
if(it != _map.end())
return it->second;
auto& res = getResource(name);
auto e = _map.emplace(name, T(res, ctor_args...));
return e.first->second;
}
};
}

View File

@ -0,0 +1,96 @@
#include "textures.hpp"
#include <SDL_image.h>
#include "../gui/gui_exceptions.hpp"
namespace ougge::resources {
Texture::Texture(const Resource& r, SDL_Renderer* renderer)
: Texture(*r.openStream(), r.size, renderer)
{}
Texture::Texture(std::istream& s, size_t size, SDL_Renderer* renderer)
: renderer(renderer), texture(nullptr), w(0), h(0)
{
SDL_RWops* sdl_stream = SDL_RWFromIStream(s, size);
if(!sdl_stream)
throw gui::SDLException();
texture = std::shared_ptr<SDL_Texture>(IMG_LoadTexture_RW(renderer, sdl_stream, 1), SDL_DestroyTexture);
if(!texture)
throw gui::IMGException();
SDL_TRY(SDL_QueryTexture(texture.get(), nullptr, nullptr, &w, &h));
}
SDL_RenderCopyExF_Params::SDL_RenderCopyExF_Params()
: rotation_angle(0), flip(SDL_FLIP_NONE)
{}
void Texture::render(const SDL_FRect& target_section){
SDL_TRY(
SDL_RenderCopyF(renderer, texture.get(), nullptr, &target_section)
);
}
void Texture::render(const SDL_FRect& target_section, const SDL_Rect& texture_section){
SDL_TRY(
SDL_RenderCopyF(renderer, texture.get(), &texture_section, &target_section)
);
}
void Texture::render(const SDL_RenderCopyExF_Params& p){
SDL_TRY(
SDL_RenderCopyExF(renderer, texture.get(),
optional_value_ptr_or_null(p.texture_section),
optional_value_ptr_or_null(p.target_section),
-1.0f * angleToDegree(normalizeAngle(p.rotation_angle)),
optional_value_ptr_or_null(p.rotation_center),
p.flip
)
)
}
static Sint64 istream_size(SDL_RWops* context){
return (Sint64)(size_t)context->hidden.unknown.data2;
}
static Sint64 istream_seek(SDL_RWops* context, Sint64 offset, i32 whence){
std::istream* stream = (std::istream*)context->hidden.unknown.data1;
switch(whence){
case SEEK_SET: stream->seekg(offset, std::ios::beg); break;
case SEEK_CUR: stream->seekg(offset, std::ios::cur); break;
case SEEK_END: stream->seekg(offset, std::ios::end); break;
default: break;
}
return stream->fail() ? -1 : (Sint64)stream->tellg();
}
static size_t istream_read(SDL_RWops* context, void *ptr, size_t size, size_t maxnum){
if(size == 0)
return -1;
std::istream* stream = (std::istream*)context->hidden.unknown.data1;
stream->read((char*)ptr, size * maxnum);
return stream->bad() ? -1 : stream->gcount() / size;
}
static i32 istream_close(SDL_RWops* context){
if (context)
SDL_FreeRW(context);
return 0;
}
SDL_RWops* SDL_RWFromIStream(std::istream& stream, size_t size){
SDL_RWops* rwops = SDL_AllocRW();
if(rwops) {
rwops->size = istream_size;
rwops->seek = istream_seek;
rwops->read = istream_read;
rwops->write = nullptr;
rwops->close = istream_close;
rwops->hidden.unknown.data1 = &stream;
rwops->hidden.unknown.data2 = (void*)size;
rwops->type = SDL_RWOPS_UNKNOWN;
}
return rwops;
}
}

View File

@ -0,0 +1,44 @@
#pragma once
#include <iostream>
#include <optional>
#include <SDL.h>
#include "resources.hpp"
#include "../common/math.hpp"
namespace ougge::resources {
#define SDL_RectConstruct(X, Y, W, H) (SDL_Rect){X, Y, W, H}
#define SDL_FRectConstruct(X, Y, W, H) (SDL_FRect){X, Y, W, H}
#define SDL_PointConstruct(X, Y) (SDL_Point){X, Y, W, H}
#define SDL_FPointConstruct(X, Y) (SDL_FPoint){X, Y, W, H}
#define optional_value_ptr_or_null(OPT) (OPT ? &(*OPT) : nullptr)
SDL_RWops* SDL_RWFromIStream(std::istream& stream, size_t size);
struct SDL_RenderCopyExF_Params {
std::optional<SDL_Rect> texture_section;
std::optional<SDL_FRect> target_section;
std::optional<SDL_FPoint> rotation_center;
angle_t rotation_angle;
SDL_RendererFlip flip;
SDL_RenderCopyExF_Params();
};
struct Texture {
SDL_Renderer* renderer;
std::shared_ptr<SDL_Texture> texture;
i32 w;
i32 h;
Texture(const Resource& r, SDL_Renderer* renderer);
Texture(std::istream& s, size_t size, SDL_Renderer* renderer);
void render(const SDL_FRect& target_section);
void render(const SDL_FRect& target_section, const SDL_Rect& texture_section);
void render(const SDL_RenderCopyExF_Params& params);
};
}