Compare commits
35 Commits
660fb5247c
...
0.0.1
| Author | SHA1 | Date | |
|---|---|---|---|
| 1940d01d9b | |||
| 5c6fe5944a | |||
| d659dcde10 | |||
| bb00392e3a | |||
| 3477b05cd8 | |||
| 366dd1214c | |||
| 51259e72fe | |||
| 5c247ce032 | |||
| 5d84e744ce | |||
| 0863408bc5 | |||
| 0852a0028f | |||
| bfa9bf592a | |||
| 25475e8013 | |||
| d4de309ca7 | |||
| d5d28d4884 | |||
| 04e4f63fd7 | |||
| 47574e9b30 | |||
| 3df4361779 | |||
| bff2182ff0 | |||
| ec7a8de0cf | |||
| e71a6b71fe | |||
| 71f77ce89c | |||
| 42b233e6a4 | |||
| 9eef21df1b | |||
|
|
71eea1c0fe | ||
|
|
478e6049c9 | ||
| d8e6c69c20 | |||
| 1859b432df | |||
| 2710e5fc9d | |||
| 0aefa70fb8 | |||
| b8f041584f | |||
| a99f58e475 | |||
| 87296180ee | |||
| 7471c09452 | |||
| 22c64c9e9b |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -1,7 +1,6 @@
|
|||||||
# build results
|
# build results
|
||||||
bin/
|
bin/
|
||||||
obj/
|
obj/
|
||||||
src/generated/
|
|
||||||
imgui.ini
|
imgui.ini
|
||||||
|
|
||||||
# IDE files
|
# IDE files
|
||||||
@@ -21,3 +20,4 @@ temp/
|
|||||||
logs/
|
logs/
|
||||||
log/
|
log/
|
||||||
*.log
|
*.log
|
||||||
|
/mono/
|
||||||
|
|||||||
2
.gitmodules
vendored
2
.gitmodules
vendored
@@ -4,4 +4,4 @@
|
|||||||
branch = docking
|
branch = docking
|
||||||
[submodule "dependencies/resource_embedder"]
|
[submodule "dependencies/resource_embedder"]
|
||||||
path = 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
|
||||||
|
|||||||
4
.vscode/c_cpp_properties.json
vendored
4
.vscode/c_cpp_properties.json
vendored
@@ -7,10 +7,12 @@
|
|||||||
"DEBUG=1"
|
"DEBUG=1"
|
||||||
],
|
],
|
||||||
"includePath": [
|
"includePath": [
|
||||||
|
"dependencies/include",
|
||||||
"dependencies/include/SDL2",
|
"dependencies/include/SDL2",
|
||||||
"dependencies/imgui",
|
"dependencies/imgui",
|
||||||
"${default}"
|
"${default}"
|
||||||
]
|
],
|
||||||
|
"cppStandard": "c++20"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"version": 4
|
"version": 4
|
||||||
|
|||||||
1
.vscode/launch.json
vendored
1
.vscode/launch.json
vendored
@@ -11,6 +11,7 @@
|
|||||||
"stopAtEntry": false,
|
"stopAtEntry": false,
|
||||||
"cwd": "${workspaceFolder}/bin",
|
"cwd": "${workspaceFolder}/bin",
|
||||||
"externalConsole": false,
|
"externalConsole": false,
|
||||||
|
"internalConsoleOptions": "neverOpen",
|
||||||
"MIMode": "gdb",
|
"MIMode": "gdb",
|
||||||
"miDebuggerPath": "gdb",
|
"miDebuggerPath": "gdb",
|
||||||
"setupCommands": [
|
"setupCommands": [
|
||||||
|
|||||||
2
.vscode/tasks.json
vendored
2
.vscode/tasks.json
vendored
@@ -10,7 +10,7 @@
|
|||||||
"command": "bash",
|
"command": "bash",
|
||||||
"args": [
|
"args": [
|
||||||
"-c",
|
"-c",
|
||||||
"cbuild build_exec_dbg"
|
"cbuild rebuild_dependencies=src-csharp build_exec_dbg"
|
||||||
],
|
],
|
||||||
"options": {
|
"options": {
|
||||||
"cwd": "${workspaceFolder}"
|
"cwd": "${workspaceFolder}"
|
||||||
|
|||||||
43
README.md
43
README.md
@@ -1,38 +1,31 @@
|
|||||||
# ougge
|
# OUGGE
|
||||||
A game engine or something, idk.
|
A game engine or something, idk.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
1. Clone the repository
|
1. **Clone the repository.**
|
||||||
```sh
|
```sh
|
||||||
git clone --recurse-submodules https://timerix.ddns.net:3322/Timerix/ougge.git
|
git clone --recurse-submodules https://timerix.ddns.net/git/Timerix/ougge.git
|
||||||
```
|
```
|
||||||
2. Install [cbuild](https://timerix.ddns.net:3322/Timerix/cbuild.git)
|
2. **Install [cbuild](https://timerix.ddns.net/git/Timerix/cbuild).**
|
||||||
3. Install **SDL2** from package manager or compile it from source.
|
3. **Install [SDL2](https://github.com/libsdl-org/SDL) and [SDL2_image](https://github.com/libsdl-org/SDL_image).**
|
||||||
**If you are using msys, switch to mingw64 sh.**
|
- 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`.
|
||||||
```sh
|
```sh
|
||||||
git clone https://github.com/libsdl-org/SDL.git
|
|
||||||
cd SDL
|
|
||||||
./configure
|
|
||||||
make -j [number of cpu threads]
|
|
||||||
```
|
|
||||||
Then you can install it systemwide (on **Linux**):
|
|
||||||
```sh
|
|
||||||
sudo make install
|
|
||||||
```
|
|
||||||
or copy to ./dependencies/precompiled/ (on **Windows**):
|
|
||||||
```sh
|
|
||||||
mkdir -p ../ougge/dependencies/precompiled/
|
|
||||||
cp ./build/.libs/SDL2.dll ../ougge/dependencies/precompiled/
|
|
||||||
```
|
|
||||||
If it doesn't work, read [SDL/INSTALL.txt](https://github.com/libsdl-org/SDL/blob/SDL2/INSTALL.txt) and [SDL/docs/README.md](https://github.com/libsdl-org/SDL/blob/SDL2/docs/README.md).
|
|
||||||
4. Symlink SDL headers directory to `dependencies/include`
|
|
||||||
```sh
|
|
||||||
cd ../ougge
|
|
||||||
ln -s SDL2_HEADERS_DIRECTORY_ABSOLUTE_PATH -T dependencies/include/SDL2
|
ln -s SDL2_HEADERS_DIRECTORY_ABSOLUTE_PATH -T dependencies/include/SDL2
|
||||||
```
|
```
|
||||||
Location of the headers can be found by `pkg-config --cflags --libs sdl2`.
|
Location of the headers can be found by `pkg-config --cflags --libs sdl2`.
|
||||||
Mingw installs SDL2 headers to `/mingw64/include/SDL2`.
|
Mingw installs SDL2 headers to `/mingw64/include/SDL2`.
|
||||||
5. Compile the program
|
5. **Download mono runtime** ([source is here](https://github.com/dotnet/runtime/tree/main/src/mono)).
|
||||||
|
- default version
|
||||||
|
```sh
|
||||||
|
cbuild download_mono_from_nuget
|
||||||
|
```
|
||||||
|
- or some specific version
|
||||||
|
```sh
|
||||||
|
cbuild download_mono_from_nuget=x.y.z
|
||||||
|
```
|
||||||
|
7. **Compile the program**
|
||||||
```sh
|
```sh
|
||||||
cbuild build_exec_dbg
|
cbuild build_exec_dbg
|
||||||
```
|
```
|
||||||
|
|||||||
16
dependencies/compile_resources.sh
vendored
Executable file
16
dependencies/compile_resources.sh
vendored
Executable file
@@ -0,0 +1,16 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -eo pipefail
|
||||||
|
echo 'compiling resources...'
|
||||||
|
inputs=""
|
||||||
|
for f in $(find ../../embedded_resources -type f); do
|
||||||
|
inputs+="-i $f "
|
||||||
|
done
|
||||||
|
mkdir -p ../obj
|
||||||
|
mkdir -p ../bin
|
||||||
|
set -x
|
||||||
|
./resource_embedder -o '../obj/embedded_resources.c' -d '../../embedded_resources' $inputs
|
||||||
|
gcc -Wall -Wextra -O2 -DEMBEDDED_RESOURCE_DEFINITION -c ../obj/embedded_resources.c -o ../bin/resources.o
|
||||||
|
set +x
|
||||||
|
# header without definitions
|
||||||
|
./resource_embedder -o '../../src/Resources/embedded_resources.h'
|
||||||
|
echo "embedded resources header was created at src/Resources/embedded_resources.h"
|
||||||
2
dependencies/imgui
vendored
2
dependencies/imgui
vendored
Submodule dependencies/imgui updated: 271910e349...7b6314f47d
0
dependencies/imgui.config
vendored
Normal file → Executable file
0
dependencies/imgui.config
vendored
Normal file → Executable file
2
dependencies/imgui.project.config
vendored
Normal file → Executable file
2
dependencies/imgui.project.config
vendored
Normal file → Executable file
@@ -16,7 +16,7 @@ SRC_CPP="imgui.cpp
|
|||||||
imgui_tables.cpp
|
imgui_tables.cpp
|
||||||
imgui_widgets.cpp
|
imgui_widgets.cpp
|
||||||
backends/imgui_impl_sdl2.cpp
|
backends/imgui_impl_sdl2.cpp
|
||||||
backends/imgui_impl_opengl3.cpp"
|
backends/imgui_impl_sdlrenderer2.cpp"
|
||||||
|
|
||||||
# Directory with dependency configs.
|
# Directory with dependency configs.
|
||||||
# See cbuild/example_dependency_configs
|
# See cbuild/example_dependency_configs
|
||||||
|
|||||||
19
dependencies/precompiled.config
vendored
Normal file → Executable file
19
dependencies/precompiled.config
vendored
Normal file → Executable file
@@ -1,19 +1,28 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
mkdir -p 'dependencies/precompiled'
|
DEP_WORKING_DIR="dependencies/precompiled/$OS-$ARCH"
|
||||||
DEP_WORKING_DIR='dependencies/precompiled'
|
mkdir -p "dependencies/precompiled"
|
||||||
|
mkdir -p "$DEP_WORKING_DIR"
|
||||||
DEP_PRE_BUILD_COMMAND=''
|
DEP_PRE_BUILD_COMMAND=''
|
||||||
DEP_BUILD_COMMAND=''
|
DEP_BUILD_COMMAND=''
|
||||||
DEP_POST_BUILD_COMMAND=''
|
DEP_POST_BUILD_COMMAND=''
|
||||||
DEP_CLEAN_COMMAND=''
|
DEP_CLEAN_COMMAND=''
|
||||||
|
|
||||||
# won't be copied to project $OUTDIR
|
# won't be copied to project $OUTDIR
|
||||||
DEP_STATIC_OUT_FILES=$(find dependencies/precompiled -name '*.a' | sed 's,dependencies/precompiled/,,')
|
DEP_STATIC_OUT_FILES=$(find "$DEP_WORKING_DIR" -name '*.a' | sed "s,$DEP_WORKING_DIR/,,")
|
||||||
|
|
||||||
|
mkdir -p "$DEP_WORKING_DIR/mono-libs"
|
||||||
|
mono_libs=$(find "$DEP_WORKING_DIR/mono-libs" -type f | sed "s,$DEP_WORKING_DIR/,,")
|
||||||
|
|
||||||
# will be copied tp project $OUTDIR
|
# will be copied tp project $OUTDIR
|
||||||
|
PRESERVE_OUT_DIRECTORY_STRUCTURE=true
|
||||||
case $OS in
|
case $OS in
|
||||||
WINDOWS)
|
WINDOWS)
|
||||||
DEP_DYNAMIC_OUT_FILES=$(find dependencies/precompiled -name '*.dll' | sed 's,dependencies/precompiled/,,')
|
DEP_DYNAMIC_OUT_FILES=$(find "$DEP_WORKING_DIR" -maxdepth 1 -name '*.dll' | sed "s,$DEP_WORKING_DIR/,,")
|
||||||
|
DEP_OTHER_OUT_FILES="$mono_libs"
|
||||||
;;
|
;;
|
||||||
LINUX)
|
LINUX)
|
||||||
DEP_DYNAMIC_OUT_FILES=$(find dependencies/precompiled -name '*.so' | sed 's,dependencies/precompiled/,,')
|
DEP_DYNAMIC_OUT_FILES=$(find "$DEP_WORKING_DIR" -name '*.so' | sed "s,$DEP_WORKING_DIR/,,")
|
||||||
|
DEP_OTHER_OUT_FILES="$mono_libs"
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
error "operating system $OS has no configuration variants"
|
error "operating system $OS has no configuration variants"
|
||||||
|
|||||||
2
dependencies/resource_embedder
vendored
2
dependencies/resource_embedder
vendored
Submodule dependencies/resource_embedder updated: 9cc2fd7814...e062aba71a
0
dependencies/resources.config
vendored
Normal file → Executable file
0
dependencies/resources.config
vendored
Normal file → Executable file
9
dependencies/src-csharp.config
vendored
Executable file
9
dependencies/src-csharp.config
vendored
Executable file
@@ -0,0 +1,9 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
DEP_WORKING_DIR='src-csharp'
|
||||||
|
CS_CONFIGURATION='Release'
|
||||||
|
if [[ "$TASK" = *_dbg ]]; then
|
||||||
|
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'
|
||||||
BIN
embedded_resources/fonts/DroidSans.ttf
Normal file
BIN
embedded_resources/fonts/DroidSans.ttf
Normal file
Binary file not shown.
BIN
embedded_resources/tutel.png
Normal file
BIN
embedded_resources/tutel.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 37 KiB |
19
project.config
Normal file → Executable file
19
project.config
Normal file → Executable file
@@ -1,12 +1,11 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
CBUILD_VERSION=2.1.2
|
CBUILD_VERSION=2.2.1
|
||||||
CONFIG_VERSION=1
|
|
||||||
|
|
||||||
PROJECT="ougge"
|
PROJECT="ougge"
|
||||||
CMP_C="gcc"
|
CMP_C="gcc"
|
||||||
CMP_CPP="g++"
|
CMP_CPP="g++"
|
||||||
STD_C="c11"
|
STD_C="c11"
|
||||||
STD_CPP="c++17"
|
STD_CPP="c++20"
|
||||||
WARN_C="-Wall -Wno-discarded-qualifiers -Wextra -Wno-unused-parameter"
|
WARN_C="-Wall -Wno-discarded-qualifiers -Wextra -Wno-unused-parameter"
|
||||||
WARN_CPP="-Wall -Wextra -Wno-unused-parameter"
|
WARN_CPP="-Wall -Wextra -Wno-unused-parameter"
|
||||||
SRC_C="$(find src -name '*.c')"
|
SRC_C="$(find src -name '*.c')"
|
||||||
@@ -16,7 +15,7 @@ SRC_CPP="$(find src -name '*.cpp')"
|
|||||||
# See cbuild/example_dependency_configs
|
# See cbuild/example_dependency_configs
|
||||||
DEPENDENCY_CONFIGS_DIR='dependencies'
|
DEPENDENCY_CONFIGS_DIR='dependencies'
|
||||||
# List of dependency config files in DEPENDENCY_CONFIGS_DIR separated by space.
|
# List of dependency config files in DEPENDENCY_CONFIGS_DIR separated by space.
|
||||||
ENABLED_DEPENDENCIES='imgui precompiled'
|
ENABLED_DEPENDENCIES='precompiled resources imgui src-csharp'
|
||||||
|
|
||||||
# OBJDIR structure:
|
# OBJDIR structure:
|
||||||
# ├── objects/ - Compiled object files. Cleans on each call of build task
|
# ├── objects/ - Compiled object files. Cleans on each call of build task
|
||||||
@@ -28,19 +27,19 @@ OUTDIR="bin"
|
|||||||
STATIC_LIB_FILE="lib$PROJECT.a"
|
STATIC_LIB_FILE="lib$PROJECT.a"
|
||||||
|
|
||||||
# header include directories
|
# header include directories
|
||||||
INCLUDE="-I./dependencies/imgui -I./dependencies/include/SDL2"
|
INCLUDE="-I./dependencies/imgui -I./dependencies/include -I./dependencies/include/SDL2"
|
||||||
|
|
||||||
# OS-specific options
|
# OS-specific options
|
||||||
case "$OS" in
|
case "$OS" in
|
||||||
WINDOWS)
|
WINDOWS)
|
||||||
EXEC_FILE="$PROJECT.exe"
|
EXEC_FILE="$PROJECT.exe"
|
||||||
SHARED_LIB_FILE="$PROJECT.dll"
|
SHARED_LIB_FILE="$PROJECT.dll"
|
||||||
LINKER_LIBS="-lopengl32 -lpthread"
|
LINKER_LIBS="-static -lstdc++ -lpthread"
|
||||||
;;
|
;;
|
||||||
LINUX)
|
LINUX)
|
||||||
EXEC_FILE="$PROJECT"
|
EXEC_FILE="$PROJECT"
|
||||||
SHARED_LIB_FILE="$PROJECT.so"
|
SHARED_LIB_FILE="$PROJECT.so"
|
||||||
LINKER_LIBS="-lSDL2 -lGL"
|
LINKER_LIBS="-lSDL2 -lSDL2_image"
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
error "operating system $OS has no configuration variants"
|
error "operating system $OS has no configuration variants"
|
||||||
@@ -114,7 +113,7 @@ case "$TASK" in
|
|||||||
;;
|
;;
|
||||||
# executes $EXEC_FILE with valgrind memory checker
|
# executes $EXEC_FILE with valgrind memory checker
|
||||||
valgrind)
|
valgrind)
|
||||||
VALGRIND_ARGS="-s --read-var-info=yes --track-origins=yes --fullpath-after=$(pwd) --leak-check=full --show-leak-kinds=all"
|
VALGRIND_ARGS="-s --read-var-info=yes --track-origins=yes --fullpath-after=$(pwd)/ --leak-check=full --show-leak-kinds=all"
|
||||||
TASK_SCRIPT=cbuild/default_tasks/valgrind.sh
|
TASK_SCRIPT=cbuild/default_tasks/valgrind.sh
|
||||||
;;
|
;;
|
||||||
# generates profiling info
|
# generates profiling info
|
||||||
@@ -176,10 +175,12 @@ case "$TASK" in
|
|||||||
rebuild_dependencies)
|
rebuild_dependencies)
|
||||||
TASK_SCRIPT=cbuild/default_tasks/rebuild_dependencies.sh
|
TASK_SCRIPT=cbuild/default_tasks/rebuild_dependencies.sh
|
||||||
;;
|
;;
|
||||||
|
download_mono_from_nuget)
|
||||||
|
TASK_SCRIPT=tasks/download_mono_from_nuget.sh
|
||||||
|
;;
|
||||||
# deletes generated files
|
# deletes generated files
|
||||||
clean)
|
clean)
|
||||||
TASK_SCRIPT=cbuild/default_tasks/clean.sh
|
TASK_SCRIPT=cbuild/default_tasks/clean.sh
|
||||||
POST_TASK_SCRIPT=tasks/clean_additions.sh
|
|
||||||
;;
|
;;
|
||||||
# nothing to do
|
# nothing to do
|
||||||
"" | no_task)
|
"" | no_task)
|
||||||
|
|||||||
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) {}
|
||||||
|
}
|
||||||
15
src-csharp/ExampleComponent.cs
Normal file
15
src-csharp/ExampleComponent.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
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}");
|
||||||
|
}
|
||||||
|
}
|
||||||
85
src-csharp/GameObject.cs
Normal file
85
src-csharp/GameObject.cs
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ougge;
|
||||||
|
|
||||||
|
[StructLayout(LayoutKind.Sequential)]
|
||||||
|
public struct Transform
|
||||||
|
{
|
||||||
|
Vector2 scale;
|
||||||
|
Vector2 position;
|
||||||
|
float rotation;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class GameObject
|
||||||
|
{
|
||||||
|
// 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()
|
||||||
|
{
|
||||||
|
NativeMethods.createGameObject(out ulong id, out uint index);
|
||||||
|
var o = new GameObject(id, index);
|
||||||
|
return o;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Destroy()
|
||||||
|
{
|
||||||
|
if(_isDestroyed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_isDestroyed = NativeMethods.destroyGameObject(_index);
|
||||||
|
if(!_isDestroyed)
|
||||||
|
throw new Exception($"Can't destroy GameObject({_id})");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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);
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
foreach(var p in Components)
|
||||||
|
{
|
||||||
|
p.Value.Update(deltaTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
13
src-csharp/NativeMethods.cs
Normal file
13
src-csharp/NativeMethods.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
|
namespace Ougge;
|
||||||
|
|
||||||
|
internal static class NativeMethods
|
||||||
|
{
|
||||||
|
[DllImport("__Internal")]
|
||||||
|
internal extern static bool destroyGameObject(uint index);
|
||||||
|
|
||||||
|
[DllImport("__Internal")]
|
||||||
|
internal extern static void createGameObject(out ulong id, out uint index);
|
||||||
|
}
|
||||||
10
src-csharp/Ougge.csproj
Normal file
10
src-csharp/Ougge.csproj
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
<RootNamespace>Ougge</RootNamespace>
|
||||||
|
<ImplicitUsings>disable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<DebugType>embedded</DebugType>
|
||||||
|
</PropertyGroup>
|
||||||
|
</Project>
|
||||||
22
src-csharp/src-csharp.sln
Normal file
22
src-csharp/src-csharp.sln
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
|
||||||
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
|
# Visual Studio Version 17
|
||||||
|
VisualStudioVersion = 17.0.31903.59
|
||||||
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Ougge", "Ougge.csproj", "{6BB6FC40-E2E4-4CFA-8A46-B3D391A5ADC7}"
|
||||||
|
EndProject
|
||||||
|
Global
|
||||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
Release|Any CPU = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
|
HideSolutionNode = FALSE
|
||||||
|
EndGlobalSection
|
||||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||||
|
{6BB6FC40-E2E4-4CFA-8A46-B3D391A5ADC7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{6BB6FC40-E2E4-4CFA-8A46-B3D391A5ADC7}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{6BB6FC40-E2E4-4CFA-8A46-B3D391A5ADC7}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{6BB6FC40-E2E4-4CFA-8A46-B3D391A5ADC7}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
EndGlobalSection
|
||||||
|
EndGlobal
|
||||||
114
src/Engine.cpp
Normal file
114
src/Engine.cpp
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
#include "Engine.hpp"
|
||||||
|
#include "gui/gui.hpp"
|
||||||
|
|
||||||
|
namespace ougge {
|
||||||
|
|
||||||
|
Engine::Engine()
|
||||||
|
: gameObjectPool(GAMEOBJECTPOOL_SIZE)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void Engine::init(){
|
||||||
|
engineManagedAssembly = mono.loadAssembly("Ougge.dll");
|
||||||
|
gameObjectClass = engineManagedAssembly->getClass("Ougge", "GameObject");
|
||||||
|
gameObjectCtor = Mono::Method<void(u64, u32)>(gameObjectClass, ".ctor");
|
||||||
|
gameObjectInvokeUpdate = Mono::Method<void(f64)>(gameObjectClass, "InvokeUpdate");
|
||||||
|
gameObjectTryCreateComponent = Mono::Method<Mono::Bool(Mono::String)>(gameObjectClass, "TryCreateComponent_internal");
|
||||||
|
}
|
||||||
|
|
||||||
|
void Engine::openMainWindow(const std::string& window_title){
|
||||||
|
mainWindow.open(window_title);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Engine::startLoop(){
|
||||||
|
if(loop_running)
|
||||||
|
throw UsefulException("loop is running already");
|
||||||
|
|
||||||
|
nsec_t prev_update_time_ns = getMonotonicTimeNsec();
|
||||||
|
loop_running=true;
|
||||||
|
// main loop
|
||||||
|
while(loop_running){
|
||||||
|
mainWindow.pollEvents(&loop_running);
|
||||||
|
if(!loop_running)
|
||||||
|
break;
|
||||||
|
|
||||||
|
nsec_t update_time_ns = getMonotonicTimeNsec();
|
||||||
|
if(update_time_ns < prev_update_time_ns)
|
||||||
|
throw UsefulException("monotonic clock returned unexpected value");
|
||||||
|
f64 delta_time_s = (f64)(update_time_ns - prev_update_time_ns) / 1e9;
|
||||||
|
prev_update_time_ns = update_time_ns;
|
||||||
|
|
||||||
|
tryDrawFrame(delta_time_s);
|
||||||
|
|
||||||
|
nsec_t after_update_time_ns = getMonotonicTimeNsec();
|
||||||
|
nsec_t frame_delay_ns = (nsec_t)1e9 / mainWindow.fps_max - (after_update_time_ns - update_time_ns);
|
||||||
|
if(frame_delay_ns > 0){
|
||||||
|
SDL_Delay(frame_delay_ns / 1e6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mainWindow.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Engine::stopLoop(){
|
||||||
|
loop_running = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Engine::tryDrawFrame(f64 deltaTime){
|
||||||
|
static std::string error_message;
|
||||||
|
static bool draw_error_window = false;
|
||||||
|
try {
|
||||||
|
mainWindow.beginFrame();
|
||||||
|
|
||||||
|
if(draw_error_window)
|
||||||
|
gui::drawErrorWindow(error_message, &draw_error_window);
|
||||||
|
else {
|
||||||
|
updateGameObjects(deltaTime);
|
||||||
|
if(!updateCallback.isNull())
|
||||||
|
updateCallback(deltaTime);
|
||||||
|
}
|
||||||
|
mainWindow.endFrame();
|
||||||
|
}
|
||||||
|
catch(const std::exception& e){
|
||||||
|
error_message = "Catched exception: " + std::string(e.what());
|
||||||
|
draw_error_window = true;
|
||||||
|
std::cerr<<error_message<<std::endl;
|
||||||
|
}
|
||||||
|
catch(const char* cstr){
|
||||||
|
error_message = "Catched error message (const char*): " + std::string(cstr);
|
||||||
|
draw_error_window = true;
|
||||||
|
std::cerr<<error_message<<std::endl;
|
||||||
|
}
|
||||||
|
catch(const std::string& str){
|
||||||
|
error_message = "Catched error message (std::string): " + str;
|
||||||
|
draw_error_window = true;
|
||||||
|
std::cerr<<error_message<<std::endl;
|
||||||
|
}
|
||||||
|
catch(...){
|
||||||
|
error_message = "Catched unknown error";
|
||||||
|
draw_error_window = true;
|
||||||
|
std::cerr<<error_message<<std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Engine::updateGameObjects(f64 deltaTime){
|
||||||
|
for(auto pair : gameObjectPool){
|
||||||
|
gameObjectInvokeUpdate(pair.second.getObjectHandle().getObject(), deltaTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
game::GameObject& Engine::createGameObject(){
|
||||||
|
auto pair = gameObjectPool.emplace(game::GameObject(mono.createObject(gameObjectClass)));
|
||||||
|
game::GameObject& obj = pair.second;
|
||||||
|
gameObjectCtor(obj.getObjectHandle().getObject(), ++obj_id, pair.first);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Engine::tryCreateComponent(game::GameObject& obj, const std::string& componentClassName){
|
||||||
|
Mono::String componentClassNameManaged = mono.createString(componentClassName);
|
||||||
|
Mono::Bool created = gameObjectTryCreateComponent(obj.getObjectHandle().getObject(), componentClassNameManaged);
|
||||||
|
return created.wide_bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
50
src/Engine.hpp
Normal file
50
src/Engine.hpp
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "common/function_shared_ptr.hpp"
|
||||||
|
#include "mono/mono.hpp"
|
||||||
|
#include "game/GameObjectPool.hpp"
|
||||||
|
#include "gui/MainWindow.hpp"
|
||||||
|
#include "resources/textures.hpp"
|
||||||
|
|
||||||
|
namespace ougge {
|
||||||
|
|
||||||
|
#define GAMEOBJECTPOOL_SIZE 64*1024
|
||||||
|
|
||||||
|
using UpdateFunc_t = function_shared_ptr<void(f64)>;
|
||||||
|
|
||||||
|
class Engine {
|
||||||
|
bool loop_running = false;
|
||||||
|
|
||||||
|
game::GameObjectPool gameObjectPool;
|
||||||
|
u64 obj_id = 0;
|
||||||
|
MonoClass* gameObjectClass;
|
||||||
|
Mono::Method<void(u64, u32)> gameObjectCtor;
|
||||||
|
Mono::Method<void(f64)> gameObjectInvokeUpdate;
|
||||||
|
Mono::Method<Mono::Bool(Mono::String)> gameObjectTryCreateComponent;
|
||||||
|
|
||||||
|
public:
|
||||||
|
gui::MainWindow mainWindow;
|
||||||
|
UpdateFunc_t updateCallback;
|
||||||
|
|
||||||
|
Mono::RuntimeJIT mono;
|
||||||
|
std::shared_ptr<Mono::Assembly> engineManagedAssembly;
|
||||||
|
|
||||||
|
resources::CacheStorage<resources::Texture> textures;
|
||||||
|
|
||||||
|
Engine();
|
||||||
|
void init();
|
||||||
|
void openMainWindow(const std::string& window_title);
|
||||||
|
// start game loop on the current thread
|
||||||
|
void startLoop();
|
||||||
|
void stopLoop();
|
||||||
|
|
||||||
|
game::GameObject& createGameObject();
|
||||||
|
bool tryCreateComponent(game::GameObject& obj, const std::string& componentClassName);
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
void tryDrawFrame(f64 deltaTime);
|
||||||
|
void updateGameObjects(f64 deltaTime);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,13 +1,19 @@
|
|||||||
|
#include <backends/imgui_impl_sdl2.h>
|
||||||
|
#include <backends/imgui_impl_sdlrenderer2.h>
|
||||||
|
#include <iostream>
|
||||||
#include "MainWindow.hpp"
|
#include "MainWindow.hpp"
|
||||||
#include "backends/imgui_impl_sdl2.h"
|
#include "gui_exceptions.hpp"
|
||||||
#include "backends/imgui_impl_opengl3.h"
|
#include "../common/ougge_format.hpp"
|
||||||
|
#include "../resources/fonts.hpp"
|
||||||
|
#include "../resources/textures.hpp"
|
||||||
|
#include "../common/math.hpp"
|
||||||
|
|
||||||
namespace ougge::GUI {
|
namespace ougge::gui {
|
||||||
|
|
||||||
f32 MainWindow::getDPI(){
|
f32 MainWindow::getDPI(){
|
||||||
int w=0, h=0;
|
i32 w=0, h=0;
|
||||||
SDL_GL_GetDrawableSize(sdl_window, &w, &h);
|
SDL_GetRendererOutputSize(sdl_renderer, &w, &h);
|
||||||
int sim_w=0, sim_h=0;
|
i32 sim_w=0, sim_h=0;
|
||||||
SDL_GetWindowSize(sdl_window, &sim_w, &sim_h);
|
SDL_GetWindowSize(sdl_window, &sim_w, &sim_h);
|
||||||
f32 wdpi=(f32)w / sim_w;
|
f32 wdpi=(f32)w / sim_w;
|
||||||
f32 hdpi=(f32)h / sim_h;
|
f32 hdpi=(f32)h / sim_h;
|
||||||
@@ -15,41 +21,36 @@ f32 MainWindow::getDPI(){
|
|||||||
return dpi;
|
return dpi;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::init(const char* window_title){
|
void MainWindow::open(const std::string& window_title){
|
||||||
SDL_TRY(SDL_Init(SDL_INIT_VIDEO));
|
SDL_TRY(SDL_Init(SDL_INIT_EVERYTHING));
|
||||||
SDL_version v;
|
SDL_version v;
|
||||||
SDL_GetVersion(&v);
|
SDL_GetVersion(&v);
|
||||||
std::cout<<format("SDL version: %u.%u.%u\n", v.major, v.minor, v.patch);
|
std::cout<<ougge_format("SDL version: %u.%u.%u\n", v.major, v.minor, v.patch);
|
||||||
// GL 3.0 + GLSL 130
|
|
||||||
const char* glsl_version = "#version 130";
|
|
||||||
SDL_TRY( SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0));
|
|
||||||
SDL_TRY( SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE));
|
|
||||||
SDL_TRY( SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3));
|
|
||||||
SDL_TRY( SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0));
|
|
||||||
|
|
||||||
// From 2.0.18: Enable native IME.
|
// From 2.0.18: Enable native IME.
|
||||||
#ifdef SDL_HINT_IME_SHOW_UI
|
#ifdef SDL_HINT_IME_SHOW_UI
|
||||||
SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");
|
SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Create window with graphics context
|
|
||||||
SDL_TRY( SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1));
|
|
||||||
SDL_TRY( SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24));
|
|
||||||
SDL_TRY( SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8));
|
|
||||||
SDL_WindowFlags window_flags = (SDL_WindowFlags)(
|
SDL_WindowFlags window_flags = (SDL_WindowFlags)(
|
||||||
SDL_WINDOW_OPENGL |
|
SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI
|
||||||
SDL_WINDOW_RESIZABLE |
|
);
|
||||||
SDL_WINDOW_ALLOW_HIGHDPI);
|
sdl_window = SDL_CreateWindow(window_title.c_str(),
|
||||||
sdl_window = SDL_CreateWindow(window_title,
|
|
||||||
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
||||||
1280, 720, window_flags);
|
1280, 720, window_flags);
|
||||||
if(sdl_window == nullptr)
|
if(sdl_window == nullptr)
|
||||||
throw SDLException();
|
throw SDLException();
|
||||||
gl_context = SDL_GL_CreateContext(sdl_window);
|
|
||||||
if(gl_context == nullptr)
|
SDL_RendererFlags renderer_flags = (SDL_RendererFlags)(
|
||||||
|
SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC
|
||||||
|
);
|
||||||
|
sdl_renderer = SDL_CreateRenderer(sdl_window, -1, renderer_flags);
|
||||||
|
if(sdl_renderer == nullptr)
|
||||||
throw SDLException();
|
throw SDLException();
|
||||||
SDL_TRY( SDL_GL_MakeCurrent(sdl_window, gl_context));
|
|
||||||
SDL_TRY( SDL_GL_SetSwapInterval(1)); // Enable vsync
|
SDL_RendererInfo info;
|
||||||
|
SDL_GetRendererInfo(sdl_renderer, &info);
|
||||||
|
std::cout<<"Current SDL_Renderer driver: "<<info.name<<std::endl;
|
||||||
|
|
||||||
// Setup Dear ImGui context
|
// Setup Dear ImGui context
|
||||||
IMGUI_CHECKVERSION();
|
IMGUI_CHECKVERSION();
|
||||||
@@ -64,170 +65,72 @@ void MainWindow::init(const char* window_title){
|
|||||||
io.ConfigWindowsMoveFromTitleBarOnly = true;
|
io.ConfigWindowsMoveFromTitleBarOnly = true;
|
||||||
|
|
||||||
// Setup Platform/Renderer backends
|
// Setup Platform/Renderer backends
|
||||||
if(ImGui_ImplSDL2_InitForOpenGL(sdl_window, gl_context) != true)
|
if(!ImGui_ImplSDL2_InitForSDLRenderer(sdl_window, sdl_renderer))
|
||||||
throw SDLException();
|
throw SDLException();
|
||||||
if(ImGui_ImplOpenGL3_Init(glsl_version) != true)
|
if(!ImGui_ImplSDLRenderer2_Init(sdl_renderer))
|
||||||
throw SDLException();
|
throw SDLException();
|
||||||
|
|
||||||
// Setup Dear ImGui style
|
// Setup Dear ImGui style
|
||||||
ImGui::StyleColorsDark();
|
ImGui::StyleColorsDark();
|
||||||
|
f32 dpi = getDPI();
|
||||||
|
io.FontDefault = resources::ImFont_LoadFromResource(default_font, default_font_size, dpi);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait, poll and handle events (inputs, window resize, etc.)
|
void MainWindow::close(){
|
||||||
void MainWindow::poll_events(u16& frame_updates_requested, bool wait){
|
ImGui_ImplSDLRenderer2_Shutdown();
|
||||||
SDL_Event event;
|
ImGui_ImplSDL2_Shutdown();
|
||||||
if(wait){
|
ImGui::DestroyContext();
|
||||||
// waits for first event in cpu-efficient way
|
SDL_DestroyRenderer(sdl_renderer);
|
||||||
SDL_TRY(SDL_WaitEvent(&event) != 1);
|
SDL_DestroyWindow(sdl_window);
|
||||||
}
|
SDL_Quit();
|
||||||
// dont wait for event
|
}
|
||||||
else if(!SDL_PollEvent(&event))
|
|
||||||
return;
|
|
||||||
|
|
||||||
do {
|
void MainWindow::pollEvents(bool* loopRunning){
|
||||||
if(ImGui_ImplSDL2_ProcessEvent(&event))
|
SDL_Event event;
|
||||||
frame_updates_requested=2;
|
while (SDL_PollEvent(&event)) {
|
||||||
|
ImGui_ImplSDL2_ProcessEvent(&event);
|
||||||
switch(event.type){
|
switch(event.type){
|
||||||
case SDL_QUIT: {
|
case SDL_QUIT: {
|
||||||
close();
|
*loopRunning = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SDL_WINDOWEVENT: {
|
case SDL_WINDOWEVENT: {
|
||||||
if(event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(sdl_window)){
|
if(event.window.event == SDL_WINDOWEVENT_CLOSE
|
||||||
close();
|
&& event.window.windowID == SDL_GetWindowID(sdl_window))
|
||||||
|
{
|
||||||
|
*loopRunning = false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} while (SDL_PollEvent(&event)); // if there are more events, handles them
|
|
||||||
}
|
|
||||||
|
|
||||||
void ImGui_drawErrorWindow(bool* draw_error_window, std::string& msg){
|
|
||||||
ImGui::Begin("ERROR", draw_error_window);
|
|
||||||
ImGui::Text("%s", msg.c_str());
|
|
||||||
ImGui::End();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::draw_ui(){
|
|
||||||
static std::string error_message;
|
|
||||||
static bool draw_error_window = false;
|
|
||||||
try {
|
|
||||||
// Draw UI
|
|
||||||
if(draw_error_window)
|
|
||||||
ImGui_drawErrorWindow(&draw_error_window, error_message);
|
|
||||||
else {
|
|
||||||
draw_bg_window();
|
|
||||||
draw_debug_window();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch(const std::exception& e){
|
|
||||||
error_message = "Catched exception: " + std::string(e.what());
|
|
||||||
draw_error_window = true;
|
|
||||||
std::cerr<<error_message<<std::endl;
|
|
||||||
}
|
|
||||||
catch(const char* cstr){
|
|
||||||
error_message = "Catched error message (const char*): " + std::string(cstr);
|
|
||||||
draw_error_window = true;
|
|
||||||
std::cerr<<error_message<<std::endl;
|
|
||||||
}
|
|
||||||
catch(const std::string& str){
|
|
||||||
error_message = "Catched error message (std::string): " + str;
|
|
||||||
draw_error_window = true;
|
|
||||||
std::cerr<<error_message<<std::endl;
|
|
||||||
}
|
|
||||||
catch(...){
|
|
||||||
error_message = "Catched unknown error";
|
|
||||||
draw_error_window = true;
|
|
||||||
std::cerr<<error_message<<std::endl;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::draw_frame(){
|
|
||||||
|
void MainWindow::beginFrame(){
|
||||||
// Start the Dear ImGui frame
|
// Start the Dear ImGui frame
|
||||||
ImGui_ImplOpenGL3_NewFrame();
|
ImGui_ImplSDLRenderer2_NewFrame();
|
||||||
ImGui_ImplSDL2_NewFrame();
|
ImGui_ImplSDL2_NewFrame();
|
||||||
ImGui::NewFrame();
|
ImGui::NewFrame();
|
||||||
|
|
||||||
draw_ui();
|
|
||||||
|
|
||||||
// Rendering
|
|
||||||
ImGui::Render();
|
|
||||||
ImGuiIO& io = ImGui::GetIO();
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y);
|
SDL_RenderSetScale(sdl_renderer, io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y);
|
||||||
glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w);
|
SDL_SetRenderDrawColor(sdl_renderer,
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
(u8)(clear_color.x * 255),
|
||||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
(u8)(clear_color.y * 255),
|
||||||
|
(u8)(clear_color.z * 255),
|
||||||
// Update and Render additional Platform Windows
|
(u8)(clear_color.w * 255));
|
||||||
// (Platform functions may change the current OpenGL context, so we save/restore it to make it easier to paste this code elsewhere.
|
SDL_RenderClear(sdl_renderer);
|
||||||
// For this specific demo app we could also call SDL_GL_MakeCurrent(window, gl_context) directly)
|
|
||||||
if (io.ConfigFlags & ImGuiConfigFlags_ViewportsEnable)
|
|
||||||
{
|
|
||||||
SDL_Window* backup_current_window = SDL_GL_GetCurrentWindow();
|
|
||||||
SDL_GLContext backup_current_context = SDL_GL_GetCurrentContext();
|
|
||||||
ImGui::UpdatePlatformWindows();
|
|
||||||
ImGui::RenderPlatformWindowsDefault();
|
|
||||||
SDL_GL_MakeCurrent(backup_current_window, backup_current_context);
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_GL_SwapWindow(sdl_window);
|
|
||||||
|
|
||||||
|
draw_bg_window();
|
||||||
|
draw_debug_window();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::startAndWait(){
|
void MainWindow::endFrame(){
|
||||||
if(loop_running)
|
ImGui::Render();
|
||||||
throw UsefulException("loop is already running");
|
ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData(), sdl_renderer);
|
||||||
|
// Swap buffers
|
||||||
// draw first frame
|
SDL_RenderPresent(sdl_renderer);
|
||||||
draw_frame();
|
|
||||||
|
|
||||||
u16 frame_updates_requested=1;
|
|
||||||
u64 prev_update_time_ms=SDL_GetTicks64();
|
|
||||||
loop_running=true;
|
|
||||||
// main loop
|
|
||||||
while(loop_running){
|
|
||||||
// waits for events
|
|
||||||
poll_events(frame_updates_requested, main_loop_wait_for_input);
|
|
||||||
|
|
||||||
if(frame_updates_requested==0)
|
|
||||||
{
|
|
||||||
u64 update_time_ms=SDL_GetTicks64();
|
|
||||||
if(update_time_ms >= prev_update_time_ms + 1000/fps_min){
|
|
||||||
// if frame rate < fps_min then requests frame draw
|
|
||||||
// works only if main_loop_wait_for_input = false
|
|
||||||
frame_updates_requested=1;
|
|
||||||
prev_update_time_ms=update_time_ms;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// skips frame rendering and waits to limit fps
|
|
||||||
u32 frame_delay_ms=1000/fps_max;
|
|
||||||
SDL_Delay(frame_delay_ms);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// deaws requested number of frames
|
|
||||||
while(frame_updates_requested>0) {
|
|
||||||
draw_frame();
|
|
||||||
frame_updates_requested--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cleanup
|
|
||||||
MainWindow::destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::close(){
|
|
||||||
loop_running=false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::destroy(){
|
|
||||||
ImGui_ImplOpenGL3_Shutdown();
|
|
||||||
ImGui_ImplSDL2_Shutdown();
|
|
||||||
ImGui::DestroyContext();
|
|
||||||
SDL_GL_DeleteContext(gl_context);
|
|
||||||
SDL_DestroyWindow(sdl_window);
|
|
||||||
SDL_Quit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::draw_bg_window(){
|
void MainWindow::draw_bg_window(){
|
||||||
@@ -296,14 +199,14 @@ void MainWindow::draw_bg_window(){
|
|||||||
|
|
||||||
void MainWindow::draw_debug_window(){
|
void MainWindow::draw_debug_window(){
|
||||||
ImGuiIO& io = ImGui::GetIO();
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
if(ImGui::Begin("Debug Options", &show_debug_window)){
|
||||||
ImGui::Begin("Debug Options");
|
ImGui::ColorEdit3("clear_color", (float*)&clear_color);
|
||||||
ImGui::ColorEdit3("clear_color", (float*)&clear_color);
|
ImGui::InputInt("fps_max", &fps_max);
|
||||||
ImGui::Checkbox("main_loop_wait_for_input", &main_loop_wait_for_input);
|
ImGui::Text("Application average %.3f ms/frame (%.2f FPS)", 1000.0f / io.Framerate, io.Framerate);
|
||||||
ImGui::Text("Application average %.3f ms/frame (%.2f FPS)", 1000.0f / io.Framerate, io.Framerate);
|
ImGui::Checkbox("Demo Window", &show_demo_window);
|
||||||
ImGui::Checkbox("Demo Window", &show_demo_window);
|
ImGui::Checkbox("Metrics/Debug Window", &show_metrics_window);
|
||||||
ImGui::Checkbox("Metrics/Debug Window", &show_metrics_window);
|
ImGui::End();
|
||||||
ImGui::End();
|
}
|
||||||
|
|
||||||
if (show_demo_window)
|
if (show_demo_window)
|
||||||
ImGui::ShowDemoWindow(&show_demo_window);
|
ImGui::ShowDemoWindow(&show_demo_window);
|
||||||
|
|||||||
@@ -1,49 +1,43 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
#include <SDL_opengl.h>
|
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
#include <iostream>
|
#include "../common/std.hpp"
|
||||||
#include <string>
|
#include "../common/time.hpp"
|
||||||
#include "../std.hpp"
|
|
||||||
#include "../format.hpp"
|
|
||||||
#include "exceptions.hpp"
|
|
||||||
|
|
||||||
/// converts hex color to float vector
|
/// converts hex color to float vector
|
||||||
#define RGBAHexToF(R8,G8,B8,A8) ImVec4(((u8)35)/255.0f, ((u8)35)/255.0f, ((u8)50)/255.0f, ((u8)255)/255.0f)
|
#define RGBAHexToF(R8,G8,B8,A8) ImVec4(((u8)35)/255.0f, ((u8)35)/255.0f, ((u8)50)/255.0f, ((u8)255)/255.0f)
|
||||||
/// converts float vector to hex color
|
/// converts float vector to hex color
|
||||||
#define RGBAFToHex(VEC4) {(u8)(VEC4.x*255), (u8)(VEC4.y*255), (u8)(VEC4.z*255), (u8)(VEC4.w*255)}
|
#define RGBAFToHex(VEC4) {(u8)(VEC4.x*255), (u8)(VEC4.y*255), (u8)(VEC4.z*255), (u8)(VEC4.w*255)}
|
||||||
|
|
||||||
namespace ougge::GUI {
|
namespace ougge::gui {
|
||||||
|
|
||||||
#define default_font "DroidSans"
|
#define default_font "DroidSans"
|
||||||
|
|
||||||
class MainWindow {
|
class MainWindow {
|
||||||
public:
|
public:
|
||||||
ImVec4 clear_color = RGBAHexToF(35,35,50,255);
|
i32 fps_max = 60;
|
||||||
f32 default_font_size = 14.0f;
|
f32 default_font_size = 14.0f;
|
||||||
u8 fps_min = 30;
|
ImVec4 clear_color = RGBAHexToF(35,35,50,255);
|
||||||
u8 fps_max = 60;
|
SDL_Window* sdl_window = nullptr;
|
||||||
|
SDL_Renderer* sdl_renderer = nullptr;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool loop_running=false;
|
bool show_debug_window = true;
|
||||||
bool main_loop_wait_for_input=true;
|
|
||||||
bool show_demo_window = false;
|
bool show_demo_window = false;
|
||||||
bool show_metrics_window = false;
|
bool show_metrics_window = false;
|
||||||
SDL_Window* sdl_window = nullptr;
|
|
||||||
SDL_GLContext gl_context = nullptr;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void init(const char* window_title);
|
void open(const std::string& window_title);
|
||||||
void startAndWait();
|
|
||||||
void close();
|
void close();
|
||||||
f32 getDPI();
|
|
||||||
|
|
||||||
|
/// process io events happened since previous frame
|
||||||
|
void pollEvents(bool* loopRunning);
|
||||||
|
void beginFrame();
|
||||||
|
void endFrame();
|
||||||
|
|
||||||
|
f32 getDPI();
|
||||||
private:
|
private:
|
||||||
void destroy();
|
|
||||||
void poll_events(u16& frame_updates_requested, bool wait);
|
|
||||||
void draw_frame();
|
|
||||||
void draw_ui();
|
|
||||||
void draw_debug_window();
|
void draw_debug_window();
|
||||||
void draw_bg_window();
|
void draw_bg_window();
|
||||||
};
|
};
|
||||||
|
|||||||
0
src/GUI/SceneView.hpp
Normal file
0
src/GUI/SceneView.hpp
Normal file
@@ -1,12 +0,0 @@
|
|||||||
#include "exceptions.hpp"
|
|
||||||
#include <SDL.h>
|
|
||||||
|
|
||||||
namespace ougge::GUI {
|
|
||||||
|
|
||||||
SDLException_::SDLException_(const std::string& _file,const std::string& _func, int _line_n)
|
|
||||||
: UsefulException_(SDL_GetError(), _file, _func, _line_n)
|
|
||||||
{
|
|
||||||
SDL_ClearError();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "../UsefulException.hpp"
|
|
||||||
|
|
||||||
namespace ougge::GUI {
|
|
||||||
|
|
||||||
#define SDLException() SDLException_(__FILE__, __func__, __LINE__)
|
|
||||||
|
|
||||||
class SDLException_ : public UsefulException_ {
|
|
||||||
public:
|
|
||||||
SDLException_(const std::string& _file, const std::string& _func, int line_n);
|
|
||||||
};
|
|
||||||
|
|
||||||
#define SDL_TRY(EXPR) if(EXPR) throw SDLException();
|
|
||||||
|
|
||||||
}
|
|
||||||
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; }
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
130
src/Game/GameObjectPool.cpp
Normal file
130
src/Game/GameObjectPool.cpp
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
#include "GameObjectPool.hpp"
|
||||||
|
#include <bit>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
namespace ougge::game {
|
||||||
|
|
||||||
|
GameObjectPool::GameObjectPool(u32 size)
|
||||||
|
{
|
||||||
|
useful_assert(size % 64 == 0, "size of GameObjectPool must be a multiple of 64");
|
||||||
|
this->size = size;
|
||||||
|
first_unused_index = 0;
|
||||||
|
buffer = new GameObject[size];
|
||||||
|
used_indices = new u64[size/64];
|
||||||
|
// std::memset(buffer, 0, size*sizeof(GameObject));
|
||||||
|
std::memset(used_indices, 0, size/8);
|
||||||
|
}
|
||||||
|
|
||||||
|
GameObjectPool::~GameObjectPool()
|
||||||
|
{
|
||||||
|
delete[] buffer;
|
||||||
|
delete[] used_indices;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GameObjectPool::isIndexUsed(u32 index)
|
||||||
|
{
|
||||||
|
return ( used_indices[index/64] & (u64(1)<<(index%64)) ) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GameObjectPool::getNearestUnusedIndex(u32 startIndex)
|
||||||
|
{
|
||||||
|
if(startIndex >= size)
|
||||||
|
return -1;
|
||||||
|
if(!isIndexUsed(startIndex))
|
||||||
|
return startIndex;
|
||||||
|
|
||||||
|
u32 i = startIndex/64;
|
||||||
|
// mark previous bits as used
|
||||||
|
u64 u = used_indices[i] | ( (u64(1)<<startIndex%64) -1 );
|
||||||
|
while(u == u64(-1)){
|
||||||
|
i++;
|
||||||
|
if(i == size/64)
|
||||||
|
return -1;
|
||||||
|
u = used_indices[i];
|
||||||
|
}
|
||||||
|
u32 bitpos = std::countr_one(u);
|
||||||
|
if(bitpos == 64)
|
||||||
|
return -1;
|
||||||
|
u32 index = i*64 + bitpos;
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GameObjectPool::getNearestUsedIndex(u32 startIndex)
|
||||||
|
{
|
||||||
|
if(startIndex >= size)
|
||||||
|
return -1;
|
||||||
|
if(isIndexUsed(startIndex))
|
||||||
|
return startIndex;
|
||||||
|
|
||||||
|
u32 i = startIndex/64;
|
||||||
|
// mark previous bits as unused
|
||||||
|
u64 u = used_indices[i] & !( (u64(1)<<startIndex%64) -1 );
|
||||||
|
while(u == 0){
|
||||||
|
i++;
|
||||||
|
if(i == size/64)
|
||||||
|
return -1;
|
||||||
|
u = used_indices[i];
|
||||||
|
}
|
||||||
|
u32 bitpos = std::countr_zero(u);
|
||||||
|
if(bitpos == 64)
|
||||||
|
return -1;
|
||||||
|
u32 index = i*64 + bitpos;
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
GameObject& GameObjectPool::get(u32 index)
|
||||||
|
{
|
||||||
|
if(index >= size)
|
||||||
|
throw UsefulException(ougge_format("index %i is out of size %i", index, size));
|
||||||
|
if(!isIndexUsed(index))
|
||||||
|
throw UsefulException(ougge_format("there is no object at index %i", index));
|
||||||
|
return buffer[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<u32, GameObject&> GameObjectPool::emplace(GameObject&& new_obj)
|
||||||
|
{
|
||||||
|
u32 i = first_unused_index;
|
||||||
|
if(i == u32(-1))
|
||||||
|
throw UsefulException("can't put new GameObject to GameObjectPool because it's full");
|
||||||
|
|
||||||
|
buffer[i] = std::move(new_obj);
|
||||||
|
GameObject& r = buffer[i];
|
||||||
|
used_indices[i/64] |= u64(1)<<(i%64); // mark index bit as used
|
||||||
|
first_unused_index = getNearestUnusedIndex(i+1);
|
||||||
|
return std::pair<u32, GameObject&>(i, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameObjectPool::erase(u32 index)
|
||||||
|
{
|
||||||
|
if(index >= size)
|
||||||
|
throw UsefulException(ougge_format("index %i is out of size %i", index, size));
|
||||||
|
if(!isIndexUsed(index))
|
||||||
|
throw UsefulException(ougge_format("there is no object at index %i", index));
|
||||||
|
|
||||||
|
buffer[index] = GameObject();
|
||||||
|
used_indices[index/64] &= ~(u64(1)<<(index%64)); // mark index bit as unused
|
||||||
|
if(index < first_unused_index)
|
||||||
|
first_unused_index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
GameObjectPool::iterator::iterator(GameObjectPool* pool, u32 index)
|
||||||
|
: pool(pool), index(index)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<u32, GameObject&> GameObjectPool::iterator::operator*()
|
||||||
|
{
|
||||||
|
if(index >= pool->size)
|
||||||
|
throw UsefulException("can't get value of end() iterator");
|
||||||
|
|
||||||
|
GameObject& r = pool->buffer[index];
|
||||||
|
return std::pair<u32, GameObject&>(index, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
GameObjectPool::iterator& GameObjectPool::iterator::operator++()
|
||||||
|
{
|
||||||
|
index = pool->getNearestUsedIndex(index+1);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
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);
|
||||||
|
void erase(u32 index);
|
||||||
|
|
||||||
|
#pragma region iterator class
|
||||||
|
class iterator {
|
||||||
|
GameObjectPool* pool;
|
||||||
|
u32 index = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
iterator(GameObjectPool* pool, u32 index);
|
||||||
|
std::pair<u32, GameObject&> operator*();
|
||||||
|
iterator& operator++();
|
||||||
|
inline bool operator!=(const iterator& o) const { return index != o.index; };
|
||||||
|
inline bool operator==(const iterator& o) const { return index == o.index; };
|
||||||
|
};
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
inline iterator begin() { return iterator(this, 0); }
|
||||||
|
inline iterator end() { return iterator(this, -1); }
|
||||||
|
|
||||||
|
friend class iterator;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
17
src/Mono/Assembly.cpp
Normal file
17
src/Mono/Assembly.cpp
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#include "mono.hpp"
|
||||||
|
|
||||||
|
namespace Mono {
|
||||||
|
|
||||||
|
Assembly::Assembly(MonoAssembly *ptr)
|
||||||
|
: ptr(ptr), image(mono_assembly_get_image(ptr))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
MonoClass* Assembly::getClass(const std::string &name_space, const std::string &name){
|
||||||
|
auto c = mono_class_from_name(image, name_space.c_str(), name.c_str());
|
||||||
|
if(!c)
|
||||||
|
throw UsefulException(ougge_format("can't get class '%s.%s'", name_space.c_str(), name.c_str()));
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
81
src/Mono/Mono.cpp
Normal file
81
src/Mono/Mono.cpp
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
#include "mono.hpp"
|
||||||
|
#include <mono/metadata/appdomain.h>
|
||||||
|
|
||||||
|
namespace Mono {
|
||||||
|
|
||||||
|
template <> MonoClass* getClass<SByte>(){ return mono_get_sbyte_class(); }
|
||||||
|
template <> MonoClass* getClass<Byte>(){ return mono_get_byte_class(); }
|
||||||
|
template <> MonoClass* getClass<Short>(){ return mono_get_int16_class(); }
|
||||||
|
template <> MonoClass* getClass<UShort>(){ return mono_get_uint16_class(); }
|
||||||
|
template <> MonoClass* getClass<Int>(){ return mono_get_int32_class(); }
|
||||||
|
template <> MonoClass* getClass<UInt>(){ return mono_get_uint32_class(); }
|
||||||
|
template <> MonoClass* getClass<Long>(){ return mono_get_int64_class(); }
|
||||||
|
template <> MonoClass* getClass<ULong>(){ return mono_get_uint64_class(); }
|
||||||
|
template <> MonoClass* getClass<Float>(){ return mono_get_single_class(); }
|
||||||
|
template <> MonoClass* getClass<Double>(){ return mono_get_double_class(); }
|
||||||
|
template <> MonoClass* getClass<Bool>(){ return mono_get_boolean_class(); }
|
||||||
|
template <> MonoClass* getClass<Char>(){ return mono_get_char_class(); }
|
||||||
|
template <> MonoClass* getClass<String>(){ return mono_get_string_class(); }
|
||||||
|
template <> MonoClass* getClass<Object>(){ return mono_get_object_class(); }
|
||||||
|
template <> MonoClass* getClass<Void>(){ return mono_get_void_class(); }
|
||||||
|
|
||||||
|
|
||||||
|
void getMethodSignatureTypes(MonoMethod* mono_method,
|
||||||
|
MonoType** return_type,
|
||||||
|
std::vector<MonoType*>& argument_types)
|
||||||
|
{
|
||||||
|
auto signature = mono_method_signature(mono_method);
|
||||||
|
void* iter = nullptr;
|
||||||
|
*return_type = mono_signature_get_return_type(signature);
|
||||||
|
MonoType* pt = nullptr;
|
||||||
|
while( (pt = mono_signature_get_params(signature, &iter)) ){
|
||||||
|
argument_types.push_back(pt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MonoMethod* tryGetMonoMethod(MonoClass* target_class, const std::string& name,
|
||||||
|
MonoClass* return_class, MonoClass* arg_classes[], size_t arg_classes_size)
|
||||||
|
{
|
||||||
|
if(target_class == nullptr)
|
||||||
|
throw UsefulException("target_class is nullptr");
|
||||||
|
|
||||||
|
// iterate each method
|
||||||
|
void* iter = nullptr;
|
||||||
|
MonoMethod* m = nullptr;
|
||||||
|
std::vector<MonoType*> argument_types;
|
||||||
|
while( (m = mono_class_get_methods(target_class, &iter)) ){
|
||||||
|
// compare name
|
||||||
|
std::string m_name = mono_method_get_name(m);
|
||||||
|
if(m_name != name)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
argument_types.clear();
|
||||||
|
MonoType* return_type = nullptr;
|
||||||
|
getMethodSignatureTypes(m, &return_type, argument_types);
|
||||||
|
// compare argument count
|
||||||
|
if(argument_types.size() != arg_classes_size)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// compare return type
|
||||||
|
if(!mono_metadata_type_equal(return_type, mono_class_get_type(return_class)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// compare argument types
|
||||||
|
bool argument_types_mismatch = false;
|
||||||
|
for(size_t i = 0; i < arg_classes_size; i++){
|
||||||
|
if(!mono_metadata_type_equal(argument_types[i], mono_class_get_type(arg_classes[i]))){
|
||||||
|
argument_types_mismatch = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(argument_types_mismatch)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// passed all tests successfully
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
187
src/Mono/Mono.hpp
Normal file
187
src/Mono/Mono.hpp
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../common/std.hpp"
|
||||||
|
#include "../common/UsefulException.hpp"
|
||||||
|
#include "../common/ougge_format.hpp"
|
||||||
|
#include <vector>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <mono/metadata/class.h>
|
||||||
|
#include <mono/metadata/assembly.h>
|
||||||
|
#include <mono/metadata/object.h>
|
||||||
|
|
||||||
|
namespace Mono {
|
||||||
|
|
||||||
|
typedef i8 SByte;
|
||||||
|
typedef u8 Byte;
|
||||||
|
typedef i16 Short;
|
||||||
|
typedef u16 UShort;
|
||||||
|
typedef i32 Int;
|
||||||
|
typedef u32 UInt;
|
||||||
|
typedef i64 Long;
|
||||||
|
typedef u64 ULong;
|
||||||
|
typedef f32 Float;
|
||||||
|
typedef f64 Double;
|
||||||
|
typedef union { mono_bool wide_bool; } Bool; //USAGE: Bool t = {true};
|
||||||
|
typedef char16_t Char;
|
||||||
|
typedef MonoString* String;
|
||||||
|
/// @warning MonoObject can be moved in memory by GC in any time and raw pointer will be invalid.
|
||||||
|
/// Use ObjectHandle where it is possible.
|
||||||
|
typedef MonoObject* Object;
|
||||||
|
typedef void Void;
|
||||||
|
|
||||||
|
|
||||||
|
template<typename PrimitiveT>
|
||||||
|
MonoClass* getClass();
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void* valueToVoidPtr(T& v){ return &v; }
|
||||||
|
template<>
|
||||||
|
inline void* valueToVoidPtr<Object>(Object& v){ return v; }
|
||||||
|
template<>
|
||||||
|
inline void* valueToVoidPtr<String>(String& v){ return v; }
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T valueFromMonoObject(MonoObject* o){
|
||||||
|
void* result_value_ptr = mono_object_unbox(o);
|
||||||
|
if(result_value_ptr == nullptr)
|
||||||
|
throw UsefulException("can't unbox method value");
|
||||||
|
return *((T*)result_value_ptr);
|
||||||
|
}
|
||||||
|
template<>
|
||||||
|
inline Object valueFromMonoObject<Object>(MonoObject* o){ return o; }
|
||||||
|
template<>
|
||||||
|
inline String valueFromMonoObject<String>(MonoObject* o){ return (String)((void*)o); }
|
||||||
|
|
||||||
|
void getMethodSignatureTypes(MonoMethod* mono_method,
|
||||||
|
MonoType** return_type,
|
||||||
|
std::vector<MonoType*>& argument_types);
|
||||||
|
|
||||||
|
/// searches for method `name` in `target_class` with return type `return_class` and argument types `arg_classes`
|
||||||
|
/// @return found method or nullptr
|
||||||
|
MonoMethod* tryGetMonoMethod(MonoClass* target_class, const std::string& name,
|
||||||
|
MonoClass* return_class, MonoClass* arg_classes[], size_t arg_classes_size);
|
||||||
|
|
||||||
|
|
||||||
|
template<typename SignatureT> class Method;
|
||||||
|
template<typename ReturnT, typename... ArgTypes>
|
||||||
|
class Method<ReturnT(ArgTypes...)>
|
||||||
|
{
|
||||||
|
MonoMethod* method_ptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Method() { method_ptr = nullptr; }
|
||||||
|
|
||||||
|
/// all types must implement getClass<T>()
|
||||||
|
Method(MonoClass* target_class, const std::string& name){
|
||||||
|
static MonoClass* arg_classes[] { getClass<ArgTypes>()... };
|
||||||
|
static MonoClass* return_class { getClass<ReturnT>() };
|
||||||
|
method_ptr = tryGetMonoMethod(target_class, name, return_class, arg_classes, sizeof...(ArgTypes));
|
||||||
|
if(method_ptr == nullptr){
|
||||||
|
throw UsefulException(ougge_format("can't get method '%s' from class '%s'",
|
||||||
|
name.c_str(), mono_class_get_name(target_class)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReturnT not is void
|
||||||
|
template<typename RT = ReturnT>
|
||||||
|
std::enable_if_t<!std::is_void<RT>::value, RT> operator()(MonoObject* class_instance, ArgTypes... args) const {
|
||||||
|
if(method_ptr == nullptr)
|
||||||
|
throw UsefulException("method_ptr is null");
|
||||||
|
|
||||||
|
void* arg_array[] = { valueToVoidPtr(args)..., nullptr };
|
||||||
|
MonoObject* ex = nullptr;
|
||||||
|
MonoObject* result = mono_runtime_invoke(method_ptr, class_instance, arg_array, &ex);
|
||||||
|
if(ex){
|
||||||
|
//TODO: call mono_trace_set_printerr_handler from mono/mono/utils/mono-logger.h
|
||||||
|
mono_print_unhandled_exception(ex);
|
||||||
|
throw UsefulException("Some C# exception occured");
|
||||||
|
}
|
||||||
|
return valueFromMonoObject<ReturnT>(result);
|
||||||
|
};
|
||||||
|
|
||||||
|
// ReturnT is void
|
||||||
|
template<typename RT = ReturnT>
|
||||||
|
std::enable_if_t<std::is_void<RT>::value, RT> operator()(MonoObject* class_instance, ArgTypes... args) const {
|
||||||
|
if(method_ptr == nullptr)
|
||||||
|
throw UsefulException("method_ptr is null");
|
||||||
|
|
||||||
|
void* arg_array[] = { valueToVoidPtr(args)..., nullptr };
|
||||||
|
MonoObject* ex = nullptr;
|
||||||
|
mono_runtime_invoke(method_ptr, class_instance, arg_array, &ex);
|
||||||
|
if(ex){
|
||||||
|
//TODO: call mono_trace_set_printerr_handler from mono/mono/utils/mono-logger.h
|
||||||
|
mono_print_unhandled_exception(ex);
|
||||||
|
throw UsefulException("Some C# exception occured");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class Assembly {
|
||||||
|
MonoAssembly* ptr;
|
||||||
|
MonoImage* image;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Assembly(MonoAssembly* ptr);
|
||||||
|
Assembly(const Assembly&) = delete;
|
||||||
|
|
||||||
|
MonoClass* getClass(const std::string& name_space, const std::string& name);
|
||||||
|
MonoAssembly* getMonoAssembly() const { return ptr; }
|
||||||
|
MonoImage* getMonoImage() const { return image; }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
///LINUX: `config.xml`, `mscorelib.dll`, `libmono-native.so` in `mono-libs` directory
|
||||||
|
///
|
||||||
|
///WINDOWS: `config.xml`, `mscorelib.dll` in `mono-libs` directory
|
||||||
|
class RuntimeJIT {
|
||||||
|
MonoDomain* domain;
|
||||||
|
public:
|
||||||
|
RuntimeJIT(const std::string& domain_name = "OuggeDomain");
|
||||||
|
RuntimeJIT(const RuntimeJIT&) = delete;
|
||||||
|
~RuntimeJIT();
|
||||||
|
|
||||||
|
inline MonoDomain* getDomain() { return domain; }
|
||||||
|
std::shared_ptr<Assembly> loadAssembly(const std::string& name);
|
||||||
|
|
||||||
|
inline Object createObject(MonoClass* klass) { return mono_object_new(domain, klass); }
|
||||||
|
inline String createString(const std::string& v) { return mono_string_new_len(domain, v.c_str(), v.size()); }
|
||||||
|
inline String createString(const char* v) { return mono_string_new(domain, v); }
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @brief ObjectHandle can be used to store reliable reference to MonoObject.
|
||||||
|
/// MonoObject can be moved in memory by GC in any time and raw pointer will be invalid.
|
||||||
|
struct ObjectHandle {
|
||||||
|
u32 gc_handle;
|
||||||
|
|
||||||
|
inline ObjectHandle() : gc_handle(0) {}
|
||||||
|
|
||||||
|
inline ObjectHandle(Object obj) {
|
||||||
|
gc_handle = mono_gchandle_new(obj, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// implicitly create new ObjectHandle instead
|
||||||
|
inline ObjectHandle(const ObjectHandle& o) = delete;
|
||||||
|
|
||||||
|
inline ObjectHandle(ObjectHandle&& o) {
|
||||||
|
gc_handle = o.gc_handle;
|
||||||
|
o.gc_handle = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ObjectHandle& operator=(ObjectHandle&& o) {
|
||||||
|
gc_handle = o.gc_handle;
|
||||||
|
o.gc_handle = 0;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ~ObjectHandle() {
|
||||||
|
if(gc_handle)
|
||||||
|
mono_gchandle_free(gc_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Object getObject() const {
|
||||||
|
return mono_gchandle_get_target(gc_handle);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
30
src/Mono/Runtime.cpp
Normal file
30
src/Mono/Runtime.cpp
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#include "mono.hpp"
|
||||||
|
#include <mono/jit/jit.h>
|
||||||
|
#include <mono/metadata/mono-config.h>
|
||||||
|
#include <mono/metadata/appdomain.h>
|
||||||
|
|
||||||
|
namespace Mono {
|
||||||
|
|
||||||
|
RuntimeJIT::RuntimeJIT(const std::string& domain_name){
|
||||||
|
mono_set_dirs("mono-libs", "mono-libs");
|
||||||
|
mono_set_assemblies_path("mono-libs");
|
||||||
|
mono_config_parse("mono-libs/config.xml");
|
||||||
|
|
||||||
|
domain = mono_jit_init(domain_name.c_str());
|
||||||
|
if(!domain)
|
||||||
|
throw UsefulException("can't initialize mono domain");
|
||||||
|
}
|
||||||
|
|
||||||
|
RuntimeJIT::~RuntimeJIT(){
|
||||||
|
// TODO: fix segfault on cleanup
|
||||||
|
// mono_jit_cleanup(domain);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Assembly> RuntimeJIT::loadAssembly(const std::string &name){
|
||||||
|
MonoAssembly* ptr = mono_domain_assembly_open(domain, name.c_str());
|
||||||
|
if(!ptr)
|
||||||
|
throw UsefulException(ougge_format("can't load assembly '%s'", name.c_str()));
|
||||||
|
return std::make_shared<Assembly>(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
94
src/Resources/Resources.cpp
Normal file
94
src/Resources/Resources.cpp
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
#include "../common/UsefulException.hpp"
|
||||||
|
#include "../common/ougge_format.hpp"
|
||||||
|
#include "resources.hpp"
|
||||||
|
#include "embedded_resources.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace ougge::resources {
|
||||||
|
|
||||||
|
Resource::Resource(const std::string& path, const std::size_t size, StreamFactoryMethod open_read_steam_func)
|
||||||
|
: path(path), size(size), openStream(open_read_steam_func)
|
||||||
|
{}
|
||||||
|
|
||||||
|
MemoryStreamBuf::MemoryStreamBuf(void* _p, const std::size_t n){
|
||||||
|
char* p=(char*)_p;
|
||||||
|
setg(p, p, p + n);
|
||||||
|
setp(p, p + n);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::istream::pos_type MemoryStreamBuf::seekoff(
|
||||||
|
std::istream::off_type off,
|
||||||
|
std::ios_base::seekdir dir,
|
||||||
|
std::ios_base::openmode which)
|
||||||
|
{
|
||||||
|
if (dir == std::ios_base::cur)
|
||||||
|
gbump(off);
|
||||||
|
else if (dir == std::ios_base::end)
|
||||||
|
setg(eback(), egptr() + off, egptr());
|
||||||
|
else if (dir == std::ios_base::beg)
|
||||||
|
setg(eback(), eback() + off, egptr());
|
||||||
|
return gptr() - eback();
|
||||||
|
}
|
||||||
|
|
||||||
|
MemoryStreamRead::MemoryStreamRead(const void* p, const std::size_t n)
|
||||||
|
: std::istream(new MemoryStreamBuf((void*)p, n))
|
||||||
|
{}
|
||||||
|
|
||||||
|
MemoryStreamRead::~MemoryStreamRead(){
|
||||||
|
delete rdbuf();
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::unordered_map<std::string, Resource>* _resourceMap = nullptr;
|
||||||
|
|
||||||
|
void loadEmbeddedresources(){
|
||||||
|
for(int i = 0; i < EmbeddedResource_table_count; i++){
|
||||||
|
const EmbeddedResource& e = EmbeddedResource_table[i];
|
||||||
|
std::cout <<"loading resource '" << e.path << "' "
|
||||||
|
<< formatSizeHumanReadable(e.size) << std::endl;
|
||||||
|
|
||||||
|
auto embedded_resource_factory = [e]() -> std::unique_ptr<std::istream> {
|
||||||
|
return std::make_unique<MemoryStreamRead>(e.data, e.size);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto r = _resourceMap->emplace(e.path,
|
||||||
|
Resource(e.path, e.size, embedded_resource_factory));
|
||||||
|
if(!r.second)
|
||||||
|
throw UsefulException(ougge_format("can't load duplicate resource '%s'", e.path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void init(){
|
||||||
|
if(_resourceMap != nullptr)
|
||||||
|
throw UsefulException("resource has been initialized already");
|
||||||
|
_resourceMap = new std::unordered_map<std::string, Resource>();
|
||||||
|
loadEmbeddedresources();
|
||||||
|
}
|
||||||
|
|
||||||
|
Resource& getResource(const std::string& path){
|
||||||
|
auto it = _resourceMap->find(path);
|
||||||
|
if(it == _resourceMap->end())
|
||||||
|
throw UsefulException(ougge_format("can't find resource '%s'", path.c_str()));
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string formatSizeHumanReadable(std::size_t b){
|
||||||
|
std::stringstream ss;
|
||||||
|
ss.precision(3);
|
||||||
|
std::size_t k = b / 1024;
|
||||||
|
std::size_t m = k / 1024;
|
||||||
|
std::size_t g = m / 1024;
|
||||||
|
|
||||||
|
if(g)
|
||||||
|
ss<<m / 1024.0f<<'G';
|
||||||
|
else if(m)
|
||||||
|
ss<<k / 1024.0f<<'M';
|
||||||
|
else if(k)
|
||||||
|
ss<<b / 1024.0f<<'K';
|
||||||
|
else ss<<b;
|
||||||
|
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
63
src/Resources/Resources.hpp
Normal file
63
src/Resources/Resources.hpp
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../common/std.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
#include <functional>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace ougge::resources {
|
||||||
|
|
||||||
|
// call this in main()
|
||||||
|
void init();
|
||||||
|
|
||||||
|
std::string formatSizeHumanReadable(std::size_t byte_n);
|
||||||
|
|
||||||
|
class Resource {
|
||||||
|
public:
|
||||||
|
const std::string path;
|
||||||
|
const std::size_t size;
|
||||||
|
|
||||||
|
using StreamFactoryMethod = std::function< std::unique_ptr<std::istream> () >;
|
||||||
|
|
||||||
|
Resource(const std::string& path, const std::size_t size, StreamFactoryMethod open_read_steam_func);
|
||||||
|
|
||||||
|
const StreamFactoryMethod openStream;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MemoryStreamBuf : public std::streambuf {
|
||||||
|
public:
|
||||||
|
MemoryStreamBuf(void* p, const std::size_t n);
|
||||||
|
|
||||||
|
virtual std::istream::pos_type seekoff(
|
||||||
|
std::istream::off_type off,
|
||||||
|
std::ios_base::seekdir dir,
|
||||||
|
std::ios_base::openmode which);
|
||||||
|
};
|
||||||
|
|
||||||
|
class MemoryStreamRead : public std::istream {
|
||||||
|
public:
|
||||||
|
MemoryStreamRead(const void* p, const std::size_t n);
|
||||||
|
|
||||||
|
virtual ~MemoryStreamRead();
|
||||||
|
};
|
||||||
|
|
||||||
|
Resource& getResource(const std::string& path);
|
||||||
|
|
||||||
|
/// @brief stores requested resources in memory
|
||||||
|
/// @tparam T must implement constructor `T(const Resource&, ...)`
|
||||||
|
template<class T>
|
||||||
|
class CacheStorage {
|
||||||
|
std::unordered_map<std::string, T> _map;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template<typename... TCtorArgs>
|
||||||
|
T& getOrCreate(const std::string& name, TCtorArgs&&... ctor_args){
|
||||||
|
auto it = _map.find(name);
|
||||||
|
if(it != _map.end())
|
||||||
|
return it->second;
|
||||||
|
auto& res = getResource(name);
|
||||||
|
auto e = _map.emplace(name, T(res, ctor_args...));
|
||||||
|
return e.first->second;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
64
src/Resources/embedded_resources.h
Normal file
64
src/Resources/embedded_resources.h
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
// This file was generated by resource_embedder //
|
||||||
|
// https://timerix.ddns.net:3322/Timerix/resource_embedder //
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
// USAGE: //
|
||||||
|
// Put it in a SOURCE file to define variables //
|
||||||
|
// #define EMBEDDED_RESOURCE_DEFINITION //
|
||||||
|
// #define EMBEDDED_RESOURCE_POSTFIX your_postfix //
|
||||||
|
// #include "../../src/Resources/embedded_resources.h" //
|
||||||
|
// //
|
||||||
|
// Put it in a HEADER file to declare external variables //
|
||||||
|
// #define EMBEDDED_RESOURCE_POSTFIX your_postfix //
|
||||||
|
// #include "../../src/Resources/embedded_resources.h" //
|
||||||
|
// //
|
||||||
|
// Then you can access embedded files through //
|
||||||
|
// EmbeddedResource_table_your_postfix. You can get table //
|
||||||
|
// content by index and put it into a hashtable or a map. //
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#if __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char* path;
|
||||||
|
const char* data;
|
||||||
|
size_t size;
|
||||||
|
} EmbeddedResource;
|
||||||
|
|
||||||
|
#define RSCAT(A,B,C...) A##B##C
|
||||||
|
#ifdef EMBEDDED_RESOURCE_POSTFIX
|
||||||
|
#define _EmbeddedResource_table(P) \
|
||||||
|
RSCAT(EmbeddedResource_table_, P)
|
||||||
|
#define _EmbeddedResource_table_count(P) \
|
||||||
|
RSCAT(EmbeddedResource_table_, P, _count)
|
||||||
|
#else
|
||||||
|
#define _EmbeddedResource_table(P) \
|
||||||
|
EmbeddedResource_table
|
||||||
|
#define _EmbeddedResource_table_count(P) \
|
||||||
|
EmbeddedResource_table_count
|
||||||
|
#endif
|
||||||
|
extern const EmbeddedResource _EmbeddedResource_table(EMBEDDED_RESOURCE_POSTFIX)[];
|
||||||
|
extern const int _EmbeddedResource_table_count(EMBEDDED_RESOURCE_POSTFIX);
|
||||||
|
|
||||||
|
#ifdef EMBEDDED_RESOURCE_DEFINITION
|
||||||
|
const EmbeddedResource _EmbeddedResource_table(EMBEDDED_RESOURCE_POSTFIX)[]={
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const int _EmbeddedResource_table_count(EMBEDDED_RESOURCE_POSTFIX)=0;
|
||||||
|
#endif // EMBEDDED_RESOURCE_DEFINITION
|
||||||
|
|
||||||
|
#undef _EmbeddedResource_table
|
||||||
|
#undef _EmbeddedResource_table_count
|
||||||
|
#undef EMBEDDED_RESOURCE_POSTFIX
|
||||||
|
#undef RSCAT
|
||||||
|
|
||||||
|
#if __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
33
src/Resources/fonts.cpp
Normal file
33
src/Resources/fonts.cpp
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#include <imgui.h>
|
||||||
|
#include "resources.hpp"
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
namespace ougge::resources {
|
||||||
|
|
||||||
|
// select all glyphs from font
|
||||||
|
static const ImWchar glyph_ranges[] = {
|
||||||
|
0x0020, 0xFFFF, 0
|
||||||
|
};
|
||||||
|
|
||||||
|
ImFont* ImFont_LoadFromFile(const std::string& file_path, f32 font_size, f32 dpi){
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
font_size *= dpi;
|
||||||
|
return io.Fonts->AddFontFromFileTTF(file_path.c_str(), font_size, nullptr, glyph_ranges);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImFont* ImFont_LoadFromResource(const std::string& font_name, f32 font_size, f32 dpi){
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
font_size *= dpi;
|
||||||
|
ImFontConfig font_cfg = ImFontConfig();
|
||||||
|
std::sprintf(font_cfg.Name, "%s %ipx", font_name.c_str(), (i32)font_size);
|
||||||
|
|
||||||
|
auto& res = getResource("fonts/" + font_name + ".ttf");
|
||||||
|
char* font_data = new char[res.size];
|
||||||
|
res.openStream()->read(font_data, res.size);
|
||||||
|
|
||||||
|
return io.Fonts->AddFontFromMemoryTTF((void*)(font_data), res.size,
|
||||||
|
font_size, &font_cfg, glyph_ranges);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
12
src/Resources/fonts.hpp
Normal file
12
src/Resources/fonts.hpp
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "imgui.h"
|
||||||
|
#include "../common/std.hpp"
|
||||||
|
|
||||||
|
namespace ougge::resources {
|
||||||
|
|
||||||
|
ImFont* ImFont_LoadFromFile(const std::string& file_path, f32 font_size, f32 dpi);
|
||||||
|
|
||||||
|
ImFont* ImFont_LoadFromResource(const std::string& font_name, f32 font_size, f32 dpi);
|
||||||
|
|
||||||
|
}
|
||||||
96
src/Resources/textures.cpp
Normal file
96
src/Resources/textures.cpp
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
#include "textures.hpp"
|
||||||
|
#include <SDL_image.h>
|
||||||
|
#include "../gui/gui_exceptions.hpp"
|
||||||
|
|
||||||
|
namespace ougge::resources {
|
||||||
|
|
||||||
|
Texture::Texture(const Resource& r, SDL_Renderer* renderer)
|
||||||
|
: Texture(*r.openStream(), r.size, renderer)
|
||||||
|
{}
|
||||||
|
|
||||||
|
Texture::Texture(std::istream& s, size_t size, SDL_Renderer* renderer)
|
||||||
|
: renderer(renderer), texture(nullptr), w(0), h(0)
|
||||||
|
{
|
||||||
|
SDL_RWops* sdl_stream = SDL_RWFromIStream(s, size);
|
||||||
|
if(!sdl_stream)
|
||||||
|
throw gui::SDLException();
|
||||||
|
texture = std::shared_ptr<SDL_Texture>(IMG_LoadTexture_RW(renderer, sdl_stream, 1), SDL_DestroyTexture);
|
||||||
|
if(!texture)
|
||||||
|
throw gui::IMGException();
|
||||||
|
SDL_TRY(SDL_QueryTexture(texture.get(), nullptr, nullptr, &w, &h));
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_RenderCopyExF_Params::SDL_RenderCopyExF_Params()
|
||||||
|
: rotation_angle(0), flip(SDL_FLIP_NONE)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void Texture::render(const SDL_FRect& target_section){
|
||||||
|
SDL_TRY(
|
||||||
|
SDL_RenderCopyF(renderer, texture.get(), nullptr, &target_section)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Texture::render(const SDL_FRect& target_section, const SDL_Rect& texture_section){
|
||||||
|
SDL_TRY(
|
||||||
|
SDL_RenderCopyF(renderer, texture.get(), &texture_section, &target_section)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Texture::render(const SDL_RenderCopyExF_Params& p){
|
||||||
|
SDL_TRY(
|
||||||
|
SDL_RenderCopyExF(renderer, texture.get(),
|
||||||
|
optional_value_ptr_or_null(p.texture_section),
|
||||||
|
optional_value_ptr_or_null(p.target_section),
|
||||||
|
-1.0f * angleToDegree(normalizeAngle(p.rotation_angle)),
|
||||||
|
optional_value_ptr_or_null(p.rotation_center),
|
||||||
|
p.flip
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
static Sint64 istream_size(SDL_RWops* context){
|
||||||
|
return (Sint64)(size_t)context->hidden.unknown.data2;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Sint64 istream_seek(SDL_RWops* context, Sint64 offset, i32 whence){
|
||||||
|
std::istream* stream = (std::istream*)context->hidden.unknown.data1;
|
||||||
|
switch(whence){
|
||||||
|
case SEEK_SET: stream->seekg(offset, std::ios::beg); break;
|
||||||
|
case SEEK_CUR: stream->seekg(offset, std::ios::cur); break;
|
||||||
|
case SEEK_END: stream->seekg(offset, std::ios::end); break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
return stream->fail() ? -1 : (Sint64)stream->tellg();
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t istream_read(SDL_RWops* context, void *ptr, size_t size, size_t maxnum){
|
||||||
|
if(size == 0)
|
||||||
|
return -1;
|
||||||
|
std::istream* stream = (std::istream*)context->hidden.unknown.data1;
|
||||||
|
stream->read((char*)ptr, size * maxnum);
|
||||||
|
|
||||||
|
return stream->bad() ? -1 : stream->gcount() / size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static i32 istream_close(SDL_RWops* context){
|
||||||
|
if (context)
|
||||||
|
SDL_FreeRW(context);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_RWops* SDL_RWFromIStream(std::istream& stream, size_t size){
|
||||||
|
SDL_RWops* rwops = SDL_AllocRW();
|
||||||
|
if(rwops) {
|
||||||
|
rwops->size = istream_size;
|
||||||
|
rwops->seek = istream_seek;
|
||||||
|
rwops->read = istream_read;
|
||||||
|
rwops->write = nullptr;
|
||||||
|
rwops->close = istream_close;
|
||||||
|
rwops->hidden.unknown.data1 = &stream;
|
||||||
|
rwops->hidden.unknown.data2 = (void*)size;
|
||||||
|
rwops->type = SDL_RWOPS_UNKNOWN;
|
||||||
|
}
|
||||||
|
return rwops;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
44
src/Resources/textures.hpp
Normal file
44
src/Resources/textures.hpp
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <optional>
|
||||||
|
#include <SDL.h>
|
||||||
|
#include "resources.hpp"
|
||||||
|
#include "../common/math.hpp"
|
||||||
|
|
||||||
|
namespace ougge::resources {
|
||||||
|
|
||||||
|
#define SDL_RectConstruct(X, Y, W, H) (SDL_Rect){X, Y, W, H}
|
||||||
|
#define SDL_FRectConstruct(X, Y, W, H) (SDL_FRect){X, Y, W, H}
|
||||||
|
#define SDL_PointConstruct(X, Y) (SDL_Point){X, Y, W, H}
|
||||||
|
#define SDL_FPointConstruct(X, Y) (SDL_FPoint){X, Y, W, H}
|
||||||
|
|
||||||
|
#define optional_value_ptr_or_null(OPT) (OPT ? &(*OPT) : nullptr)
|
||||||
|
|
||||||
|
SDL_RWops* SDL_RWFromIStream(std::istream& stream, size_t size);
|
||||||
|
|
||||||
|
struct SDL_RenderCopyExF_Params {
|
||||||
|
std::optional<SDL_Rect> texture_section;
|
||||||
|
std::optional<SDL_FRect> target_section;
|
||||||
|
std::optional<SDL_FPoint> rotation_center;
|
||||||
|
angle_t rotation_angle;
|
||||||
|
SDL_RendererFlip flip;
|
||||||
|
|
||||||
|
SDL_RenderCopyExF_Params();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Texture {
|
||||||
|
SDL_Renderer* renderer;
|
||||||
|
std::shared_ptr<SDL_Texture> texture;
|
||||||
|
i32 w;
|
||||||
|
i32 h;
|
||||||
|
|
||||||
|
Texture(const Resource& r, SDL_Renderer* renderer);
|
||||||
|
Texture(std::istream& s, size_t size, SDL_Renderer* renderer);
|
||||||
|
|
||||||
|
void render(const SDL_FRect& target_section);
|
||||||
|
void render(const SDL_FRect& target_section, const SDL_Rect& texture_section);
|
||||||
|
void render(const SDL_RenderCopyExF_Params& params);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -18,4 +18,4 @@ public:
|
|||||||
virtual char const* what() const noexcept;
|
virtual char const* what() const noexcept;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define useful_assert(EXPR, ERRMSG) if(!EXPR) throw UsefulException(ERRMSG);
|
#define useful_assert(EXPR, ERRMSG) if(!(EXPR)) throw UsefulException(ERRMSG);
|
||||||
50
src/common/function_shared_ptr.hpp
Normal file
50
src/common/function_shared_ptr.hpp
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
#include "UsefulException.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
template<typename SignatureT> class function_shared_ptr;
|
||||||
|
template<typename ReturnT, typename... ArgTypes>
|
||||||
|
class function_shared_ptr<ReturnT(ArgTypes...)>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using func_t = std::function<ReturnT(ArgTypes...)>;
|
||||||
|
using ptr_t = std::shared_ptr<func_t>;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
ptr_t func_ptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
function_shared_ptr() = default;
|
||||||
|
function_shared_ptr(const function_shared_ptr&) = default;
|
||||||
|
function_shared_ptr(function_shared_ptr&&) = default;
|
||||||
|
function_shared_ptr(const ptr_t& p) : func_ptr(p) { }
|
||||||
|
function_shared_ptr(ptr_t&& p) : func_ptr(p) { }
|
||||||
|
template<typename FunctionT>
|
||||||
|
function_shared_ptr(FunctionT f){ func_ptr = std::make_shared<func_t>(f); }
|
||||||
|
|
||||||
|
function_shared_ptr& operator=(const function_shared_ptr&) = default;
|
||||||
|
function_shared_ptr& operator=(function_shared_ptr&&) = default;
|
||||||
|
|
||||||
|
bool isNull() const { return func_ptr == nullptr; }
|
||||||
|
|
||||||
|
//TODO: make inline
|
||||||
|
|
||||||
|
// ReturnT is not void
|
||||||
|
template<typename RT = ReturnT>
|
||||||
|
std::enable_if_t<!std::is_void<RT>::value, RT> operator()(ArgTypes... args){
|
||||||
|
if(func_ptr == nullptr)
|
||||||
|
throw UsefulException("function_shared_ptr is null");
|
||||||
|
return func_ptr->operator()(args...);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReturnT is void
|
||||||
|
template<typename RT = ReturnT>
|
||||||
|
std::enable_if_t<std::is_void<RT>::value, RT> operator()(ArgTypes... args){
|
||||||
|
if(func_ptr == nullptr)
|
||||||
|
throw UsefulException("function_shared_ptr is null");
|
||||||
|
func_ptr->operator()(args...);
|
||||||
|
}
|
||||||
|
};
|
||||||
47
src/common/math.hpp
Normal file
47
src/common/math.hpp
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "std.hpp"
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
#ifndef M_PI
|
||||||
|
#define M_E 2.7182818284590452354
|
||||||
|
#define M_LOG2E 1.4426950408889634074
|
||||||
|
#define M_LOG10E 0.43429448190325182765
|
||||||
|
#define M_LN2 0.69314718055994530942
|
||||||
|
#define M_LN10 2.30258509299404568402
|
||||||
|
#define M_PI 3.14159265358979323846
|
||||||
|
#define M_PI_2 1.57079632679489661923
|
||||||
|
#define M_PI_4 0.78539816339744830962
|
||||||
|
#define M_1_PI 0.31830988618379067154
|
||||||
|
#define M_2_PI 0.63661977236758134308
|
||||||
|
#define M_2_SQRTPI 1.12837916709551257390
|
||||||
|
#define M_SQRT2 1.41421356237309504880
|
||||||
|
#define M_SQRT1_2 0.70710678118654752440
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct Vec2 {
|
||||||
|
f32 x = 0, y = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Vec3 {
|
||||||
|
f32 x = 0, y = 0, z = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Vec4 {
|
||||||
|
f32 x = 0, y = 0, z = 0, w = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// radians
|
||||||
|
typedef f32 angle_t;
|
||||||
|
|
||||||
|
static inline angle_t normalizeAngle(angle_t a){
|
||||||
|
return std::fmodf(a + M_PI, 2*M_PI) - M_PI;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline f32 angleToDegree(angle_t a){
|
||||||
|
return (a / M_PI) * 180;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline angle_t degreeToRadian(f32 d){
|
||||||
|
return (d / 180) * M_PI;
|
||||||
|
}
|
||||||
@@ -1,10 +1,9 @@
|
|||||||
#include <string>
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
#include "ougge_format.hpp"
|
||||||
#include "UsefulException.hpp"
|
#include "UsefulException.hpp"
|
||||||
#include "std.hpp"
|
|
||||||
|
|
||||||
std::string _format(const std::string& format_str, const size_t args_count, ...){
|
std::string _ougge_format(const std::string& format_str, const size_t args_count, ...){
|
||||||
va_list vl;
|
va_list vl;
|
||||||
va_start(vl, args_count);
|
va_start(vl, args_count);
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
6
src/common/ougge_format.hpp
Normal file
6
src/common/ougge_format.hpp
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "std.hpp"
|
||||||
|
|
||||||
|
std::string _ougge_format(const std::string& format_str, const size_t args_count, ...);
|
||||||
|
#define ougge_format(FORMAT_STR, ARGS...) _ougge_format(FORMAT_STR, count_args(ARGS) ,##ARGS)
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
typedef int8_t i8;
|
typedef int8_t i8;
|
||||||
typedef uint8_t u8;
|
typedef uint8_t u8;
|
||||||
19
src/common/time.cpp
Normal file
19
src/common/time.cpp
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
// posix version definition to use clock_gettime
|
||||||
|
#ifndef _XOPEN_SOURCE
|
||||||
|
#if __STDC_VERSION__ >= 199901L
|
||||||
|
#define _XOPEN_SOURCE 600
|
||||||
|
#else
|
||||||
|
#define _XOPEN_SOURCE 500
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "time.hpp"
|
||||||
|
#include <ctime>
|
||||||
|
#include "UsefulException.hpp"
|
||||||
|
|
||||||
|
nsec_t getMonotonicTimeNsec(){
|
||||||
|
struct timespec t;
|
||||||
|
if(clock_gettime(CLOCK_MONOTONIC, &t) != 0)
|
||||||
|
throw UsefulException("clock_gettime(CLOCK_MONOTONIC) error");
|
||||||
|
return (nsec_t)t.tv_sec * 1e9 + (nsec_t)t.tv_nsec;
|
||||||
|
}
|
||||||
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;\
|
||||||
|
}
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#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)
|
|
||||||
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; }
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
130
src/game/GameObjectPool.cpp
Normal file
130
src/game/GameObjectPool.cpp
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
#include "GameObjectPool.hpp"
|
||||||
|
#include <bit>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
namespace ougge::game {
|
||||||
|
|
||||||
|
GameObjectPool::GameObjectPool(u32 size)
|
||||||
|
{
|
||||||
|
useful_assert(size % 64 == 0, "size of GameObjectPool must be a multiple of 64");
|
||||||
|
this->size = size;
|
||||||
|
first_unused_index = 0;
|
||||||
|
buffer = new GameObject[size];
|
||||||
|
used_indices = new u64[size/64];
|
||||||
|
// std::memset(buffer, 0, size*sizeof(GameObject));
|
||||||
|
std::memset(used_indices, 0, size/8);
|
||||||
|
}
|
||||||
|
|
||||||
|
GameObjectPool::~GameObjectPool()
|
||||||
|
{
|
||||||
|
delete[] buffer;
|
||||||
|
delete[] used_indices;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GameObjectPool::isIndexUsed(u32 index)
|
||||||
|
{
|
||||||
|
return ( used_indices[index/64] & (u64(1)<<(index%64)) ) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GameObjectPool::getNearestUnusedIndex(u32 startIndex)
|
||||||
|
{
|
||||||
|
if(startIndex >= size)
|
||||||
|
return -1;
|
||||||
|
if(!isIndexUsed(startIndex))
|
||||||
|
return startIndex;
|
||||||
|
|
||||||
|
u32 i = startIndex/64;
|
||||||
|
// mark previous bits as used
|
||||||
|
u64 u = used_indices[i] | ( (u64(1)<<startIndex%64) -1 );
|
||||||
|
while(u == u64(-1)){
|
||||||
|
i++;
|
||||||
|
if(i == size/64)
|
||||||
|
return -1;
|
||||||
|
u = used_indices[i];
|
||||||
|
}
|
||||||
|
u32 bitpos = std::countr_one(u);
|
||||||
|
if(bitpos == 64)
|
||||||
|
return -1;
|
||||||
|
u32 index = i*64 + bitpos;
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 GameObjectPool::getNearestUsedIndex(u32 startIndex)
|
||||||
|
{
|
||||||
|
if(startIndex >= size)
|
||||||
|
return -1;
|
||||||
|
if(isIndexUsed(startIndex))
|
||||||
|
return startIndex;
|
||||||
|
|
||||||
|
u32 i = startIndex/64;
|
||||||
|
// mark previous bits as unused
|
||||||
|
u64 u = used_indices[i] & !( (u64(1)<<startIndex%64) -1 );
|
||||||
|
while(u == 0){
|
||||||
|
i++;
|
||||||
|
if(i == size/64)
|
||||||
|
return -1;
|
||||||
|
u = used_indices[i];
|
||||||
|
}
|
||||||
|
u32 bitpos = std::countr_zero(u);
|
||||||
|
if(bitpos == 64)
|
||||||
|
return -1;
|
||||||
|
u32 index = i*64 + bitpos;
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
GameObject& GameObjectPool::get(u32 index)
|
||||||
|
{
|
||||||
|
if(index >= size)
|
||||||
|
throw UsefulException(ougge_format("index %i is out of size %i", index, size));
|
||||||
|
if(!isIndexUsed(index))
|
||||||
|
throw UsefulException(ougge_format("there is no object at index %i", index));
|
||||||
|
return buffer[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<u32, GameObject&> GameObjectPool::emplace(GameObject&& new_obj)
|
||||||
|
{
|
||||||
|
u32 i = first_unused_index;
|
||||||
|
if(i == u32(-1))
|
||||||
|
throw UsefulException("can't put new GameObject to GameObjectPool because it's full");
|
||||||
|
|
||||||
|
buffer[i] = std::move(new_obj);
|
||||||
|
GameObject& r = buffer[i];
|
||||||
|
used_indices[i/64] |= u64(1)<<(i%64); // mark index bit as used
|
||||||
|
first_unused_index = getNearestUnusedIndex(i+1);
|
||||||
|
return std::pair<u32, GameObject&>(i, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameObjectPool::erase(u32 index)
|
||||||
|
{
|
||||||
|
if(index >= size)
|
||||||
|
throw UsefulException(ougge_format("index %i is out of size %i", index, size));
|
||||||
|
if(!isIndexUsed(index))
|
||||||
|
throw UsefulException(ougge_format("there is no object at index %i", index));
|
||||||
|
|
||||||
|
buffer[index] = GameObject();
|
||||||
|
used_indices[index/64] &= ~(u64(1)<<(index%64)); // mark index bit as unused
|
||||||
|
if(index < first_unused_index)
|
||||||
|
first_unused_index = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
GameObjectPool::iterator::iterator(GameObjectPool* pool, u32 index)
|
||||||
|
: pool(pool), index(index)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<u32, GameObject&> GameObjectPool::iterator::operator*()
|
||||||
|
{
|
||||||
|
if(index >= pool->size)
|
||||||
|
throw UsefulException("can't get value of end() iterator");
|
||||||
|
|
||||||
|
GameObject& r = pool->buffer[index];
|
||||||
|
return std::pair<u32, GameObject&>(index, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
GameObjectPool::iterator& GameObjectPool::iterator::operator++()
|
||||||
|
{
|
||||||
|
index = pool->getNearestUsedIndex(index+1);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
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);
|
||||||
|
void erase(u32 index);
|
||||||
|
|
||||||
|
#pragma region iterator class
|
||||||
|
class iterator {
|
||||||
|
GameObjectPool* pool;
|
||||||
|
u32 index = 0;
|
||||||
|
|
||||||
|
public:
|
||||||
|
iterator(GameObjectPool* pool, u32 index);
|
||||||
|
std::pair<u32, GameObject&> operator*();
|
||||||
|
iterator& operator++();
|
||||||
|
inline bool operator!=(const iterator& o) const { return index != o.index; };
|
||||||
|
inline bool operator==(const iterator& o) const { return index == o.index; };
|
||||||
|
};
|
||||||
|
#pragma endregion
|
||||||
|
|
||||||
|
inline iterator begin() { return iterator(this, 0); }
|
||||||
|
inline iterator end() { return iterator(this, -1); }
|
||||||
|
|
||||||
|
friend class iterator;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
11
src/gui/ErrorWindow.cpp
Normal file
11
src/gui/ErrorWindow.cpp
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#include "gui.hpp"
|
||||||
|
|
||||||
|
namespace ougge::gui {
|
||||||
|
|
||||||
|
void drawErrorWindow(const std::string& msg, bool* draw_error_window){
|
||||||
|
ImGui::Begin("ERROR", draw_error_window);
|
||||||
|
ImGui::Text("%s", msg.c_str());
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
218
src/gui/MainWindow.cpp
Normal file
218
src/gui/MainWindow.cpp
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
#include <backends/imgui_impl_sdl2.h>
|
||||||
|
#include <backends/imgui_impl_sdlrenderer2.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include "MainWindow.hpp"
|
||||||
|
#include "gui_exceptions.hpp"
|
||||||
|
#include "../common/ougge_format.hpp"
|
||||||
|
#include "../resources/fonts.hpp"
|
||||||
|
#include "../resources/textures.hpp"
|
||||||
|
#include "../common/math.hpp"
|
||||||
|
|
||||||
|
namespace ougge::gui {
|
||||||
|
|
||||||
|
f32 MainWindow::getDPI(){
|
||||||
|
i32 w=0, h=0;
|
||||||
|
SDL_GetRendererOutputSize(sdl_renderer, &w, &h);
|
||||||
|
i32 sim_w=0, sim_h=0;
|
||||||
|
SDL_GetWindowSize(sdl_window, &sim_w, &sim_h);
|
||||||
|
f32 wdpi=(f32)w / sim_w;
|
||||||
|
f32 hdpi=(f32)h / sim_h;
|
||||||
|
f32 dpi=SDL_sqrtf(wdpi*wdpi + hdpi*hdpi);
|
||||||
|
return dpi;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::open(const std::string& window_title){
|
||||||
|
SDL_TRY(SDL_Init(SDL_INIT_EVERYTHING));
|
||||||
|
SDL_version v;
|
||||||
|
SDL_GetVersion(&v);
|
||||||
|
std::cout<<ougge_format("SDL version: %u.%u.%u\n", v.major, v.minor, v.patch);
|
||||||
|
|
||||||
|
// From 2.0.18: Enable native IME.
|
||||||
|
#ifdef SDL_HINT_IME_SHOW_UI
|
||||||
|
SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
SDL_WindowFlags window_flags = (SDL_WindowFlags)(
|
||||||
|
SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI
|
||||||
|
);
|
||||||
|
sdl_window = SDL_CreateWindow(window_title.c_str(),
|
||||||
|
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
||||||
|
1280, 720, window_flags);
|
||||||
|
if(sdl_window == nullptr)
|
||||||
|
throw SDLException();
|
||||||
|
|
||||||
|
SDL_RendererFlags renderer_flags = (SDL_RendererFlags)(
|
||||||
|
SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC
|
||||||
|
);
|
||||||
|
sdl_renderer = SDL_CreateRenderer(sdl_window, -1, renderer_flags);
|
||||||
|
if(sdl_renderer == nullptr)
|
||||||
|
throw SDLException();
|
||||||
|
|
||||||
|
SDL_RendererInfo info;
|
||||||
|
SDL_GetRendererInfo(sdl_renderer, &info);
|
||||||
|
std::cout<<"Current SDL_Renderer driver: "<<info.name<<std::endl;
|
||||||
|
|
||||||
|
// Setup Dear ImGui context
|
||||||
|
IMGUI_CHECKVERSION();
|
||||||
|
ImGui::CreateContext();
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls
|
||||||
|
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls
|
||||||
|
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking
|
||||||
|
io.ConfigDockingTransparentPayload = true;
|
||||||
|
io.ConfigInputTextCursorBlink = false;
|
||||||
|
// io.ConfigViewportsNoTaskBarIcon = true;
|
||||||
|
io.ConfigWindowsMoveFromTitleBarOnly = true;
|
||||||
|
|
||||||
|
// Setup Platform/Renderer backends
|
||||||
|
if(!ImGui_ImplSDL2_InitForSDLRenderer(sdl_window, sdl_renderer))
|
||||||
|
throw SDLException();
|
||||||
|
if(!ImGui_ImplSDLRenderer2_Init(sdl_renderer))
|
||||||
|
throw SDLException();
|
||||||
|
|
||||||
|
// Setup Dear ImGui style
|
||||||
|
ImGui::StyleColorsDark();
|
||||||
|
f32 dpi = getDPI();
|
||||||
|
io.FontDefault = resources::ImFont_LoadFromResource(default_font, default_font_size, dpi);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::close(){
|
||||||
|
ImGui_ImplSDLRenderer2_Shutdown();
|
||||||
|
ImGui_ImplSDL2_Shutdown();
|
||||||
|
ImGui::DestroyContext();
|
||||||
|
SDL_DestroyRenderer(sdl_renderer);
|
||||||
|
SDL_DestroyWindow(sdl_window);
|
||||||
|
SDL_Quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::pollEvents(bool* loopRunning){
|
||||||
|
SDL_Event event;
|
||||||
|
while (SDL_PollEvent(&event)) {
|
||||||
|
ImGui_ImplSDL2_ProcessEvent(&event);
|
||||||
|
switch(event.type){
|
||||||
|
case SDL_QUIT: {
|
||||||
|
*loopRunning = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SDL_WINDOWEVENT: {
|
||||||
|
if(event.window.event == SDL_WINDOWEVENT_CLOSE
|
||||||
|
&& event.window.windowID == SDL_GetWindowID(sdl_window))
|
||||||
|
{
|
||||||
|
*loopRunning = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void MainWindow::beginFrame(){
|
||||||
|
// Start the Dear ImGui frame
|
||||||
|
ImGui_ImplSDLRenderer2_NewFrame();
|
||||||
|
ImGui_ImplSDL2_NewFrame();
|
||||||
|
ImGui::NewFrame();
|
||||||
|
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
SDL_RenderSetScale(sdl_renderer, io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y);
|
||||||
|
SDL_SetRenderDrawColor(sdl_renderer,
|
||||||
|
(u8)(clear_color.x * 255),
|
||||||
|
(u8)(clear_color.y * 255),
|
||||||
|
(u8)(clear_color.z * 255),
|
||||||
|
(u8)(clear_color.w * 255));
|
||||||
|
SDL_RenderClear(sdl_renderer);
|
||||||
|
|
||||||
|
draw_bg_window();
|
||||||
|
draw_debug_window();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::endFrame(){
|
||||||
|
ImGui::Render();
|
||||||
|
ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData(), sdl_renderer);
|
||||||
|
// Swap buffers
|
||||||
|
SDL_RenderPresent(sdl_renderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::draw_bg_window(){
|
||||||
|
const ImGuiDockNodeFlags dockspace_flags =
|
||||||
|
ImGuiDockNodeFlags_PassthruCentralNode;
|
||||||
|
const ImGuiWindowFlags window_flags =
|
||||||
|
ImGuiWindowFlags_MenuBar |
|
||||||
|
ImGuiWindowFlags_NoDocking |
|
||||||
|
ImGuiWindowFlags_NoScrollbar |
|
||||||
|
ImGuiWindowFlags_NoScrollWithMouse |
|
||||||
|
ImGuiWindowFlags_NoBringToFrontOnFocus |
|
||||||
|
ImGuiWindowFlags_NoFocusOnAppearing |
|
||||||
|
ImGuiWindowFlags_NoMove |
|
||||||
|
ImGuiWindowFlags_NoResize |
|
||||||
|
ImGuiWindowFlags_NoCollapse |
|
||||||
|
ImGuiWindowFlags_NoTitleBar |
|
||||||
|
ImGuiWindowFlags_NoBackground;
|
||||||
|
// not dockable window that always bound to viewport
|
||||||
|
ImGuiViewport* viewport = ImGui::GetWindowViewport();
|
||||||
|
ImGui::SetNextWindowPos(viewport->Pos, ImGuiCond_Always);
|
||||||
|
ImGui::SetNextWindowSize(viewport->Size, ImGuiCond_Always);
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
|
||||||
|
ImGui::Begin("bg_window", nullptr, window_flags);
|
||||||
|
ImGui::PopStyleVar(3);
|
||||||
|
|
||||||
|
// DockSpace
|
||||||
|
ImGuiID dockspace_id = ImGui::GetID("bg_dockspace");
|
||||||
|
ImGui::DockSpace(dockspace_id, ImVec2(0.0f, 0.0f), dockspace_flags);
|
||||||
|
|
||||||
|
// MenuBar
|
||||||
|
if(ImGui::BeginMainMenuBar()){
|
||||||
|
if(ImGui::BeginMenu("test")){
|
||||||
|
if(ImGui::MenuItem("throw exception")){
|
||||||
|
ImGui::EndMenu();
|
||||||
|
ImGui::EndMainMenuBar();
|
||||||
|
ImGui::End();
|
||||||
|
throw UsefulException("example exception");
|
||||||
|
}
|
||||||
|
if(ImGui::MenuItem("throw const char*")){
|
||||||
|
ImGui::EndMenu();
|
||||||
|
ImGui::EndMainMenuBar();
|
||||||
|
ImGui::End();
|
||||||
|
throw "cptr";
|
||||||
|
}
|
||||||
|
if(ImGui::MenuItem("throw std::string")){
|
||||||
|
ImGui::EndMenu();
|
||||||
|
ImGui::EndMainMenuBar();
|
||||||
|
ImGui::End();
|
||||||
|
throw std::string("str");
|
||||||
|
}
|
||||||
|
if(ImGui::MenuItem("throw unknown")){
|
||||||
|
ImGui::EndMenu();
|
||||||
|
ImGui::EndMainMenuBar();
|
||||||
|
ImGui::End();
|
||||||
|
throw 111;
|
||||||
|
}
|
||||||
|
ImGui::EndMenu();
|
||||||
|
}
|
||||||
|
ImGui::EndMainMenuBar();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::draw_debug_window(){
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
if(ImGui::Begin("Debug Options", &show_debug_window)){
|
||||||
|
ImGui::ColorEdit3("clear_color", (float*)&clear_color);
|
||||||
|
ImGui::InputInt("fps_max", &fps_max);
|
||||||
|
ImGui::Text("Application average %.3f ms/frame (%.2f FPS)", 1000.0f / io.Framerate, io.Framerate);
|
||||||
|
ImGui::Checkbox("Demo Window", &show_demo_window);
|
||||||
|
ImGui::Checkbox("Metrics/Debug Window", &show_metrics_window);
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (show_demo_window)
|
||||||
|
ImGui::ShowDemoWindow(&show_demo_window);
|
||||||
|
|
||||||
|
if (show_metrics_window)
|
||||||
|
ImGui::ShowMetricsWindow(&show_metrics_window);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
45
src/gui/MainWindow.hpp
Normal file
45
src/gui/MainWindow.hpp
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <SDL.h>
|
||||||
|
#include <imgui.h>
|
||||||
|
#include "../common/std.hpp"
|
||||||
|
#include "../common/time.hpp"
|
||||||
|
|
||||||
|
/// converts hex color to float vector
|
||||||
|
#define RGBAHexToF(R8,G8,B8,A8) ImVec4(((u8)35)/255.0f, ((u8)35)/255.0f, ((u8)50)/255.0f, ((u8)255)/255.0f)
|
||||||
|
/// converts float vector to hex color
|
||||||
|
#define RGBAFToHex(VEC4) {(u8)(VEC4.x*255), (u8)(VEC4.y*255), (u8)(VEC4.z*255), (u8)(VEC4.w*255)}
|
||||||
|
|
||||||
|
namespace ougge::gui {
|
||||||
|
|
||||||
|
#define default_font "DroidSans"
|
||||||
|
|
||||||
|
class MainWindow {
|
||||||
|
public:
|
||||||
|
i32 fps_max = 60;
|
||||||
|
f32 default_font_size = 14.0f;
|
||||||
|
ImVec4 clear_color = RGBAHexToF(35,35,50,255);
|
||||||
|
SDL_Window* sdl_window = nullptr;
|
||||||
|
SDL_Renderer* sdl_renderer = nullptr;
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool show_debug_window = true;
|
||||||
|
bool show_demo_window = false;
|
||||||
|
bool show_metrics_window = false;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void open(const std::string& window_title);
|
||||||
|
void close();
|
||||||
|
|
||||||
|
/// process io events happened since previous frame
|
||||||
|
void pollEvents(bool* loopRunning);
|
||||||
|
void beginFrame();
|
||||||
|
void endFrame();
|
||||||
|
|
||||||
|
f32 getDPI();
|
||||||
|
private:
|
||||||
|
void draw_debug_window();
|
||||||
|
void draw_bg_window();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
0
src/gui/SceneView.hpp
Normal file
0
src/gui/SceneView.hpp
Normal file
11
src/gui/gui.hpp
Normal file
11
src/gui/gui.hpp
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <SDL.h>
|
||||||
|
#include <imgui.h>
|
||||||
|
#include "gui_exceptions.hpp"
|
||||||
|
|
||||||
|
namespace ougge::gui {
|
||||||
|
|
||||||
|
void drawErrorWindow(const std::string& msg, bool* draw_error_window);
|
||||||
|
|
||||||
|
}
|
||||||
23
src/gui/gui_exceptions.cpp
Normal file
23
src/gui/gui_exceptions.cpp
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#include "gui_exceptions.hpp"
|
||||||
|
#include <SDL.h>
|
||||||
|
#include <SDL_image.h>
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
IMG_ClearError();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
27
src/gui/gui_exceptions.hpp
Normal file
27
src/gui/gui_exceptions.hpp
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
|
#include "../common/std.hpp"
|
||||||
|
#include "../common/UsefulException.hpp"
|
||||||
|
|
||||||
|
namespace ougge::gui {
|
||||||
|
|
||||||
|
#define SDLException() SDLException_(__FILE__, __func__, __LINE__)
|
||||||
|
|
||||||
|
class SDLException_ : public UsefulException_ {
|
||||||
|
public:
|
||||||
|
SDLException_(const std::string& _file, const std::string& _func, int line_n);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#define IMGException() IMGException_(__FILE__, __func__, __LINE__)
|
||||||
|
|
||||||
|
class IMGException_ : public UsefulException_ {
|
||||||
|
public:
|
||||||
|
IMGException_(const std::string& _file, const std::string& _func, int line_n);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#define SDL_TRY(EXPR) if(EXPR) throw ::ougge::gui::SDLException();
|
||||||
|
|
||||||
|
}
|
||||||
45
src/main.cpp
45
src/main.cpp
@@ -1,18 +1,47 @@
|
|||||||
#define SDL_MAIN_HANDLED
|
#define SDL_MAIN_HANDLED
|
||||||
#include "GUI/MainWindow.hpp"
|
|
||||||
#include "std.hpp"
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <iomanip>
|
||||||
|
#include "common/UsefulException.hpp"
|
||||||
|
#include "common/ougge_format.hpp"
|
||||||
|
#include "Engine.hpp"
|
||||||
|
#include "gui/gui.hpp"
|
||||||
|
|
||||||
using namespace ougge;
|
using namespace ougge;
|
||||||
|
|
||||||
int main(int argc, const char** argv){
|
void drawTutel(Engine& engine){
|
||||||
if(setlocale(LC_CTYPE, "C.UTF-8")!=0)
|
resources::Texture& tutel = engine.textures.getOrCreate("tutel.png", engine.mainWindow.sdl_renderer);
|
||||||
std::cerr<<"\e[93msetlocale failed!\n";
|
resources::SDL_RenderCopyExF_Params params;
|
||||||
|
params.target_section = SDL_FRectConstruct(100, 100, 400, 400);
|
||||||
|
static i32 si = 1;
|
||||||
|
params.rotation_angle = M_PI_4 * (si++ / engine.mainWindow.fps_max);
|
||||||
|
tutel.render(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, const char** argv){
|
||||||
try {
|
try {
|
||||||
GUI::MainWindow w;
|
resources::init();
|
||||||
w.init("ougge");
|
std::cout<<"initialized resource loader"<<std::endl;
|
||||||
w.startAndWait();
|
|
||||||
|
Engine engine;
|
||||||
|
std::cout<<"initialized mono jit runtime"<<std::endl;
|
||||||
|
engine.init();
|
||||||
|
std::cout<<"initialized game engine"<<std::endl;
|
||||||
|
|
||||||
|
game::GameObject& exampleObj = engine.createGameObject();
|
||||||
|
std::string componentClassName = "Ougge.ExampleComponent";
|
||||||
|
if(!engine.tryCreateComponent(exampleObj, componentClassName))
|
||||||
|
throw UsefulException(ougge_format("couldn't create component '%s'", componentClassName.c_str()));
|
||||||
|
|
||||||
|
std::cout<<"created ExampleObject"<<std::endl;
|
||||||
|
|
||||||
|
engine.updateCallback = [&engine](f64 deltaTime) -> void {
|
||||||
|
drawTutel(engine);
|
||||||
|
};
|
||||||
|
|
||||||
|
engine.openMainWindow("ougge");
|
||||||
|
std::cout<<"created sdl window"<<std::endl;
|
||||||
|
engine.startLoop();
|
||||||
|
std::cout<<"sdl window has been cosed"<<std::endl;
|
||||||
}
|
}
|
||||||
catch(const std::exception& e){
|
catch(const std::exception& e){
|
||||||
std::cerr<<"Catched exception: "<<e.what()<<std::endl;
|
std::cerr<<"Catched exception: "<<e.what()<<std::endl;
|
||||||
|
|||||||
17
src/mono/Assembly.cpp
Normal file
17
src/mono/Assembly.cpp
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
#include "mono.hpp"
|
||||||
|
|
||||||
|
namespace Mono {
|
||||||
|
|
||||||
|
Assembly::Assembly(MonoAssembly *ptr)
|
||||||
|
: ptr(ptr), image(mono_assembly_get_image(ptr))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
MonoClass* Assembly::getClass(const std::string &name_space, const std::string &name){
|
||||||
|
auto c = mono_class_from_name(image, name_space.c_str(), name.c_str());
|
||||||
|
if(!c)
|
||||||
|
throw UsefulException(ougge_format("can't get class '%s.%s'", name_space.c_str(), name.c_str()));
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
30
src/mono/Runtime.cpp
Normal file
30
src/mono/Runtime.cpp
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
#include "mono.hpp"
|
||||||
|
#include <mono/jit/jit.h>
|
||||||
|
#include <mono/metadata/mono-config.h>
|
||||||
|
#include <mono/metadata/appdomain.h>
|
||||||
|
|
||||||
|
namespace Mono {
|
||||||
|
|
||||||
|
RuntimeJIT::RuntimeJIT(const std::string& domain_name){
|
||||||
|
mono_set_dirs("mono-libs", "mono-libs");
|
||||||
|
mono_set_assemblies_path("mono-libs");
|
||||||
|
mono_config_parse("mono-libs/config.xml");
|
||||||
|
|
||||||
|
domain = mono_jit_init(domain_name.c_str());
|
||||||
|
if(!domain)
|
||||||
|
throw UsefulException("can't initialize mono domain");
|
||||||
|
}
|
||||||
|
|
||||||
|
RuntimeJIT::~RuntimeJIT(){
|
||||||
|
// TODO: fix segfault on cleanup
|
||||||
|
// mono_jit_cleanup(domain);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Assembly> RuntimeJIT::loadAssembly(const std::string &name){
|
||||||
|
MonoAssembly* ptr = mono_domain_assembly_open(domain, name.c_str());
|
||||||
|
if(!ptr)
|
||||||
|
throw UsefulException(ougge_format("can't load assembly '%s'", name.c_str()));
|
||||||
|
return std::make_shared<Assembly>(ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
81
src/mono/mono.cpp
Normal file
81
src/mono/mono.cpp
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
#include "mono.hpp"
|
||||||
|
#include <mono/metadata/appdomain.h>
|
||||||
|
|
||||||
|
namespace Mono {
|
||||||
|
|
||||||
|
template <> MonoClass* getClass<SByte>(){ return mono_get_sbyte_class(); }
|
||||||
|
template <> MonoClass* getClass<Byte>(){ return mono_get_byte_class(); }
|
||||||
|
template <> MonoClass* getClass<Short>(){ return mono_get_int16_class(); }
|
||||||
|
template <> MonoClass* getClass<UShort>(){ return mono_get_uint16_class(); }
|
||||||
|
template <> MonoClass* getClass<Int>(){ return mono_get_int32_class(); }
|
||||||
|
template <> MonoClass* getClass<UInt>(){ return mono_get_uint32_class(); }
|
||||||
|
template <> MonoClass* getClass<Long>(){ return mono_get_int64_class(); }
|
||||||
|
template <> MonoClass* getClass<ULong>(){ return mono_get_uint64_class(); }
|
||||||
|
template <> MonoClass* getClass<Float>(){ return mono_get_single_class(); }
|
||||||
|
template <> MonoClass* getClass<Double>(){ return mono_get_double_class(); }
|
||||||
|
template <> MonoClass* getClass<Bool>(){ return mono_get_boolean_class(); }
|
||||||
|
template <> MonoClass* getClass<Char>(){ return mono_get_char_class(); }
|
||||||
|
template <> MonoClass* getClass<String>(){ return mono_get_string_class(); }
|
||||||
|
template <> MonoClass* getClass<Object>(){ return mono_get_object_class(); }
|
||||||
|
template <> MonoClass* getClass<Void>(){ return mono_get_void_class(); }
|
||||||
|
|
||||||
|
|
||||||
|
void getMethodSignatureTypes(MonoMethod* mono_method,
|
||||||
|
MonoType** return_type,
|
||||||
|
std::vector<MonoType*>& argument_types)
|
||||||
|
{
|
||||||
|
auto signature = mono_method_signature(mono_method);
|
||||||
|
void* iter = nullptr;
|
||||||
|
*return_type = mono_signature_get_return_type(signature);
|
||||||
|
MonoType* pt = nullptr;
|
||||||
|
while( (pt = mono_signature_get_params(signature, &iter)) ){
|
||||||
|
argument_types.push_back(pt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MonoMethod* tryGetMonoMethod(MonoClass* target_class, const std::string& name,
|
||||||
|
MonoClass* return_class, MonoClass* arg_classes[], size_t arg_classes_size)
|
||||||
|
{
|
||||||
|
if(target_class == nullptr)
|
||||||
|
throw UsefulException("target_class is nullptr");
|
||||||
|
|
||||||
|
// iterate each method
|
||||||
|
void* iter = nullptr;
|
||||||
|
MonoMethod* m = nullptr;
|
||||||
|
std::vector<MonoType*> argument_types;
|
||||||
|
while( (m = mono_class_get_methods(target_class, &iter)) ){
|
||||||
|
// compare name
|
||||||
|
std::string m_name = mono_method_get_name(m);
|
||||||
|
if(m_name != name)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
argument_types.clear();
|
||||||
|
MonoType* return_type = nullptr;
|
||||||
|
getMethodSignatureTypes(m, &return_type, argument_types);
|
||||||
|
// compare argument count
|
||||||
|
if(argument_types.size() != arg_classes_size)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// compare return type
|
||||||
|
if(!mono_metadata_type_equal(return_type, mono_class_get_type(return_class)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// compare argument types
|
||||||
|
bool argument_types_mismatch = false;
|
||||||
|
for(size_t i = 0; i < arg_classes_size; i++){
|
||||||
|
if(!mono_metadata_type_equal(argument_types[i], mono_class_get_type(arg_classes[i]))){
|
||||||
|
argument_types_mismatch = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(argument_types_mismatch)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// passed all tests successfully
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return m;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
187
src/mono/mono.hpp
Normal file
187
src/mono/mono.hpp
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../common/std.hpp"
|
||||||
|
#include "../common/UsefulException.hpp"
|
||||||
|
#include "../common/ougge_format.hpp"
|
||||||
|
#include <vector>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <mono/metadata/class.h>
|
||||||
|
#include <mono/metadata/assembly.h>
|
||||||
|
#include <mono/metadata/object.h>
|
||||||
|
|
||||||
|
namespace Mono {
|
||||||
|
|
||||||
|
typedef i8 SByte;
|
||||||
|
typedef u8 Byte;
|
||||||
|
typedef i16 Short;
|
||||||
|
typedef u16 UShort;
|
||||||
|
typedef i32 Int;
|
||||||
|
typedef u32 UInt;
|
||||||
|
typedef i64 Long;
|
||||||
|
typedef u64 ULong;
|
||||||
|
typedef f32 Float;
|
||||||
|
typedef f64 Double;
|
||||||
|
typedef union { mono_bool wide_bool; } Bool; //USAGE: Bool t = {true};
|
||||||
|
typedef char16_t Char;
|
||||||
|
typedef MonoString* String;
|
||||||
|
/// @warning MonoObject can be moved in memory by GC in any time and raw pointer will be invalid.
|
||||||
|
/// Use ObjectHandle where it is possible.
|
||||||
|
typedef MonoObject* Object;
|
||||||
|
typedef void Void;
|
||||||
|
|
||||||
|
|
||||||
|
template<typename PrimitiveT>
|
||||||
|
MonoClass* getClass();
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
void* valueToVoidPtr(T& v){ return &v; }
|
||||||
|
template<>
|
||||||
|
inline void* valueToVoidPtr<Object>(Object& v){ return v; }
|
||||||
|
template<>
|
||||||
|
inline void* valueToVoidPtr<String>(String& v){ return v; }
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
T valueFromMonoObject(MonoObject* o){
|
||||||
|
void* result_value_ptr = mono_object_unbox(o);
|
||||||
|
if(result_value_ptr == nullptr)
|
||||||
|
throw UsefulException("can't unbox method value");
|
||||||
|
return *((T*)result_value_ptr);
|
||||||
|
}
|
||||||
|
template<>
|
||||||
|
inline Object valueFromMonoObject<Object>(MonoObject* o){ return o; }
|
||||||
|
template<>
|
||||||
|
inline String valueFromMonoObject<String>(MonoObject* o){ return (String)((void*)o); }
|
||||||
|
|
||||||
|
void getMethodSignatureTypes(MonoMethod* mono_method,
|
||||||
|
MonoType** return_type,
|
||||||
|
std::vector<MonoType*>& argument_types);
|
||||||
|
|
||||||
|
/// searches for method `name` in `target_class` with return type `return_class` and argument types `arg_classes`
|
||||||
|
/// @return found method or nullptr
|
||||||
|
MonoMethod* tryGetMonoMethod(MonoClass* target_class, const std::string& name,
|
||||||
|
MonoClass* return_class, MonoClass* arg_classes[], size_t arg_classes_size);
|
||||||
|
|
||||||
|
|
||||||
|
template<typename SignatureT> class Method;
|
||||||
|
template<typename ReturnT, typename... ArgTypes>
|
||||||
|
class Method<ReturnT(ArgTypes...)>
|
||||||
|
{
|
||||||
|
MonoMethod* method_ptr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Method() { method_ptr = nullptr; }
|
||||||
|
|
||||||
|
/// all types must implement getClass<T>()
|
||||||
|
Method(MonoClass* target_class, const std::string& name){
|
||||||
|
static MonoClass* arg_classes[] { getClass<ArgTypes>()... };
|
||||||
|
static MonoClass* return_class { getClass<ReturnT>() };
|
||||||
|
method_ptr = tryGetMonoMethod(target_class, name, return_class, arg_classes, sizeof...(ArgTypes));
|
||||||
|
if(method_ptr == nullptr){
|
||||||
|
throw UsefulException(ougge_format("can't get method '%s' from class '%s'",
|
||||||
|
name.c_str(), mono_class_get_name(target_class)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReturnT not is void
|
||||||
|
template<typename RT = ReturnT>
|
||||||
|
std::enable_if_t<!std::is_void<RT>::value, RT> operator()(MonoObject* class_instance, ArgTypes... args) const {
|
||||||
|
if(method_ptr == nullptr)
|
||||||
|
throw UsefulException("method_ptr is null");
|
||||||
|
|
||||||
|
void* arg_array[] = { valueToVoidPtr(args)..., nullptr };
|
||||||
|
MonoObject* ex = nullptr;
|
||||||
|
MonoObject* result = mono_runtime_invoke(method_ptr, class_instance, arg_array, &ex);
|
||||||
|
if(ex){
|
||||||
|
//TODO: call mono_trace_set_printerr_handler from mono/mono/utils/mono-logger.h
|
||||||
|
mono_print_unhandled_exception(ex);
|
||||||
|
throw UsefulException("Some C# exception occured");
|
||||||
|
}
|
||||||
|
return valueFromMonoObject<ReturnT>(result);
|
||||||
|
};
|
||||||
|
|
||||||
|
// ReturnT is void
|
||||||
|
template<typename RT = ReturnT>
|
||||||
|
std::enable_if_t<std::is_void<RT>::value, RT> operator()(MonoObject* class_instance, ArgTypes... args) const {
|
||||||
|
if(method_ptr == nullptr)
|
||||||
|
throw UsefulException("method_ptr is null");
|
||||||
|
|
||||||
|
void* arg_array[] = { valueToVoidPtr(args)..., nullptr };
|
||||||
|
MonoObject* ex = nullptr;
|
||||||
|
mono_runtime_invoke(method_ptr, class_instance, arg_array, &ex);
|
||||||
|
if(ex){
|
||||||
|
//TODO: call mono_trace_set_printerr_handler from mono/mono/utils/mono-logger.h
|
||||||
|
mono_print_unhandled_exception(ex);
|
||||||
|
throw UsefulException("Some C# exception occured");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class Assembly {
|
||||||
|
MonoAssembly* ptr;
|
||||||
|
MonoImage* image;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Assembly(MonoAssembly* ptr);
|
||||||
|
Assembly(const Assembly&) = delete;
|
||||||
|
|
||||||
|
MonoClass* getClass(const std::string& name_space, const std::string& name);
|
||||||
|
MonoAssembly* getMonoAssembly() const { return ptr; }
|
||||||
|
MonoImage* getMonoImage() const { return image; }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
///LINUX: `config.xml`, `mscorelib.dll`, `libmono-native.so` in `mono-libs` directory
|
||||||
|
///
|
||||||
|
///WINDOWS: `config.xml`, `mscorelib.dll` in `mono-libs` directory
|
||||||
|
class RuntimeJIT {
|
||||||
|
MonoDomain* domain;
|
||||||
|
public:
|
||||||
|
RuntimeJIT(const std::string& domain_name = "OuggeDomain");
|
||||||
|
RuntimeJIT(const RuntimeJIT&) = delete;
|
||||||
|
~RuntimeJIT();
|
||||||
|
|
||||||
|
inline MonoDomain* getDomain() { return domain; }
|
||||||
|
std::shared_ptr<Assembly> loadAssembly(const std::string& name);
|
||||||
|
|
||||||
|
inline Object createObject(MonoClass* klass) { return mono_object_new(domain, klass); }
|
||||||
|
inline String createString(const std::string& v) { return mono_string_new_len(domain, v.c_str(), v.size()); }
|
||||||
|
inline String createString(const char* v) { return mono_string_new(domain, v); }
|
||||||
|
};
|
||||||
|
|
||||||
|
/// @brief ObjectHandle can be used to store reliable reference to MonoObject.
|
||||||
|
/// MonoObject can be moved in memory by GC in any time and raw pointer will be invalid.
|
||||||
|
struct ObjectHandle {
|
||||||
|
u32 gc_handle;
|
||||||
|
|
||||||
|
inline ObjectHandle() : gc_handle(0) {}
|
||||||
|
|
||||||
|
inline ObjectHandle(Object obj) {
|
||||||
|
gc_handle = mono_gchandle_new(obj, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// implicitly create new ObjectHandle instead
|
||||||
|
inline ObjectHandle(const ObjectHandle& o) = delete;
|
||||||
|
|
||||||
|
inline ObjectHandle(ObjectHandle&& o) {
|
||||||
|
gc_handle = o.gc_handle;
|
||||||
|
o.gc_handle = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ObjectHandle& operator=(ObjectHandle&& o) {
|
||||||
|
gc_handle = o.gc_handle;
|
||||||
|
o.gc_handle = 0;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline ~ObjectHandle() {
|
||||||
|
if(gc_handle)
|
||||||
|
mono_gchandle_free(gc_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Object getObject() const {
|
||||||
|
return mono_gchandle_get_target(gc_handle);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
64
src/resources/embedded_resources.h
Normal file
64
src/resources/embedded_resources.h
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
// This file was generated by resource_embedder //
|
||||||
|
// https://timerix.ddns.net:3322/Timerix/resource_embedder //
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
// USAGE: //
|
||||||
|
// Put it in a SOURCE file to define variables //
|
||||||
|
// #define EMBEDDED_RESOURCE_DEFINITION //
|
||||||
|
// #define EMBEDDED_RESOURCE_POSTFIX your_postfix //
|
||||||
|
// #include "../../src/Resources/embedded_resources.h" //
|
||||||
|
// //
|
||||||
|
// Put it in a HEADER file to declare external variables //
|
||||||
|
// #define EMBEDDED_RESOURCE_POSTFIX your_postfix //
|
||||||
|
// #include "../../src/Resources/embedded_resources.h" //
|
||||||
|
// //
|
||||||
|
// Then you can access embedded files through //
|
||||||
|
// EmbeddedResource_table_your_postfix. You can get table //
|
||||||
|
// content by index and put it into a hashtable or a map. //
|
||||||
|
////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#if __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
const char* path;
|
||||||
|
const char* data;
|
||||||
|
size_t size;
|
||||||
|
} EmbeddedResource;
|
||||||
|
|
||||||
|
#define RSCAT(A,B,C...) A##B##C
|
||||||
|
#ifdef EMBEDDED_RESOURCE_POSTFIX
|
||||||
|
#define _EmbeddedResource_table(P) \
|
||||||
|
RSCAT(EmbeddedResource_table_, P)
|
||||||
|
#define _EmbeddedResource_table_count(P) \
|
||||||
|
RSCAT(EmbeddedResource_table_, P, _count)
|
||||||
|
#else
|
||||||
|
#define _EmbeddedResource_table(P) \
|
||||||
|
EmbeddedResource_table
|
||||||
|
#define _EmbeddedResource_table_count(P) \
|
||||||
|
EmbeddedResource_table_count
|
||||||
|
#endif
|
||||||
|
extern const EmbeddedResource _EmbeddedResource_table(EMBEDDED_RESOURCE_POSTFIX)[];
|
||||||
|
extern const int _EmbeddedResource_table_count(EMBEDDED_RESOURCE_POSTFIX);
|
||||||
|
|
||||||
|
#ifdef EMBEDDED_RESOURCE_DEFINITION
|
||||||
|
const EmbeddedResource _EmbeddedResource_table(EMBEDDED_RESOURCE_POSTFIX)[]={
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const int _EmbeddedResource_table_count(EMBEDDED_RESOURCE_POSTFIX)=0;
|
||||||
|
#endif // EMBEDDED_RESOURCE_DEFINITION
|
||||||
|
|
||||||
|
#undef _EmbeddedResource_table
|
||||||
|
#undef _EmbeddedResource_table_count
|
||||||
|
#undef EMBEDDED_RESOURCE_POSTFIX
|
||||||
|
#undef RSCAT
|
||||||
|
|
||||||
|
#if __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
33
src/resources/fonts.cpp
Normal file
33
src/resources/fonts.cpp
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
#include <imgui.h>
|
||||||
|
#include "resources.hpp"
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
namespace ougge::resources {
|
||||||
|
|
||||||
|
// select all glyphs from font
|
||||||
|
static const ImWchar glyph_ranges[] = {
|
||||||
|
0x0020, 0xFFFF, 0
|
||||||
|
};
|
||||||
|
|
||||||
|
ImFont* ImFont_LoadFromFile(const std::string& file_path, f32 font_size, f32 dpi){
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
font_size *= dpi;
|
||||||
|
return io.Fonts->AddFontFromFileTTF(file_path.c_str(), font_size, nullptr, glyph_ranges);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImFont* ImFont_LoadFromResource(const std::string& font_name, f32 font_size, f32 dpi){
|
||||||
|
ImGuiIO& io = ImGui::GetIO();
|
||||||
|
font_size *= dpi;
|
||||||
|
ImFontConfig font_cfg = ImFontConfig();
|
||||||
|
std::sprintf(font_cfg.Name, "%s %ipx", font_name.c_str(), (i32)font_size);
|
||||||
|
|
||||||
|
auto& res = getResource("fonts/" + font_name + ".ttf");
|
||||||
|
char* font_data = new char[res.size];
|
||||||
|
res.openStream()->read(font_data, res.size);
|
||||||
|
|
||||||
|
return io.Fonts->AddFontFromMemoryTTF((void*)(font_data), res.size,
|
||||||
|
font_size, &font_cfg, glyph_ranges);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
12
src/resources/fonts.hpp
Normal file
12
src/resources/fonts.hpp
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "imgui.h"
|
||||||
|
#include "../common/std.hpp"
|
||||||
|
|
||||||
|
namespace ougge::resources {
|
||||||
|
|
||||||
|
ImFont* ImFont_LoadFromFile(const std::string& file_path, f32 font_size, f32 dpi);
|
||||||
|
|
||||||
|
ImFont* ImFont_LoadFromResource(const std::string& font_name, f32 font_size, f32 dpi);
|
||||||
|
|
||||||
|
}
|
||||||
94
src/resources/resources.cpp
Normal file
94
src/resources/resources.cpp
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
#include "../common/UsefulException.hpp"
|
||||||
|
#include "../common/ougge_format.hpp"
|
||||||
|
#include "resources.hpp"
|
||||||
|
#include "embedded_resources.h"
|
||||||
|
|
||||||
|
|
||||||
|
namespace ougge::resources {
|
||||||
|
|
||||||
|
Resource::Resource(const std::string& path, const std::size_t size, StreamFactoryMethod open_read_steam_func)
|
||||||
|
: path(path), size(size), openStream(open_read_steam_func)
|
||||||
|
{}
|
||||||
|
|
||||||
|
MemoryStreamBuf::MemoryStreamBuf(void* _p, const std::size_t n){
|
||||||
|
char* p=(char*)_p;
|
||||||
|
setg(p, p, p + n);
|
||||||
|
setp(p, p + n);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::istream::pos_type MemoryStreamBuf::seekoff(
|
||||||
|
std::istream::off_type off,
|
||||||
|
std::ios_base::seekdir dir,
|
||||||
|
std::ios_base::openmode which)
|
||||||
|
{
|
||||||
|
if (dir == std::ios_base::cur)
|
||||||
|
gbump(off);
|
||||||
|
else if (dir == std::ios_base::end)
|
||||||
|
setg(eback(), egptr() + off, egptr());
|
||||||
|
else if (dir == std::ios_base::beg)
|
||||||
|
setg(eback(), eback() + off, egptr());
|
||||||
|
return gptr() - eback();
|
||||||
|
}
|
||||||
|
|
||||||
|
MemoryStreamRead::MemoryStreamRead(const void* p, const std::size_t n)
|
||||||
|
: std::istream(new MemoryStreamBuf((void*)p, n))
|
||||||
|
{}
|
||||||
|
|
||||||
|
MemoryStreamRead::~MemoryStreamRead(){
|
||||||
|
delete rdbuf();
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::unordered_map<std::string, Resource>* _resourceMap = nullptr;
|
||||||
|
|
||||||
|
void loadEmbeddedresources(){
|
||||||
|
for(int i = 0; i < EmbeddedResource_table_count; i++){
|
||||||
|
const EmbeddedResource& e = EmbeddedResource_table[i];
|
||||||
|
std::cout <<"loading resource '" << e.path << "' "
|
||||||
|
<< formatSizeHumanReadable(e.size) << std::endl;
|
||||||
|
|
||||||
|
auto embedded_resource_factory = [e]() -> std::unique_ptr<std::istream> {
|
||||||
|
return std::make_unique<MemoryStreamRead>(e.data, e.size);
|
||||||
|
};
|
||||||
|
|
||||||
|
auto r = _resourceMap->emplace(e.path,
|
||||||
|
Resource(e.path, e.size, embedded_resource_factory));
|
||||||
|
if(!r.second)
|
||||||
|
throw UsefulException(ougge_format("can't load duplicate resource '%s'", e.path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void init(){
|
||||||
|
if(_resourceMap != nullptr)
|
||||||
|
throw UsefulException("resource has been initialized already");
|
||||||
|
_resourceMap = new std::unordered_map<std::string, Resource>();
|
||||||
|
loadEmbeddedresources();
|
||||||
|
}
|
||||||
|
|
||||||
|
Resource& getResource(const std::string& path){
|
||||||
|
auto it = _resourceMap->find(path);
|
||||||
|
if(it == _resourceMap->end())
|
||||||
|
throw UsefulException(ougge_format("can't find resource '%s'", path.c_str()));
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string formatSizeHumanReadable(std::size_t b){
|
||||||
|
std::stringstream ss;
|
||||||
|
ss.precision(3);
|
||||||
|
std::size_t k = b / 1024;
|
||||||
|
std::size_t m = k / 1024;
|
||||||
|
std::size_t g = m / 1024;
|
||||||
|
|
||||||
|
if(g)
|
||||||
|
ss<<m / 1024.0f<<'G';
|
||||||
|
else if(m)
|
||||||
|
ss<<k / 1024.0f<<'M';
|
||||||
|
else if(k)
|
||||||
|
ss<<b / 1024.0f<<'K';
|
||||||
|
else ss<<b;
|
||||||
|
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
63
src/resources/resources.hpp
Normal file
63
src/resources/resources.hpp
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "../common/std.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
#include <functional>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
namespace ougge::resources {
|
||||||
|
|
||||||
|
// call this in main()
|
||||||
|
void init();
|
||||||
|
|
||||||
|
std::string formatSizeHumanReadable(std::size_t byte_n);
|
||||||
|
|
||||||
|
class Resource {
|
||||||
|
public:
|
||||||
|
const std::string path;
|
||||||
|
const std::size_t size;
|
||||||
|
|
||||||
|
using StreamFactoryMethod = std::function< std::unique_ptr<std::istream> () >;
|
||||||
|
|
||||||
|
Resource(const std::string& path, const std::size_t size, StreamFactoryMethod open_read_steam_func);
|
||||||
|
|
||||||
|
const StreamFactoryMethod openStream;
|
||||||
|
};
|
||||||
|
|
||||||
|
class MemoryStreamBuf : public std::streambuf {
|
||||||
|
public:
|
||||||
|
MemoryStreamBuf(void* p, const std::size_t n);
|
||||||
|
|
||||||
|
virtual std::istream::pos_type seekoff(
|
||||||
|
std::istream::off_type off,
|
||||||
|
std::ios_base::seekdir dir,
|
||||||
|
std::ios_base::openmode which);
|
||||||
|
};
|
||||||
|
|
||||||
|
class MemoryStreamRead : public std::istream {
|
||||||
|
public:
|
||||||
|
MemoryStreamRead(const void* p, const std::size_t n);
|
||||||
|
|
||||||
|
virtual ~MemoryStreamRead();
|
||||||
|
};
|
||||||
|
|
||||||
|
Resource& getResource(const std::string& path);
|
||||||
|
|
||||||
|
/// @brief stores requested resources in memory
|
||||||
|
/// @tparam T must implement constructor `T(const Resource&, ...)`
|
||||||
|
template<class T>
|
||||||
|
class CacheStorage {
|
||||||
|
std::unordered_map<std::string, T> _map;
|
||||||
|
|
||||||
|
public:
|
||||||
|
template<typename... TCtorArgs>
|
||||||
|
T& getOrCreate(const std::string& name, TCtorArgs&&... ctor_args){
|
||||||
|
auto it = _map.find(name);
|
||||||
|
if(it != _map.end())
|
||||||
|
return it->second;
|
||||||
|
auto& res = getResource(name);
|
||||||
|
auto e = _map.emplace(name, T(res, ctor_args...));
|
||||||
|
return e.first->second;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
96
src/resources/textures.cpp
Normal file
96
src/resources/textures.cpp
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
#include "textures.hpp"
|
||||||
|
#include <SDL_image.h>
|
||||||
|
#include "../gui/gui_exceptions.hpp"
|
||||||
|
|
||||||
|
namespace ougge::resources {
|
||||||
|
|
||||||
|
Texture::Texture(const Resource& r, SDL_Renderer* renderer)
|
||||||
|
: Texture(*r.openStream(), r.size, renderer)
|
||||||
|
{}
|
||||||
|
|
||||||
|
Texture::Texture(std::istream& s, size_t size, SDL_Renderer* renderer)
|
||||||
|
: renderer(renderer), texture(nullptr), w(0), h(0)
|
||||||
|
{
|
||||||
|
SDL_RWops* sdl_stream = SDL_RWFromIStream(s, size);
|
||||||
|
if(!sdl_stream)
|
||||||
|
throw gui::SDLException();
|
||||||
|
texture = std::shared_ptr<SDL_Texture>(IMG_LoadTexture_RW(renderer, sdl_stream, 1), SDL_DestroyTexture);
|
||||||
|
if(!texture)
|
||||||
|
throw gui::IMGException();
|
||||||
|
SDL_TRY(SDL_QueryTexture(texture.get(), nullptr, nullptr, &w, &h));
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_RenderCopyExF_Params::SDL_RenderCopyExF_Params()
|
||||||
|
: rotation_angle(0), flip(SDL_FLIP_NONE)
|
||||||
|
{}
|
||||||
|
|
||||||
|
void Texture::render(const SDL_FRect& target_section){
|
||||||
|
SDL_TRY(
|
||||||
|
SDL_RenderCopyF(renderer, texture.get(), nullptr, &target_section)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Texture::render(const SDL_FRect& target_section, const SDL_Rect& texture_section){
|
||||||
|
SDL_TRY(
|
||||||
|
SDL_RenderCopyF(renderer, texture.get(), &texture_section, &target_section)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Texture::render(const SDL_RenderCopyExF_Params& p){
|
||||||
|
SDL_TRY(
|
||||||
|
SDL_RenderCopyExF(renderer, texture.get(),
|
||||||
|
optional_value_ptr_or_null(p.texture_section),
|
||||||
|
optional_value_ptr_or_null(p.target_section),
|
||||||
|
-1.0f * angleToDegree(normalizeAngle(p.rotation_angle)),
|
||||||
|
optional_value_ptr_or_null(p.rotation_center),
|
||||||
|
p.flip
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
static Sint64 istream_size(SDL_RWops* context){
|
||||||
|
return (Sint64)(size_t)context->hidden.unknown.data2;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Sint64 istream_seek(SDL_RWops* context, Sint64 offset, i32 whence){
|
||||||
|
std::istream* stream = (std::istream*)context->hidden.unknown.data1;
|
||||||
|
switch(whence){
|
||||||
|
case SEEK_SET: stream->seekg(offset, std::ios::beg); break;
|
||||||
|
case SEEK_CUR: stream->seekg(offset, std::ios::cur); break;
|
||||||
|
case SEEK_END: stream->seekg(offset, std::ios::end); break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
return stream->fail() ? -1 : (Sint64)stream->tellg();
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t istream_read(SDL_RWops* context, void *ptr, size_t size, size_t maxnum){
|
||||||
|
if(size == 0)
|
||||||
|
return -1;
|
||||||
|
std::istream* stream = (std::istream*)context->hidden.unknown.data1;
|
||||||
|
stream->read((char*)ptr, size * maxnum);
|
||||||
|
|
||||||
|
return stream->bad() ? -1 : stream->gcount() / size;
|
||||||
|
}
|
||||||
|
|
||||||
|
static i32 istream_close(SDL_RWops* context){
|
||||||
|
if (context)
|
||||||
|
SDL_FreeRW(context);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_RWops* SDL_RWFromIStream(std::istream& stream, size_t size){
|
||||||
|
SDL_RWops* rwops = SDL_AllocRW();
|
||||||
|
if(rwops) {
|
||||||
|
rwops->size = istream_size;
|
||||||
|
rwops->seek = istream_seek;
|
||||||
|
rwops->read = istream_read;
|
||||||
|
rwops->write = nullptr;
|
||||||
|
rwops->close = istream_close;
|
||||||
|
rwops->hidden.unknown.data1 = &stream;
|
||||||
|
rwops->hidden.unknown.data2 = (void*)size;
|
||||||
|
rwops->type = SDL_RWOPS_UNKNOWN;
|
||||||
|
}
|
||||||
|
return rwops;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
44
src/resources/textures.hpp
Normal file
44
src/resources/textures.hpp
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <optional>
|
||||||
|
#include <SDL.h>
|
||||||
|
#include "resources.hpp"
|
||||||
|
#include "../common/math.hpp"
|
||||||
|
|
||||||
|
namespace ougge::resources {
|
||||||
|
|
||||||
|
#define SDL_RectConstruct(X, Y, W, H) (SDL_Rect){X, Y, W, H}
|
||||||
|
#define SDL_FRectConstruct(X, Y, W, H) (SDL_FRect){X, Y, W, H}
|
||||||
|
#define SDL_PointConstruct(X, Y) (SDL_Point){X, Y, W, H}
|
||||||
|
#define SDL_FPointConstruct(X, Y) (SDL_FPoint){X, Y, W, H}
|
||||||
|
|
||||||
|
#define optional_value_ptr_or_null(OPT) (OPT ? &(*OPT) : nullptr)
|
||||||
|
|
||||||
|
SDL_RWops* SDL_RWFromIStream(std::istream& stream, size_t size);
|
||||||
|
|
||||||
|
struct SDL_RenderCopyExF_Params {
|
||||||
|
std::optional<SDL_Rect> texture_section;
|
||||||
|
std::optional<SDL_FRect> target_section;
|
||||||
|
std::optional<SDL_FPoint> rotation_center;
|
||||||
|
angle_t rotation_angle;
|
||||||
|
SDL_RendererFlip flip;
|
||||||
|
|
||||||
|
SDL_RenderCopyExF_Params();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Texture {
|
||||||
|
SDL_Renderer* renderer;
|
||||||
|
std::shared_ptr<SDL_Texture> texture;
|
||||||
|
i32 w;
|
||||||
|
i32 h;
|
||||||
|
|
||||||
|
Texture(const Resource& r, SDL_Renderer* renderer);
|
||||||
|
Texture(std::istream& s, size_t size, SDL_Renderer* renderer);
|
||||||
|
|
||||||
|
void render(const SDL_FRect& target_section);
|
||||||
|
void render(const SDL_FRect& target_section, const SDL_Rect& texture_section);
|
||||||
|
void render(const SDL_RenderCopyExF_Params& params);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
try_delete_dir_or_file src/generated
|
|
||||||
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"
|
||||||
Reference in New Issue
Block a user