Compare commits

...

43 Commits

Author SHA1 Message Date
7e7454278d cimgui update 2026-01-09 12:12:45 +05:00
346779060e something something 2026-01-09 11:57:24 +05:00
175fe61e5c implemented functions from NativeFunctions.cs 2025-07-05 03:22:48 +03:00
f7a8d32865 cimgui update 2025-07-05 01:49:54 +03:00
9a283a2904 implemented createModule() and getModule() 2025-06-20 21:51:56 +05:00
a288d0961f split engine code into modules 2025-06-18 16:06:54 +05:00
8fd6cee223 added game_object_pool_size as Engine constructor argument 2025-05-19 03:45:28 +05:00
851e1ee122 added src back 2025-05-19 03:37:02 +05:00
fbd6d43e89 removed src (to get rid of duplicates) 2025-05-19 03:35:56 +05:00
609f7337da created ResourceManager 2025-05-19 03:26:18 +05:00
d700aae02e duplicate files fix 2025-05-19 02:27:39 +05:00
d5531ce370 replaced imgui with cimgui shared lib to use with c# bindings 2025-04-29 02:37:15 +05:00
72f47c297e fixed nullable object warning 2025-04-29 01:33:26 +05:00
1940d01d9b fixed mingw libraries static compilation 2025-04-26 01:41:04 +05:00
5c6fe5944a c# TargetFramework net8.0 2025-04-26 01:09:50 +05:00
d659dcde10 refactored MainWindow and Engine, changed project structure 2025-04-26 00:59:47 +05:00
bb00392e3a update to cbuild 2.2.1 2025-04-26 00:47:25 +05:00
3477b05cd8 created Engine class 2025-04-24 04:50:56 +05:00
366dd1214c switched to mono continued fork 2025-04-22 00:15:28 +05:00
51259e72fe fixed GameObject initialization 2025-04-21 21:19:17 +05:00
5c247ce032 calling mono_gchandle_get_target instead of using expiring raw pointers 2025-04-21 21:16:07 +05:00
5d84e744ce new GameObject[] instead of uninitialized byte buffer 2025-04-21 20:13:11 +05:00
0863408bc5 disabled mono libs strip 2025-04-21 20:02:16 +05:00
0852a0028f fixed valgrind sources path trimming 2025-04-21 18:43:54 +05:00
bfa9bf592a mkdir -p "dependencies/precompiled" 2025-04-21 17:57:35 +05:00
25475e8013 fixed git repo url 2025-04-21 16:50:22 +05:00
d4de309ca7 changed my git server address 2025-04-18 22:49:45 +05:00
d5d28d4884 tried to fix bugs (partial success) 2025-04-18 22:31:32 +05:00
04e4f63fd7 GameObject and Component 2025-04-18 18:24:02 +05:00
47574e9b30 GameObjectPool fixes 2024-09-14 21:28:18 +05:00
3df4361779 GameObjectPool 2024-09-13 22:58:31 +05:00
bff2182ff0 chmod +x 2024-09-13 21:01:35 +05:00
ec7a8de0cf c# update callback 2024-09-10 23:23:37 +05:00
e71a6b71fe removed temporary code 2024-09-10 20:28:11 +05:00
71f77ce89c Mono::Method 2024-09-10 19:15:41 +05:00
42b233e6a4 include 2024-09-10 16:00:16 +05:00
9eef21df1b removed unnecessary OOP stuff 2024-09-10 02:40:00 +05:00
Timerix
71eea1c0fe Class::getMethod 2024-08-22 08:36:01 +05:00
Altair-sh
478e6049c9 gitignore 2024-08-22 04:29:07 +05:00
d8e6c69c20 mono C++ wrapper 2024-08-16 19:32:36 +03:00
1859b432df fixed bugs on windows 2024-08-10 00:51:34 +03:00
2710e5fc9d embedded mono runtime 2024-08-09 23:02:20 +03:00
0aefa70fb8 math.hpp 2024-08-08 18:03:20 +03:00
65 changed files with 1994 additions and 585 deletions

1
.gitignore vendored
View File

@@ -20,3 +20,4 @@ temp/
logs/ logs/
log/ log/
*.log *.log
/mono/

9
.gitmodules vendored
View File

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

View File

@@ -7,10 +7,12 @@
"DEBUG=1" "DEBUG=1"
], ],
"includePath": [ "includePath": [
"dependencies/include",
"dependencies/include/SDL2", "dependencies/include/SDL2",
"dependencies/imgui", "dependencies/cimgui/imgui",
"${default}" "${default}"
] ],
"cppStandard": "c++20"
} }
], ],
"version": 4 "version": 4

1
.vscode/launch.json vendored
View File

@@ -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
View File

@@ -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}"

View File

@@ -2,22 +2,30 @@
A game engine or something, idk. A game engine or something, idk.
## Installation ## Installation
1. Clone the repository. 1. **Clone the repository.**
```sh ```sh
git clone --recurse-submodules https://timerix.ddns.net:3322/Timerix/ougge.git git clone --recurse-submodules --depth 1 https://timerix.ddns.net/git/Timerix/ougge.git
``` ```
2. Install [cbuild](https://timerix.ddns.net:3322/Timerix/cbuild.git). 2. **Install [cbuild](https://timerix.ddns.net/git/Timerix/cbuild).**
3. Install [SDL2](https://github.com/libsdl-org/SDL) and [SDL2_image](https://github.com/libsdl-org/SDL_image). 3. **Install [SDL2](https://github.com/libsdl-org/SDL) and [SDL2_image](https://github.com/libsdl-org/SDL_image).**
- On **Linux** install shared libraries from a package manager or compile them from source. - On **Linux** install shared libraries from a package manager or compile them from source.
- On **Windows** download pre-built dll's from github releases and put them into `dependencies/precompiled/`. - On **Windows** download pre-built dll's from github releases and put them into `dependencies/precompiled/`.
4. Symlink SDL headers directory to `dependencies/include`. 4. **Symlink SDL headers directory** to `dependencies/include`.
```sh ```sh
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
``` ```

1
dependencies/cimgui vendored Submodule

Submodule dependencies/cimgui added at d94ad1b162

22
dependencies/cimgui.config vendored Normal file
View File

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

View File

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

0
dependencies/compile_resources.sh vendored Normal file → Executable file
View File

1
dependencies/imgui vendored

Submodule dependencies/imgui deleted from 7b6314f47d

View File

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

19
dependencies/precompiled.config vendored Normal file → Executable file
View 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"

0
dependencies/resources.config vendored Normal file → Executable file
View File

12
dependencies/src-csharp.config vendored Executable file
View File

@@ -0,0 +1,12 @@
#!/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/Tomlyn.dll
bin/Hexa.NET.ImGui.dll
bin/HexaGen.Runtime.dll'

16
project.config Normal file → Executable file
View 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='precompiled resources imgui' ENABLED_DEPENDENCIES='precompiled resources cimgui src-csharp'
# OBJDIR structure: # OBJDIR structure:
# ├── objects/ - Compiled object files. Cleans on each call of build task # ├── objects/ - Compiled object files. Cleans on each call of build task
@@ -28,14 +27,14 @@ OUTDIR="bin"
STATIC_LIB_FILE="lib$PROJECT.a" STATIC_LIB_FILE="lib$PROJECT.a"
# header include directories # header include directories
INCLUDE="-I./dependencies/imgui -I./dependencies/include/SDL2" INCLUDE="-I./dependencies/cimgui/imgui -I./dependencies/include -I./dependencies/include/SDL2"
# OS-specific options # OS-specific options
case "$OS" in case "$OS" in
WINDOWS) WINDOWS)
EXEC_FILE="$PROJECT.exe" EXEC_FILE="$PROJECT.exe"
SHARED_LIB_FILE="$PROJECT.dll" SHARED_LIB_FILE="$PROJECT.dll"
LINKER_LIBS="" LINKER_LIBS="-static -lstdc++ -lpthread"
;; ;;
LINUX) LINUX)
EXEC_FILE="$PROJECT" EXEC_FILE="$PROJECT"
@@ -114,7 +113,7 @@ case "$TASK" in
;; ;;
# executes $EXEC_FILE with valgrind memory checker # executes $EXEC_FILE with valgrind memory checker
valgrind) valgrind)
VALGRIND_ARGS="-s --read-var-info=yes --track-origins=yes --fullpath-after=$(pwd) --leak-check=full --show-leak-kinds=all" VALGRIND_ARGS="-s --read-var-info=yes --track-origins=yes --fullpath-after=$(pwd)/ --leak-check=full --show-leak-kinds=all"
TASK_SCRIPT=cbuild/default_tasks/valgrind.sh TASK_SCRIPT=cbuild/default_tasks/valgrind.sh
;; ;;
# generates profiling info # generates profiling info
@@ -176,6 +175,9 @@ 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

16
src-csharp/Component.cs Normal file
View 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) {}
}

View File

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

95
src-csharp/GameObject.cs Normal file
View File

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

View File

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

15
src-csharp/Ougge.csproj Normal file
View File

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

22
src-csharp/src-csharp.sln Normal file
View 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

102
src/Engine.cpp Normal file
View File

@@ -0,0 +1,102 @@
#include "Engine.hpp"
#include "common/UsefulException.hpp"
#include "common/ougge_format.hpp"
#include "common/time.hpp"
#include <SDL2/SDL.h>
#include <iostream>
namespace ougge {
IEngineModule::IEngineModule(Engine& engine)
: engine(engine)
{}
void IEngineModule::beginFrame() {}
void IEngineModule::endFrame() {}
void Engine::startLoop()
{
if(loop_running)
throw UsefulException("loop is running already");
nsec_t prev_update_time_ns = getMonotonicTimeNsec();
loop_running=true;
// main loop
while(loop_running){
nsec_t update_time_ns = getMonotonicTimeNsec();
if(update_time_ns < prev_update_time_ns)
throw UsefulException("monotonic clock returned unexpected value");
f64 delta_time_s = (f64)(update_time_ns - prev_update_time_ns) / 1e9;
prev_update_time_ns = update_time_ns;
deltaTime = delta_time_s;
tryDrawFrame();
nsec_t after_update_time_ns = getMonotonicTimeNsec();
nsec_t frame_delay_ns = (nsec_t)1e9 / fps_max - (after_update_time_ns - update_time_ns);
if(frame_delay_ns > 0){
SDL_Delay(frame_delay_ns / 1e6);
}
}
}
void Engine::stopLoop(){
loop_running = false;
}
void Engine::handleModuleError(IEngineModule& module, const char* type, const char* method, const char* error){
std::string error_message = ougge_format(
"Catched %s at %s.%s(): %s",
type, module.getName().c_str(), method, error);
std::cerr<<error_message<<std::endl;
error_messages.push_back(error_message);
}
void Engine::tryDrawFrame(){
auto it = modules.begin();
while(it != modules.end())
{
IEngineModule& module = **it;
it++;
try {
module.beginFrame();
}
catch(const std::exception& e){
handleModuleError(module, "exception", "beginFrame", e.what());
}
catch(const char* cstr){
handleModuleError(module, "error message (const char*)", "beginFrame", cstr);
}
catch(const std::string& str){
handleModuleError(module, "error message (std::string)", "beginFrame", str.c_str());
}
catch(...){
handleModuleError(module, "unknown", "beginFrame", "unknown");
}
}
it = modules.end();
while(it != modules.begin())
{
it--;
IEngineModule& module = **it;
try {
module.endFrame();
}
catch(const std::exception& e){
handleModuleError(module, "exception", "endFrame", e.what());
}
catch(const char* cstr){
handleModuleError(module, "error message (const char*)", "endFrame", cstr);
}
catch(const std::string& str){
handleModuleError(module, "error message (std::string)", "endFrame", str.c_str());
}
catch(...){
handleModuleError(module, "unknown", "endFrame", "unknown");
}
}
}
}

106
src/Engine.hpp Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

47
src/common/math.hpp Normal file
View 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;
}

View File

@@ -1,9 +1,9 @@
#include <sstream> #include <sstream>
#include <stdarg.h> #include <stdarg.h>
#include "format.hpp" #include "ougge_format.hpp"
#include "UsefulException.hpp" #include "UsefulException.hpp"
std::string _format(const std::string& format_str, const size_t args_count, ...){ std::string _ougge_format(const std::string& format_str, const size_t args_count, ...){
va_list vl; va_list vl;
va_start(vl, args_count); va_start(vl, args_count);
std::stringstream ss; std::stringstream ss;
@@ -63,3 +63,22 @@ std::string _format(const std::string& format_str, const size_t args_count, ...)
va_end(vl); va_end(vl);
return ss.str(); return ss.str();
} }
std::string formatSizeHumanReadable(std::size_t b){
std::stringstream ss;
ss.precision(3);
std::size_t k = b / 1024;
std::size_t m = k / 1024;
std::size_t g = m / 1024;
if(g)
ss<<m / 1024.0f<<'G';
else if(m)
ss<<k / 1024.0f<<'M';
else if(k)
ss<<b / 1024.0f<<'K';
else ss<<b;
return ss.str();
}

View File

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

View File

@@ -2,6 +2,7 @@
#include <cstdint> #include <cstdint>
#include <string> #include <string>
#include <memory>
typedef int8_t i8; typedef int8_t i8;
typedef uint8_t u8; typedef uint8_t u8;
@@ -16,7 +17,6 @@ typedef double f64;
/// anonymous pointer without specified freeMembers() func /// anonymous pointer without specified freeMembers() func
typedef void* Pointer; typedef void* Pointer;
#define nameof(V) #V
#ifdef _MSC_VER #ifdef _MSC_VER
#pragma comment(lib, "mincore_downlevel.lib") // Support OS older than SDK #pragma comment(lib, "mincore_downlevel.lib") // Support OS older than SDK

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

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

View File

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

View File

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

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

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

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

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

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

@@ -0,0 +1,133 @@
#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);
}
bool 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));
return false;
}
buffer[index] = GameObject();
used_indices[index/64] &= ~(u64(1)<<(index%64)); // mark index bit as unused
if(index < first_unused_index)
first_unused_index = index;
return true;
}
GameObjectPool::iterator::iterator(GameObjectPool* pool, u32 index)
: pool(pool), index(index)
{
}
std::pair<u32, GameObject&> GameObjectPool::iterator::operator*()
{
if(index >= pool->size)
throw UsefulException("can't get value of end() iterator");
GameObject& r = pool->buffer[index];
return std::pair<u32, GameObject&>(index, r);
}
GameObjectPool::iterator& GameObjectPool::iterator::operator++()
{
index = pool->getNearestUsedIndex(index+1);
return *this;
}
}

View File

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

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

View File

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

View File

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

View File

@@ -1,23 +1,67 @@
#define SDL_MAIN_HANDLED #define SDL_MAIN_HANDLED
#include <iostream> #include <iostream>
#include "std.hpp" #include <iomanip>
#include "GUI/MainWindow.hpp" #include "Engine.hpp"
#include "Resources/Resources.hpp" #include "modules/MainWindowSDL2.hpp"
#include "format.hpp" #include "modules/MonoGameObjectSystem.hpp"
#include "exceptions.hpp" #include "resources/textures.hpp"
using namespace ougge; using namespace ougge;
void update(f64 deltaTime){ class TutelModule : public IEngineModule {
std::cout<<"deltaTime: "<<deltaTime<<std::endl; resources::CacheStorage<resources::Texture> textures;
public:
TutelModule(Engine& engine, resources::ResourceManager& resourceManager) :
IEngineModule(engine),
textures(&resourceManager)
{
//TODO: add something like `assert(requireModule(MainWindow))`
}
const std::string& getName() override {
return ougge_type_name<TutelModule>();
}
void beginFrame() override {
auto& mainWindow = engine.getModule<modules::MainWindowSDL2>();
resources::Texture* tutel = textures.tryGetOrCreate("tutel.png", mainWindow.sdl_renderer);
if(tutel == nullptr){
throw new UsefulException("couldn't find resource 'tutel.png'");
}
resources::SDL_RenderCopyExF_Params params;
params.target_section = SDL_FRectConstruct(100, 100, 400, 400);
static i32 si = 1;
params.rotation_angle = M_PI_4 * (si++ / engine.fps_max);
tutel->render(params);
}
};
void createExampleObject(Engine& engine){
std::cout<<"creating ExampleObject"<<std::endl;
auto& monoSystem = engine.getModule<modules::MonoGameObjectSystem>();
game::GameObject& exampleObj = monoSystem.createAndConstructGameObject();
std::string componentClassName = "Ougge.ExampleComponent";
if(!monoSystem.tryCreateComponent(exampleObj, componentClassName))
throw UsefulException(ougge_format("couldn't create component '%s'", componentClassName.c_str()));
} }
int main(int argc, const char** argv){ int main(int argc, const char** argv){
try { try {
Resources::init(); std::cout<<"Initializing Engine"<<std::endl;
GUI::MainWindow w; Engine engine;
w.open("ougge", update);
w.startUpdateLoop(); std::cout<<"Initializing ResourceManager"<<std::endl;
resources::ResourceManager resourceManager;
engine.createModule<modules::MainWindowSDL2>("ougge", resourceManager);
engine.createModule<modules::MonoGameObjectSystem>(64*1024);
createExampleObject(engine);
engine.createModule<TutelModule>(resourceManager);
std::cout<<"main loop start"<<std::endl;
engine.startLoop();
std::cout<<"main loop stop"<<std::endl;
} }
catch(const std::exception& e){ catch(const std::exception& e){
std::cerr<<"Catched exception: "<<e.what()<<std::endl; std::cerr<<"Catched exception: "<<e.what()<<std::endl;

View File

@@ -1,18 +1,23 @@
#include <backends/imgui_impl_sdl2.h> #include <backends/imgui_impl_sdl2.h>
#include <backends/imgui_impl_sdlrenderer2.h> #include <backends/imgui_impl_sdlrenderer2.h>
#include <iostream> #include <iostream>
#include "MainWindow.hpp" #include "MainWindowSDL2.hpp"
#include "../exceptions.hpp" #include "../gui/gui_exceptions.hpp"
#include "../format.hpp" #include "../common/ougge_format.hpp"
#include "../Resources/fonts.hpp" #include "../common/math.hpp"
#include "../Resources/textures.hpp"
namespace ougge::GUI { using namespace ougge::gui;
f32 MainWindow::getDPI(){ namespace ougge::modules {
int w=0, h=0;
const std::string& MainWindowSDL2::getName() {
return ougge_type_name<MainWindowSDL2>();
}
f32 MainWindowSDL2::getDPI(){
i32 w=0, h=0;
SDL_GetRendererOutputSize(sdl_renderer, &w, &h); SDL_GetRendererOutputSize(sdl_renderer, &w, &h);
int sim_w=0, sim_h=0; i32 sim_w=0, sim_h=0;
SDL_GetWindowSize(sdl_window, &sim_w, &sim_h); SDL_GetWindowSize(sdl_window, &sim_w, &sim_h);
f32 wdpi=(f32)w / sim_w; f32 wdpi=(f32)w / sim_w;
f32 hdpi=(f32)h / sim_h; f32 hdpi=(f32)h / sim_h;
@@ -20,13 +25,15 @@ f32 MainWindow::getDPI(){
return dpi; return dpi;
} }
void MainWindow::open(const char* window_title, UpdatingFunc _update){ MainWindowSDL2::MainWindowSDL2(Engine& engine,
update = _update; const std::string& window_title,
resources::ResourceManager& resourceManager)
: IEngineModule(engine)
{
SDL_TRY(SDL_Init(SDL_INIT_EVERYTHING)); SDL_TRY(SDL_Init(SDL_INIT_EVERYTHING));
SDL_version v; SDL_version v;
SDL_GetVersion(&v); SDL_GetVersion(&v);
std::cout<<format("SDL version: %u.%u.%u\n", v.major, v.minor, v.patch); std::cout<<ougge_format("SDL version: %u.%u.%u\n", v.major, v.minor, v.patch);
// From 2.0.18: Enable native IME. // From 2.0.18: Enable native IME.
#ifdef SDL_HINT_IME_SHOW_UI #ifdef SDL_HINT_IME_SHOW_UI
@@ -36,7 +43,7 @@ void MainWindow::open(const char* window_title, UpdatingFunc _update){
SDL_WindowFlags window_flags = (SDL_WindowFlags)( SDL_WindowFlags window_flags = (SDL_WindowFlags)(
SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI
); );
sdl_window = SDL_CreateWindow(window_title, sdl_window = SDL_CreateWindow(window_title.c_str(),
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
1280, 720, window_flags); 1280, 720, window_flags);
if(sdl_window == nullptr) if(sdl_window == nullptr)
@@ -73,135 +80,15 @@ void MainWindow::open(const char* window_title, UpdatingFunc _update){
// Setup Dear ImGui style // Setup Dear ImGui style
ImGui::StyleColorsDark(); ImGui::StyleColorsDark();
resources::ResourceFactory* font_res = resourceManager.tryGetResource(default_font_path);
if(font_res == nullptr)
throw UsefulException("can't find default font resource");
f32 dpi = getDPI(); f32 dpi = getDPI();
io.FontDefault = Resources::ImFont_LoadFromResource(default_font, default_font_size, dpi); io.FontDefault = resources::ImFont_LoadFromResource(font_res, default_font_size, dpi);
} }
// Wait, poll and handle events (inputs, window resize, etc.) MainWindowSDL2::~MainWindowSDL2(){
void MainWindow::poll_events(bool waitForEvent){
SDL_Event event;
if(waitForEvent){
// waits for first event in cpu-efficient way
SDL_TRY(SDL_WaitEvent(&event) != 1);
}
// dont wait for event
else if(!SDL_PollEvent(&event))
return;
do {
ImGui_ImplSDL2_ProcessEvent(&event);
switch(event.type){
case SDL_QUIT: {
close();
break;
}
case SDL_WINDOWEVENT: {
if(event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(sdl_window)){
close();
}
break;
}
}
} while (SDL_PollEvent(&event)); // if there are more events, handles them
}
void ImGui_drawErrorWindow(bool* draw_error_window, std::string& msg){
ImGui::Begin("ERROR", draw_error_window);
ImGui::Text("%s", msg.c_str());
ImGui::End();
}
void MainWindow::draw_ui(){
static std::string error_message;
static bool draw_error_window = false;
try {
// Draw UI
if(draw_error_window)
ImGui_drawErrorWindow(&draw_error_window, error_message);
else {
draw_bg_window();
draw_debug_window();
}
}
catch(const std::exception& e){
error_message = "Catched exception: " + std::string(e.what());
draw_error_window = true;
std::cerr<<error_message<<std::endl;
}
catch(const char* cstr){
error_message = "Catched error message (const char*): " + std::string(cstr);
draw_error_window = true;
std::cerr<<error_message<<std::endl;
}
catch(const std::string& str){
error_message = "Catched error message (std::string): " + str;
draw_error_window = true;
std::cerr<<error_message<<std::endl;
}
catch(...){
error_message = "Catched unknown error";
draw_error_window = true;
std::cerr<<error_message<<std::endl;
}
}
void MainWindow::draw_frame(){
// Start the Dear ImGui frame
ImGui_ImplSDLRenderer2_NewFrame();
ImGui_ImplSDL2_NewFrame();
ImGui::NewFrame();
draw_ui();
// Rendering
ImGui::Render();
ImGuiIO& io = ImGui::GetIO();
SDL_RenderSetScale(sdl_renderer, io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y);
SDL_SetRenderDrawColor(sdl_renderer, (Uint8)(clear_color.x * 255), (Uint8)(clear_color.y * 255), (Uint8)(clear_color.z * 255), (Uint8)(clear_color.w * 255));
SDL_RenderClear(sdl_renderer);
// Example sprite
//Resources::Texture tutel(Resources::getResource("tutel.png"), sdl_renderer);
static Resources::CacheStorage<Resources::Texture> textures;
Resources::Texture& tutel = textures.getOrCreate("tutel.png", sdl_renderer);
tutel.render(SDL_FRectConstruct(100, 100, 400, 400));
ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData(), sdl_renderer);
// Swap buffers
SDL_RenderPresent(sdl_renderer);
}
void MainWindow::startUpdateLoop(){
if(loop_running)
throw UsefulException("loop is running already");
nsec_t prev_update_time_ns = getMonotonicTimeNsec();
loop_running=true;
// main loop
while(loop_running){
poll_events(false);
nsec_t update_time_ns = getMonotonicTimeNsec();
if(update_time_ns < prev_update_time_ns)
throw UsefulException("monotonic clock returned unexpected value");
f64 delta_time_s = (f64)(update_time_ns - prev_update_time_ns) / 1e9;
prev_update_time_ns = update_time_ns;
update(delta_time_s);
draw_frame();
nsec_t after_update_time_ns = getMonotonicTimeNsec();
nsec_t frame_delay_ns = (nsec_t)1e9 / fps_max - (after_update_time_ns - update_time_ns);
if(frame_delay_ns > 0){
std::cout<<"frameDelay: "<<frame_delay_ns / 1e9f<<std::endl;
SDL_Delay(frame_delay_ns / 1e6);
}
}
destroy();
}
void MainWindow::destroy(){
ImGui_ImplSDLRenderer2_Shutdown(); ImGui_ImplSDLRenderer2_Shutdown();
ImGui_ImplSDL2_Shutdown(); ImGui_ImplSDL2_Shutdown();
ImGui::DestroyContext(); ImGui::DestroyContext();
@@ -210,11 +97,60 @@ void MainWindow::destroy(){
SDL_Quit(); SDL_Quit();
} }
void MainWindow::close(){ void MainWindowSDL2::pollEvents(){
loop_running = false; SDL_Event event;
while (SDL_PollEvent(&event)) {
ImGui_ImplSDL2_ProcessEvent(&event);
switch(event.type){
case SDL_QUIT: {
//TODO: log SDL_QUIT event
engine.stopLoop();
break;
}
case SDL_WINDOWEVENT: {
if(event.window.event == SDL_WINDOWEVENT_CLOSE
&& event.window.windowID == SDL_GetWindowID(sdl_window))
{
//TODO: log SDL_WINDOWEVENT event
engine.stopLoop();
}
break;
}
}
}
} }
void MainWindow::draw_bg_window(){ void MainWindowSDL2::beginFrame(){
// process events happend since previous frame
pollEvents();
// Start the Dear ImGui frame
ImGui_ImplSDLRenderer2_NewFrame();
ImGui_ImplSDL2_NewFrame();
ImGui::NewFrame();
ImGuiIO& io = ImGui::GetIO();
SDL_RenderSetScale(sdl_renderer, io.DisplayFramebufferScale.x, io.DisplayFramebufferScale.y);
SDL_SetRenderDrawColor(sdl_renderer,
(u8)(clear_color.x * 255),
(u8)(clear_color.y * 255),
(u8)(clear_color.z * 255),
(u8)(clear_color.w * 255));
SDL_RenderClear(sdl_renderer);
draw_bg_window();
draw_debug_window();
drawErrorWindow();
}
void MainWindowSDL2::endFrame(){
ImGui::Render();
ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData(), sdl_renderer);
// Swap buffers
SDL_RenderPresent(sdl_renderer);
}
void MainWindowSDL2::draw_bg_window(){
const ImGuiDockNodeFlags dockspace_flags = const ImGuiDockNodeFlags dockspace_flags =
ImGuiDockNodeFlags_PassthruCentralNode; ImGuiDockNodeFlags_PassthruCentralNode;
const ImGuiWindowFlags window_flags = const ImGuiWindowFlags window_flags =
@@ -278,15 +214,16 @@ void MainWindow::draw_bg_window(){
ImGui::End(); ImGui::End();
} }
void MainWindow::draw_debug_window(){ void MainWindowSDL2::draw_debug_window(){
ImGuiIO& io = ImGui::GetIO(); ImGuiIO& io = ImGui::GetIO();
ImGui::Begin("Debug Options"); if(ImGui::Begin("Debug Options", &show_debug_window)){
ImGui::ColorEdit3("clear_color", (float*)&clear_color); ImGui::ColorEdit3("clear_color", (float*)&clear_color);
ImGui::InputInt("fps_max", &fps_max); ImGui::InputInt("fps_max", (int*)&engine.fps_max);
ImGui::Text("Application average %.3f ms/frame (%.2f FPS)", 1000.0f / io.Framerate, io.Framerate); ImGui::Text("Application average %.3f ms/frame (%.2f FPS)", 1000.0f / io.Framerate, io.Framerate);
ImGui::Checkbox("Demo Window", &show_demo_window); ImGui::Checkbox("Demo Window", &show_demo_window);
ImGui::Checkbox("Metrics/Debug Window", &show_metrics_window); ImGui::Checkbox("Metrics/Debug Window", &show_metrics_window);
ImGui::End(); ImGui::End();
}
if (show_demo_window) if (show_demo_window)
ImGui::ShowDemoWindow(&show_demo_window); ImGui::ShowDemoWindow(&show_demo_window);
@@ -295,4 +232,16 @@ void MainWindow::draw_debug_window(){
ImGui::ShowMetricsWindow(&show_metrics_window); ImGui::ShowMetricsWindow(&show_metrics_window);
} }
void MainWindowSDL2::drawErrorWindow(){
if(engine.error_messages.size() < 1)
return;
ImGui::Begin("ERRORS");
if(ImGui::Button("Clear"))
engine.error_messages.clear();
for(auto& msg : engine.error_messages){
ImGui::TextWrapped("%s", msg.c_str());
}
ImGui::End();
}
} }

View File

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

View File

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

View File

@@ -0,0 +1,35 @@
#pragma once
#include "../Engine.hpp"
#include "../common/function_shared_ptr.hpp"
#include "../mono/mono.hpp"
#include "../game/GameObjectPool.hpp"
namespace ougge::modules {
class MonoGameObjectSystem : public IEngineModule {
Mono::RuntimeJIT mono;
game::GameObjectPool gameObjectPool;
u64 obj_id = 0;
MonoClass* gameObjectClass;
Mono::Method<void(u64, u32)> gameObjectCtor;
Mono::Method<void(f64)> gameObjectInvokeUpdate;
Mono::Method<Mono::Bool(Mono::String)> gameObjectTryCreateComponent;
public:
std::shared_ptr<Mono::Assembly> engineManagedAssembly;
MonoGameObjectSystem(Engine& engine, u32 max_game_objects);
const std::string& getName() override;
void beginFrame() override;
game::GameObject& createAndConstructGameObject();
bool tryCreateComponent(game::GameObject& obj, const std::string& componentClassName);
private:
void registerNativeCallbacks();
game::GameObject& createGameObjectInPool(u64* id_out, u32* index_out);
};
}

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

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

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

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

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

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

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

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

View File

@@ -0,0 +1,40 @@
#include "MemoryStream.hpp"
class MemoryStreamBuf : public std::streambuf {
public:
MemoryStreamBuf(void* p, const std::size_t n);
std::istream::pos_type seekoff(
std::istream::off_type off,
std::ios_base::seekdir dir,
std::ios_base::openmode which) override;
};
MemoryStreamBuf::MemoryStreamBuf(void* _p, const std::size_t n){
char* p=(char*)_p;
setg(p, p, p + n);
setp(p, p + n);
}
std::istream::pos_type MemoryStreamBuf::seekoff(
std::istream::off_type off,
std::ios_base::seekdir dir,
std::ios_base::openmode which)
{
if (dir == std::ios_base::cur)
gbump(off);
else if (dir == std::ios_base::end)
setg(eback(), egptr() + off, egptr());
else if (dir == std::ios_base::beg)
setg(eback(), eback() + off, egptr());
return gptr() - eback();
}
MemoryStreamRead::MemoryStreamRead(const void* p, const std::size_t n)
: std::istream(new MemoryStreamBuf((void*)p, n))
{}
MemoryStreamRead::~MemoryStreamRead(){
delete rdbuf();
}

View File

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

View File

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

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

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

View File

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

View File

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

View File

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

View File

@@ -1,13 +1,12 @@
#pragma once #pragma once
#include <string>
#include <iostream> #include <iostream>
#include <optional> #include <optional>
#include <memory>
#include <SDL.h> #include <SDL.h>
#include "Resources.hpp" #include "resources.hpp"
#include "../common/math.hpp"
namespace ougge::Resources { namespace ougge::resources {
#define SDL_RectConstruct(X, Y, W, H) (SDL_Rect){X, Y, W, H} #define SDL_RectConstruct(X, Y, W, H) (SDL_Rect){X, Y, W, H}
#define SDL_FRectConstruct(X, Y, W, H) (SDL_FRect){X, Y, W, H} #define SDL_FRectConstruct(X, Y, W, H) (SDL_FRect){X, Y, W, H}
@@ -22,7 +21,7 @@ struct SDL_RenderCopyExF_Params {
std::optional<SDL_Rect> texture_section; std::optional<SDL_Rect> texture_section;
std::optional<SDL_FRect> target_section; std::optional<SDL_FRect> target_section;
std::optional<SDL_FPoint> rotation_center; std::optional<SDL_FPoint> rotation_center;
double rotation_angle; angle_t rotation_angle;
SDL_RendererFlip flip; SDL_RendererFlip flip;
SDL_RenderCopyExF_Params(); SDL_RenderCopyExF_Params();
@@ -31,10 +30,10 @@ struct SDL_RenderCopyExF_Params {
struct Texture { struct Texture {
SDL_Renderer* renderer; SDL_Renderer* renderer;
std::shared_ptr<SDL_Texture> texture; std::shared_ptr<SDL_Texture> texture;
int w; i32 w;
int h; i32 h;
Texture(const Resource& r, SDL_Renderer* renderer); Texture(ResourceFactory* r, SDL_Renderer* renderer);
Texture(std::istream& s, size_t size, SDL_Renderer* renderer); Texture(std::istream& s, size_t size, SDL_Renderer* renderer);
void render(const SDL_FRect& target_section); void render(const SDL_FRect& target_section);

View File

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

View File

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