Compare commits
30 Commits
3df4361779
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 7e7454278d | |||
| 346779060e | |||
| 175fe61e5c | |||
| f7a8d32865 | |||
| 9a283a2904 | |||
| a288d0961f | |||
| 8fd6cee223 | |||
| 851e1ee122 | |||
| fbd6d43e89 | |||
| 609f7337da | |||
| d700aae02e | |||
| d5531ce370 | |||
| 72f47c297e | |||
| 1940d01d9b | |||
| 5c6fe5944a | |||
| d659dcde10 | |||
| bb00392e3a | |||
| 3477b05cd8 | |||
| 366dd1214c | |||
| 51259e72fe | |||
| 5c247ce032 | |||
| 5d84e744ce | |||
| 0863408bc5 | |||
| 0852a0028f | |||
| bfa9bf592a | |||
| 25475e8013 | |||
| d4de309ca7 | |||
| d5d28d4884 | |||
| 04e4f63fd7 | |||
| 47574e9b30 |
9
.gitmodules
vendored
9
.gitmodules
vendored
@@ -1,7 +1,6 @@
|
||||
[submodule "dependencies/imgui"]
|
||||
path = dependencies/imgui
|
||||
url = https://github.com/ocornut/imgui.git
|
||||
branch = docking
|
||||
[submodule "dependencies/resource_embedder"]
|
||||
path = dependencies/resource_embedder
|
||||
url = https://timerix.ddns.net:3322/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
|
||||
|
||||
2
.vscode/c_cpp_properties.json
vendored
2
.vscode/c_cpp_properties.json
vendored
@@ -9,7 +9,7 @@
|
||||
"includePath": [
|
||||
"dependencies/include",
|
||||
"dependencies/include/SDL2",
|
||||
"dependencies/imgui",
|
||||
"dependencies/cimgui/imgui",
|
||||
"${default}"
|
||||
],
|
||||
"cppStandard": "c++20"
|
||||
|
||||
2
.vscode/tasks.json
vendored
2
.vscode/tasks.json
vendored
@@ -10,7 +10,7 @@
|
||||
"command": "bash",
|
||||
"args": [
|
||||
"-c",
|
||||
"cbuild build_exec_dbg"
|
||||
"cbuild rebuild_dependencies=src-csharp build_exec_dbg"
|
||||
],
|
||||
"options": {
|
||||
"cwd": "${workspaceFolder}"
|
||||
|
||||
48
README.md
48
README.md
@@ -2,56 +2,30 @@
|
||||
A game engine or something, idk.
|
||||
|
||||
## Installation
|
||||
1. Clone the repository.
|
||||
1. **Clone the repository.**
|
||||
```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).
|
||||
3. Install [SDL2](https://github.com/libsdl-org/SDL) and [SDL2_image](https://github.com/libsdl-org/SDL_image).
|
||||
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).**
|
||||
- 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/`.
|
||||
4. Symlink SDL headers directory to `dependencies/include`.
|
||||
4. **Symlink SDL headers directory** to `dependencies/include`.
|
||||
```sh
|
||||
ln -s SDL2_HEADERS_DIRECTORY_ABSOLUTE_PATH -T dependencies/include/SDL2
|
||||
```
|
||||
Location of the headers can be found by `pkg-config --cflags --libs sdl2`.
|
||||
Mingw installs SDL2 headers to `/mingw64/include/SDL2`.
|
||||
5. Install [mono](https://github.com/mono/mono).
|
||||
|
||||
**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)
|
||||
5. **Download mono runtime** ([source is here](https://github.com/dotnet/runtime/tree/main/src/mono)).
|
||||
- default version
|
||||
```sh
|
||||
mkdir mono
|
||||
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
|
||||
cbuild download_mono_from_nuget
|
||||
```
|
||||
2. Install `libz autoconf automake libtool gettext cmake python3 curl` and
|
||||
3. Build mono. If something doesn't work, read [documentation](https://www.mono-project.com/docs/compiling-mono/)
|
||||
- or some specific version
|
||||
```sh
|
||||
mkdir -p mono_prefix
|
||||
./autogen.sh --prefix=$(realpath mono_prefix) --disable-boehm
|
||||
make get-monolite-latest
|
||||
make -j8
|
||||
make install
|
||||
cbuild download_mono_from_nuget=x.y.z
|
||||
```
|
||||
3. Install `patchelf`
|
||||
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
|
||||
7. **Compile the program**
|
||||
```sh
|
||||
cbuild build_exec_dbg
|
||||
```
|
||||
|
||||
1
dependencies/cimgui
vendored
Submodule
1
dependencies/cimgui
vendored
Submodule
Submodule dependencies/cimgui added at d94ad1b162
22
dependencies/cimgui.config
vendored
Normal file
22
dependencies/cimgui.config
vendored
Normal 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
|
||||
45
dependencies/imgui.project.config → dependencies/cimgui.project.config
vendored
Executable file → Normal file
45
dependencies/imgui.project.config → dependencies/cimgui.project.config
vendored
Executable file → Normal file
@@ -1,8 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
CBUILD_VERSION=2.1.2
|
||||
CBUILD_VERSION=2.2.1
|
||||
CONFIG_VERSION=1
|
||||
|
||||
PROJECT="imgui"
|
||||
PROJECT="cimgui"
|
||||
CMP_C="gcc"
|
||||
CMP_CPP="g++"
|
||||
STD_C="c11"
|
||||
@@ -10,13 +10,14 @@ STD_CPP="c++11"
|
||||
WARN_C="-Wall -Wno-discarded-qualifiers -Wno-unused-parameter"
|
||||
WARN_CPP="-Wall -Wno-unused-parameter"
|
||||
SRC_C=""
|
||||
SRC_CPP="imgui.cpp
|
||||
imgui_demo.cpp
|
||||
imgui_draw.cpp
|
||||
imgui_tables.cpp
|
||||
imgui_widgets.cpp
|
||||
backends/imgui_impl_sdl2.cpp
|
||||
backends/imgui_impl_sdlrenderer2.cpp"
|
||||
SRC_CPP="imgui/imgui.cpp
|
||||
imgui/imgui_demo.cpp
|
||||
imgui/imgui_draw.cpp
|
||||
imgui/imgui_tables.cpp
|
||||
imgui/imgui_widgets.cpp
|
||||
imgui/backends/imgui_impl_sdl2.cpp
|
||||
imgui/backends/imgui_impl_sdlrenderer2.cpp
|
||||
cimgui.cpp"
|
||||
|
||||
# Directory with dependency configs.
|
||||
# See cbuild/example_dependency_configs
|
||||
@@ -31,20 +32,22 @@ ENABLED_DEPENDENCIES=''
|
||||
# └── profile/ - gcc *.gcda profiling info files
|
||||
OBJDIR="../obj"
|
||||
OUTDIR="../bin"
|
||||
STATIC_LIB_FILE="lib$PROJECT.a"
|
||||
|
||||
# header include directories
|
||||
INCLUDE="-I. -I../include/SDL2"
|
||||
INCLUDE="-I. -I./imgui -I../include/SDL2"
|
||||
|
||||
STATIC_LIB_FILE="$PROJECT.a"
|
||||
# OS-specific options
|
||||
case "$OS" in
|
||||
WINDOWS)
|
||||
EXEC_FILE="$PROJECT.exe"
|
||||
SHARED_LIB_FILE="lib$PROJECT.dll"
|
||||
SHARED_LIB_FILE="$PROJECT.dll"
|
||||
LINKER_LIBS="-L../precompiled/$OS-$ARCH -l:SDL2.dll"
|
||||
DEFINE="-DIMGUI_API=__declspec(dllexport)"
|
||||
;;
|
||||
LINUX)
|
||||
EXEC_FILE="$PROJECT"
|
||||
SHARED_LIB_FILE="lib$PROJECT.so"
|
||||
SHARED_LIB_FILE="$PROJECT.so"
|
||||
LINKER_LIBS="-lSDL2"
|
||||
DEFINE="-DIMGUI_API=__attribute__((__visibility__(\"default\")))"
|
||||
;;
|
||||
*)
|
||||
error "operating system $OS has no configuration variants"
|
||||
@@ -55,25 +58,25 @@ esac
|
||||
case "$TASK" in
|
||||
# creates shared library
|
||||
build_shared_lib)
|
||||
C_ARGS="-O2 -fpic -flto -shared"
|
||||
C_ARGS="-O2 -fpic -shared $DEFINE"
|
||||
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=
|
||||
TASK_SCRIPT=cbuild/default_tasks/build_shared_lib.sh
|
||||
POST_TASK_SCRIPT=
|
||||
;;
|
||||
# creates shared library with debug symbols and no optimizations
|
||||
build_shared_lib_dbg)
|
||||
C_ARGS="-O0 -g3 -fpic -shared"
|
||||
C_ARGS="-O0 -g3 -fpic -shared $DEFINE"
|
||||
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=
|
||||
TASK_SCRIPT=cbuild/default_tasks/build_shared_lib.sh
|
||||
POST_TASK_SCRIPT=
|
||||
;;
|
||||
# creates static library
|
||||
build_static_lib)
|
||||
C_ARGS="-O2"
|
||||
C_ARGS="-O2 $DEFINE"
|
||||
CPP_ARGS="$C_ARGS"
|
||||
PRE_TASK_SCRIPT=
|
||||
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
|
||||
build_static_lib_dbg)
|
||||
C_ARGS="-O0 -g3"
|
||||
C_ARGS="-O0 -g3 $DEFINE"
|
||||
CPP_ARGS="$C_ARGS"
|
||||
PRE_TASK_SCRIPT=
|
||||
TASK_SCRIPT=cbuild/default_tasks/build_static_lib.sh
|
||||
1
dependencies/imgui
vendored
1
dependencies/imgui
vendored
Submodule dependencies/imgui deleted from 7b6314f47d
12
dependencies/imgui.config
vendored
12
dependencies/imgui.config
vendored
@@ -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=''
|
||||
23
dependencies/precompiled.config
vendored
23
dependencies/precompiled.config
vendored
@@ -1,23 +1,28 @@
|
||||
#!/usr/bin/env bash
|
||||
mkdir -p 'dependencies/precompiled'
|
||||
DEP_WORKING_DIR='dependencies/precompiled'
|
||||
DEP_WORKING_DIR="dependencies/precompiled/$OS-$ARCH"
|
||||
mkdir -p "dependencies/precompiled"
|
||||
mkdir -p "$DEP_WORKING_DIR"
|
||||
DEP_PRE_BUILD_COMMAND=''
|
||||
DEP_BUILD_COMMAND=''
|
||||
DEP_POST_BUILD_COMMAND=''
|
||||
DEP_CLEAN_COMMAND=''
|
||||
|
||||
# won't be copied to project $OUTDIR
|
||||
DEP_STATIC_OUT_FILES=$(find dependencies/precompiled -name '*.a' | sed 's,dependencies/precompiled/,,')
|
||||
PRESERVE_OUT_DIRECTORY_STRUCTURE=true
|
||||
DEP_STATIC_OUT_FILES=$(find "$DEP_WORKING_DIR" -name '*.a' | sed "s,$DEP_WORKING_DIR/,,")
|
||||
|
||||
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
|
||||
PRESERVE_OUT_DIRECTORY_STRUCTURE=true
|
||||
case $OS in
|
||||
WINDOWS)
|
||||
DEP_DYNAMIC_OUT_FILES=$(find dependencies/precompiled -maxdepth 1 -name '*.dll' | sed 's,dependencies/precompiled/,,')
|
||||
DEP_OTHER_OUT_FILES="mono-libs/mscorlib.dll mono-libs/config.xml"
|
||||
# DEP_STATIC_OUT_FILES+=" mono-libs/libmono-static-sgen.lib"
|
||||
DEP_DYNAMIC_OUT_FILES=$(find "$DEP_WORKING_DIR" -maxdepth 1 -name '*.dll' | sed "s,$DEP_WORKING_DIR/,,")
|
||||
DEP_OTHER_OUT_FILES="$mono_libs"
|
||||
;;
|
||||
LINUX)
|
||||
DEP_DYNAMIC_OUT_FILES=$(find dependencies/precompiled -name '*.so' | sed 's,dependencies/precompiled/,,')
|
||||
DEP_OTHER_OUT_FILES="mono-libs/mscorlib.dll mono-libs/config.xml"
|
||||
DEP_DYNAMIC_OUT_FILES=$(find "$DEP_WORKING_DIR" -name '*.so' | sed "s,$DEP_WORKING_DIR/,,")
|
||||
DEP_OTHER_OUT_FILES="$mono_libs"
|
||||
;;
|
||||
*)
|
||||
error "operating system $OS has no configuration variants"
|
||||
|
||||
11
dependencies/src-csharp.config
vendored
11
dependencies/src-csharp.config
vendored
@@ -1,9 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
DEP_WORKING_DIR='src-csharp'
|
||||
CS_CONFIGURATION='Release'
|
||||
if [[ "$TASK" = *_dbg ]]; then
|
||||
DEP_BUILD_COMMAND='dotnet build src-csharp.sln -o bin -c Debug'
|
||||
else
|
||||
DEP_BUILD_COMMAND='dotnet build src-csharp.sln -o bin -c Release'
|
||||
CS_CONFIGURATION='Debug'
|
||||
fi
|
||||
DEP_BUILD_COMMAND=$"dotnet build src-csharp.sln -o bin -c $CS_CONFIGURATION"
|
||||
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'
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
CBUILD_VERSION=2.1.4
|
||||
CONFIG_VERSION=1
|
||||
CBUILD_VERSION=2.2.1
|
||||
|
||||
PROJECT="ougge"
|
||||
CMP_C="gcc"
|
||||
@@ -16,7 +15,7 @@ SRC_CPP="$(find src -name '*.cpp')"
|
||||
# See cbuild/example_dependency_configs
|
||||
DEPENDENCY_CONFIGS_DIR='dependencies'
|
||||
# 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:
|
||||
# ├── objects/ - Compiled object files. Cleans on each call of build task
|
||||
@@ -28,14 +27,14 @@ OUTDIR="bin"
|
||||
STATIC_LIB_FILE="lib$PROJECT.a"
|
||||
|
||||
# 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
|
||||
case "$OS" in
|
||||
WINDOWS)
|
||||
EXEC_FILE="$PROJECT.exe"
|
||||
SHARED_LIB_FILE="$PROJECT.dll"
|
||||
LINKER_LIBS=""
|
||||
LINKER_LIBS="-static -lstdc++ -lpthread"
|
||||
;;
|
||||
LINUX)
|
||||
EXEC_FILE="$PROJECT"
|
||||
@@ -114,7 +113,7 @@ case "$TASK" in
|
||||
;;
|
||||
# executes $EXEC_FILE with valgrind memory checker
|
||||
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
|
||||
;;
|
||||
# generates profiling info
|
||||
@@ -176,8 +175,8 @@ case "$TASK" in
|
||||
rebuild_dependencies)
|
||||
TASK_SCRIPT=cbuild/default_tasks/rebuild_dependencies.sh
|
||||
;;
|
||||
get_mono_files_from)
|
||||
TASK_SCRIPT=tasks/get_mono_files_from.sh
|
||||
download_mono_from_nuget)
|
||||
TASK_SCRIPT=tasks/download_mono_from_nuget.sh
|
||||
;;
|
||||
# deletes generated files
|
||||
clean)
|
||||
|
||||
16
src-csharp/Component.cs
Normal file
16
src-csharp/Component.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
|
||||
namespace Ougge;
|
||||
|
||||
public abstract class Component {
|
||||
private GameObject _gameObject;
|
||||
|
||||
internal Component(GameObject gameObject)
|
||||
{
|
||||
_gameObject = gameObject;
|
||||
}
|
||||
|
||||
public GameObject Owner { get => _gameObject.IsDestroyed ? throw new Exception("GameObject") : _gameObject; }
|
||||
|
||||
public virtual void Update(double deltaTime) {}
|
||||
}
|
||||
27
src-csharp/ExampleComponent.cs
Normal file
27
src-csharp/ExampleComponent.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using Hexa.NET.ImGui;
|
||||
|
||||
namespace Ougge;
|
||||
|
||||
public class ExampleComponent : Component
|
||||
{
|
||||
public ExampleComponent(GameObject gameObject) : base(gameObject)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Update(double deltaTime)
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Management.Instrumentation;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Numerics;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
@@ -7,18 +8,88 @@ namespace Ougge;
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct Transform
|
||||
{
|
||||
Vector2 scale;
|
||||
Vector2 scale;
|
||||
Vector2 position;
|
||||
float rotation;
|
||||
}
|
||||
|
||||
public class Component
|
||||
{
|
||||
GameObject owner;
|
||||
}
|
||||
|
||||
public class GameObject
|
||||
{
|
||||
Transform transform { get; }
|
||||
GameObject? parent;
|
||||
}
|
||||
// index in GameObjectPool
|
||||
private ulong _id;
|
||||
private uint _index;
|
||||
private bool _isDestroyed;
|
||||
|
||||
public ulong Id => _id;
|
||||
public bool IsDestroyed => _isDestroyed;
|
||||
|
||||
public Transform Transform { get; }
|
||||
public GameObject? Parent;
|
||||
|
||||
public Dictionary<Type, Component> Components;
|
||||
|
||||
private GameObject(ulong id, uint nativePoolIndex)
|
||||
{
|
||||
_id = id;
|
||||
_index = nativePoolIndex;
|
||||
// Do not move this line or mono runtime will throw SEGFAULT.
|
||||
// Mono runtime can't set values in field declaration, but initializing everything in constructor is OK.
|
||||
Components = new();
|
||||
}
|
||||
|
||||
static public GameObject Create()
|
||||
{
|
||||
NativeFunctions.createGameObject(out ulong id, out uint index);
|
||||
var o = new GameObject(id, index);
|
||||
Console.WriteLine($"C# created object with id {id}, index {index}");
|
||||
return o;
|
||||
}
|
||||
|
||||
public void Destroy()
|
||||
{
|
||||
if(_isDestroyed)
|
||||
return;
|
||||
|
||||
_isDestroyed = NativeFunctions.freeGameObject(_index);
|
||||
if(!_isDestroyed)
|
||||
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>
|
||||
/// <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>
|
||||
public bool TryCreateComponent(Type t)
|
||||
{
|
||||
if (!t.IsSubclassOf(typeof(Component)))
|
||||
throw new Exception($"type {t.FullName} is not a derived class of {typeof(Component).FullName}");
|
||||
|
||||
if (Components.ContainsKey(t))
|
||||
return false;
|
||||
|
||||
Component component = (Component?)Activator.CreateInstance(t, this)
|
||||
?? throw new Exception($"can't create instance of class {t.FullName}");
|
||||
Components.Add(t, component);
|
||||
return true;
|
||||
}
|
||||
private bool TryCreateComponent_internal(string fullName)
|
||||
{
|
||||
Type t = Type.GetType(fullName) ?? throw new Exception($"type not found '{fullName}'");
|
||||
return TryCreateComponent(t);
|
||||
}
|
||||
|
||||
private void InvokeUpdate(double deltaTime)
|
||||
{
|
||||
Console.WriteLine("C# InvokeUpdate");
|
||||
Console.WriteLine($"id {_id}, index {_index}, destroyed {_isDestroyed}");
|
||||
foreach (var p in Components)
|
||||
{
|
||||
p.Value.Update(deltaTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
12
src-csharp/NativeFunctions.cs
Normal file
12
src-csharp/NativeFunctions.cs
Normal 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);
|
||||
}
|
||||
@@ -1,11 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net48</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<RootNamespace>Ougge</RootNamespace>
|
||||
<ImplicitUsings>disable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<DebugType>embedded</DebugType>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Hexa.NET.ImGui" Version="2.2.7" />
|
||||
<PackageReference Include="Tomlyn" Version="0.19.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Ougge;
|
||||
|
||||
public abstract class ScriptBase {
|
||||
public virtual void Update(double deltaTime) {}
|
||||
}
|
||||
|
||||
public class ExampleScript : ScriptBase
|
||||
{
|
||||
public override void Update(double deltaTime)
|
||||
{
|
||||
Console.WriteLine($"C# deltaTime {deltaTime}");
|
||||
}
|
||||
}
|
||||
102
src/Engine.cpp
Normal file
102
src/Engine.cpp
Normal 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
106
src/Engine.hpp
Normal 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();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,29 +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::operator=(GameObject &&o){
|
||||
transform = o.transform;
|
||||
components = std::move(o.components);
|
||||
object_handle = std::move(o.object_handle);
|
||||
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;
|
||||
}
|
||||
@@ -1,41 +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& operator=(GameObject&& o);
|
||||
|
||||
inline Transform& getTransform(){ return transform; }
|
||||
bool tryGetComponent(const std::u16string& name, Component** out_component);
|
||||
bool tryAddComponent(const std::u16string& name, Component&& component);
|
||||
};
|
||||
@@ -1,37 +0,0 @@
|
||||
#include "GameObject.hpp"
|
||||
|
||||
class GameObjectPool {
|
||||
void* buffer;
|
||||
u64* used_indices;
|
||||
u32 size;
|
||||
u32 first_unused_index;
|
||||
|
||||
bool isIndexUsed(u32 index);
|
||||
u32 getNearestUnusedIndex(u32 startIndex);
|
||||
u32 getNearestUsedIndex(u32 startIndex);
|
||||
|
||||
friend class iterator;
|
||||
public:
|
||||
|
||||
///@param size must be a multiple of 64
|
||||
GameObjectPool(u32 size);
|
||||
~GameObjectPool();
|
||||
GameObject& get(u32 index);
|
||||
std::pair<u32, GameObject&> put_new(GameObject&& new_obj);
|
||||
void remove(u32 index);
|
||||
|
||||
class iterator {
|
||||
GameObjectPool* p;
|
||||
u32 index = 0;
|
||||
|
||||
public:
|
||||
iterator(GameObjectPool* p, u32 index);
|
||||
std::pair<u32, GameObject&> operator*();
|
||||
iterator& operator++();
|
||||
inline bool operator!=(const iterator& o) const { return index != o.index; };
|
||||
inline bool operator==(const iterator& o) const { return index == o.index; };
|
||||
};
|
||||
|
||||
inline iterator begin() { return iterator(this, 0); }
|
||||
inline iterator end() { return iterator(this, -1); }
|
||||
};
|
||||
@@ -1,7 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "GameObject.hpp"
|
||||
|
||||
class Scene {
|
||||
|
||||
};
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -15,7 +15,7 @@ class UsefulException_ : public std::exception {
|
||||
public:
|
||||
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);
|
||||
49
src/common/function_shared_ptr.hpp
Normal file
49
src/common/function_shared_ptr.hpp
Normal 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...);
|
||||
}
|
||||
};
|
||||
@@ -1,65 +1,84 @@
|
||||
#include <sstream>
|
||||
#include <stdarg.h>
|
||||
#include "format.hpp"
|
||||
#include "UsefulException.hpp"
|
||||
|
||||
std::string _format(const std::string& format_str, const size_t args_count, ...){
|
||||
va_list vl;
|
||||
va_start(vl, args_count);
|
||||
std::stringstream ss;
|
||||
|
||||
for(size_t i = 0; i < format_str.length(); i++){
|
||||
char c = format_str[i];
|
||||
|
||||
// format specifier
|
||||
if(c == '%'){
|
||||
c = format_str[++i];
|
||||
bool l = false;
|
||||
while(c == 'l'){
|
||||
l = true;
|
||||
c = format_str[++i];
|
||||
}
|
||||
|
||||
switch(c){
|
||||
case 'u':
|
||||
if(l) ss<<(u64)va_arg(vl, u64);
|
||||
else ss<<(u32)va_arg(vl, u32);
|
||||
break;
|
||||
case 'i':
|
||||
case 'd':
|
||||
if(l) ss<<(i64)va_arg(vl, i64);
|
||||
else ss<<(i32)va_arg(vl, i32);
|
||||
break;
|
||||
case 'f':
|
||||
// f32 is promoted to f64 when passed through '...'
|
||||
ss<<(f64)va_arg(vl, f64);
|
||||
break;
|
||||
case 'p':
|
||||
ss<<(void*)va_arg(vl, void*);
|
||||
break;
|
||||
case 'x':
|
||||
if(l) ss<<std::hex<<(u64)va_arg(vl, u64);
|
||||
else ss<<std::hex<<(u32)va_arg(vl, u32);
|
||||
break;
|
||||
case 's': {
|
||||
const char* cptr = va_arg(vl,char*);
|
||||
if(cptr != nullptr)
|
||||
ss<<cptr;
|
||||
else ss<<"<nullptr>";
|
||||
break;
|
||||
}
|
||||
case 'c':
|
||||
ss<<(char)va_arg(vl,int);
|
||||
break;
|
||||
default:
|
||||
throw UsefulException("invalid format cpecifier");
|
||||
}
|
||||
}
|
||||
|
||||
// regular character
|
||||
else ss<<c;
|
||||
}
|
||||
|
||||
va_end(vl);
|
||||
return ss.str();
|
||||
}
|
||||
#include <sstream>
|
||||
#include <stdarg.h>
|
||||
#include "ougge_format.hpp"
|
||||
#include "UsefulException.hpp"
|
||||
|
||||
std::string _ougge_format(const std::string& format_str, const size_t args_count, ...){
|
||||
va_list vl;
|
||||
va_start(vl, args_count);
|
||||
std::stringstream ss;
|
||||
|
||||
for(size_t i = 0; i < format_str.length(); i++){
|
||||
char c = format_str[i];
|
||||
|
||||
// format specifier
|
||||
if(c == '%'){
|
||||
c = format_str[++i];
|
||||
bool l = false;
|
||||
while(c == 'l'){
|
||||
l = true;
|
||||
c = format_str[++i];
|
||||
}
|
||||
|
||||
switch(c){
|
||||
case 'u':
|
||||
if(l) ss<<(u64)va_arg(vl, u64);
|
||||
else ss<<(u32)va_arg(vl, u32);
|
||||
break;
|
||||
case 'i':
|
||||
case 'd':
|
||||
if(l) ss<<(i64)va_arg(vl, i64);
|
||||
else ss<<(i32)va_arg(vl, i32);
|
||||
break;
|
||||
case 'f':
|
||||
// f32 is promoted to f64 when passed through '...'
|
||||
ss<<(f64)va_arg(vl, f64);
|
||||
break;
|
||||
case 'p':
|
||||
ss<<(void*)va_arg(vl, void*);
|
||||
break;
|
||||
case 'x':
|
||||
if(l) ss<<std::hex<<(u64)va_arg(vl, u64);
|
||||
else ss<<std::hex<<(u32)va_arg(vl, u32);
|
||||
break;
|
||||
case 's': {
|
||||
const char* cptr = va_arg(vl,char*);
|
||||
if(cptr != nullptr)
|
||||
ss<<cptr;
|
||||
else ss<<"<nullptr>";
|
||||
break;
|
||||
}
|
||||
case 'c':
|
||||
ss<<(char)va_arg(vl,int);
|
||||
break;
|
||||
default:
|
||||
throw UsefulException("invalid format cpecifier");
|
||||
}
|
||||
}
|
||||
|
||||
// regular character
|
||||
else ss<<c;
|
||||
}
|
||||
|
||||
va_end(vl);
|
||||
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();
|
||||
}
|
||||
8
src/common/ougge_format.hpp
Normal file
8
src/common/ougge_format.hpp
Normal 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);
|
||||
@@ -17,7 +17,6 @@ typedef double f64;
|
||||
/// anonymous pointer without specified freeMembers() func
|
||||
typedef void* Pointer;
|
||||
|
||||
#define nameof(V) #V
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#pragma comment(lib, "mincore_downlevel.lib") // Support OS older than SDK
|
||||
20
src/common/time.hpp
Normal file
20
src/common/time.hpp
Normal 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;\
|
||||
}
|
||||
44
src/common/type_name_demangled.hpp
Normal file
44
src/common/type_name_demangled.hpp
Normal 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;
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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
30
src/game/GameObject.cpp
Normal 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
45
src/game/GameObject.hpp
Normal 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; }
|
||||
};
|
||||
|
||||
}
|
||||
@@ -2,31 +2,28 @@
|
||||
#include <bit>
|
||||
#include <cstring>
|
||||
|
||||
namespace ougge::game {
|
||||
|
||||
GameObjectPool::GameObjectPool(u32 size)
|
||||
{
|
||||
useful_assert(size % 64 == 0, "size of GameObjectPool must be a multiple of 64");
|
||||
this->size = size;
|
||||
first_unused_index = 0;
|
||||
buffer = new char[size*sizeof(GameObject)];
|
||||
buffer = new GameObject[size];
|
||||
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);
|
||||
}
|
||||
|
||||
GameObjectPool::~GameObjectPool()
|
||||
{
|
||||
// int i = 0;
|
||||
for(auto&& p : *this){
|
||||
// std::cout<<"~GameObjectPool i="<<i++<<std::endl;
|
||||
p.second.~GameObject();
|
||||
}
|
||||
delete (char*)buffer;
|
||||
delete used_indices;
|
||||
delete[] buffer;
|
||||
delete[] used_indices;
|
||||
}
|
||||
|
||||
bool GameObjectPool::isIndexUsed(u32 index)
|
||||
{
|
||||
return ( used_indices[index/64] & (1<<(index%64)) ) != 0;
|
||||
return ( used_indices[index/64] & (u64(1)<<(index%64)) ) != 0;
|
||||
}
|
||||
|
||||
u32 GameObjectPool::getNearestUnusedIndex(u32 startIndex)
|
||||
@@ -38,7 +35,7 @@ u32 GameObjectPool::getNearestUnusedIndex(u32 startIndex)
|
||||
|
||||
u32 i = startIndex/64;
|
||||
// mark previous bits as used
|
||||
u64 u = used_indices[i] | ( (1<<startIndex%64) -1 );
|
||||
u64 u = used_indices[i] | ( (u64(1)<<startIndex%64) -1 );
|
||||
while(u == u64(-1)){
|
||||
i++;
|
||||
if(i == size/64)
|
||||
@@ -61,7 +58,7 @@ u32 GameObjectPool::getNearestUsedIndex(u32 startIndex)
|
||||
|
||||
u32 i = startIndex/64;
|
||||
// mark previous bits as unused
|
||||
u64 u = used_indices[i] & !( (1<<startIndex%64) -1 );
|
||||
u64 u = used_indices[i] & !( (u64(1)<<startIndex%64) -1 );
|
||||
while(u == 0){
|
||||
i++;
|
||||
if(i == size/64)
|
||||
@@ -78,50 +75,59 @@ u32 GameObjectPool::getNearestUsedIndex(u32 startIndex)
|
||||
GameObject& GameObjectPool::get(u32 index)
|
||||
{
|
||||
if(index >= size)
|
||||
throw UsefulException(format("index %i is out of size %i", index, size));
|
||||
throw UsefulException(ougge_format("index %i is out of size %i", index, size));
|
||||
if(!isIndexUsed(index))
|
||||
throw UsefulException(format("there is no object at index %i", index));
|
||||
return ((GameObject*)buffer)[index];
|
||||
throw UsefulException(ougge_format("there is no object at index %i", index));
|
||||
return buffer[index];
|
||||
}
|
||||
|
||||
std::pair<u32, GameObject&> GameObjectPool::put_new(GameObject&& new_obj)
|
||||
std::pair<u32, GameObject&> GameObjectPool::emplace(GameObject&& new_obj)
|
||||
{
|
||||
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");
|
||||
GameObject& r = ( ((GameObject*)buffer)[i] = std::move(new_obj) );
|
||||
used_indices[i] |= 1<<(i%64); // mark index bit as used
|
||||
|
||||
buffer[i] = std::move(new_obj);
|
||||
GameObject& r = buffer[i];
|
||||
used_indices[i/64] |= u64(1)<<(i%64); // mark index bit as used
|
||||
first_unused_index = getNearestUnusedIndex(i+1);
|
||||
return std::pair<u32, GameObject&>(i, r);
|
||||
}
|
||||
|
||||
void GameObjectPool::remove(u32 index)
|
||||
bool GameObjectPool::erase(u32 index)
|
||||
{
|
||||
if(index >= size)
|
||||
throw UsefulException(format("index %i is out of size %i", index, size));
|
||||
if(!isIndexUsed(index))
|
||||
throw UsefulException(format("there is no object at index %i", index));
|
||||
((GameObject*)buffer)[index].~GameObject();
|
||||
used_indices[index/64] ^= ~(1<<(index%64)); // mark index bit as unused
|
||||
throw UsefulException(ougge_format("index %i is out of size %i", index, size));
|
||||
if(!isIndexUsed(index)){
|
||||
// throw UsefulException(ougge_format("there is no object at index %i", index));
|
||||
return false;
|
||||
}
|
||||
|
||||
buffer[index] = GameObject();
|
||||
used_indices[index/64] &= ~(u64(1)<<(index%64)); // mark index bit as unused
|
||||
if(index < first_unused_index)
|
||||
first_unused_index = index;
|
||||
return true;
|
||||
}
|
||||
|
||||
GameObjectPool::iterator::iterator(GameObjectPool* p, u32 index)
|
||||
: p(p), index(index)
|
||||
GameObjectPool::iterator::iterator(GameObjectPool* pool, u32 index)
|
||||
: pool(pool), index(index)
|
||||
{
|
||||
}
|
||||
|
||||
std::pair<u32, GameObject&> GameObjectPool::iterator::operator*()
|
||||
{
|
||||
if(index >= p->size)
|
||||
if(index >= pool->size)
|
||||
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);
|
||||
}
|
||||
|
||||
GameObjectPool::iterator& GameObjectPool::iterator::operator++()
|
||||
{
|
||||
index = p->getNearestUsedIndex(index+1);
|
||||
index = pool->getNearestUsedIndex(index+1);
|
||||
return *this;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
70
src/game/GameObjectPool.hpp
Normal file
70
src/game/GameObjectPool.hpp
Normal file
@@ -0,0 +1,70 @@
|
||||
#include "GameObject.hpp"
|
||||
|
||||
namespace ougge::game {
|
||||
|
||||
/*
|
||||
Fixed array that stores deleted elements indices as bits in array of u64.
|
||||
Fast emplace, erase and lookup.
|
||||
|
||||
------------------------[construct]------------------------
|
||||
operation 'GameObjectPool::GameObjectPool()' took 1.0549 ms
|
||||
operation 'other_collections_construct' took 0.0133 ms
|
||||
-------------------------[emplace]-------------------------
|
||||
operation 'GameObjectPool::emplace' took 8.0557 ms
|
||||
operation 'vector::emplace_back' took 11.3735 ms
|
||||
operation 'set::emplace' took 80.5633 ms
|
||||
operation 'list::emplace_front' took 18.1442 ms
|
||||
operation 'forward_list::emplace_front' took 11.5467 ms
|
||||
--------------------------[erase]--------------------------
|
||||
operation 'GameObjectPool::erase' took 0.2745 ms
|
||||
operation 'vector::erase' took 15790.6 ms
|
||||
operation 'set::erase' took 1.2697 ms
|
||||
operation 'list::erase_after' took 0.93 ms
|
||||
operation 'forward_list::erase_after' took 1.1127 ms
|
||||
-------------------------[iterate]-------------------------
|
||||
operation 'GameObjectPool::iterate' took 1.1166 ms
|
||||
operation 'vector::iterate' took 0.8883 ms
|
||||
operation 'set::iterate' took 2.8011 ms
|
||||
operation 'list::iterate' took 2.0766 ms
|
||||
operation 'forward_list::iterate' took 2.0823 ms
|
||||
*/
|
||||
|
||||
class GameObjectPool {
|
||||
GameObject* buffer;
|
||||
u64* used_indices;
|
||||
u32 size;
|
||||
u32 first_unused_index;
|
||||
|
||||
bool isIndexUsed(u32 index);
|
||||
u32 getNearestUnusedIndex(u32 startIndex);
|
||||
u32 getNearestUsedIndex(u32 startIndex);
|
||||
public:
|
||||
|
||||
///@param size must be a multiple of 64
|
||||
GameObjectPool(u32 size);
|
||||
~GameObjectPool();
|
||||
GameObject& get(u32 index);
|
||||
std::pair<u32, GameObject&> emplace(GameObject&& new_obj);
|
||||
bool erase(u32 index);
|
||||
|
||||
#pragma region iterator class
|
||||
class iterator {
|
||||
GameObjectPool* pool;
|
||||
u32 index = 0;
|
||||
|
||||
public:
|
||||
iterator(GameObjectPool* pool, u32 index);
|
||||
std::pair<u32, GameObject&> operator*();
|
||||
iterator& operator++();
|
||||
inline bool operator!=(const iterator& o) const { return index != o.index; };
|
||||
inline bool operator==(const iterator& o) const { return index == o.index; };
|
||||
};
|
||||
#pragma endregion
|
||||
|
||||
inline iterator begin() { return iterator(this, 0); }
|
||||
inline iterator end() { return iterator(this, -1); }
|
||||
|
||||
friend class iterator;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
#include "exceptions.hpp"
|
||||
#include "gui_exceptions.hpp"
|
||||
#include <SDL.h>
|
||||
#include <SDL_image.h>
|
||||
|
||||
namespace ougge {
|
||||
namespace ougge::gui {
|
||||
|
||||
SDLException_::SDLException_(const std::string& _file,const std::string& _func, int _line_n)
|
||||
: UsefulException_(std::string("SDLException: ") + SDL_GetError(), _file, _func, _line_n)
|
||||
@@ -10,10 +10,14 @@ SDLException_::SDLException_(const std::string& _file,const std::string& _func,
|
||||
SDL_ClearError();
|
||||
}
|
||||
|
||||
#ifndef IMG_ClearError
|
||||
#define IMG_ClearError SDL_ClearError
|
||||
#endif
|
||||
|
||||
IMGException_::IMGException_(const std::string& _file,const std::string& _func, int _line_n)
|
||||
: UsefulException_(std::string("IMGException: ") + IMG_GetError(), _file, _func, _line_n)
|
||||
{
|
||||
SDL_ClearError();
|
||||
IMG_ClearError();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include "../UsefulException.hpp"
|
||||
|
||||
namespace ougge {
|
||||
#include "../common/std.hpp"
|
||||
#include "../common/UsefulException.hpp"
|
||||
|
||||
namespace ougge::gui {
|
||||
|
||||
#define SDLException() SDLException_(__FILE__, __func__, __LINE__)
|
||||
|
||||
@@ -20,6 +22,6 @@ public:
|
||||
};
|
||||
|
||||
|
||||
#define SDL_TRY(EXPR) if(EXPR) throw SDLException();
|
||||
#define SDL_TRY(EXPR) if(EXPR) throw ::ougge::gui::SDLException();
|
||||
|
||||
}
|
||||
156
src/main.cpp
156
src/main.cpp
@@ -1,73 +1,83 @@
|
||||
#define SDL_MAIN_HANDLED
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include "GUI/MainWindow.hpp"
|
||||
#include "Resources/Resources.hpp"
|
||||
#include "Game/Scene.hpp"
|
||||
#include "format.hpp"
|
||||
#include "UsefulException.hpp"
|
||||
#include "Mono/Mono.hpp"
|
||||
#include "Game/GameObjectPool.hpp"
|
||||
#include <bitset>
|
||||
#include <bit>
|
||||
|
||||
using namespace ougge;
|
||||
|
||||
std::vector<GUI::UpdateFunc_t> updateCallbacks;
|
||||
|
||||
void update(f64 deltaTime){
|
||||
for(auto upd : updateCallbacks){
|
||||
upd(deltaTime);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, const char** argv){
|
||||
try {
|
||||
Resources::init();
|
||||
std::cout<<"initialized resource loader"<<std::endl;
|
||||
|
||||
Mono::RuntimeJIT mono;
|
||||
std::cout<<"initialized mono jit runtime"<<std::endl;
|
||||
GameObjectPool p(64*512);
|
||||
|
||||
auto a = mono.loadAssembly("Ougge.dll");
|
||||
auto gameObjectClass = a->getClass("Ougge", "GameObject");
|
||||
for(int i = 0; i < 32*1024; i++){
|
||||
Mono::Object gameObjectManaged = mono_object_new(mono.getDomain(), gameObjectClass);
|
||||
p.put_new(GameObject(gameObjectManaged, nullptr));
|
||||
GameObject& o = p.get(0);
|
||||
std::cout<<'['<<i<<"] "<<o.getTransform()<<std::endl;
|
||||
}
|
||||
return 0;
|
||||
|
||||
|
||||
// mono_runtime_object_init(scriptInstance);
|
||||
// auto scriptUpdate = Mono::Method<void(f64)>(c, "Update");
|
||||
// updateCallbacks.push_back([scriptInstance, scriptUpdate](f64 deltaTime) -> void {
|
||||
// scriptUpdate(scriptInstance, deltaTime);
|
||||
// });
|
||||
|
||||
GUI::MainWindow w;
|
||||
w.open("ougge", update);
|
||||
std::cout<<"created sdl window"<<std::endl;
|
||||
w.startUpdateLoop();
|
||||
std::cout<<"sdl window has been cosed"<<std::endl;
|
||||
}
|
||||
catch(const std::exception& e){
|
||||
std::cerr<<"Catched exception: "<<e.what()<<std::endl;
|
||||
return -1;
|
||||
}
|
||||
catch(const char* cstr){
|
||||
std::cerr<<"Catched error message (const char*): "<<cstr<<std::endl;
|
||||
return -1;
|
||||
}
|
||||
catch(const std::string& str){
|
||||
std::cerr<<"Catched error message (std::string): "<<str<<std::endl;
|
||||
return -1;
|
||||
}
|
||||
catch(...){
|
||||
std::cerr<<"Catched unknown error"<<std::endl;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#define SDL_MAIN_HANDLED
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include "Engine.hpp"
|
||||
#include "modules/MainWindowSDL2.hpp"
|
||||
#include "modules/MonoGameObjectSystem.hpp"
|
||||
#include "resources/textures.hpp"
|
||||
|
||||
using namespace ougge;
|
||||
|
||||
class TutelModule : public IEngineModule {
|
||||
resources::CacheStorage<resources::Texture> textures;
|
||||
public:
|
||||
TutelModule(Engine& engine, resources::ResourceManager& resourceManager) :
|
||||
IEngineModule(engine),
|
||||
textures(&resourceManager)
|
||||
{
|
||||
//TODO: add something like `assert(requireModule(MainWindow))`
|
||||
}
|
||||
|
||||
const std::string& getName() override {
|
||||
return ougge_type_name<TutelModule>();
|
||||
}
|
||||
|
||||
void beginFrame() override {
|
||||
auto& mainWindow = engine.getModule<modules::MainWindowSDL2>();
|
||||
resources::Texture* tutel = textures.tryGetOrCreate("tutel.png", mainWindow.sdl_renderer);
|
||||
if(tutel == nullptr){
|
||||
throw new UsefulException("couldn't find resource 'tutel.png'");
|
||||
}
|
||||
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);
|
||||
tutel->render(params);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
void createExampleObject(Engine& engine){
|
||||
std::cout<<"creating ExampleObject"<<std::endl;
|
||||
auto& monoSystem = engine.getModule<modules::MonoGameObjectSystem>();
|
||||
game::GameObject& exampleObj = monoSystem.createAndConstructGameObject();
|
||||
std::string componentClassName = "Ougge.ExampleComponent";
|
||||
if(!monoSystem.tryCreateComponent(exampleObj, componentClassName))
|
||||
throw UsefulException(ougge_format("couldn't create component '%s'", componentClassName.c_str()));
|
||||
}
|
||||
|
||||
int main(int argc, const char** argv){
|
||||
try {
|
||||
std::cout<<"Initializing Engine"<<std::endl;
|
||||
Engine engine;
|
||||
|
||||
std::cout<<"Initializing ResourceManager"<<std::endl;
|
||||
resources::ResourceManager resourceManager;
|
||||
|
||||
engine.createModule<modules::MainWindowSDL2>("ougge", resourceManager);
|
||||
engine.createModule<modules::MonoGameObjectSystem>(64*1024);
|
||||
createExampleObject(engine);
|
||||
engine.createModule<TutelModule>(resourceManager);
|
||||
|
||||
std::cout<<"main loop start"<<std::endl;
|
||||
engine.startLoop();
|
||||
std::cout<<"main loop stop"<<std::endl;
|
||||
}
|
||||
catch(const std::exception& e){
|
||||
std::cerr<<"Catched exception: "<<e.what()<<std::endl;
|
||||
return -1;
|
||||
}
|
||||
catch(const char* cstr){
|
||||
std::cerr<<"Catched error message (const char*): "<<cstr<<std::endl;
|
||||
return -1;
|
||||
}
|
||||
catch(const std::string& str){
|
||||
std::cerr<<"Catched error message (std::string): "<<str<<std::endl;
|
||||
return -1;
|
||||
}
|
||||
catch(...){
|
||||
std::cerr<<"Catched unknown error"<<std::endl;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,19 +1,23 @@
|
||||
#include <backends/imgui_impl_sdl2.h>
|
||||
#include <backends/imgui_impl_sdlrenderer2.h>
|
||||
#include <iostream>
|
||||
#include "MainWindow.hpp"
|
||||
#include "exceptions.hpp"
|
||||
#include "../format.hpp"
|
||||
#include "../Resources/fonts.hpp"
|
||||
#include "../Resources/textures.hpp"
|
||||
#include "../math.hpp"
|
||||
#include "MainWindowSDL2.hpp"
|
||||
#include "../gui/gui_exceptions.hpp"
|
||||
#include "../common/ougge_format.hpp"
|
||||
#include "../common/math.hpp"
|
||||
|
||||
namespace ougge::GUI {
|
||||
using namespace ougge::gui;
|
||||
|
||||
f32 MainWindow::getDPI(){
|
||||
int w=0, h=0;
|
||||
namespace ougge::modules {
|
||||
|
||||
const std::string& MainWindowSDL2::getName() {
|
||||
return ougge_type_name<MainWindowSDL2>();
|
||||
}
|
||||
|
||||
f32 MainWindowSDL2::getDPI(){
|
||||
i32 w=0, h=0;
|
||||
SDL_GetRendererOutputSize(sdl_renderer, &w, &h);
|
||||
int sim_w=0, sim_h=0;
|
||||
i32 sim_w=0, sim_h=0;
|
||||
SDL_GetWindowSize(sdl_window, &sim_w, &sim_h);
|
||||
f32 wdpi=(f32)w / sim_w;
|
||||
f32 hdpi=(f32)h / sim_h;
|
||||
@@ -21,13 +25,15 @@ f32 MainWindow::getDPI(){
|
||||
return dpi;
|
||||
}
|
||||
|
||||
void MainWindow::open(const char* window_title, UpdateFunc_t _update){
|
||||
update = _update;
|
||||
|
||||
MainWindowSDL2::MainWindowSDL2(Engine& engine,
|
||||
const std::string& window_title,
|
||||
resources::ResourceManager& resourceManager)
|
||||
: IEngineModule(engine)
|
||||
{
|
||||
SDL_TRY(SDL_Init(SDL_INIT_EVERYTHING));
|
||||
SDL_version v;
|
||||
SDL_GetVersion(&v);
|
||||
std::cout<<format("SDL version: %u.%u.%u\n", v.major, v.minor, v.patch);
|
||||
std::cout<<ougge_format("SDL version: %u.%u.%u\n", v.major, v.minor, v.patch);
|
||||
|
||||
// From 2.0.18: Enable native IME.
|
||||
#ifdef SDL_HINT_IME_SHOW_UI
|
||||
@@ -37,7 +43,7 @@ void MainWindow::open(const char* window_title, UpdateFunc_t _update){
|
||||
SDL_WindowFlags window_flags = (SDL_WindowFlags)(
|
||||
SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI
|
||||
);
|
||||
sdl_window = SDL_CreateWindow(window_title,
|
||||
sdl_window = SDL_CreateWindow(window_title.c_str(),
|
||||
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
||||
1280, 720, window_flags);
|
||||
if(sdl_window == nullptr)
|
||||
@@ -74,139 +80,15 @@ void MainWindow::open(const char* window_title, UpdateFunc_t _update){
|
||||
|
||||
// Setup Dear ImGui style
|
||||
ImGui::StyleColorsDark();
|
||||
f32 dpi = getDPI();
|
||||
io.FontDefault = Resources::ImFont_LoadFromResource(default_font, default_font_size, dpi);
|
||||
}
|
||||
|
||||
// Wait, poll and handle events (inputs, window resize, etc.)
|
||||
void MainWindow::poll_events(bool waitForEvent){
|
||||
SDL_Event event;
|
||||
if(waitForEvent){
|
||||
// waits for first event in cpu-efficient way
|
||||
SDL_TRY(SDL_WaitEvent(&event) != 1);
|
||||
}
|
||||
// dont wait for event
|
||||
else if(!SDL_PollEvent(&event))
|
||||
return;
|
||||
|
||||
do {
|
||||
ImGui_ImplSDL2_ProcessEvent(&event);
|
||||
switch(event.type){
|
||||
case SDL_QUIT: {
|
||||
close();
|
||||
break;
|
||||
}
|
||||
case SDL_WINDOWEVENT: {
|
||||
if(event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(sdl_window)){
|
||||
close();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} while (SDL_PollEvent(&event)); // if there are more events, handles them
|
||||
resources::ResourceFactory* font_res = resourceManager.tryGetResource(default_font_path);
|
||||
if(font_res == nullptr)
|
||||
throw UsefulException("can't find default font resource");
|
||||
f32 dpi = getDPI();
|
||||
io.FontDefault = resources::ImFont_LoadFromResource(font_res, default_font_size, dpi);
|
||||
}
|
||||
|
||||
void ImGui_drawErrorWindow(bool* draw_error_window, std::string& msg){
|
||||
ImGui::Begin("ERROR", draw_error_window);
|
||||
ImGui::Text("%s", msg.c_str());
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void MainWindow::draw_ui(){
|
||||
static std::string error_message;
|
||||
static bool draw_error_window = false;
|
||||
try {
|
||||
// Draw UI
|
||||
if(draw_error_window)
|
||||
ImGui_drawErrorWindow(&draw_error_window, error_message);
|
||||
else {
|
||||
draw_bg_window();
|
||||
draw_debug_window();
|
||||
}
|
||||
}
|
||||
catch(const std::exception& e){
|
||||
error_message = "Catched exception: " + std::string(e.what());
|
||||
draw_error_window = true;
|
||||
std::cerr<<error_message<<std::endl;
|
||||
}
|
||||
catch(const char* cstr){
|
||||
error_message = "Catched error message (const char*): " + std::string(cstr);
|
||||
draw_error_window = true;
|
||||
std::cerr<<error_message<<std::endl;
|
||||
}
|
||||
catch(const std::string& str){
|
||||
error_message = "Catched error message (std::string): " + str;
|
||||
draw_error_window = true;
|
||||
std::cerr<<error_message<<std::endl;
|
||||
}
|
||||
catch(...){
|
||||
error_message = "Catched unknown error";
|
||||
draw_error_window = true;
|
||||
std::cerr<<error_message<<std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::draw_frame(){
|
||||
// Start the Dear ImGui frame
|
||||
ImGui_ImplSDLRenderer2_NewFrame();
|
||||
ImGui_ImplSDL2_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
|
||||
draw_ui();
|
||||
|
||||
// Rendering
|
||||
ImGui::Render();
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
SDL_RenderSetScale(sdl_renderer, io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y);
|
||||
SDL_SetRenderDrawColor(sdl_renderer, (Uint8)(clear_color.x * 255), (Uint8)(clear_color.y * 255), (Uint8)(clear_color.z * 255), (Uint8)(clear_color.w * 255));
|
||||
SDL_RenderClear(sdl_renderer);
|
||||
|
||||
// Example sprite
|
||||
//Resources::Texture tutel(Resources::getResource("tutel.png"), sdl_renderer);
|
||||
static Resources::CacheStorage<Resources::Texture> textures;
|
||||
Resources::Texture& tutel = textures.getOrCreate("tutel.png", sdl_renderer);
|
||||
Resources::SDL_RenderCopyExF_Params rp;
|
||||
rp.target_section = SDL_FRectConstruct(100, 100, 400, 400);
|
||||
static int si = 1;
|
||||
rp.rotation_angle = M_PI_4 * (si++ / fps_max);
|
||||
tutel.render(rp);
|
||||
|
||||
|
||||
ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData(), sdl_renderer);
|
||||
// Swap buffers
|
||||
SDL_RenderPresent(sdl_renderer);
|
||||
}
|
||||
|
||||
void MainWindow::startUpdateLoop(){
|
||||
if(loop_running)
|
||||
throw UsefulException("loop is running already");
|
||||
|
||||
nsec_t prev_update_time_ns = getMonotonicTimeNsec();
|
||||
loop_running=true;
|
||||
// main loop
|
||||
while(loop_running){
|
||||
poll_events(false);
|
||||
|
||||
nsec_t update_time_ns = getMonotonicTimeNsec();
|
||||
if(update_time_ns < prev_update_time_ns)
|
||||
throw UsefulException("monotonic clock returned unexpected value");
|
||||
f64 delta_time_s = (f64)(update_time_ns - prev_update_time_ns) / 1e9;
|
||||
prev_update_time_ns = update_time_ns;
|
||||
|
||||
update(delta_time_s);
|
||||
draw_frame();
|
||||
|
||||
nsec_t after_update_time_ns = getMonotonicTimeNsec();
|
||||
nsec_t frame_delay_ns = (nsec_t)1e9 / fps_max - (after_update_time_ns - update_time_ns);
|
||||
if(frame_delay_ns > 0){
|
||||
SDL_Delay(frame_delay_ns / 1e6);
|
||||
}
|
||||
}
|
||||
|
||||
destroy();
|
||||
}
|
||||
|
||||
void MainWindow::destroy(){
|
||||
MainWindowSDL2::~MainWindowSDL2(){
|
||||
ImGui_ImplSDLRenderer2_Shutdown();
|
||||
ImGui_ImplSDL2_Shutdown();
|
||||
ImGui::DestroyContext();
|
||||
@@ -215,11 +97,60 @@ void MainWindow::destroy(){
|
||||
SDL_Quit();
|
||||
}
|
||||
|
||||
void MainWindow::close(){
|
||||
loop_running = false;
|
||||
void MainWindowSDL2::pollEvents(){
|
||||
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 =
|
||||
ImGuiDockNodeFlags_PassthruCentralNode;
|
||||
const ImGuiWindowFlags window_flags =
|
||||
@@ -283,15 +214,16 @@ void MainWindow::draw_bg_window(){
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void MainWindow::draw_debug_window(){
|
||||
void MainWindowSDL2::draw_debug_window(){
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
ImGui::Begin("Debug Options");
|
||||
if(ImGui::Begin("Debug Options", &show_debug_window)){
|
||||
ImGui::ColorEdit3("clear_color", (float*)&clear_color);
|
||||
ImGui::InputInt("fps_max", &fps_max);
|
||||
ImGui::InputInt("fps_max", (int*)&engine.fps_max);
|
||||
ImGui::Text("Application average %.3f ms/frame (%.2f FPS)", 1000.0f / io.Framerate, io.Framerate);
|
||||
ImGui::Checkbox("Demo Window", &show_demo_window);
|
||||
ImGui::Checkbox("Metrics/Debug Window", &show_metrics_window);
|
||||
ImGui::End();
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
if (show_demo_window)
|
||||
ImGui::ShowDemoWindow(&show_demo_window);
|
||||
@@ -300,4 +232,16 @@ void MainWindow::draw_debug_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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,50 +1,49 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL.h>
|
||||
#include <imgui.h>
|
||||
#include "../std.hpp"
|
||||
#include "../time.hpp"
|
||||
#include <functional>
|
||||
|
||||
/// converts hex color to float vector
|
||||
#define RGBAHexToF(R8,G8,B8,A8) ImVec4(((u8)35)/255.0f, ((u8)35)/255.0f, ((u8)50)/255.0f, ((u8)255)/255.0f)
|
||||
/// converts float vector to hex color
|
||||
#define RGBAFToHex(VEC4) {(u8)(VEC4.x*255), (u8)(VEC4.y*255), (u8)(VEC4.z*255), (u8)(VEC4.w*255)}
|
||||
|
||||
namespace ougge::GUI {
|
||||
|
||||
#define default_font "DroidSans"
|
||||
|
||||
using UpdateFunc_t = std::function<void(f64)>;
|
||||
|
||||
class MainWindow {
|
||||
public:
|
||||
ImVec4 clear_color = RGBAHexToF(35,35,50,255);
|
||||
f32 default_font_size = 14.0f;
|
||||
int fps_max = 60;
|
||||
// called on each frame
|
||||
UpdateFunc_t update = nullptr;
|
||||
|
||||
private:
|
||||
bool loop_running = false;
|
||||
bool show_demo_window = false;
|
||||
bool show_metrics_window = false;
|
||||
SDL_Window* sdl_window = nullptr;
|
||||
SDL_Renderer* sdl_renderer = nullptr;
|
||||
|
||||
public:
|
||||
void open(const char* window_title, UpdateFunc_t update);
|
||||
void startUpdateLoop();
|
||||
void close();
|
||||
|
||||
private:
|
||||
void destroy();
|
||||
f32 getDPI();
|
||||
void poll_events(bool waitForEvent);
|
||||
void draw_frame();
|
||||
void draw_ui();
|
||||
void draw_debug_window();
|
||||
void draw_bg_window();
|
||||
};
|
||||
|
||||
}
|
||||
#pragma once
|
||||
|
||||
#include <SDL.h>
|
||||
#include <imgui.h>
|
||||
#include <vector>
|
||||
#include "../common/std.hpp"
|
||||
#include "../resources/resources.hpp"
|
||||
#include "../resources/fonts.hpp"
|
||||
#include "../Engine.hpp"
|
||||
|
||||
namespace ougge::modules {
|
||||
|
||||
/// converts hex color to float vector
|
||||
#define RGBAHexToF(R8,G8,B8,A8) ImVec4(((u8)35)/255.0f, ((u8)35)/255.0f, ((u8)50)/255.0f, ((u8)255)/255.0f)
|
||||
/// converts float vector to hex color
|
||||
#define RGBAFToHex(VEC4) {(u8)(VEC4.x*255), (u8)(VEC4.y*255), (u8)(VEC4.z*255), (u8)(VEC4.w*255)}
|
||||
|
||||
#define default_font_path "fonts/DroidSans.ttf"
|
||||
|
||||
class MainWindowSDL2 : public IEngineModule {
|
||||
public:
|
||||
f32 default_font_size = 14.0f;
|
||||
ImVec4 clear_color = RGBAHexToF(35,35,50,255);
|
||||
SDL_Window* sdl_window = nullptr;
|
||||
SDL_Renderer* sdl_renderer = nullptr;
|
||||
|
||||
private:
|
||||
bool show_debug_window = true;
|
||||
bool show_demo_window = false;
|
||||
bool show_metrics_window = false;
|
||||
|
||||
public:
|
||||
MainWindowSDL2(Engine& engine, const std::string& window_title,
|
||||
resources::ResourceManager& resourceManager);
|
||||
~MainWindowSDL2();
|
||||
|
||||
const std::string& getName() override;
|
||||
void beginFrame() override;
|
||||
void endFrame() override;
|
||||
|
||||
f32 getDPI();
|
||||
private:
|
||||
void pollEvents();
|
||||
void draw_debug_window();
|
||||
void draw_bg_window();
|
||||
void drawErrorWindow();
|
||||
};
|
||||
|
||||
}
|
||||
76
src/modules/MonoGameObjectSystem.cpp
Normal file
76
src/modules/MonoGameObjectSystem.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
35
src/modules/MonoGameObjectSystem.hpp
Normal file
35
src/modules/MonoGameObjectSystem.hpp
Normal 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);
|
||||
};
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "Mono.hpp"
|
||||
#include "mono.hpp"
|
||||
|
||||
namespace Mono {
|
||||
|
||||
@@ -10,7 +10,7 @@ Assembly::Assembly(MonoAssembly *ptr)
|
||||
MonoClass* Assembly::getClass(const std::string &name_space, const std::string &name){
|
||||
auto c = mono_class_from_name(image, name_space.c_str(), name.c_str());
|
||||
if(!c)
|
||||
throw UsefulException(format("can't get class '%s.%s'", name_space.c_str(), name.c_str()));
|
||||
throw UsefulException(ougge_format("can't get class '%s.%s'", name_space.c_str(), name.c_str()));
|
||||
return c;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "Mono.hpp"
|
||||
#include "mono.hpp"
|
||||
#include <mono/jit/jit.h>
|
||||
#include <mono/metadata/mono-config.h>
|
||||
#include <mono/metadata/appdomain.h>
|
||||
|
||||
namespace Mono {
|
||||
|
||||
@@ -15,13 +16,14 @@ RuntimeJIT::RuntimeJIT(const std::string& domain_name){
|
||||
}
|
||||
|
||||
RuntimeJIT::~RuntimeJIT(){
|
||||
mono_jit_cleanup(domain);
|
||||
// TODO: fix segfault on cleanup
|
||||
// mono_jit_cleanup(domain);
|
||||
}
|
||||
|
||||
std::shared_ptr<Assembly> RuntimeJIT::loadAssembly(const std::string &name){
|
||||
MonoAssembly* ptr = mono_domain_assembly_open(domain, name.c_str());
|
||||
if(!ptr)
|
||||
throw UsefulException(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);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "Mono.hpp"
|
||||
#include "mono.hpp"
|
||||
#include <mono/metadata/appdomain.h>
|
||||
|
||||
namespace Mono {
|
||||
@@ -38,20 +38,28 @@ MonoMethod* tryGetMonoMethod(MonoClass* target_class, const std::string& name,
|
||||
{
|
||||
if(target_class == nullptr)
|
||||
throw UsefulException("target_class is nullptr");
|
||||
|
||||
// iterate each method
|
||||
void* iter = nullptr;
|
||||
MonoMethod* m = nullptr;
|
||||
MonoType* return_type = nullptr;
|
||||
std::vector<MonoType*> argument_types;
|
||||
while( (m = mono_class_get_methods(target_class, &iter)) ){
|
||||
// compare name
|
||||
std::string m_name = mono_method_get_name(m);
|
||||
if(m_name != name)
|
||||
continue;
|
||||
|
||||
argument_types.clear();
|
||||
MonoType* return_type = nullptr;
|
||||
getMethodSignatureTypes(m, &return_type, argument_types);
|
||||
// compare argument count
|
||||
if(argument_types.size() != arg_classes_size)
|
||||
continue;
|
||||
|
||||
// compare return type
|
||||
if(!mono_metadata_type_equal(return_type, mono_class_get_type(return_class)))
|
||||
continue;
|
||||
|
||||
// compare argument types
|
||||
bool argument_types_mismatch = false;
|
||||
for(size_t i = 0; i < arg_classes_size; i++){
|
||||
@@ -62,6 +70,7 @@ MonoMethod* tryGetMonoMethod(MonoClass* target_class, const std::string& name,
|
||||
}
|
||||
if(argument_types_mismatch)
|
||||
continue;
|
||||
|
||||
// passed all tests successfully
|
||||
break;
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "../std.hpp"
|
||||
#include "../UsefulException.hpp"
|
||||
#include "../format.hpp"
|
||||
#include "../common/std.hpp"
|
||||
#include "../common/UsefulException.hpp"
|
||||
#include "../common/ougge_format.hpp"
|
||||
#include <vector>
|
||||
#include <type_traits>
|
||||
#include <mono/metadata/metadata.h>
|
||||
#include <mono/metadata/class.h>
|
||||
#include <mono/metadata/assembly.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 char16_t Char;
|
||||
typedef MonoString* String;
|
||||
/// @warning MonoObject can be moved in memory by GC in any time and raw pointer will be invalid.
|
||||
/// Use ObjectHandle where it is possible.
|
||||
typedef MonoObject* Object;
|
||||
typedef void Void;
|
||||
|
||||
@@ -67,34 +69,51 @@ class Method<ReturnT(ArgTypes...)>
|
||||
MonoMethod* method_ptr;
|
||||
|
||||
public:
|
||||
|
||||
template<typename RT = ReturnT>
|
||||
std::enable_if_t<!std::is_void<RT>::value, RT> operator()(MonoObject* class_instance, ArgTypes... args) const {
|
||||
void* arg_array[] = { valueToVoidPtr(args)..., nullptr };
|
||||
MonoObject* ex = nullptr;
|
||||
MonoObject* result = mono_runtime_invoke(method_ptr, class_instance, arg_array, &ex);
|
||||
if(ex) //TODO: call mono_trace_set_printerr_handler from mono/mono/utils/mono-logger.h
|
||||
mono_print_unhandled_exception(ex);
|
||||
return valueFromMonoObject<ReturnT>(result);
|
||||
};
|
||||
template<typename RT = ReturnT>
|
||||
std::enable_if_t<std::is_void<RT>::value, RT> operator()(MonoObject* class_instance, ArgTypes... args) const {
|
||||
void* arg_array[] = { valueToVoidPtr(args)..., nullptr };
|
||||
MonoObject* ex = nullptr;
|
||||
mono_runtime_invoke(method_ptr, class_instance, arg_array, &ex);
|
||||
if(ex)
|
||||
mono_print_unhandled_exception(ex);
|
||||
};
|
||||
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(format("can't get method '%s' from class '%s'",
|
||||
if(method_ptr == nullptr){
|
||||
throw UsefulException(ougge_format("can't get method '%s' from class '%s'",
|
||||
name.c_str(), mono_class_get_name(target_class)));
|
||||
}
|
||||
}
|
||||
|
||||
// ReturnT not is void
|
||||
template<typename RT = ReturnT>
|
||||
std::enable_if_t<!std::is_void<RT>::value, RT> operator()(MonoObject* class_instance, ArgTypes... args) const {
|
||||
if(method_ptr == nullptr)
|
||||
throw UsefulException("method_ptr is null");
|
||||
|
||||
void* arg_array[] = { valueToVoidPtr(args)..., nullptr };
|
||||
MonoObject* ex = nullptr;
|
||||
MonoObject* result = mono_runtime_invoke(method_ptr, class_instance, arg_array, &ex);
|
||||
if(ex){
|
||||
//TODO: call mono_trace_set_printerr_handler from mono/mono/utils/mono-logger.h
|
||||
mono_print_unhandled_exception(ex);
|
||||
throw UsefulException("Some C# exception occured");
|
||||
}
|
||||
return valueFromMonoObject<ReturnT>(result);
|
||||
};
|
||||
|
||||
// ReturnT is void
|
||||
template<typename RT = ReturnT>
|
||||
std::enable_if_t<std::is_void<RT>::value, RT> operator()(MonoObject* class_instance, ArgTypes... args) const {
|
||||
if(method_ptr == nullptr)
|
||||
throw UsefulException("method_ptr is null");
|
||||
|
||||
void* arg_array[] = { valueToVoidPtr(args)..., nullptr };
|
||||
MonoObject* ex = nullptr;
|
||||
mono_runtime_invoke(method_ptr, class_instance, arg_array, &ex);
|
||||
if(ex){
|
||||
//TODO: call mono_trace_set_printerr_handler from mono/mono/utils/mono-logger.h
|
||||
mono_print_unhandled_exception(ex);
|
||||
throw UsefulException("Some C# exception occured");
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -107,6 +126,8 @@ public:
|
||||
Assembly(const Assembly&) = delete;
|
||||
|
||||
MonoClass* getClass(const std::string& name_space, const std::string& name);
|
||||
MonoAssembly* getMonoAssembly() const { return ptr; }
|
||||
MonoImage* getMonoImage() const { return image; }
|
||||
};
|
||||
|
||||
|
||||
@@ -116,33 +137,51 @@ public:
|
||||
class RuntimeJIT {
|
||||
MonoDomain* domain;
|
||||
public:
|
||||
RuntimeJIT(const std::string& domain_name = "MonoApp");
|
||||
RuntimeJIT(const std::string& domain_name = "OuggeDomain");
|
||||
RuntimeJIT(const RuntimeJIT&) = delete;
|
||||
~RuntimeJIT();
|
||||
|
||||
inline MonoDomain* getDomain() { return domain; }
|
||||
std::shared_ptr<Assembly> loadAssembly(const std::string& name);
|
||||
|
||||
inline Object createObject(MonoClass* klass) { return mono_object_new(domain, klass); }
|
||||
inline String createString(const std::string& v) { return mono_string_new_len(domain, v.c_str(), v.size()); }
|
||||
inline String createString(const char* v) { return mono_string_new(domain, v); }
|
||||
};
|
||||
|
||||
class ObjectHandle {
|
||||
Object object;
|
||||
/// @brief ObjectHandle can be used to store reliable reference to MonoObject.
|
||||
/// MonoObject can be moved in memory by GC in any time and raw pointer will be invalid.
|
||||
struct ObjectHandle {
|
||||
u32 gc_handle;
|
||||
public:
|
||||
inline ObjectHandle(Object obj) : object(obj) { gc_handle = mono_gchandle_new(obj, false); }
|
||||
|
||||
inline ObjectHandle() : gc_handle(0) {}
|
||||
|
||||
inline ObjectHandle(Object obj) {
|
||||
gc_handle = mono_gchandle_new(obj, false);
|
||||
}
|
||||
|
||||
/// implicitly create new ObjectHandle instead
|
||||
inline ObjectHandle(const ObjectHandle& o) = delete;
|
||||
|
||||
inline ObjectHandle(ObjectHandle&& o) {
|
||||
object = o.object;
|
||||
gc_handle = o.gc_handle;
|
||||
o.gc_handle = 0;
|
||||
};
|
||||
}
|
||||
|
||||
inline ObjectHandle& operator=(ObjectHandle&& o) {
|
||||
object = o.object;
|
||||
gc_handle = o.gc_handle;
|
||||
o.gc_handle = 0;
|
||||
return *this;
|
||||
};
|
||||
inline ~ObjectHandle() { if(gc_handle) mono_gchandle_free(gc_handle); }
|
||||
inline Object getObject() { return object; }
|
||||
}
|
||||
|
||||
inline ~ObjectHandle() {
|
||||
if(gc_handle)
|
||||
mono_gchandle_free(gc_handle);
|
||||
}
|
||||
|
||||
inline Object getObject() const {
|
||||
return mono_gchandle_get_target(gc_handle);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
40
src/resources/MemorySteam.cpp
Normal file
40
src/resources/MemorySteam.cpp
Normal 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();
|
||||
}
|
||||
8
src/resources/MemoryStream.hpp
Normal file
8
src/resources/MemoryStream.hpp
Normal file
@@ -0,0 +1,8 @@
|
||||
#include <iostream>
|
||||
|
||||
class MemoryStreamRead : public std::istream {
|
||||
public:
|
||||
MemoryStreamRead(const void* p, const std::size_t n);
|
||||
|
||||
virtual ~MemoryStreamRead();
|
||||
};
|
||||
@@ -1,9 +1,6 @@
|
||||
#include <imgui.h>
|
||||
#include "Resources.hpp"
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include "fonts.hpp"
|
||||
|
||||
namespace ougge::Resources {
|
||||
namespace ougge::resources {
|
||||
|
||||
// select all glyphs from font
|
||||
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);
|
||||
}
|
||||
|
||||
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();
|
||||
font_size *= dpi;
|
||||
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];
|
||||
res.openStream()->read(font_data, res.size);
|
||||
char* font_data = new char[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);
|
||||
}
|
||||
|
||||
13
src/resources/fonts.hpp
Normal file
13
src/resources/fonts.hpp
Normal 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);
|
||||
|
||||
}
|
||||
64
src/resources/resources.cpp
Normal file
64
src/resources/resources.cpp
Normal 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;
|
||||
}
|
||||
|
||||
}
|
||||
75
src/resources/resources.hpp
Normal file
75
src/resources/resources.hpp
Normal 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)
|
||||
{}
|
||||
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
#include "textures.hpp"
|
||||
#include <SDL_image.h>
|
||||
#include "../GUI/exceptions.hpp"
|
||||
#include "../gui/gui_exceptions.hpp"
|
||||
|
||||
namespace ougge::Resources {
|
||||
namespace ougge::resources {
|
||||
|
||||
Texture::Texture(const Resource& r, SDL_Renderer* renderer)
|
||||
: Texture(*r.openStream(), r.size, renderer)
|
||||
Texture::Texture(ResourceFactory* r, SDL_Renderer* renderer)
|
||||
: Texture(*r->openStream(), r->size, 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);
|
||||
if(!sdl_stream)
|
||||
throw SDLException();
|
||||
throw gui::SDLException();
|
||||
texture = std::shared_ptr<SDL_Texture>(IMG_LoadTexture_RW(renderer, sdl_stream, 1), SDL_DestroyTexture);
|
||||
if(!texture)
|
||||
throw IMGException();
|
||||
throw gui::IMGException();
|
||||
SDL_TRY(SDL_QueryTexture(texture.get(), nullptr, nullptr, &w, &h));
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ static Sint64 istream_size(SDL_RWops* context){
|
||||
return (Sint64)(size_t)context->hidden.unknown.data2;
|
||||
}
|
||||
|
||||
static Sint64 istream_seek(SDL_RWops* context, Sint64 offset, int whence){
|
||||
static Sint64 istream_seek(SDL_RWops* context, Sint64 offset, i32 whence){
|
||||
std::istream* stream = (std::istream*)context->hidden.unknown.data1;
|
||||
switch(whence){
|
||||
case SEEK_SET: stream->seekg(offset, std::ios::beg); break;
|
||||
@@ -72,7 +72,7 @@ static size_t istream_read(SDL_RWops* context, void *ptr, size_t size, size_t ma
|
||||
return stream->bad() ? -1 : stream->gcount() / size;
|
||||
}
|
||||
|
||||
static int istream_close(SDL_RWops* context){
|
||||
static i32 istream_close(SDL_RWops* context){
|
||||
if (context)
|
||||
SDL_FreeRW(context);
|
||||
return 0;
|
||||
@@ -3,10 +3,10 @@
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <SDL.h>
|
||||
#include "Resources.hpp"
|
||||
#include "../math.hpp"
|
||||
#include "resources.hpp"
|
||||
#include "../common/math.hpp"
|
||||
|
||||
namespace ougge::Resources {
|
||||
namespace ougge::resources {
|
||||
|
||||
#define SDL_RectConstruct(X, Y, W, H) (SDL_Rect){X, Y, W, H}
|
||||
#define SDL_FRectConstruct(X, Y, W, H) (SDL_FRect){X, Y, W, H}
|
||||
@@ -30,10 +30,10 @@ struct SDL_RenderCopyExF_Params {
|
||||
struct Texture {
|
||||
SDL_Renderer* renderer;
|
||||
std::shared_ptr<SDL_Texture> texture;
|
||||
int w;
|
||||
int h;
|
||||
i32 w;
|
||||
i32 h;
|
||||
|
||||
Texture(const Resource& r, SDL_Renderer* renderer);
|
||||
Texture(ResourceFactory* r, SDL_Renderer* renderer);
|
||||
Texture(std::istream& s, size_t size, SDL_Renderer* renderer);
|
||||
|
||||
void render(const SDL_FRect& target_section);
|
||||
10
src/time.hpp
10
src/time.hpp
@@ -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();
|
||||
69
tasks/download_mono_from_nuget.sh
Normal file
69
tasks/download_mono_from_nuget.sh
Normal 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"
|
||||
@@ -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!"
|
||||
Reference in New Issue
Block a user