Compare commits

..

10 Commits

61 changed files with 1896 additions and 518 deletions

View File

@@ -2,56 +2,30 @@
A game engine or something, idk. A game engine or something, idk.
## Installation ## Installation
1. Clone the repository. 1. **Clone the repository.**
```sh ```sh
git clone --recurse-submodules https://timerix.ddns.net/git/Timerix/ougge.git git clone --recurse-submodules https://timerix.ddns.net/git/Timerix/ougge.git
``` ```
2. Install [cbuild](https://timerix.ddns.net/git/Timerix/cbuild). 2. **Install [cbuild](https://timerix.ddns.net/git/Timerix/cbuild).**
3. Install [SDL2](https://github.com/libsdl-org/SDL) and [SDL2_image](https://github.com/libsdl-org/SDL_image). 3. **Install [SDL2](https://github.com/libsdl-org/SDL) and [SDL2_image](https://github.com/libsdl-org/SDL_image).**
- On **Linux** install shared libraries from a package manager or compile them from source. - On **Linux** install shared libraries from a package manager or compile them from source.
- On **Windows** download pre-built dll's from github releases and put them into `dependencies/precompiled/`. - On **Windows** download pre-built dll's from github releases and put them into `dependencies/precompiled/`.
4. Symlink SDL headers directory to `dependencies/include`. 4. **Symlink SDL headers directory** to `dependencies/include`.
```sh ```sh
ln -s SDL2_HEADERS_DIRECTORY_ABSOLUTE_PATH -T dependencies/include/SDL2 ln -s SDL2_HEADERS_DIRECTORY_ABSOLUTE_PATH -T dependencies/include/SDL2
``` ```
Location of the headers can be found by `pkg-config --cflags --libs sdl2`. Location of the headers can be found by `pkg-config --cflags --libs sdl2`.
Mingw installs SDL2 headers to `/mingw64/include/SDL2`. Mingw installs SDL2 headers to `/mingw64/include/SDL2`.
5. Install [mono](https://github.com/mono/mono). 5. **Download mono runtime** ([source is here](https://github.com/dotnet/runtime/tree/main/src/mono)).
- default version
**Windows:**
1. Install mono from official website
2. Copy `*.dll` files from `C:\Program Files\Mono\bin` to `dependencies/precompiled/`
3. Copy `config` from `C:\Program Files\Mono\etc\mono` to `dependencies/precompiled/mono-libs` and rename to `config.xml`
4. Copy `mscorlib.dll` from `C:\Program Files\Mono\lib\mono\4.5\` to `dependencies/precompiled/mono-libs`
**Linux:**
1. Download and extract [sources](https://download.mono-project.com/sources/mono/index.html)
```sh ```sh
mkdir mono cbuild download_mono_from_nuget
cd mono
wget https://download.mono-project.com/sources/mono/mono-6.12.0.199.tar.xz
tar xJf mono-6.12.0.199.tar.xz
mv mono-6.12.0.199/* ./
rm -r mono-6.12.0.199
rm mono-6.12.0.199.tar.xz
``` ```
2. Install `libz autoconf automake libtool gettext cmake python3 curl` and - or some specific version
3. Build mono. If something doesn't work, read [documentation](https://www.mono-project.com/docs/compiling-mono/)
```sh ```sh
mkdir -p mono_prefix cbuild download_mono_from_nuget=x.y.z
./autogen.sh --prefix=$(realpath mono_prefix) --disable-boehm
make get-monolite-latest
make -j8
make install
``` ```
3. Install `patchelf` 7. **Compile the program**
4. Install mono files in project.
```sh
cd ..
cbuild get_mono_files_from=mono/mono_prefix
```
5. Now you can delete `mono` directory and save 3 gigabytes of disk space :3
7. Compile the program
```sh ```sh
cbuild build_exec_dbg cbuild build_exec_dbg
``` ```

View File

@@ -1,23 +1,28 @@
#!/usr/bin/env bash #!/usr/bin/env bash
mkdir -p 'dependencies/precompiled' DEP_WORKING_DIR="dependencies/precompiled/$OS-$ARCH"
DEP_WORKING_DIR='dependencies/precompiled' mkdir -p "dependencies/precompiled"
mkdir -p "$DEP_WORKING_DIR"
DEP_PRE_BUILD_COMMAND='' DEP_PRE_BUILD_COMMAND=''
DEP_BUILD_COMMAND='' DEP_BUILD_COMMAND=''
DEP_POST_BUILD_COMMAND='' DEP_POST_BUILD_COMMAND=''
DEP_CLEAN_COMMAND='' DEP_CLEAN_COMMAND=''
# won't be copied to project $OUTDIR # won't be copied to project $OUTDIR
DEP_STATIC_OUT_FILES=$(find dependencies/precompiled -name '*.a' | sed 's,dependencies/precompiled/,,') DEP_STATIC_OUT_FILES=$(find "$DEP_WORKING_DIR" -name '*.a' | sed "s,$DEP_WORKING_DIR/,,")
PRESERVE_OUT_DIRECTORY_STRUCTURE=true
mkdir -p "$DEP_WORKING_DIR/mono-libs"
mono_libs=$(find "$DEP_WORKING_DIR/mono-libs" -type f | sed "s,$DEP_WORKING_DIR/,,")
# will be copied tp project $OUTDIR # will be copied tp project $OUTDIR
PRESERVE_OUT_DIRECTORY_STRUCTURE=true
case $OS in case $OS in
WINDOWS) WINDOWS)
DEP_DYNAMIC_OUT_FILES=$(find dependencies/precompiled -maxdepth 1 -name '*.dll' | sed 's,dependencies/precompiled/,,') DEP_DYNAMIC_OUT_FILES=$(find "$DEP_WORKING_DIR" -maxdepth 1 -name '*.dll' | sed "s,$DEP_WORKING_DIR/,,")
DEP_OTHER_OUT_FILES="mono-libs/mscorlib.dll mono-libs/config.xml" DEP_OTHER_OUT_FILES="$mono_libs"
# DEP_STATIC_OUT_FILES+=" mono-libs/libmono-static-sgen.lib"
;; ;;
LINUX) LINUX)
DEP_DYNAMIC_OUT_FILES=$(find dependencies/precompiled -name '*.so' | sed 's,dependencies/precompiled/,,') DEP_DYNAMIC_OUT_FILES=$(find "$DEP_WORKING_DIR" -name '*.so' | sed "s,$DEP_WORKING_DIR/,,")
DEP_OTHER_OUT_FILES="mono-libs/mscorlib.dll mono-libs/config.xml" DEP_OTHER_OUT_FILES="$mono_libs"
;; ;;
*) *)
error "operating system $OS has no configuration variants" error "operating system $OS has no configuration variants"

View File

@@ -1,6 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
CBUILD_VERSION=2.1.4 CBUILD_VERSION=2.2.1
CONFIG_VERSION=1
PROJECT="ougge" PROJECT="ougge"
CMP_C="gcc" CMP_C="gcc"
@@ -35,7 +34,7 @@ case "$OS" in
WINDOWS) WINDOWS)
EXEC_FILE="$PROJECT.exe" EXEC_FILE="$PROJECT.exe"
SHARED_LIB_FILE="$PROJECT.dll" SHARED_LIB_FILE="$PROJECT.dll"
LINKER_LIBS="" LINKER_LIBS="-static -lstdc++ -lpthread"
;; ;;
LINUX) LINUX)
EXEC_FILE="$PROJECT" EXEC_FILE="$PROJECT"
@@ -176,8 +175,8 @@ case "$TASK" in
rebuild_dependencies) rebuild_dependencies)
TASK_SCRIPT=cbuild/default_tasks/rebuild_dependencies.sh TASK_SCRIPT=cbuild/default_tasks/rebuild_dependencies.sh
;; ;;
get_mono_files_from) download_mono_from_nuget)
TASK_SCRIPT=tasks/get_mono_files_from.sh TASK_SCRIPT=tasks/download_mono_from_nuget.sh
;; ;;
# deletes generated files # deletes generated files
clean) clean)

View File

@@ -26,15 +26,15 @@ public class GameObject
public Transform Transform { get; } public Transform Transform { get; }
public GameObject? Parent; public GameObject? Parent;
public Dictionary<Type, Component> Components = new(); public Dictionary<Type, Component> Components;
private GameObject(ulong id, uint nativePoolIndex) private GameObject(ulong id, uint nativePoolIndex)
{ {
_id = id; _id = id;
_index = nativePoolIndex; _index = nativePoolIndex;
// constructor doesn't work without writing to console // Do not move this line or mono runtime will throw SEGFAULT.
// TODO: FIX THIS BULLSHIT // Mono runtime can't set values in field declaration, but initializing everything in constructor is OK.
Console.WriteLine($"GameObject(id: {id}, nativePoolIndex: {nativePoolIndex})"); Components = new();
} }
static public GameObject Create() static public GameObject Create()
@@ -65,7 +65,8 @@ public class GameObject
if(Components.ContainsKey(t)) if(Components.ContainsKey(t))
return false; return false;
Components.Add(t, (Component)Activator.CreateInstance(t, this)); Component component = (Component)Activator.CreateInstance(t, this);
Components.Add(t, component);
return true; return true;
} }
private bool TryCreateComponent_internal(string fullName) private bool TryCreateComponent_internal(string fullName)

View File

@@ -1,6 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net48</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<RootNamespace>Ougge</RootNamespace> <RootNamespace>Ougge</RootNamespace>
<ImplicitUsings>disable</ImplicitUsings> <ImplicitUsings>disable</ImplicitUsings>

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 <backends/imgui_impl_sdlrenderer2.h>
#include <iostream> #include <iostream>
#include "MainWindow.hpp" #include "MainWindow.hpp"
#include "exceptions.hpp" #include "gui_exceptions.hpp"
#include "../format.hpp" #include "../common/ougge_format.hpp"
#include "../Resources/fonts.hpp" #include "../resources/fonts.hpp"
#include "../Resources/textures.hpp" #include "../resources/textures.hpp"
#include "../math.hpp" #include "../common/math.hpp"
namespace ougge::GUI { namespace ougge::gui {
f32 MainWindow::getDPI(){ f32 MainWindow::getDPI(){
int w=0, h=0; i32 w=0, h=0;
SDL_GetRendererOutputSize(sdl_renderer, &w, &h); 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); SDL_GetWindowSize(sdl_window, &sim_w, &sim_h);
f32 wdpi=(f32)w / sim_w; f32 wdpi=(f32)w / sim_w;
f32 hdpi=(f32)h / sim_h; f32 hdpi=(f32)h / sim_h;
@@ -21,13 +21,11 @@ f32 MainWindow::getDPI(){
return dpi; return dpi;
} }
void MainWindow::open(const char* window_title, UpdateFunc_t _update){ void MainWindow::open(const std::string& window_title){
update = _update;
SDL_TRY(SDL_Init(SDL_INIT_EVERYTHING)); SDL_TRY(SDL_Init(SDL_INIT_EVERYTHING));
SDL_version v; SDL_version v;
SDL_GetVersion(&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. // From 2.0.18: Enable native IME.
#ifdef SDL_HINT_IME_SHOW_UI #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_WindowFlags window_flags = (SDL_WindowFlags)(
SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI 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, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
1280, 720, window_flags); 1280, 720, window_flags);
if(sdl_window == nullptr) if(sdl_window == nullptr)
@@ -75,138 +73,10 @@ void MainWindow::open(const char* window_title, UpdateFunc_t _update){
// Setup Dear ImGui style // Setup Dear ImGui style
ImGui::StyleColorsDark(); ImGui::StyleColorsDark();
f32 dpi = getDPI(); 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::close(){
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(){
ImGui_ImplSDLRenderer2_Shutdown(); ImGui_ImplSDLRenderer2_Shutdown();
ImGui_ImplSDL2_Shutdown(); ImGui_ImplSDL2_Shutdown();
ImGui::DestroyContext(); ImGui::DestroyContext();
@@ -215,8 +85,52 @@ void MainWindow::destroy(){
SDL_Quit(); SDL_Quit();
} }
void MainWindow::close(){ void MainWindow::pollEvents(bool* loopRunning){
loop_running = false; 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(){ void MainWindow::draw_bg_window(){
@@ -285,13 +199,14 @@ void MainWindow::draw_bg_window(){
void MainWindow::draw_debug_window(){ void MainWindow::draw_debug_window(){
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
ImGui::Begin("Debug Options"); if(ImGui::Begin("Debug Options", &show_debug_window)){
ImGui::ColorEdit3("clear_color", (float*)&clear_color); ImGui::ColorEdit3("clear_color", (float*)&clear_color);
ImGui::InputInt("fps_max", &fps_max); ImGui::InputInt("fps_max", &fps_max);
ImGui::Text("Application average %.3f ms/frame (%.2f FPS)", 1000.0f / io.Framerate, io.Framerate); ImGui::Text("Application average %.3f ms/frame (%.2f FPS)", 1000.0f / io.Framerate, io.Framerate);
ImGui::Checkbox("Demo Window", &show_demo_window); ImGui::Checkbox("Demo Window", &show_demo_window);
ImGui::Checkbox("Metrics/Debug Window", &show_metrics_window); ImGui::Checkbox("Metrics/Debug Window", &show_metrics_window);
ImGui::End(); ImGui::End();
}
if (show_demo_window) if (show_demo_window)
ImGui::ShowDemoWindow(&show_demo_window); ImGui::ShowDemoWindow(&show_demo_window);

View File

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

View File

@@ -1,5 +1,7 @@
#include "GameObject.hpp" #include "GameObject.hpp"
namespace ougge::game {
std::ostream& operator<<(std::ostream& s, Transform& t){ std::ostream& operator<<(std::ostream& s, Transform& t){
s<<"{ position: {x: "<<t.position.x<<", y: "<<t.position.y; s<<"{ position: {x: "<<t.position.x<<", y: "<<t.position.y;
s<<"}, rotation: "<<t.rotation; s<<"}, rotation: "<<t.rotation;
@@ -7,32 +9,22 @@ std::ostream& operator<<(std::ostream& s, Transform& t){
return s; return s;
} }
GameObject::GameObject(Mono::Object managed_obj, GameObject *parent) GameObject::GameObject(Mono::Object managed_obj)
: object_handle(managed_obj), parent(parent) : object_handle(managed_obj)
{} {}
GameObject::GameObject(GameObject &&o) : GameObject::GameObject(GameObject &&o) :
transform(o.transform),
components(std::move(o.components)),
object_handle(std::move(o.object_handle)), object_handle(std::move(o.object_handle)),
parent(o.parent) parent(o.parent),
transform(o.transform)
{ {
} }
GameObject &GameObject::operator=(GameObject &&o){ GameObject& GameObject::operator=(GameObject &&o){
transform = o.transform;
components = std::move(o.components);
object_handle = std::move(o.object_handle); object_handle = std::move(o.object_handle);
parent = o.parent; parent = o.parent;
transform = o.transform;
return *this; return *this;
} }
bool GameObject::tryGetComponent(const std::u16string &name, Component **out_component)
{
return false;
}
bool GameObject::tryAddComponent(const std::u16string &name, Component &&component)
{
return false;
} }

View File

@@ -2,20 +2,14 @@
#include <map> #include <map>
#include <iostream> #include <iostream>
#include "../math.hpp" #include "../common/math.hpp"
#include "../UsefulException.hpp" #include "../common/UsefulException.hpp"
#include "../Mono/Mono.hpp" #include "../mono/mono.hpp"
namespace ougge::game {
class GameObject; class GameObject;
class Component {
Mono::ObjectHandle object_handle;
GameObject* owner;
public:
inline Component(Mono::Object managed_obj, GameObject* owner)
: object_handle(managed_obj), owner(owner) {}
};
struct Transform { struct Transform {
Vec2 scale = { 1, 1 }; Vec2 scale = { 1, 1 };
Vec2 position = { 0, 0 }; Vec2 position = { 0, 0 };
@@ -26,21 +20,26 @@ std::ostream& operator<<(std::ostream& s, Transform& t);
class GameObject { class GameObject {
Transform transform;
std::map<std::u16string, Component> components;
Mono::ObjectHandle object_handle; Mono::ObjectHandle object_handle;
GameObject* parent; GameObject* parent;
Transform transform;
public: public:
GameObject(Mono::Object managed_obj, GameObject* parent); /// @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(const GameObject& o) = delete;
GameObject(GameObject&& o); GameObject(GameObject&& o);
GameObject& operator=(GameObject&& o); GameObject& operator=(GameObject&& o);
inline Transform& getTransform() { return transform; }
inline const Transform& getTransform() const { return transform; }
inline Mono::ObjectHandle& getObjectHandle() { return object_handle; } inline Mono::ObjectHandle& getObjectHandle() { return object_handle; }
inline const Mono::ObjectHandle& getObjectHandle() const { return object_handle; } inline GameObject* getParent() { return parent; }
bool tryGetComponent(const std::u16string& name, Component** out_component); inline void setParent(GameObject* p) { parent = p; }
bool tryAddComponent(const std::u16string& name, Component&& component); inline Transform& getTransform() { return transform; }
}; };
}

View File

@@ -2,12 +2,14 @@
#include <bit> #include <bit>
#include <cstring> #include <cstring>
namespace ougge::game {
GameObjectPool::GameObjectPool(u32 size) GameObjectPool::GameObjectPool(u32 size)
{ {
useful_assert(size % 64 == 0, "size of GameObjectPool must be a multiple of 64"); useful_assert(size % 64 == 0, "size of GameObjectPool must be a multiple of 64");
this->size = size; this->size = size;
first_unused_index = 0; first_unused_index = 0;
buffer = new char[size*sizeof(GameObject)]; buffer = new GameObject[size];
used_indices = new u64[size/64]; used_indices = new u64[size/64];
// std::memset(buffer, 0, size*sizeof(GameObject)); // std::memset(buffer, 0, size*sizeof(GameObject));
std::memset(used_indices, 0, size/8); std::memset(used_indices, 0, size/8);
@@ -15,13 +17,8 @@ GameObjectPool::GameObjectPool(u32 size)
GameObjectPool::~GameObjectPool() GameObjectPool::~GameObjectPool()
{ {
// int i = 0; delete[] buffer;
for(auto&& p : *this){ delete[] used_indices;
// std::cout<<"~GameObjectPool i="<<i++<<std::endl;
p.second.~GameObject();
}
delete (char*)buffer;
delete used_indices;
} }
bool GameObjectPool::isIndexUsed(u32 index) bool GameObjectPool::isIndexUsed(u32 index)
@@ -78,10 +75,10 @@ u32 GameObjectPool::getNearestUsedIndex(u32 startIndex)
GameObject& GameObjectPool::get(u32 index) GameObject& GameObjectPool::get(u32 index)
{ {
if(index >= size) 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)) 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 ((GameObject*)buffer)[index]; return buffer[index];
} }
std::pair<u32, GameObject&> GameObjectPool::emplace(GameObject&& new_obj) std::pair<u32, GameObject&> GameObjectPool::emplace(GameObject&& new_obj)
@@ -89,7 +86,9 @@ std::pair<u32, GameObject&> GameObjectPool::emplace(GameObject&& new_obj)
u32 i = first_unused_index; u32 i = first_unused_index;
if(i == u32(-1)) if(i == u32(-1))
throw UsefulException("can't put new GameObject to GameObjectPool because it's full"); throw UsefulException("can't put new GameObject to GameObjectPool because it's full");
GameObject& r = ( ((GameObject*)buffer)[i] = std::move(new_obj) );
buffer[i] = std::move(new_obj);
GameObject& r = buffer[i];
used_indices[i/64] |= u64(1)<<(i%64); // mark index bit as used used_indices[i/64] |= u64(1)<<(i%64); // mark index bit as used
first_unused_index = getNearestUnusedIndex(i+1); first_unused_index = getNearestUnusedIndex(i+1);
return std::pair<u32, GameObject&>(i, r); return std::pair<u32, GameObject&>(i, r);
@@ -98,30 +97,34 @@ std::pair<u32, GameObject&> GameObjectPool::emplace(GameObject&& new_obj)
void GameObjectPool::erase(u32 index) void GameObjectPool::erase(u32 index)
{ {
if(index >= size) 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)) 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));
((GameObject*)buffer)[index].~GameObject();
buffer[index] = GameObject();
used_indices[index/64] &= ~(u64(1)<<(index%64)); // mark index bit as unused used_indices[index/64] &= ~(u64(1)<<(index%64)); // mark index bit as unused
if(index < first_unused_index) if(index < first_unused_index)
first_unused_index = index; first_unused_index = index;
} }
GameObjectPool::iterator::iterator(GameObjectPool* p, u32 index) GameObjectPool::iterator::iterator(GameObjectPool* pool, u32 index)
: p(p), index(index) : pool(pool), index(index)
{ {
} }
std::pair<u32, GameObject&> GameObjectPool::iterator::operator*() std::pair<u32, GameObject&> GameObjectPool::iterator::operator*()
{ {
if(index >= p->size) if(index >= pool->size)
throw UsefulException("can't get value of end() iterator"); throw UsefulException("can't get value of end() iterator");
GameObject& r = ((GameObject*)p->buffer)[index];
GameObject& r = pool->buffer[index];
return std::pair<u32, GameObject&>(index, r); return std::pair<u32, GameObject&>(index, r);
} }
GameObjectPool::iterator& GameObjectPool::iterator::operator++() GameObjectPool::iterator& GameObjectPool::iterator::operator++()
{ {
index = p->getNearestUsedIndex(index+1); index = pool->getNearestUsedIndex(index+1);
return *this; return *this;
} }
}

View File

@@ -1,7 +1,9 @@
#include "GameObject.hpp" #include "GameObject.hpp"
namespace ougge::game {
/* /*
Fixed array stkring deleted elements indices as bits in array of u64. Fixed array that stores deleted elements indices as bits in array of u64.
Fast emplace, erase and lookup. Fast emplace, erase and lookup.
------------------------[construct]------------------------ ------------------------[construct]------------------------
@@ -28,7 +30,7 @@ operation 'forward_list::iterate' took 2.0823 ms
*/ */
class GameObjectPool { class GameObjectPool {
void* buffer; GameObject* buffer;
u64* used_indices; u64* used_indices;
u32 size; u32 size;
u32 first_unused_index; u32 first_unused_index;
@@ -45,20 +47,24 @@ public:
std::pair<u32, GameObject&> emplace(GameObject&& new_obj); std::pair<u32, GameObject&> emplace(GameObject&& new_obj);
void erase(u32 index); void erase(u32 index);
#pragma region iterator class
class iterator { class iterator {
GameObjectPool* p; GameObjectPool* pool;
u32 index = 0; u32 index = 0;
public: public:
iterator(GameObjectPool* p, u32 index); iterator(GameObjectPool* pool, u32 index);
std::pair<u32, GameObject&> operator*(); std::pair<u32, GameObject&> operator*();
iterator& 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; };
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 begin() { return iterator(this, 0); }
inline iterator end() { return iterator(this, -1); } inline iterator end() { return iterator(this, -1); }
friend class iterator; friend class iterator;
}; };
}

View File

@@ -1,7 +0,0 @@
#pragma once
#include "GameObject.hpp"
class Scene {
};

View File

@@ -1,4 +1,4 @@
#include "Mono.hpp" #include "mono.hpp"
namespace Mono { namespace Mono {
@@ -10,7 +10,7 @@ Assembly::Assembly(MonoAssembly *ptr)
MonoClass* Assembly::getClass(const std::string &name_space, const std::string &name){ 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()); auto c = mono_class_from_name(image, name_space.c_str(), name.c_str());
if(!c) 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; return c;
} }

View File

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

View File

@@ -1,11 +1,11 @@
#pragma once #pragma once
#include "../std.hpp" #include "../common/std.hpp"
#include "../UsefulException.hpp" #include "../common/UsefulException.hpp"
#include "../format.hpp" #include "../common/ougge_format.hpp"
#include <vector> #include <vector>
#include <type_traits> #include <type_traits>
#include <mono/metadata/metadata.h> #include <mono/metadata/class.h>
#include <mono/metadata/assembly.h> #include <mono/metadata/assembly.h>
#include <mono/metadata/object.h> #include <mono/metadata/object.h>
@@ -24,6 +24,8 @@ typedef f64 Double;
typedef union { mono_bool wide_bool; } Bool; //USAGE: Bool t = {true}; typedef union { mono_bool wide_bool; } Bool; //USAGE: Bool t = {true};
typedef char16_t Char; typedef char16_t Char;
typedef MonoString* String; 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 MonoObject* Object;
typedef void Void; typedef void Void;
@@ -67,9 +69,25 @@ class Method<ReturnT(ArgTypes...)>
MonoMethod* method_ptr; MonoMethod* method_ptr;
public: 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> template<typename RT = ReturnT>
std::enable_if_t<!std::is_void<RT>::value, RT> operator()(MonoObject* class_instance, ArgTypes... args) const { 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 }; void* arg_array[] = { valueToVoidPtr(args)..., nullptr };
MonoObject* ex = nullptr; MonoObject* ex = nullptr;
MonoObject* result = mono_runtime_invoke(method_ptr, class_instance, arg_array, &ex); MonoObject* result = mono_runtime_invoke(method_ptr, class_instance, arg_array, &ex);
@@ -80,8 +98,13 @@ public:
} }
return valueFromMonoObject<ReturnT>(result); return valueFromMonoObject<ReturnT>(result);
}; };
// ReturnT is void
template<typename RT = ReturnT> template<typename RT = ReturnT>
std::enable_if_t<std::is_void<RT>::value, RT> operator()(MonoObject* class_instance, ArgTypes... args) const { 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 }; void* arg_array[] = { valueToVoidPtr(args)..., nullptr };
MonoObject* ex = nullptr; MonoObject* ex = nullptr;
mono_runtime_invoke(method_ptr, class_instance, arg_array, &ex); mono_runtime_invoke(method_ptr, class_instance, arg_array, &ex);
@@ -91,17 +114,6 @@ public:
throw UsefulException("Some C# exception occured"); throw UsefulException("Some C# exception occured");
} }
}; };
/// 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(format("can't get method '%s' from class '%s'",
name.c_str(), mono_class_get_name(target_class)));
}
}
}; };
@@ -125,33 +137,51 @@ public:
class RuntimeJIT { class RuntimeJIT {
MonoDomain* domain; MonoDomain* domain;
public: public:
RuntimeJIT(const std::string& domain_name = "MonoApp"); RuntimeJIT(const std::string& domain_name = "OuggeDomain");
RuntimeJIT(const RuntimeJIT&) = delete; RuntimeJIT(const RuntimeJIT&) = delete;
~RuntimeJIT(); ~RuntimeJIT();
inline MonoDomain* getDomain() { return domain; } inline MonoDomain* getDomain() { return domain; }
std::shared_ptr<Assembly> loadAssembly(const std::string& name); 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); }
}; };
class ObjectHandle { /// @brief ObjectHandle can be used to store reliable reference to MonoObject.
Object object; /// MonoObject can be moved in memory by GC in any time and raw pointer will be invalid.
struct ObjectHandle {
u32 gc_handle; u32 gc_handle;
public:
inline ObjectHandle() : object(nullptr), gc_handle(0) {} inline ObjectHandle() : gc_handle(0) {}
inline ObjectHandle(Object obj) : object(obj) { gc_handle = mono_gchandle_new(obj, false); }
inline ObjectHandle(Object obj) {
gc_handle = mono_gchandle_new(obj, false);
}
/// implicitly create new ObjectHandle instead
inline ObjectHandle(const ObjectHandle& o) = delete; inline ObjectHandle(const ObjectHandle& o) = delete;
inline ObjectHandle(ObjectHandle&& o)
: object(o.object), gc_handle(o.gc_handle) inline ObjectHandle(ObjectHandle&& o) {
{ o.gc_handle = 0; }; gc_handle = o.gc_handle;
o.gc_handle = 0;
}
inline ObjectHandle& operator=(ObjectHandle&& o) { inline ObjectHandle& operator=(ObjectHandle&& o) {
object = o.object;
gc_handle = o.gc_handle; gc_handle = o.gc_handle;
o.gc_handle = 0; o.gc_handle = 0;
return *this; return *this;
}; }
inline ~ObjectHandle() { if(gc_handle) mono_gchandle_free(gc_handle); }
inline Object getObject() const { return object; } inline ~ObjectHandle() {
inline u32 getGCHandle() const { return gc_handle; } if(gc_handle)
mono_gchandle_free(gc_handle);
}
inline Object getObject() const {
return mono_gchandle_get_target(gc_handle);
}
}; };
} }

View File

@@ -1,6 +1,7 @@
#include "Mono.hpp" #include "mono.hpp"
#include <mono/jit/jit.h> #include <mono/jit/jit.h>
#include <mono/metadata/mono-config.h> #include <mono/metadata/mono-config.h>
#include <mono/metadata/appdomain.h>
namespace Mono { namespace Mono {
@@ -22,7 +23,7 @@ RuntimeJIT::~RuntimeJIT(){
std::shared_ptr<Assembly> RuntimeJIT::loadAssembly(const std::string &name){ std::shared_ptr<Assembly> RuntimeJIT::loadAssembly(const std::string &name){
MonoAssembly* ptr = mono_domain_assembly_open(domain, name.c_str()); MonoAssembly* ptr = mono_domain_assembly_open(domain, name.c_str());
if(!ptr) 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); return std::make_shared<Assembly>(ptr);
} }

View File

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

View File

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

View File

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

View File

@@ -1,9 +1,9 @@
#pragma once #pragma once
#include "imgui.h" #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); ImFont* ImFont_LoadFromFile(const std::string& file_path, f32 font_size, f32 dpi);

View File

@@ -1,8 +1,8 @@
#include "textures.hpp" #include "textures.hpp"
#include <SDL_image.h> #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::Texture(const Resource& r, SDL_Renderer* renderer)
: Texture(*r.openStream(), r.size, 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); SDL_RWops* sdl_stream = SDL_RWFromIStream(s, size);
if(!sdl_stream) if(!sdl_stream)
throw SDLException(); throw gui::SDLException();
texture = std::shared_ptr<SDL_Texture>(IMG_LoadTexture_RW(renderer, sdl_stream, 1), SDL_DestroyTexture); texture = std::shared_ptr<SDL_Texture>(IMG_LoadTexture_RW(renderer, sdl_stream, 1), SDL_DestroyTexture);
if(!texture) if(!texture)
throw IMGException(); throw gui::IMGException();
SDL_TRY(SDL_QueryTexture(texture.get(), nullptr, nullptr, &w, &h)); 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; 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; std::istream* stream = (std::istream*)context->hidden.unknown.data1;
switch(whence){ switch(whence){
case SEEK_SET: stream->seekg(offset, std::ios::beg); break; 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; return stream->bad() ? -1 : stream->gcount() / size;
} }
static int istream_close(SDL_RWops* context){ static i32 istream_close(SDL_RWops* context){
if (context) if (context)
SDL_FreeRW(context); SDL_FreeRW(context);
return 0; return 0;

View File

@@ -3,10 +3,10 @@
#include <iostream> #include <iostream>
#include <optional> #include <optional>
#include <SDL.h> #include <SDL.h>
#include "Resources.hpp" #include "resources.hpp"
#include "../math.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_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_FRectConstruct(X, Y, W, H) (SDL_FRect){X, Y, W, H}
@@ -30,8 +30,8 @@ struct SDL_RenderCopyExF_Params {
struct Texture { struct Texture {
SDL_Renderer* renderer; SDL_Renderer* renderer;
std::shared_ptr<SDL_Texture> texture; std::shared_ptr<SDL_Texture> texture;
int w; i32 w;
int h; i32 h;
Texture(const Resource& r, SDL_Renderer* renderer); Texture(const Resource& r, SDL_Renderer* renderer);
Texture(std::istream& s, size_t size, 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 <sstream>
#include <stdarg.h> #include <stdarg.h>
#include "format.hpp" #include "ougge_format.hpp"
#include "UsefulException.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_list vl;
va_start(vl, args_count); va_start(vl, args_count);
std::stringstream ss; 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)

20
src/common/time.hpp Normal file
View File

@@ -0,0 +1,20 @@
#pragma once
#include "std.hpp"
/// nanoseconds
typedef i64 nsec_t;
/// can be used to measure delta time
///@return time from some moment in nanoseconds.
nsec_t getMonotonicTimeNsec();
#define optime(N, LABEL, CODE) {\
nsec_t b = getMonotonicTimeNsec();\
for(u32 i = 0; i < (u32)N; i++) {\
CODE ;\
}\
nsec_t e = getMonotonicTimeNsec();\
nsec_t t = e-b;\
std::cout<<"operation '"<<LABEL<<"' took "<<t/1e6f<<" ms"<<std::endl;\
}

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.h>
#include <SDL_image.h> #include <SDL_image.h>
namespace ougge { namespace ougge::gui {
SDLException_::SDLException_(const std::string& _file,const std::string& _func, int _line_n) SDLException_::SDLException_(const std::string& _file,const std::string& _func, int _line_n)
: UsefulException_(std::string("SDLException: ") + SDL_GetError(), _file, _func, _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(); SDL_ClearError();
} }
#ifndef IMG_ClearError
#define IMG_ClearError SDL_ClearError
#endif
IMGException_::IMGException_(const std::string& _file,const std::string& _func, int _line_n) IMGException_::IMGException_(const std::string& _file,const std::string& _func, int _line_n)
: UsefulException_(std::string("IMGException: ") + IMG_GetError(), _file, _func, _line_n) : UsefulException_(std::string("IMGException: ") + IMG_GetError(), _file, _func, _line_n)
{ {
SDL_ClearError(); IMG_ClearError();
} }
} }

View File

@@ -1,8 +1,10 @@
#pragma once #pragma once
#include "../UsefulException.hpp"
namespace ougge { #include "../common/std.hpp"
#include "../common/UsefulException.hpp"
namespace ougge::gui {
#define SDLException() SDLException_(__FILE__, __func__, __LINE__) #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,72 +1,46 @@
#define SDL_MAIN_HANDLED #define SDL_MAIN_HANDLED
#include <iostream> #include <iostream>
#include <iomanip> #include <iomanip>
#include "GUI/MainWindow.hpp" #include "common/UsefulException.hpp"
#include "Resources/Resources.hpp" #include "common/ougge_format.hpp"
#include "Game/Scene.hpp" #include "Engine.hpp"
#include "format.hpp" #include "gui/gui.hpp"
#include "UsefulException.hpp"
#include "Mono/Mono.hpp"
#include "Game/GameObjectPool.hpp"
using namespace ougge; using namespace ougge;
#define GAMEOBJECTPOOL_SIZE 64*1024 void drawTutel(Engine& engine){
resources::Texture& tutel = engine.textures.getOrCreate("tutel.png", engine.mainWindow.sdl_renderer);
#define optime(N, LABEL, CODE) {\ resources::SDL_RenderCopyExF_Params params;
nsec_t b = getMonotonicTimeNsec();\ params.target_section = SDL_FRectConstruct(100, 100, 400, 400);
for(u32 i = 0; i < (u32)N; i++) {\ static i32 si = 1;
CODE ;\ params.rotation_angle = M_PI_4 * (si++ / engine.mainWindow.fps_max);
}\ tutel.render(params);
nsec_t e = getMonotonicTimeNsec();\
nsec_t t = e-b;\
std::cout<<"operation '"<<LABEL<<"' took "<<t/1e6f<<" ms"<<std::endl;\
}\
std::vector<GUI::UpdateFunc_t> updateCallbacks;
void update(f64 deltaTime){
for(auto upd : updateCallbacks){
upd(deltaTime);
}
} }
int main(int argc, const char** argv){ int main(int argc, const char** argv){
try { try {
Resources::init(); resources::init();
std::cout<<"initialized resource loader"<<std::endl; std::cout<<"initialized resource loader"<<std::endl;
Mono::RuntimeJIT mono; Engine engine;
std::cout<<"initialized mono jit runtime"<<std::endl; std::cout<<"initialized mono jit runtime"<<std::endl;
GameObjectPool p(GAMEOBJECTPOOL_SIZE); engine.init();
std::cout<<"initialized game engine"<<std::endl;
auto a = mono.loadAssembly("Ougge.dll"); game::GameObject& exampleObj = engine.createGameObject();
MonoClass* gameObjectClass = a->getClass("Ougge", "GameObject"); std::string componentClassName = "Ougge.ExampleComponent";
MonoObject* exampleObjectManaged = mono_object_new(mono.getDomain(), gameObjectClass); if(!engine.tryCreateComponent(exampleObj, componentClassName))
u64 obj_id = 0; throw UsefulException(ougge_format("couldn't create component '%s'", componentClassName.c_str()));
auto pair = p.emplace(GameObject(exampleObjectManaged, nullptr));
auto gameObjectCtor = Mono::Method<void(u64, u32)>(gameObjectClass, ".ctor");
gameObjectCtor(exampleObjectManaged, obj_id++, pair.first);
auto exampleObjectUpdate = Mono::Method<void(f64)>(gameObjectClass, "InvokeUpdate"); std::cout<<"created ExampleObject"<<std::endl;
updateCallbacks.push_back([exampleObjectManaged, exampleObjectUpdate](f64 deltaTime) -> void {
exampleObjectUpdate(exampleObjectManaged, deltaTime);
});
auto tryCreateComponent = Mono::Method<Mono::Bool(Mono::String)>(gameObjectClass, "TryCreateComponent_internal"); engine.updateCallback = [&engine](f64 deltaTime) -> void {
Mono::String componentNameManaged = mono_string_new(mono.getDomain(), "Ougge.ExampleComponent"); drawTutel(engine);
// Mono::ObjectHandle componentNameObjectHandle((MonoObject*)(void*)componentNameManaged); };
Mono::Bool created = tryCreateComponent(exampleObjectManaged, componentNameManaged);
if(!created.wide_bool)
throw UsefulException("couldn't create ExampleComponent");
std::cout<<"OK!"<<std::endl; engine.openMainWindow("ougge");
return 0;
GUI::MainWindow w;
w.open("ougge", update);
std::cout<<"created sdl window"<<std::endl; std::cout<<"created sdl window"<<std::endl;
w.startUpdateLoop(); engine.startLoop();
std::cout<<"sdl window has been cosed"<<std::endl; std::cout<<"sdl window has been cosed"<<std::endl;
} }
catch(const std::exception& e){ 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);
};
}

View File

@@ -1,10 +0,0 @@
#pragma once
#include "std.hpp"
/// nanoseconds
typedef i64 nsec_t;
/// can be used to measure delta time
///@return time from some moment in nanoseconds.
nsec_t getMonotonicTimeNsec();

View File

@@ -0,0 +1,69 @@
#!/usr/bin/env bash
package_version="$TASK_ARGS"
if [ -z "$package_version" ]; then
package_version="8.0.15"
myprint "${YELLOW}You can choose package version manually: cbuild get_mono_files_from=x.y.z"
fi
myprint "${BLUE}package_version: ${CYAN}$package_version"
case "$OS" in
LINUX)
package_platform="linux-$ARCH"
;;
WINDOWS)
package_platform="win-$ARCH"
;;
esac
package_name="Microsoft.NETCore.App.Runtime.Mono.$package_platform"
package_dir="$package_name.$package_version"
package_file="$package_name.$package_version.nupkg"
package_url="https://www.nuget.org/api/v2/package/$package_name/$package_version"
mkdir -p "$OBJDIR/downloads"
myprint "${BLUE}downloading nuget package: ${WHITE}$package_name ${BLUE}to $OBJDIR/downloads"
cd "$OBJDIR/downloads"
wget -q $package_url -O $package_file
clean_dir $package_dir
cd $package_dir
myprint "${BLUE}extracting nuget package"
unzip -oq "../$package_file"
# copy headers
myprint "${BLUE}copying headers"
mkdir -p "../../../dependencies/include"
delete_dir "../../../dependencies/include/mono"
cp -r "runtimes/$package_platform/native/include/mono-2.0/mono" "../../../dependencies/include/"
precompiled_dir="../../../dependencies/precompiled/$OS-$ARCH"
mkdir -p "$precompiled_dir"
clean_dir "$precompiled_dir/mono-libs"
# copy mono native libraries
myprint "${BLUE}copying mono native libraries"
shared_libs=$(find "runtimes/$package_platform/native" -maxdepth 1 \( -not -name "System.Private.CoreLib.dll" -a \( -name '*.so*' -o -name '*.dll' \) \) -type f)
if [ -z "$shared_libs" ]; then
error "can't find mono shared libraries"
fi
for l in $shared_libs ; do
cp -v "$l" "$precompiled_dir"
done
# copy mono c# libraries
myprint "${BLUE}copying mono managed libraries"
managed_libraries="runtimes/$package_platform/native/System.Private.CoreLib.dll"
_subdirs=(runtimes/$package_platform/lib/*)
_managed_lib_dir="${_subdirs[0]}"
for l in $(find $_managed_lib_dir -type f); do
managed_libraries+=" $l"
done
for l in $managed_libraries; do
cp -v "$l" "$precompiled_dir/mono-libs"
done
myprint "${GREEN}mono files have been copied successfully!"
cd ../../..
clean_dir "$OBJDIR/downloads"

View File

@@ -1,59 +0,0 @@
#!/usr/bin/env bash
mono_prefix_path="$TASK_ARGS"
if [ -z "$mono_prefix_path" ]; then
error "required argument 'mono_prefix_path'
Usage: cbuild get_mono_files_from=mono_prefix_path"
fi
myprint "${BLUE}mono prefix: ${WHITE}$mono_prefix_path"
# copy headers
rm -rf 'dependencies/include/mono'
mkdir -p 'dependencies/include'
cp -r "$mono_prefix_path/include/mono-2.0/mono" 'dependencies/include/'
rm -rf "dependencies/precompiled/mono-libs"
mkdir -p "dependencies/precompiled"
mkdir -p "dependencies/precompiled/mono-libs"
case "$OS" in
LINUX)
# copy mono dynamic libraries
shared_libs=$(find "$mono_prefix_path/lib" -maxdepth 1 \( -name '*.so*' -a -not -name '*-profiler*' \) -type f)
if [ -z "$shared_libs" ]; then
error "can't find mono shared libraries"
fi
for l in $shared_libs ; do
cp -v "$l" "dependencies/precompiled/mono-libs/"
done
so_v_libs=$(find "dependencies/precompiled/mono-libs" -name '*.so.*')
for l in $so_v_libs ; do
l_basename=$(basename "$l")
soname_without_version=$(safeprint "$l_basename" | sed 's,.so.*,.so,')
myprint "${BLUE}patching ${WHITE}$l_basename${BLUE}: replacing soname with ${CYAN}$soname_without_version"
patchelf --set-soname "$soname_without_version" "$l"
mv -v "$l" "dependencies/precompiled/mono-libs/$soname_without_version"
done
myprint "${BLUE}stripping debug symbols from mono shared libraries"
for l in $(find "dependencies/precompiled/mono-libs" -name '*.so') ; do
strip -g "$l"
done
# copy mono c# libraries
managed_libraries="mscorlib.dll"
myprint "${BLUE}copying mono managed libraries"
for l in $managed_libraries ; do
cp -v "$mono_prefix_path/lib/mono/4.5/$l" "dependencies/precompiled/mono-libs/"
done
# copy config
myprint "${BLUE}copying mono config"
cp "$mono_prefix_path/etc/mono/config" "dependencies/precompiled/mono-libs/config.xml"
myprint "${BLUE}removing '$mono_libdir/' from mono config"
sed 's,$mono_libdir/,,g' -i "dependencies/precompiled/mono-libs/config.xml"
;;
*)
error "operating system $OS has no configuration variants"
;;
esac
myprint "${GREEN}mono files were copied successfully!"