Compare commits

..

26 Commits

Author SHA1 Message Date
7e7454278d cimgui update 2026-01-09 12:12:45 +05:00
346779060e something something 2026-01-09 11:57:24 +05:00
175fe61e5c implemented functions from NativeFunctions.cs 2025-07-05 03:22:48 +03:00
f7a8d32865 cimgui update 2025-07-05 01:49:54 +03:00
9a283a2904 implemented createModule() and getModule() 2025-06-20 21:51:56 +05:00
a288d0961f split engine code into modules 2025-06-18 16:06:54 +05:00
8fd6cee223 added game_object_pool_size as Engine constructor argument 2025-05-19 03:45:28 +05:00
851e1ee122 added src back 2025-05-19 03:37:02 +05:00
fbd6d43e89 removed src (to get rid of duplicates) 2025-05-19 03:35:56 +05:00
609f7337da created ResourceManager 2025-05-19 03:26:18 +05:00
d700aae02e duplicate files fix 2025-05-19 02:27:39 +05:00
d5531ce370 replaced imgui with cimgui shared lib to use with c# bindings 2025-04-29 02:37:15 +05:00
72f47c297e fixed nullable object warning 2025-04-29 01:33:26 +05:00
1940d01d9b fixed mingw libraries static compilation 2025-04-26 01:41:04 +05:00
5c6fe5944a c# TargetFramework net8.0 2025-04-26 01:09:50 +05:00
d659dcde10 refactored MainWindow and Engine, changed project structure 2025-04-26 00:59:47 +05:00
bb00392e3a update to cbuild 2.2.1 2025-04-26 00:47:25 +05:00
3477b05cd8 created Engine class 2025-04-24 04:50:56 +05:00
366dd1214c switched to mono continued fork 2025-04-22 00:15:28 +05:00
51259e72fe fixed GameObject initialization 2025-04-21 21:19:17 +05:00
5c247ce032 calling mono_gchandle_get_target instead of using expiring raw pointers 2025-04-21 21:16:07 +05:00
5d84e744ce new GameObject[] instead of uninitialized byte buffer 2025-04-21 20:13:11 +05:00
0863408bc5 disabled mono libs strip 2025-04-21 20:02:16 +05:00
0852a0028f fixed valgrind sources path trimming 2025-04-21 18:43:54 +05:00
bfa9bf592a mkdir -p "dependencies/precompiled" 2025-04-21 17:57:35 +05:00
25475e8013 fixed git repo url 2025-04-21 16:50:22 +05:00
64 changed files with 1374 additions and 934 deletions

7
.gitmodules vendored
View File

@@ -1,7 +1,6 @@
[submodule "dependencies/imgui"]
path = dependencies/imgui
url = https://github.com/ocornut/imgui.git
branch = docking
[submodule "dependencies/resource_embedder"] [submodule "dependencies/resource_embedder"]
path = dependencies/resource_embedder path = dependencies/resource_embedder
url = https://timerix.ddns.net/git/Timerix/resource_embedder.git url = https://timerix.ddns.net/git/Timerix/resource_embedder.git
[submodule "dependencies/cimgui"]
path = dependencies/cimgui
url = https://github.com/cimgui/cimgui.git

View File

@@ -9,7 +9,7 @@
"includePath": [ "includePath": [
"dependencies/include", "dependencies/include",
"dependencies/include/SDL2", "dependencies/include/SDL2",
"dependencies/imgui", "dependencies/cimgui/imgui",
"${default}" "${default}"
], ],
"cppStandard": "c++20" "cppStandard": "c++20"

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:3322/Timerix/ougge.git git clone --recurse-submodules --depth 1 https://timerix.ddns.net/git/Timerix/ougge.git
``` ```
2. Install [cbuild](https://timerix.ddns.net:3322/Timerix/cbuild.git). 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
``` ```

1
dependencies/cimgui vendored Submodule

Submodule dependencies/cimgui added at d94ad1b162

22
dependencies/cimgui.config vendored Normal file
View File

@@ -0,0 +1,22 @@
#!/usr/bin/env bash
DEP_WORKING_DIR='dependencies/cimgui'
DEP_PRE_BUILD_COMMAND=''
if [[ "$TASK" = *_dbg ]]; then
DEP_BUILD_COMMAND='cbuild -c ../cimgui.project.config build_shared_lib_dbg'
else
DEP_BUILD_COMMAND='cbuild -c ../cimgui.project.config build_shared_lib'
fi
DEP_POST_BUILD_COMMAND='rm -f cbuild.log'
DEP_CLEAN_COMMAND='cbuild clean -c ../cimgui.project.config'
DEP_STATIC_OUT_FILES=''
case $OS in
WINDOWS)
DEP_DYNAMIC_OUT_FILES="../bin/cimgui.dll"
;;
LINUX)
DEP_DYNAMIC_OUT_FILES="../bin/cimgui.so"
;;
*)
error "operating system $OS has no configuration variants"
;;
esac

View File

@@ -1,8 +1,8 @@
#!/usr/bin/env bash #!/usr/bin/env bash
CBUILD_VERSION=2.1.2 CBUILD_VERSION=2.2.1
CONFIG_VERSION=1 CONFIG_VERSION=1
PROJECT="imgui" PROJECT="cimgui"
CMP_C="gcc" CMP_C="gcc"
CMP_CPP="g++" CMP_CPP="g++"
STD_C="c11" STD_C="c11"
@@ -10,13 +10,14 @@ STD_CPP="c++11"
WARN_C="-Wall -Wno-discarded-qualifiers -Wno-unused-parameter" WARN_C="-Wall -Wno-discarded-qualifiers -Wno-unused-parameter"
WARN_CPP="-Wall -Wno-unused-parameter" WARN_CPP="-Wall -Wno-unused-parameter"
SRC_C="" SRC_C=""
SRC_CPP="imgui.cpp SRC_CPP="imgui/imgui.cpp
imgui_demo.cpp imgui/imgui_demo.cpp
imgui_draw.cpp imgui/imgui_draw.cpp
imgui_tables.cpp imgui/imgui_tables.cpp
imgui_widgets.cpp imgui/imgui_widgets.cpp
backends/imgui_impl_sdl2.cpp imgui/backends/imgui_impl_sdl2.cpp
backends/imgui_impl_sdlrenderer2.cpp" imgui/backends/imgui_impl_sdlrenderer2.cpp
cimgui.cpp"
# Directory with dependency configs. # Directory with dependency configs.
# See cbuild/example_dependency_configs # See cbuild/example_dependency_configs
@@ -31,20 +32,22 @@ ENABLED_DEPENDENCIES=''
# └── profile/ - gcc *.gcda profiling info files # └── profile/ - gcc *.gcda profiling info files
OBJDIR="../obj" OBJDIR="../obj"
OUTDIR="../bin" OUTDIR="../bin"
STATIC_LIB_FILE="lib$PROJECT.a"
# header include directories # header include directories
INCLUDE="-I. -I../include/SDL2" INCLUDE="-I. -I./imgui -I../include/SDL2"
STATIC_LIB_FILE="$PROJECT.a"
# OS-specific options # OS-specific options
case "$OS" in case "$OS" in
WINDOWS) WINDOWS)
EXEC_FILE="$PROJECT.exe" SHARED_LIB_FILE="$PROJECT.dll"
SHARED_LIB_FILE="lib$PROJECT.dll" LINKER_LIBS="-L../precompiled/$OS-$ARCH -l:SDL2.dll"
DEFINE="-DIMGUI_API=__declspec(dllexport)"
;; ;;
LINUX) LINUX)
EXEC_FILE="$PROJECT" SHARED_LIB_FILE="$PROJECT.so"
SHARED_LIB_FILE="lib$PROJECT.so" LINKER_LIBS="-lSDL2"
DEFINE="-DIMGUI_API=__attribute__((__visibility__(\"default\")))"
;; ;;
*) *)
error "operating system $OS has no configuration variants" error "operating system $OS has no configuration variants"
@@ -55,25 +58,25 @@ esac
case "$TASK" in case "$TASK" in
# creates shared library # creates shared library
build_shared_lib) build_shared_lib)
C_ARGS="-O2 -fpic -flto -shared" C_ARGS="-O2 -fpic -shared $DEFINE"
CPP_ARGS="$C_ARGS" CPP_ARGS="$C_ARGS"
LINKER_ARGS="$CPP_ARGS -Wl,-soname,$SHARED_LIB_FILE" LINKER_ARGS="$CPP_ARGS $LINKER_LIBS -Wl,-soname,$SHARED_LIB_FILE"
PRE_TASK_SCRIPT= PRE_TASK_SCRIPT=
TASK_SCRIPT=cbuild/default_tasks/build_shared_lib.sh TASK_SCRIPT=cbuild/default_tasks/build_shared_lib.sh
POST_TASK_SCRIPT= POST_TASK_SCRIPT=
;; ;;
# creates shared library with debug symbols and no optimizations # creates shared library with debug symbols and no optimizations
build_shared_lib_dbg) build_shared_lib_dbg)
C_ARGS="-O0 -g3 -fpic -shared" C_ARGS="-O0 -g3 -fpic -shared $DEFINE"
CPP_ARGS="$C_ARGS" CPP_ARGS="$C_ARGS"
LINKER_ARGS="$CPP_ARGS -Wl,-soname,$SHARED_LIB_FILE" LINKER_ARGS="$CPP_ARGS $LINKER_LIBS -Wl,-soname,$SHARED_LIB_FILE"
PRE_TASK_SCRIPT= PRE_TASK_SCRIPT=
TASK_SCRIPT=cbuild/default_tasks/build_shared_lib.sh TASK_SCRIPT=cbuild/default_tasks/build_shared_lib.sh
POST_TASK_SCRIPT= POST_TASK_SCRIPT=
;; ;;
# creates static library # creates static library
build_static_lib) build_static_lib)
C_ARGS="-O2" C_ARGS="-O2 $DEFINE"
CPP_ARGS="$C_ARGS" CPP_ARGS="$C_ARGS"
PRE_TASK_SCRIPT= PRE_TASK_SCRIPT=
TASK_SCRIPT=cbuild/default_tasks/build_static_lib.sh TASK_SCRIPT=cbuild/default_tasks/build_static_lib.sh
@@ -81,7 +84,7 @@ case "$TASK" in
;; ;;
# creates static library with debug symbols and no optimizations # creates static library with debug symbols and no optimizations
build_static_lib_dbg) build_static_lib_dbg)
C_ARGS="-O0 -g3" C_ARGS="-O0 -g3 $DEFINE"
CPP_ARGS="$C_ARGS" CPP_ARGS="$C_ARGS"
PRE_TASK_SCRIPT= PRE_TASK_SCRIPT=
TASK_SCRIPT=cbuild/default_tasks/build_static_lib.sh TASK_SCRIPT=cbuild/default_tasks/build_static_lib.sh

1
dependencies/imgui vendored

Submodule dependencies/imgui deleted from 7b6314f47d

View File

@@ -1,12 +0,0 @@
#!/usr/bin/env bash
DEP_WORKING_DIR='dependencies/imgui'
DEP_PRE_BUILD_COMMAND=''
if [[ "$TASK" = *_dbg ]]; then
DEP_BUILD_COMMAND='cbuild -c ../imgui.project.config build_static_lib_dbg'
else
DEP_BUILD_COMMAND='cbuild -c ../imgui.project.config build_static_lib'
fi
DEP_POST_BUILD_COMMAND=''
DEP_CLEAN_COMMAND='cbuild clean -c ../imgui.project.config'
DEP_STATIC_OUT_FILES='../bin/libimgui.a'
DEP_DYNAMIC_OUT_FILES=''

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

@@ -6,4 +6,7 @@ if [[ "$TASK" = *_dbg ]]; then
fi fi
DEP_BUILD_COMMAND=$"dotnet build src-csharp.sln -o bin -c $CS_CONFIGURATION" DEP_BUILD_COMMAND=$"dotnet build src-csharp.sln -o bin -c $CS_CONFIGURATION"
DEP_CLEAN_COMMAND='rm -rf bin obj' DEP_CLEAN_COMMAND='rm -rf bin obj'
DEP_OTHER_OUT_FILES='bin/Ougge.dll' DEP_OTHER_OUT_FILES='bin/Ougge.dll
bin/Tomlyn.dll
bin/Hexa.NET.ImGui.dll
bin/HexaGen.Runtime.dll'

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"
@@ -16,7 +15,7 @@ SRC_CPP="$(find src -name '*.cpp')"
# See cbuild/example_dependency_configs # See cbuild/example_dependency_configs
DEPENDENCY_CONFIGS_DIR='dependencies' DEPENDENCY_CONFIGS_DIR='dependencies'
# List of dependency config files in DEPENDENCY_CONFIGS_DIR separated by space. # List of dependency config files in DEPENDENCY_CONFIGS_DIR separated by space.
ENABLED_DEPENDENCIES='precompiled resources imgui src-csharp' ENABLED_DEPENDENCIES='precompiled resources cimgui src-csharp'
# OBJDIR structure: # OBJDIR structure:
# ├── objects/ - Compiled object files. Cleans on each call of build task # ├── objects/ - Compiled object files. Cleans on each call of build task
@@ -28,14 +27,14 @@ OUTDIR="bin"
STATIC_LIB_FILE="lib$PROJECT.a" STATIC_LIB_FILE="lib$PROJECT.a"
# header include directories # header include directories
INCLUDE="-I./dependencies/imgui -I./dependencies/include -I./dependencies/include/SDL2" INCLUDE="-I./dependencies/cimgui/imgui -I./dependencies/include -I./dependencies/include/SDL2"
# OS-specific options # OS-specific options
case "$OS" in 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"
@@ -114,7 +113,7 @@ case "$TASK" in
;; ;;
# executes $EXEC_FILE with valgrind memory checker # executes $EXEC_FILE with valgrind memory checker
valgrind) valgrind)
VALGRIND_ARGS="-s --read-var-info=yes --track-origins=yes --fullpath-after=$(pwd) --leak-check=full --show-leak-kinds=all" VALGRIND_ARGS="-s --read-var-info=yes --track-origins=yes --fullpath-after=$(pwd)/ --leak-check=full --show-leak-kinds=all"
TASK_SCRIPT=cbuild/default_tasks/valgrind.sh TASK_SCRIPT=cbuild/default_tasks/valgrind.sh
;; ;;
# generates profiling info # generates profiling info
@@ -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

@@ -1,4 +1,5 @@
using System; using System;
using Hexa.NET.ImGui;
namespace Ougge; namespace Ougge;
@@ -11,5 +12,16 @@ public class ExampleComponent : Component
public override void Update(double deltaTime) public override void Update(double deltaTime)
{ {
Console.WriteLine($"C# deltaTime {deltaTime} object id {Owner.Id}"); Console.WriteLine($"C# deltaTime {deltaTime} object id {Owner.Id}");
ImGui.Begin("C# WINDOW");
ImGui.Text("Hello from ExampleComponent!");
if (ImGui.Button("create GameObject"))
{
GameObject.Create();
}
if (ImGui.Button("GC Collect"))
{
GC.Collect();
}
ImGui.End();
} }
} }

View File

@@ -26,21 +26,22 @@ 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()
{ {
NativeMethods.createGameObject(out ulong id, out uint index); NativeFunctions.createGameObject(out ulong id, out uint index);
var o = new GameObject(id, index); var o = new GameObject(id, index);
Console.WriteLine($"C# created object with id {id}, index {index}");
return o; return o;
} }
@@ -49,23 +50,31 @@ public class GameObject
if(_isDestroyed) if(_isDestroyed)
return; return;
_isDestroyed = NativeMethods.destroyGameObject(_index); _isDestroyed = NativeFunctions.freeGameObject(_index);
if(!_isDestroyed) if(!_isDestroyed)
throw new Exception($"Can't destroy GameObject({_id})"); throw new Exception($"Can't destroy GameObject({_id})");
} }
~GameObject()
{
// destroys object native part when managed part is garbage-collected
Destroy();
}
/// <param name="t">type derived from Component</param> /// <param name="t">type derived from Component</param>
/// <returns>true if new component instance was created, false if component of the same tipe is already added to GameObject</returns> /// <returns>true if new component instance was created, false if component of the same tipe is already added to GameObject</returns>
/// <exception cref="Exception"></exception> /// <exception cref="Exception"></exception>
public bool TryCreateComponent(Type t) public bool TryCreateComponent(Type t)
{ {
if(!t.IsSubclassOf(typeof(Component))) if (!t.IsSubclassOf(typeof(Component)))
throw new Exception($"type {t.FullName} is not a derived class of {typeof(Component).FullName}"); throw new Exception($"type {t.FullName} is not a derived class of {typeof(Component).FullName}");
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)
?? throw new Exception($"can't create instance of class {t.FullName}");
Components.Add(t, component);
return true; return true;
} }
private bool TryCreateComponent_internal(string fullName) private bool TryCreateComponent_internal(string fullName)
@@ -76,7 +85,9 @@ public class GameObject
private void InvokeUpdate(double deltaTime) private void InvokeUpdate(double deltaTime)
{ {
foreach(var p in Components) Console.WriteLine("C# InvokeUpdate");
Console.WriteLine($"id {_id}, index {_index}, destroyed {_isDestroyed}");
foreach (var p in Components)
{ {
p.Value.Update(deltaTime); p.Value.Update(deltaTime);
} }

View File

@@ -0,0 +1,12 @@
using System.Runtime.CompilerServices;
namespace Ougge;
internal static class NativeFunctions
{
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static void createGameObject(out ulong id, out uint index);
[MethodImpl(MethodImplOptions.InternalCall)]
internal extern static bool freeGameObject(uint index);
}

View File

@@ -1,13 +0,0 @@
using System;
using System.Runtime.InteropServices;
namespace Ougge;
internal static class NativeMethods
{
[DllImport("__Internal")]
internal extern static bool destroyGameObject(uint index);
[DllImport("__Internal")]
internal extern static void createGameObject(out ulong id, out uint index);
}

View File

@@ -1,10 +1,15 @@
<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>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<DebugType>embedded</DebugType> <DebugType>embedded</DebugType>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<PackageReference Include="Hexa.NET.ImGui" Version="2.2.7" />
<PackageReference Include="Tomlyn" Version="0.19.0" />
</ItemGroup>
</Project> </Project>

102
src/Engine.cpp Normal file
View File

@@ -0,0 +1,102 @@
#include "Engine.hpp"
#include "common/UsefulException.hpp"
#include "common/ougge_format.hpp"
#include "common/time.hpp"
#include <SDL2/SDL.h>
#include <iostream>
namespace ougge {
IEngineModule::IEngineModule(Engine& engine)
: engine(engine)
{}
void IEngineModule::beginFrame() {}
void IEngineModule::endFrame() {}
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){
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;
deltaTime = delta_time_s;
tryDrawFrame();
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);
}
}
}
void Engine::stopLoop(){
loop_running = false;
}
void Engine::handleModuleError(IEngineModule& module, const char* type, const char* method, const char* error){
std::string error_message = ougge_format(
"Catched %s at %s.%s(): %s",
type, module.getName().c_str(), method, error);
std::cerr<<error_message<<std::endl;
error_messages.push_back(error_message);
}
void Engine::tryDrawFrame(){
auto it = modules.begin();
while(it != modules.end())
{
IEngineModule& module = **it;
it++;
try {
module.beginFrame();
}
catch(const std::exception& e){
handleModuleError(module, "exception", "beginFrame", e.what());
}
catch(const char* cstr){
handleModuleError(module, "error message (const char*)", "beginFrame", cstr);
}
catch(const std::string& str){
handleModuleError(module, "error message (std::string)", "beginFrame", str.c_str());
}
catch(...){
handleModuleError(module, "unknown", "beginFrame", "unknown");
}
}
it = modules.end();
while(it != modules.begin())
{
it--;
IEngineModule& module = **it;
try {
module.endFrame();
}
catch(const std::exception& e){
handleModuleError(module, "exception", "endFrame", e.what());
}
catch(const char* cstr){
handleModuleError(module, "error message (const char*)", "endFrame", cstr);
}
catch(const std::string& str){
handleModuleError(module, "error message (std::string)", "endFrame", str.c_str());
}
catch(...){
handleModuleError(module, "unknown", "endFrame", "unknown");
}
}
}
}

106
src/Engine.hpp Normal file
View File

@@ -0,0 +1,106 @@
#pragma once
#include <vector>
#include "common/std.hpp"
#include "common/UsefulException.hpp"
#include "common/ougge_format.hpp"
#include "common/type_name_demangled.hpp"
#include <type_traits>
#include <memory>
#include <iostream>
namespace ougge {
class Engine;
class IEngineModule {
public:
IEngineModule() = delete;
IEngineModule(const IEngineModule&) = delete;
IEngineModule& operator=(const IEngineModule&) = delete;
IEngineModule(IEngineModule&&) = delete;
IEngineModule& operator=(IEngineModule&&) = delete;
Engine& engine;
virtual const std::string& getName() = 0;
virtual void beginFrame();
virtual void endFrame();
protected:
IEngineModule(Engine& engine);
};
class Engine {
std::vector<std::unique_ptr<IEngineModule>> modules;
bool loop_running = false;
public:
u32 fps_max = 60;
f64 deltaTime;
std::vector<std::string> error_messages;
Engine() = default;
Engine(const Engine&) = delete;
Engine& operator=(const Engine&) = delete;
Engine(Engine&&) = delete;
Engine& operator=(Engine&&) = delete;
// start game loop on the current thread
void startLoop();
void stopLoop();
//TODO: add std::enable_if
template<typename ModuleT, typename... ConstructorArgsT>
ModuleT& createModule(ConstructorArgsT&&... args){
static_assert(std::is_base_of<IEngineModule, ModuleT>::value);
auto& module_type_name = ougge_type_name<ModuleT>();
//TODO: replace with some logger call
std::cout<<"Initializing module '"<<module_type_name<<"'"<<std::endl;
size_t module_index;
bool module_exists;
getOrCreateModuleIndex<ModuleT>(&module_index, &module_exists);
if(module_exists){
throw UsefulException(ougge_format("can't create second instance of module '%s'",
module_type_name.c_str()));
}
//TODO: std::forward(args)...
modules.push_back(std::make_unique<ModuleT>(*this, args...));
return *static_cast<ModuleT*>(modules[module_index].get());
}
//TODO: add std::enable_if
template<typename ModuleT>
ModuleT& getModule(){
static_assert(std::is_base_of<IEngineModule, ModuleT>::value);
size_t module_index;
bool module_exists;
getOrCreateModuleIndex<ModuleT>(&module_index, &module_exists);
if(!module_exists){
auto& module_type_name = ougge_type_name<ModuleT>();
throw UsefulException(ougge_format("engine has no module '%s'",
module_type_name.c_str()));
}
return *static_cast<ModuleT*>(modules[module_index].get());
}
private:
void tryDrawFrame();
void handleModuleError(IEngineModule& module, const char *type, const char *method, const char *error);
//TODO: add std::enable_if
template<typename ModuleT>
void getOrCreateModuleIndex(size_t* out_index, bool* out_module_exists){
static size_t module_index = modules.size();
*out_index = module_index;
// if module_index == modules.size(), then the module hasn't been pushed to the vector yet
*out_module_exists = module_index != modules.size();
}
};
}

View File

@@ -1,38 +0,0 @@
#include "GameObject.hpp"
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, GameObject *parent)
: object_handle(managed_obj), parent(parent)
{}
GameObject::GameObject(GameObject &&o) :
transform(o.transform),
components(std::move(o.components)),
object_handle(std::move(o.object_handle)),
parent(o.parent)
{
}
GameObject &GameObject::operator=(GameObject &&o){
transform = o.transform;
components = std::move(o.components);
object_handle = std::move(o.object_handle);
parent = o.parent;
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

@@ -1,46 +0,0 @@
#pragma once
#include <map>
#include <iostream>
#include "../math.hpp"
#include "../UsefulException.hpp"
#include "../Mono/Mono.hpp"
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 {
Vec2 scale = { 1, 1 };
Vec2 position = { 0, 0 };
angle_t rotation = 0;
};
std::ostream& operator<<(std::ostream& s, Transform& t);
class GameObject {
Transform transform;
std::map<std::u16string, Component> components;
Mono::ObjectHandle object_handle;
GameObject* parent;
public:
GameObject(Mono::Object managed_obj, GameObject* parent);
GameObject(const GameObject& o) = delete;
GameObject(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 const Mono::ObjectHandle& getObjectHandle() const { return object_handle; }
bool tryGetComponent(const std::u16string& name, Component** out_component);
bool tryAddComponent(const std::u16string& name, Component&& component);
};

View File

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

View File

@@ -1,94 +0,0 @@
#include <fstream>
#include <sstream>
#include "../UsefulException.hpp"
#include "../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(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(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

@@ -1,63 +0,0 @@
#pragma once
#include "../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

@@ -1,12 +0,0 @@
#pragma once
#include "imgui.h"
#include "../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

@@ -15,7 +15,7 @@ class UsefulException_ : public std::exception {
public: public:
UsefulException_(const std::string& msg, const std::string& _file, const std::string& _func, int line_n); UsefulException_(const std::string& msg, const std::string& _file, const std::string& _func, int line_n);
virtual char const* what() const noexcept; char const* what() const noexcept override;
}; };
#define useful_assert(EXPR, ERRMSG) if(!(EXPR)) throw UsefulException(ERRMSG); #define useful_assert(EXPR, ERRMSG) if(!(EXPR)) throw UsefulException(ERRMSG);

View File

@@ -0,0 +1,49 @@
#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; }
// 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");
//TODO: think about std::forward(args)...
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,65 +1,84 @@
#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;
for(size_t i = 0; i < format_str.length(); i++){ for(size_t i = 0; i < format_str.length(); i++){
char c = format_str[i]; char c = format_str[i];
// format specifier // format specifier
if(c == '%'){ if(c == '%'){
c = format_str[++i]; c = format_str[++i];
bool l = false; bool l = false;
while(c == 'l'){ while(c == 'l'){
l = true; l = true;
c = format_str[++i]; c = format_str[++i];
} }
switch(c){ switch(c){
case 'u': case 'u':
if(l) ss<<(u64)va_arg(vl, u64); if(l) ss<<(u64)va_arg(vl, u64);
else ss<<(u32)va_arg(vl, u32); else ss<<(u32)va_arg(vl, u32);
break; break;
case 'i': case 'i':
case 'd': case 'd':
if(l) ss<<(i64)va_arg(vl, i64); if(l) ss<<(i64)va_arg(vl, i64);
else ss<<(i32)va_arg(vl, i32); else ss<<(i32)va_arg(vl, i32);
break; break;
case 'f': case 'f':
// f32 is promoted to f64 when passed through '...' // f32 is promoted to f64 when passed through '...'
ss<<(f64)va_arg(vl, f64); ss<<(f64)va_arg(vl, f64);
break; break;
case 'p': case 'p':
ss<<(void*)va_arg(vl, void*); ss<<(void*)va_arg(vl, void*);
break; break;
case 'x': case 'x':
if(l) ss<<std::hex<<(u64)va_arg(vl, u64); if(l) ss<<std::hex<<(u64)va_arg(vl, u64);
else ss<<std::hex<<(u32)va_arg(vl, u32); else ss<<std::hex<<(u32)va_arg(vl, u32);
break; break;
case 's': { case 's': {
const char* cptr = va_arg(vl,char*); const char* cptr = va_arg(vl,char*);
if(cptr != nullptr) if(cptr != nullptr)
ss<<cptr; ss<<cptr;
else ss<<"<nullptr>"; else ss<<"<nullptr>";
break; break;
} }
case 'c': case 'c':
ss<<(char)va_arg(vl,int); ss<<(char)va_arg(vl,int);
break; break;
default: default:
throw UsefulException("invalid format cpecifier"); throw UsefulException("invalid format cpecifier");
} }
} }
// regular character // regular character
else ss<<c; else ss<<c;
} }
va_end(vl); va_end(vl);
return ss.str(); return ss.str();
} }
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,8 @@
#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)
std::string formatSizeHumanReadable(std::size_t byte_n);

View File

@@ -17,7 +17,6 @@ typedef double f64;
/// anonymous pointer without specified freeMembers() func /// anonymous pointer without specified freeMembers() func
typedef void* Pointer; typedef void* Pointer;
#define nameof(V) #V
#ifdef _MSC_VER #ifdef _MSC_VER
#pragma comment(lib, "mincore_downlevel.lib") // Support OS older than SDK #pragma comment(lib, "mincore_downlevel.lib") // Support OS older than SDK

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

@@ -0,0 +1,44 @@
// https://stackoverflow.com/a/53865723
#pragma once
#ifndef _MSC_VER
#include <cxxabi.h>
#endif
#include <memory>
#include <string>
#include <cstdlib>
template <class T>
std::string _ougge_type_name_demangled()
{
typedef typename std::remove_reference<T>::type TR;
const char* type_name_mangled = typeid(TR).name();
std::unique_ptr<char, void(*)(void*)> own
(
#ifndef _MSC_VER
abi::__cxa_demangle(type_name_mangled, nullptr, nullptr, nullptr),
#else
nullptr,
#endif
std::free
);
std::string r = own ? own.get() : type_name_mangled;
if (std::is_const<TR>::value)
r += " const";
if (std::is_volatile<TR>::value)
r += " volatile";
if (std::is_lvalue_reference<T>::value)
r += "&";
else if (std::is_rvalue_reference<T>::value)
r += "&&";
return r;
}
/// @return human-readable type name
template <class T>
const std::string& ougge_type_name(){
static std::string name = _ougge_type_name_demangled<T>();
return name;
}

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; }
};
}

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,39 +86,48 @@ 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);
} }
void GameObjectPool::erase(u32 index) bool 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(); return false;
}
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;
return true;
} }
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;
@@ -43,22 +45,26 @@ public:
~GameObjectPool(); ~GameObjectPool();
GameObject& get(u32 index); GameObject& get(u32 index);
std::pair<u32, GameObject&> emplace(GameObject&& new_obj); std::pair<u32, GameObject&> emplace(GameObject&& new_obj);
void erase(u32 index); bool 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,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,89 +1,83 @@
#define SDL_MAIN_HANDLED #define SDL_MAIN_HANDLED
#include <iostream> #include <iostream>
#include <iomanip> #include <iomanip>
#include "GUI/MainWindow.hpp" #include "Engine.hpp"
#include "Resources/Resources.hpp" #include "modules/MainWindowSDL2.hpp"
#include "Game/Scene.hpp" #include "modules/MonoGameObjectSystem.hpp"
#include "format.hpp" #include "resources/textures.hpp"
#include "UsefulException.hpp"
#include "Mono/Mono.hpp" using namespace ougge;
#include "Game/GameObjectPool.hpp"
class TutelModule : public IEngineModule {
using namespace ougge; resources::CacheStorage<resources::Texture> textures;
public:
#define GAMEOBJECTPOOL_SIZE 64*1024 TutelModule(Engine& engine, resources::ResourceManager& resourceManager) :
IEngineModule(engine),
#define optime(N, LABEL, CODE) {\ textures(&resourceManager)
nsec_t b = getMonotonicTimeNsec();\ {
for(u32 i = 0; i < (u32)N; i++) {\ //TODO: add something like `assert(requireModule(MainWindow))`
CODE ;\ }
}\
nsec_t e = getMonotonicTimeNsec();\ const std::string& getName() override {
nsec_t t = e-b;\ return ougge_type_name<TutelModule>();
std::cout<<"operation '"<<LABEL<<"' took "<<t/1e6f<<" ms"<<std::endl;\ }
}\
void beginFrame() override {
auto& mainWindow = engine.getModule<modules::MainWindowSDL2>();
std::vector<GUI::UpdateFunc_t> updateCallbacks; resources::Texture* tutel = textures.tryGetOrCreate("tutel.png", mainWindow.sdl_renderer);
if(tutel == nullptr){
void update(f64 deltaTime){ throw new UsefulException("couldn't find resource 'tutel.png'");
for(auto upd : updateCallbacks){ }
upd(deltaTime); resources::SDL_RenderCopyExF_Params params;
} params.target_section = SDL_FRectConstruct(100, 100, 400, 400);
} static i32 si = 1;
params.rotation_angle = M_PI_4 * (si++ / engine.fps_max);
int main(int argc, const char** argv){ tutel->render(params);
try { }
Resources::init();
std::cout<<"initialized resource loader"<<std::endl; };
Mono::RuntimeJIT mono; void createExampleObject(Engine& engine){
std::cout<<"initialized mono jit runtime"<<std::endl; std::cout<<"creating ExampleObject"<<std::endl;
GameObjectPool p(GAMEOBJECTPOOL_SIZE); auto& monoSystem = engine.getModule<modules::MonoGameObjectSystem>();
game::GameObject& exampleObj = monoSystem.createAndConstructGameObject();
auto a = mono.loadAssembly("Ougge.dll"); std::string componentClassName = "Ougge.ExampleComponent";
MonoClass* gameObjectClass = a->getClass("Ougge", "GameObject"); if(!monoSystem.tryCreateComponent(exampleObj, componentClassName))
MonoObject* exampleObjectManaged = mono_object_new(mono.getDomain(), gameObjectClass); throw UsefulException(ougge_format("couldn't create component '%s'", componentClassName.c_str()));
u64 obj_id = 0; }
auto pair = p.emplace(GameObject(exampleObjectManaged, nullptr));
auto gameObjectCtor = Mono::Method<void(u64, u32)>(gameObjectClass, ".ctor"); int main(int argc, const char** argv){
gameObjectCtor(exampleObjectManaged, obj_id++, pair.first); try {
std::cout<<"Initializing Engine"<<std::endl;
auto exampleObjectUpdate = Mono::Method<void(f64)>(gameObjectClass, "InvokeUpdate"); Engine engine;
updateCallbacks.push_back([exampleObjectManaged, exampleObjectUpdate](f64 deltaTime) -> void {
exampleObjectUpdate(exampleObjectManaged, deltaTime); std::cout<<"Initializing ResourceManager"<<std::endl;
}); resources::ResourceManager resourceManager;
auto tryCreateComponent = Mono::Method<Mono::Bool(Mono::String)>(gameObjectClass, "TryCreateComponent_internal"); engine.createModule<modules::MainWindowSDL2>("ougge", resourceManager);
Mono::String componentNameManaged = mono_string_new(mono.getDomain(), "Ougge.ExampleComponent"); engine.createModule<modules::MonoGameObjectSystem>(64*1024);
// Mono::ObjectHandle componentNameObjectHandle((MonoObject*)(void*)componentNameManaged); createExampleObject(engine);
Mono::Bool created = tryCreateComponent(exampleObjectManaged, componentNameManaged); engine.createModule<TutelModule>(resourceManager);
if(!created.wide_bool)
throw UsefulException("couldn't create ExampleComponent"); std::cout<<"main loop start"<<std::endl;
engine.startLoop();
std::cout<<"OK!"<<std::endl; std::cout<<"main loop stop"<<std::endl;
return 0; }
GUI::MainWindow w; catch(const std::exception& e){
w.open("ougge", update); std::cerr<<"Catched exception: "<<e.what()<<std::endl;
std::cout<<"created sdl window"<<std::endl; return -1;
w.startUpdateLoop(); }
std::cout<<"sdl window has been cosed"<<std::endl; catch(const char* cstr){
} std::cerr<<"Catched error message (const char*): "<<cstr<<std::endl;
catch(const std::exception& e){ return -1;
std::cerr<<"Catched exception: "<<e.what()<<std::endl; }
return -1; catch(const std::string& str){
} std::cerr<<"Catched error message (std::string): "<<str<<std::endl;
catch(const char* cstr){ return -1;
std::cerr<<"Catched error message (const char*): "<<cstr<<std::endl; }
return -1; catch(...){
} std::cerr<<"Catched unknown error"<<std::endl;
catch(const std::string& str){ return -1;
std::cerr<<"Catched error message (std::string): "<<str<<std::endl; }
return -1; return 0;
} }
catch(...){
std::cerr<<"Catched unknown error"<<std::endl;
return -1;
}
return 0;
}

View File

@@ -1,19 +1,23 @@
#include <backends/imgui_impl_sdl2.h> #include <backends/imgui_impl_sdl2.h>
#include <backends/imgui_impl_sdlrenderer2.h> #include <backends/imgui_impl_sdlrenderer2.h>
#include <iostream> #include <iostream>
#include "MainWindow.hpp" #include "MainWindowSDL2.hpp"
#include "exceptions.hpp" #include "../gui/gui_exceptions.hpp"
#include "../format.hpp" #include "../common/ougge_format.hpp"
#include "../Resources/fonts.hpp" #include "../common/math.hpp"
#include "../Resources/textures.hpp"
#include "../math.hpp"
namespace ougge::GUI { using namespace ougge::gui;
f32 MainWindow::getDPI(){ namespace ougge::modules {
int w=0, h=0;
const std::string& MainWindowSDL2::getName() {
return ougge_type_name<MainWindowSDL2>();
}
f32 MainWindowSDL2::getDPI(){
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 +25,15 @@ f32 MainWindow::getDPI(){
return dpi; return dpi;
} }
void MainWindow::open(const char* window_title, UpdateFunc_t _update){ MainWindowSDL2::MainWindowSDL2(Engine& engine,
update = _update; const std::string& window_title,
resources::ResourceManager& resourceManager)
: IEngineModule(engine)
{
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 +43,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)
@@ -74,139 +80,15 @@ 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();
io.FontDefault = Resources::ImFont_LoadFromResource(default_font, default_font_size, dpi);
}
// Wait, poll and handle events (inputs, window resize, etc.)
void MainWindow::poll_events(bool waitForEvent){
SDL_Event event;
if(waitForEvent){
// waits for first event in cpu-efficient way
SDL_TRY(SDL_WaitEvent(&event) != 1);
}
// dont wait for event
else if(!SDL_PollEvent(&event))
return;
do { resources::ResourceFactory* font_res = resourceManager.tryGetResource(default_font_path);
ImGui_ImplSDL2_ProcessEvent(&event); if(font_res == nullptr)
switch(event.type){ throw UsefulException("can't find default font resource");
case SDL_QUIT: { f32 dpi = getDPI();
close(); io.FontDefault = resources::ImFont_LoadFromResource(font_res, default_font_size, dpi);
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){ MainWindowSDL2::~MainWindowSDL2(){
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,11 +97,60 @@ void MainWindow::destroy(){
SDL_Quit(); SDL_Quit();
} }
void MainWindow::close(){ void MainWindowSDL2::pollEvents(){
loop_running = false; SDL_Event event;
while (SDL_PollEvent(&event)) {
ImGui_ImplSDL2_ProcessEvent(&event);
switch(event.type){
case SDL_QUIT: {
//TODO: log SDL_QUIT event
engine.stopLoop();
break;
}
case SDL_WINDOWEVENT: {
if(event.window.event == SDL_WINDOWEVENT_CLOSE
&& event.window.windowID == SDL_GetWindowID(sdl_window))
{
//TODO: log SDL_WINDOWEVENT event
engine.stopLoop();
}
break;
}
}
}
} }
void MainWindow::draw_bg_window(){ void MainWindowSDL2::beginFrame(){
// process events happend since previous frame
pollEvents();
// 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();
drawErrorWindow();
}
void MainWindowSDL2::endFrame(){
ImGui::Render();
ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData(), sdl_renderer);
// Swap buffers
SDL_RenderPresent(sdl_renderer);
}
void MainWindowSDL2::draw_bg_window(){
const ImGuiDockNodeFlags dockspace_flags = const ImGuiDockNodeFlags dockspace_flags =
ImGuiDockNodeFlags_PassthruCentralNode; ImGuiDockNodeFlags_PassthruCentralNode;
const ImGuiWindowFlags window_flags = const ImGuiWindowFlags window_flags =
@@ -283,15 +214,16 @@ void MainWindow::draw_bg_window(){
ImGui::End(); ImGui::End();
} }
void MainWindow::draw_debug_window(){ void MainWindowSDL2::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", (int*)&engine.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);
@@ -300,4 +232,16 @@ void MainWindow::draw_debug_window(){
ImGui::ShowMetricsWindow(&show_metrics_window); ImGui::ShowMetricsWindow(&show_metrics_window);
} }
void MainWindowSDL2::drawErrorWindow(){
if(engine.error_messages.size() < 1)
return;
ImGui::Begin("ERRORS");
if(ImGui::Button("Clear"))
engine.error_messages.clear();
for(auto& msg : engine.error_messages){
ImGui::TextWrapped("%s", msg.c_str());
}
ImGui::End();
}
} }

View File

@@ -1,50 +1,49 @@
#pragma once #pragma once
#include <SDL.h> #include <SDL.h>
#include <imgui.h> #include <imgui.h>
#include "../std.hpp" #include <vector>
#include "../time.hpp" #include "../common/std.hpp"
#include <functional> #include "../resources/resources.hpp"
#include "../resources/fonts.hpp"
/// converts hex color to float vector #include "../Engine.hpp"
#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 namespace ougge::modules {
#define RGBAFToHex(VEC4) {(u8)(VEC4.x*255), (u8)(VEC4.y*255), (u8)(VEC4.z*255), (u8)(VEC4.w*255)}
/// converts hex color to float vector
namespace ougge::GUI { #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 default_font "DroidSans" #define RGBAFToHex(VEC4) {(u8)(VEC4.x*255), (u8)(VEC4.y*255), (u8)(VEC4.z*255), (u8)(VEC4.w*255)}
using UpdateFunc_t = std::function<void(f64)>; #define default_font_path "fonts/DroidSans.ttf"
class MainWindow { class MainWindowSDL2 : public IEngineModule {
public: public:
ImVec4 clear_color = RGBAHexToF(35,35,50,255); f32 default_font_size = 14.0f;
f32 default_font_size = 14.0f; ImVec4 clear_color = RGBAHexToF(35,35,50,255);
int fps_max = 60; SDL_Window* sdl_window = nullptr;
// called on each frame SDL_Renderer* sdl_renderer = nullptr;
UpdateFunc_t update = nullptr;
private:
private: bool show_debug_window = true;
bool loop_running = false; bool show_demo_window = false;
bool show_demo_window = false; bool show_metrics_window = false;
bool show_metrics_window = false;
SDL_Window* sdl_window = nullptr; public:
SDL_Renderer* sdl_renderer = nullptr; MainWindowSDL2(Engine& engine, const std::string& window_title,
resources::ResourceManager& resourceManager);
public: ~MainWindowSDL2();
void open(const char* window_title, UpdateFunc_t update);
void startUpdateLoop(); const std::string& getName() override;
void close(); void beginFrame() override;
void endFrame() override;
private:
void destroy(); f32 getDPI();
f32 getDPI(); private:
void poll_events(bool waitForEvent); void pollEvents();
void draw_frame(); void draw_debug_window();
void draw_ui(); void draw_bg_window();
void draw_debug_window(); void drawErrorWindow();
void draw_bg_window(); };
};
}
}

View File

@@ -0,0 +1,76 @@
#include "MonoGameObjectSystem.hpp"
namespace ougge::modules {
const std::string& MonoGameObjectSystem::getName() {
return ougge_type_name<MonoGameObjectSystem>();
}
MonoGameObjectSystem::MonoGameObjectSystem(Engine& engine, u32 max_game_objects) :
IEngineModule(engine),
gameObjectPool(max_game_objects)
{
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");
registerNativeCallbacks();
}
// sets implementations for extern functions from src-csharp/NativeFunctions.cs
void MonoGameObjectSystem::registerNativeCallbacks(){
static MonoGameObjectSystem* this_static = this;
if(this_static != this)
throw new UsefulException("creation of more than one instance of MonoGameObjectSystem is not allowed");
auto createGameObject_callback = static_cast<void(*)(u64*, u32*)> (
[](u64* id_out, u32* index_out) -> void {
std::cout<<"createGameObject_callback"<<std::endl;
this_static->createGameObjectInPool(id_out, index_out);
}
);
mono_add_internal_call("Ougge.NativeFunctions::createGameObject", (void*)createGameObject_callback);
auto freeGameObject_callback = static_cast<bool(*)(u32)>(
[](u32 index) -> bool {
std::cout<<"freeGameObject_callback"<<std::endl;
return this_static->gameObjectPool.erase(index);
}
);
mono_add_internal_call("Ougge.NativeFunctions::freeGameObject", (void*)freeGameObject_callback);
}
void MonoGameObjectSystem::beginFrame(){
for(auto pair : gameObjectPool){
auto obj = pair.second.getObjectHandle().getObject();
std::cout<<"updating obj: "<<obj<<std::endl;
gameObjectInvokeUpdate(obj, engine.deltaTime);
}
}
// is used in NativeFunctions.cs
game::GameObject& MonoGameObjectSystem::createGameObjectInPool(u64* id_out, u32* index_out){
auto pair = gameObjectPool.emplace(game::GameObject(mono.createObject(gameObjectClass)));
*id_out = ++obj_id;
*index_out = pair.first;
game::GameObject& obj = pair.second;
return obj;
}
game::GameObject& MonoGameObjectSystem::createAndConstructGameObject(){
u64 obj_id;
u32 obj_index;
game::GameObject& obj = createGameObjectInPool(&obj_id, &obj_index);
gameObjectCtor(obj.getObjectHandle().getObject(), obj_id, obj_index);
return obj;
}
bool MonoGameObjectSystem::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;
}
}

View File

@@ -0,0 +1,35 @@
#pragma once
#include "../Engine.hpp"
#include "../common/function_shared_ptr.hpp"
#include "../mono/mono.hpp"
#include "../game/GameObjectPool.hpp"
namespace ougge::modules {
class MonoGameObjectSystem : public IEngineModule {
Mono::RuntimeJIT mono;
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:
std::shared_ptr<Mono::Assembly> engineManagedAssembly;
MonoGameObjectSystem(Engine& engine, u32 max_game_objects);
const std::string& getName() override;
void beginFrame() override;
game::GameObject& createAndConstructGameObject();
bool tryCreateComponent(game::GameObject& obj, const std::string& componentClassName);
private:
void registerNativeCallbacks();
game::GameObject& createGameObjectInPool(u64* id_out, u32* index_out);
};
}

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,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,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

@@ -0,0 +1,40 @@
#include "MemoryStream.hpp"
class MemoryStreamBuf : public std::streambuf {
public:
MemoryStreamBuf(void* p, const std::size_t n);
std::istream::pos_type seekoff(
std::istream::off_type off,
std::ios_base::seekdir dir,
std::ios_base::openmode which) override;
};
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();
}

View File

@@ -0,0 +1,8 @@
#include <iostream>
class MemoryStreamRead : public std::istream {
public:
MemoryStreamRead(const void* p, const std::size_t n);
virtual ~MemoryStreamRead();
};

View File

@@ -1,9 +1,6 @@
#include <imgui.h> #include "fonts.hpp"
#include "Resources.hpp"
#include <cstdio>
#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[] = {
@@ -16,17 +13,16 @@ ImFont* ImFont_LoadFromFile(const std::string& file_path, f32 font_size, f32 dpi
return io.Fonts->AddFontFromFileTTF(file_path.c_str(), font_size, nullptr, glyph_ranges); 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){ ImFont* ImFont_LoadFromResource(ResourceFactory* res, f32 font_size, f32 dpi){
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
font_size *= dpi; font_size *= dpi;
ImFontConfig font_cfg = ImFontConfig(); ImFontConfig font_cfg = ImFontConfig();
std::sprintf(font_cfg.Name, "%s %ipx", font_name.c_str(), (i32)font_size); std::sprintf(font_cfg.Name, "%s %ipx", res->path.c_str(), (i32)font_size);
auto& res = getResource("fonts/" + font_name + ".ttf"); char* font_data = new char[res->size];
char* font_data = new char[res.size]; res->openStream()->read(font_data, res->size);
res.openStream()->read(font_data, res.size);
return io.Fonts->AddFontFromMemoryTTF((void*)(font_data), res.size, return io.Fonts->AddFontFromMemoryTTF((void*)(font_data), res->size,
font_size, &font_cfg, glyph_ranges); font_size, &font_cfg, glyph_ranges);
} }

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

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

View File

@@ -0,0 +1,64 @@
#include <fstream>
#include <sstream>
#include "../common/UsefulException.hpp"
#include "../common/ougge_format.hpp"
#include "resources.hpp"
#include "embedded_resources.h"
#include "MemoryStream.hpp"
namespace ougge::resources {
ResourceFactory::ResourceFactory(const std::string& path, const std::size_t size, StreamFactoryMethod open_read_steam_func)
: path(path), size(size), openStream(open_read_steam_func)
{}
std::shared_ptr<ResourceMap> ResourceManager::_embeddedResourcesMap;
void ResourceManager::loadEmbeddedResources(){
if(_embeddedResourcesMap != nullptr)
return;
_embeddedResourcesMap = std::make_shared<ResourceMap>();
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 = _embeddedResourcesMap->emplace(e.path,
ResourceFactory(e.path, e.size, embedded_resource_factory));
if(!r.second)
throw UsefulException(ougge_format("can't load duplicate resource '%s'", e.path));
}
_resourceMaps.push_back(_embeddedResourcesMap);
}
ResourceManager::ResourceManager(){
loadEmbeddedResources();
}
ResourceFactory* ResourceManager::tryGetResource(const std::string& path){
// reverse iteration of linked list
auto map_list_iterator = _resourceMaps.end();
while(map_list_iterator != _resourceMaps.begin()){
--map_list_iterator;
auto map = *map_list_iterator;
auto resource_iterator = map->find(path);
// resource not found
if(resource_iterator == map->end())
return nullptr;
return &resource_iterator->second;
}
return nullptr;
}
}

View File

@@ -0,0 +1,75 @@
#pragma once
#include "../common/std.hpp"
#include <iostream>
#include <functional>
#include <unordered_map>
#include <memory>
#include <list>
namespace ougge::resources {
class ResourceFactory {
public:
const std::string path;
const std::size_t size;
using StreamFactoryMethod = std::function< std::unique_ptr<std::istream> () >;
ResourceFactory(const std::string& path, const std::size_t size, StreamFactoryMethod open_read_steam_func);
const StreamFactoryMethod openStream;
};
using ResourceMap = std::unordered_map<std::string, ResourceFactory>;
//TODO: rewrite ResourceManager as a module
class ResourceManager {
std::list<std::shared_ptr<ResourceMap>> _resourceMaps;
static std::shared_ptr<ResourceMap> _embeddedResourcesMap;
void loadEmbeddedResources();
public:
ResourceManager();
void addResourceMap(std::shared_ptr<ResourceMap> m);
std::shared_ptr<ResourceMap> loadResouceMap();
void removeResourceMap();
ResourceFactory* tryGetResource(const std::string& path);
};
/// @brief stores requested resources in memory
/// @tparam T must implement constructor `T(ResourceFactory*, ...)`
template<class T>
class CacheStorage {
std::unordered_map<std::string, T> _map;
ResourceManager* _resourceManager;
public:
CacheStorage(ResourceManager*);
CacheStorage(CacheStorage&&) = delete;
CacheStorage(const CacheStorage&) = delete;
template<typename... TCtorArgs>
T* tryGetOrCreate(const std::string& name, TCtorArgs&&... ctor_args){
auto it = _map.find(name);
if(it != _map.end())
return &it->second;
ResourceFactory* res = _resourceManager->tryGetResource(name);
if(res == nullptr)
return nullptr;
auto e = _map.emplace(name, T(res, ctor_args...));
return &e.first->second;
}
};
template <class T> inline CacheStorage<T>::CacheStorage(ResourceManager* resourceManager)
: _resourceManager(resourceManager)
{}
}

View File

@@ -1,11 +1,11 @@
#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(ResourceFactory* r, SDL_Renderer* renderer)
: Texture(*r.openStream(), r.size, renderer) : Texture(*r->openStream(), r->size, renderer)
{} {}
Texture::Texture(std::istream& s, size_t size, SDL_Renderer* renderer) Texture::Texture(std::istream& s, size_t size, SDL_Renderer* 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,10 +30,10 @@ 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(ResourceFactory* r, SDL_Renderer* renderer);
Texture(std::istream& s, size_t size, 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);

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,58 +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/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!"