Compare commits

...

68 commits

Author SHA1 Message Date
16f973c9c2 misc(rendering): temporarily disabled fresnel reflections until a setting is added for them 2025-08-29 00:12:52 +02:00
dingusreal
5b5006eb90 SHINY REFLECTIONS YAY 2025-08-28 14:52:33 +02:00
e2054a51a8 feat(editor): recent files 2025-08-19 22:43:52 +02:00
93984ce1c0 fix(editor): hidden internal services 2025-08-14 17:21:55 +02:00
6803a659cb fix(clangd): annoying error 2025-08-14 16:59:44 +02:00
a619fa3afc fix(physics): reduced iteration count 2025-08-13 23:46:08 +02:00
43e41caebf fix(editor): lock the cursor on wayland, too 2025-08-13 03:40:20 +02:00
1bd9b00c47 fix(cmake); find qscintilla on fedora 2025-08-13 03:40:20 +02:00
ae9a4adf67 fix(editor): miniaudio crash in wasapi due to loading order 2025-08-12 14:53:28 +02:00
7bd3e70c3a feat(editor): lock the cursor on platforms other than wayland 2025-08-12 03:38:42 +02:00
52cfa69a6e fix(editor): replaced QSoundEffect with Miniaudio 2025-08-12 01:05:33 +02:00
62743d8998 fix(editor): use OpenGL 3.3 2025-08-12 00:35:10 +02:00
2fec4cc7f2 fix(cmake): client no longer opens with console 2025-08-12 00:26:16 +02:00
56ffc3f88c fix(cmake): copy assets 2025-08-12 00:19:15 +02:00
384c249874 fix(cmake): copy the right dlls in debug mode 2025-08-12 00:06:00 +02:00
692bb17d44 feat(cmake): move to qt6 2025-08-11 22:01:28 +02:00
2ed8c83ec3 tempfix(editor): gl 3.3 and qsoundeffect not working on linux 2025-08-11 21:59:09 +02:00
801b00ad97 fix(cmake): cleaner output 2025-08-04 22:26:36 +02:00
4940b07403 fix(editor): handles crashing on hover 2025-08-01 00:59:08 +02:00
2f09c6eb9c fix(logger): use whole seconds rather than fractions for log filenames 2025-07-25 23:28:56 +02:00
3521f50d1b fix: glfw error not properly formatted, so format args were ignored 2025-07-25 19:14:25 +02:00
11df6595c0 feat(cmake): replaced glew with glad 2025-07-25 19:13:53 +02:00
44c28b6825 feat(test): lua signal test 2025-07-25 18:15:18 +02:00
92ab9f6fb9 feat(test): lua timing checks 2025-07-24 23:36:28 +02:00
b117f3cd4d feat(test): some few lua testing 2025-07-24 22:57:00 +02:00
d086cf629b feat(test): added testing setup 2025-07-24 21:42:49 +02:00
143d3769c7 fix(autogen): opening log file before created generated directory 2025-07-24 19:59:36 +02:00
243af95a3b chore: note for llvm 2025-07-24 02:46:57 +02:00
3df575314e misc(cmake): use ccache 2025-07-24 02:00:08 +02:00
8d5fb3b2c6 chore: updated build instructions 2025-07-24 01:52:40 +02:00
471b2472e3 fix(autogen): autogen failing in msbuild because of "error" in stdout 2025-07-24 01:45:55 +02:00
be324e0aa8 refactor(cmake): made sources explicit rather than globbed 2025-07-24 01:25:39 +02:00
330f128dd3 refactor(cmake): (Thanks @FloofyPlasma!) replaced vcpkg with CPM 2025-07-23 03:24:34 +02:00
8b8776cbd7 fix(part): temporarily soft-disabled wedges as they crash the engine currently 2025-07-23 03:24:34 +02:00
74a4a01ebf refactor(cmake): windeployqt deploys debug dlls which the binary doesn't link against 2025-07-23 03:24:34 +02:00
12fc8906bc refactor(cmake): removed vcpkg 2025-07-23 03:24:34 +02:00
6d95cc8e1a fix(editor): added undo history state for surface changes 2025-07-21 17:25:27 +02:00
6e387a72d2 fix(part): velocity being overidden on run 2025-07-21 17:16:56 +02:00
d79d4e3b4a feat(editor): added version string to client and editor 2025-07-19 04:47:57 +02:00
4b6fa1d3ff feat(core): added vresion tracking 2025-07-19 04:13:52 +02:00
259c14294c fix(physics): joints were being created on load, leading to long load times
(cherry picked from commit 2a60994013)
2025-07-18 23:57:48 +02:00
99f4da8b2f feat(rendering): replaced sphere with more accurate sphere mesh 2025-07-16 00:31:13 +02:00
1538d06c39 chore: check-in .obj models used via genmesh.py 2025-07-16 00:31:13 +02:00
275229a37c feat(rendering): replaced ico sphere with uv sphere 2025-07-16 00:02:47 +02:00
aedcd29a1e refactor(rendering): changed winding order of meshes to all be clockwise 2025-07-15 17:43:50 +02:00
b8c4d130d4 fix(rendering): changed the way faces are determined 2025-07-15 17:30:18 +02:00
c944c0cb09 feat(part): added balls 2025-07-15 10:30:07 +02:00
8b789fc9b6 feat(part): made wedge part mesh (not working) 2025-07-15 03:15:13 +02:00
bb3132d4ba feat(rendering): wedge part 2025-07-15 03:15:13 +02:00
31aa28909e feat(physics): added wedge part 2025-07-15 03:15:13 +02:00
2538673fb2 refactor(part): moved collider builder to subclass 2025-07-15 03:15:13 +02:00
d23206b1fc refactor(part): refactored part into basepart 2025-07-15 03:15:13 +02:00
0196d80944 feat(rendering): specified explicit OpenGL versions and profiles for GLFW and Qt 2025-07-15 03:14:11 +02:00
dca5c483c4 fix(workspace): do not fire signals from physics thread as lua has to always be executed from main thread 2025-07-13 18:45:19 +02:00
0ded4ac7fb fix(rendering): stroked message 2025-07-13 17:42:18 +02:00
a75119a8c2 feat(rendering): stroked text 2025-07-13 16:58:30 +02:00
acc1f93f27 fix(rendering): hint and message should render on the same layer 2025-07-13 16:58:30 +02:00
bb2b0a2762 feat(rendering): added messages and hints 2025-07-13 16:58:30 +02:00
86b750b1a3 feat(editor): added a proper title 2025-07-13 16:58:30 +02:00
41c5b74527 feat(editor): click on stack trace to go to line in script 2025-07-13 16:58:30 +02:00
fcf4343509 feat(lua): track source of scripts to allow hyperlinking to script 2025-07-13 16:58:30 +02:00
783fd17563 fix(editor): command bar selects all on press enter 2025-07-13 16:58:30 +02:00
e40b594ae5 fix(lua): share environment between command bar calls 2025-07-13 16:58:30 +02:00
133ca0bb5e refactor(lua): generate separate environments for script 2025-07-13 16:58:30 +02:00
e28436b76c feat(editor): command bar 2025-07-13 16:58:30 +02:00
c628fa2b83 refactor(logging): changed the way source-linked traces are logged 2025-07-13 16:58:30 +02:00
c63e91285b refactor(lua): pre-parse pcall wrapper ahead of time 2025-07-13 16:58:30 +02:00
5c8c39cc33 fix(editor): editing individual component of Vector3 in properties resulted in a crash 2025-07-13 16:58:30 +02:00
128 changed files with 13780 additions and 2354 deletions

View file

@ -1,2 +1,3 @@
CompileFlags:
Add: [-std=c++20]
Remove: [-mno-direct-extern-access]

View file

@ -1,20 +1,27 @@
# Building on Linux
For pre-requisite packages, check [deps.txt](./deps.txt)
You will need to install Qt and LLVM/libclang beforehand. All other packages will automatically be obtained through CPM.
If your distribution does not provide ReactPhysics3D, you will have to build (and install) it yourself prior to this step
ccache is highly recommended.
Use the following to generate the build files:
cmake -Bbuild .
> [!NOTE]
> Add -DCMAKE_BUILD_TYPE=Release to produce a release build
Then build the project using:
cmake --build build
The compiled binaries should then be located in `./build/bin/` and should be ready for redistribution without any further work.
If any of the compilation steps fail, or the binaries fail to execute, please create an issue so that this can be corrected.
# Building on Windows
The project will be built using VCPKG and MSVC
The process is very similar on Windows
## Pre-requisites
@ -22,6 +29,7 @@ The project will be built using VCPKG and MSVC
* Qt 6.8.3 or higher, with MSVC toolchain
* CMake
* Git (for cloning the repo, optional)
* LLVM/libclang 18.1.8+ (from https://github.com/llvm/llvm-project/releases/download/llvmorg-18.1.8/LLVM-18.1.8-win64.exe)
* QScintilla already built (see [docs/qscintilla.md](./docs/qscintilla.md)) *\*likely temporary\**
To start, clone the repository:
@ -34,18 +42,18 @@ Once in the directory, add Qt to the `CMAKE_PREFIX_PATH` variable (for instance,
set CMAKE_PREFIX_PATH=C:\Qt\6.8.3\msvc2022_64
Now, generate the build files with cmake via the vcpkg preset:
Now, generate the build files:
cmake -Bbuild . --preset vcpkg
cmake -Bbuild .
Then, finally, build in release mode\*:
Then, finally build:
cmake --build build --config Release
cmake --build build
The compiled binaries should then be placed in `./build/bin/` and should be ready for redistribution without any further work.
> [!NOTE]
> To build in release mode, add -DCMAKE_BUILD_TYPE=Release to the configure (first) command,
> and add --config Release to the build (second) command
If any of the compilation steps fail, or the binaries fail to execute, please create an issue so that this can be corrected.
The compiled binaries should then be located in `./build/bin/[Debug|Release]` and should be ready for redistribution without any further work.
\* Release mode is necessary as debug mode copies DLLs that are not linked to the output binary
DEVELOPER NOTE: AKA Not for you. If you get CUSTOM COMMAND BUILD errors just keep rerunning build
If any of the compilation steps fail, or the binaries fail to execute, please create an issue so that this can be corrected.

View file

@ -10,6 +10,7 @@ else()
endif()
set( CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake" )
set(USE_CCACHE ON)
add_subdirectory(autogen)
@ -24,4 +25,6 @@ add_subdirectory(client)
add_subdirectory(editor)
install(FILES $<TARGET_RUNTIME_DLLS:editor> TYPE BIN)
set( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/tests )
enable_testing()
add_subdirectory(tests)

View file

@ -1,21 +0,0 @@
{
"version": 2,
"configurePresets": [
{
"name": "vcpkg",
"generator": "Visual Studio 17 2022",
"binaryDir": "${sourceDir}/build",
"cacheVariables": {
"CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"
}
},
{
"name": "vcpkg-linux",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build",
"cacheVariables": {
"CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake"
}
}
]
}

View file

@ -1,13 +0,0 @@
{
"version": 2,
"configurePresets": [
{
"name": "default",
"inherits": "vcpkg",
"environment": {
"VCPKG_ROOT": "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\VC\\vcpkg\\vcpkg.exe"
}
}
]
}

View file

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

View file

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 3.3 KiB

View file

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

View file

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

View file

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

View file

Before

Width:  |  Height:  |  Size: 5.3 KiB

After

Width:  |  Height:  |  Size: 5.3 KiB

View file

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View file

Before

Width:  |  Height:  |  Size: 4.5 KiB

After

Width:  |  Height:  |  Size: 4.5 KiB

View file

@ -0,0 +1,9 @@
sphere.obj was made by subdividing a cube, unwrapping it using cube projection, and then using the "To-sphere" tool in Blender to fully smoothen it out.
Make sure you select "shade smooth" before exporting!
Other settings:
Up axis: Y
Forward axis: -Z
The winding order in .obj files is always counter-clockwise, so my script (genmesh.py) uses reversed() to reverse this and homogenous the winding order
across meshes

565
asset-srcs/models/arc.obj Normal file
View file

@ -0,0 +1,565 @@
# Blender 4.4.0
# www.blender.org
mtllib arc.mtl
o Torus.001
v 1.090000 0.000000 0.000000
v 1.060000 0.051962 0.000000
v 1.000000 0.051962 0.000000
v 0.970000 0.000000 0.000000
v 1.000000 -0.051962 0.000000
v 1.060000 -0.051962 0.000000
v 1.030941 0.000000 -0.353922
v 1.002566 0.051962 -0.344181
v 0.945817 0.051962 -0.324699
v 0.917443 0.000000 -0.314958
v 0.945817 -0.051962 -0.324699
v 1.002566 -0.051962 -0.344181
v 0.860163 0.000000 -0.669492
v 0.836489 0.051962 -0.651065
v 0.789140 0.051962 -0.614213
v 0.765466 0.000000 -0.595786
v 0.789140 -0.051962 -0.614213
v 0.836489 -0.051962 -0.651065
v 0.596173 0.000000 -0.912512
v 0.579765 0.051962 -0.887396
v 0.546948 0.051962 -0.837166
v 0.530540 0.000000 -0.812051
v 0.546948 -0.051962 -0.837166
v 0.579765 -0.051962 -0.887396
v 0.267579 0.000000 -1.056646
v 0.260214 0.051962 -1.027564
v 0.245485 0.051962 -0.969400
v 0.238121 0.000000 -0.940318
v 0.245485 -0.051962 -0.969400
v 0.260214 -0.051962 -1.027564
v -0.090011 0.000000 -1.086277
v -0.087534 0.051962 -1.056379
v -0.082579 0.051962 -0.996584
v -0.080102 0.000000 -0.966687
v -0.082579 -0.051962 -0.996584
v -0.087534 -0.051962 -1.056379
v -0.437848 0.000000 -0.998193
v -0.425797 0.051962 -0.970720
v -0.401695 0.051962 -0.915773
v -0.389644 0.000000 -0.888300
v -0.401695 -0.051962 -0.915773
v -0.425797 -0.051962 -0.970720
v -0.738237 0.000000 -0.801939
v -0.717918 0.051962 -0.779867
v -0.677281 0.051962 -0.735724
v -0.656963 0.000000 -0.713652
v -0.677281 -0.051962 -0.735724
v -0.717918 -0.051962 -0.779867
v -0.958627 0.000000 -0.518782
v -0.932242 0.051962 -0.504504
v -0.879474 0.051962 -0.475947
v -0.853090 0.000000 -0.461669
v -0.879474 -0.051962 -0.475947
v -0.932242 -0.051962 -0.504504
v -1.075134 0.000000 -0.179408
v -1.045543 0.051962 -0.174470
v -0.986361 0.051962 -0.164595
v -0.956770 0.000000 -0.159657
v -0.986361 -0.051962 -0.164595
v -1.045543 -0.051962 -0.174470
v -1.075134 0.000000 0.179408
v -1.045543 0.051962 0.174470
v -0.986361 0.051962 0.164594
v -0.956771 0.000000 0.159656
v -0.986361 -0.051962 0.164594
v -1.045543 -0.051962 0.174470
v -0.958627 0.000000 0.518782
v -0.932242 0.051962 0.504504
v -0.879474 0.051962 0.475947
v -0.853090 0.000000 0.461669
v -0.879474 -0.051962 0.475947
v -0.932242 -0.051962 0.504504
v -0.738237 0.000000 0.801939
v -0.717918 0.051962 0.779867
v -0.677281 0.051962 0.735724
v -0.656963 0.000000 0.713652
v -0.677281 -0.051962 0.735724
v -0.717918 -0.051962 0.779867
v -0.437848 0.000000 0.998193
v -0.425797 0.051962 0.970720
v -0.401695 0.051962 0.915773
v -0.389644 0.000000 0.888300
v -0.401695 -0.051962 0.915773
v -0.425797 -0.051962 0.970720
v -0.090012 0.000000 1.086277
v -0.087535 0.051962 1.056379
v -0.082580 0.051962 0.996584
v -0.080102 0.000000 0.966687
v -0.082580 -0.051962 0.996584
v -0.087535 -0.051962 1.056379
v 0.267579 0.000000 1.056646
v 0.260214 0.051962 1.027564
v 0.245485 0.051962 0.969400
v 0.238121 0.000000 0.940318
v 0.245485 -0.051962 0.969400
v 0.260214 -0.051962 1.027564
v 0.596173 0.000000 0.912512
v 0.579765 0.051962 0.887396
v 0.546948 0.051962 0.837166
v 0.530540 0.000000 0.812051
v 0.546948 -0.051962 0.837166
v 0.579765 -0.051962 0.887396
v 0.860163 0.000000 0.669492
v 0.836489 0.051962 0.651065
v 0.789141 0.051962 0.614212
v 0.765466 0.000000 0.595786
v 0.789141 -0.051962 0.614212
v 0.836489 -0.051962 0.651065
v 1.030941 0.000000 0.353923
v 1.002566 0.051962 0.344182
v 0.945817 0.051962 0.324700
v 0.917443 0.000000 0.314959
v 0.945817 -0.051962 0.324700
v 1.002566 -0.051962 0.344182
vn 0.8571 0.4949 -0.1430
vn -0.0000 1.0000 -0.0000
vn -0.8571 0.4949 0.1430
vn -0.8571 -0.4949 0.1430
vn -0.0000 -1.0000 -0.0000
vn 0.8571 -0.4949 -0.1430
vn 0.7642 0.4949 -0.4136
vn -0.7642 0.4949 0.4136
vn -0.7642 -0.4949 0.4136
vn 0.7642 -0.4949 -0.4136
vn 0.5885 0.4949 -0.6393
vn -0.5885 0.4949 0.6393
vn -0.5885 -0.4949 0.6393
vn 0.5885 -0.4949 -0.6393
vn 0.3491 0.4949 -0.7958
vn -0.3491 0.4949 0.7958
vn -0.3491 -0.4949 0.7958
vn 0.3491 -0.4949 -0.7958
vn 0.0718 0.4949 -0.8660
vn -0.0718 0.4949 0.8660
vn -0.0718 -0.4949 0.8660
vn 0.0718 -0.4949 -0.8660
vn -0.2133 0.4949 -0.8424
vn 0.2133 0.4949 0.8424
vn 0.2133 -0.4949 0.8424
vn -0.2133 -0.4949 -0.8424
vn -0.4753 0.4949 -0.7275
vn 0.4753 0.4949 0.7275
vn 0.4753 -0.4949 0.7275
vn -0.4753 -0.4949 -0.7275
vn -0.6857 0.4949 -0.5337
vn 0.6857 0.4949 0.5337
vn 0.6857 -0.4949 0.5337
vn -0.6857 -0.4949 -0.5337
vn -0.8219 0.4949 -0.2822
vn 0.8219 0.4949 0.2822
vn 0.8219 -0.4949 0.2822
vn -0.8219 -0.4949 -0.2822
vn -0.8690 0.4949 -0.0000
vn 0.8690 0.4949 -0.0000
vn 0.8690 -0.4949 -0.0000
vn -0.8690 -0.4949 -0.0000
vn -0.8219 0.4949 0.2822
vn 0.8219 0.4949 -0.2822
vn 0.8219 -0.4949 -0.2822
vn -0.8219 -0.4949 0.2822
vn -0.6857 0.4949 0.5337
vn 0.6857 0.4949 -0.5337
vn 0.6857 -0.4949 -0.5337
vn -0.6857 -0.4949 0.5337
vn -0.4753 0.4949 0.7275
vn 0.4753 0.4949 -0.7275
vn 0.4753 -0.4949 -0.7275
vn -0.4753 -0.4949 0.7275
vn -0.2133 0.4949 0.8424
vn 0.2133 0.4949 -0.8424
vn 0.2133 -0.4949 -0.8424
vn -0.2133 -0.4949 0.8424
vn 0.0718 0.4949 0.8660
vn -0.0718 0.4949 -0.8660
vn -0.0718 -0.4949 -0.8660
vn 0.0718 -0.4949 0.8660
vn 0.3491 0.4949 0.7958
vn -0.3491 0.4949 -0.7958
vn -0.3491 -0.4949 -0.7958
vn 0.3491 -0.4949 0.7958
vn 0.5885 0.4949 0.6393
vn -0.5885 0.4949 -0.6393
vn -0.5885 -0.4949 -0.6393
vn 0.5885 -0.4949 0.6393
vn 0.7642 0.4949 0.4136
vn -0.7642 0.4949 -0.4136
vn -0.7642 -0.4949 -0.4136
vn 0.7642 -0.4949 0.4136
vn 0.8571 0.4949 0.1430
vn -0.8571 0.4949 -0.1430
vn -0.8571 -0.4949 -0.1430
vn 0.8571 -0.4949 0.1430
vt 0.526316 0.500000
vt 0.578947 0.666667
vt 0.526316 0.666667
vt 0.526316 0.833333
vt 0.578947 0.833333
vt 0.526316 1.000000
vt 0.578947 -0.000000
vt 0.526316 0.166667
vt 0.526316 -0.000000
vt 0.578947 0.333333
vt 0.526316 0.333333
vt 0.578947 0.500000
vt 0.631579 0.500000
vt 0.631579 0.666667
vt 0.631579 0.833333
vt 0.578947 1.000000
vt 0.631579 -0.000000
vt 0.578947 0.166667
vt 0.631579 0.166667
vt 0.684211 0.500000
vt 0.684211 0.666667
vt 0.684211 0.833333
vt 0.631579 1.000000
vt 0.684211 0.166667
vt 0.631579 0.333333
vt 0.684211 0.333333
vt 0.736842 0.666667
vt 0.736842 0.833333
vt 0.684211 1.000000
vt 0.736842 -0.000000
vt 0.684211 -0.000000
vt 0.736842 0.333333
vt 0.789474 0.500000
vt 0.736842 0.500000
vt 0.789474 0.666667
vt 0.789474 0.833333
vt 0.736842 1.000000
vt 0.789474 -0.000000
vt 0.736842 0.166667
vt 0.789474 0.166667
vt 0.789474 0.333333
vt 0.842105 0.666667
vt 0.842105 0.833333
vt 0.789474 1.000000
vt 0.842105 -0.000000
vt 0.842105 0.166667
vt 0.842105 0.333333
vt 0.842105 0.500000
vt 0.894737 0.666667
vt 0.894737 0.833333
vt 0.842105 1.000000
vt 0.894737 -0.000000
vt 0.894737 0.333333
vt 0.894737 0.500000
vt 0.947368 0.666667
vt 0.947368 0.833333
vt 0.894737 1.000000
vt 0.947368 -0.000000
vt 0.894737 0.166667
vt 0.947368 0.333333
vt 0.947368 0.500000
vt 1.000000 0.666667
vt 1.000000 0.833333
vt 0.947368 1.000000
vt 1.000000 -0.000000
vt 0.947368 0.166667
vt 1.000000 0.166667
vt 1.000000 0.333333
vt 0.052632 0.500000
vt -0.000000 0.666667
vt -0.000000 0.500000
vt 0.052632 0.666667
vt -0.000000 0.833333
vt 0.052632 1.000000
vt -0.000000 1.000000
vt 0.052632 -0.000000
vt -0.000000 0.166667
vt -0.000000 -0.000000
vt 0.052632 0.166667
vt -0.000000 0.333333
vt 0.052632 0.333333
vt 0.105263 0.500000
vt 0.105263 0.833333
vt 0.052632 0.833333
vt 0.105263 1.000000
vt 0.105263 -0.000000
vt 0.105263 0.166667
vt 0.157895 0.500000
vt 0.105263 0.666667
vt 0.157895 0.666667
vt 0.157895 0.833333
vt 0.157895 0.166667
vt 0.105263 0.333333
vt 0.210526 0.500000
vt 0.210526 0.833333
vt 0.157895 1.000000
vt 0.210526 -0.000000
vt 0.157895 -0.000000
vt 0.210526 0.166667
vt 0.157895 0.333333
vt 0.210526 0.333333
vt 0.263158 0.500000
vt 0.210526 0.666667
vt 0.263158 0.666667
vt 0.263158 0.833333
vt 0.210526 1.000000
vt 0.263158 0.166667
vt 0.263158 0.333333
vt 0.315789 0.500000
vt 0.315789 0.833333
vt 0.263158 1.000000
vt 0.315789 -0.000000
vt 0.263158 -0.000000
vt 0.315789 0.166667
vt 0.315789 0.333333
vt 0.368421 0.500000
vt 0.315789 0.666667
vt 0.368421 0.833333
vt 0.315789 1.000000
vt 0.368421 -0.000000
vt 0.368421 0.166667
vt 0.421053 0.666667
vt 0.368421 0.666667
vt 0.421053 0.833333
vt 0.368421 1.000000
vt 0.421053 -0.000000
vt 0.421053 0.333333
vt 0.368421 0.333333
vt 0.421053 0.500000
vt 0.473684 0.666667
vt 0.473684 0.833333
vt 0.421053 1.000000
vt 0.473684 -0.000000
vt 0.421053 0.166667
vt 0.473684 0.166667
vt 0.473684 0.333333
vt 0.473684 0.500000
vt 0.473684 1.000000
vt 1.000000 0.500000
vt 1.000000 1.000000
s 0
f 1/1/1 8/2/1 2/3/1
f 8/2/2 3/4/2 2/3/2
f 9/5/3 4/6/3 3/4/3
f 10/7/4 5/8/4 4/9/4
f 5/8/5 12/10/5 6/11/5
f 6/11/6 7/12/6 1/1/6
f 13/13/7 8/2/7 7/12/7
f 14/14/2 9/5/2 8/2/2
f 15/15/8 10/16/8 9/5/8
f 16/17/9 11/18/9 10/7/9
f 17/19/5 12/10/5 11/18/5
f 12/10/10 13/13/10 7/12/10
f 19/20/11 14/14/11 13/13/11
f 20/21/2 15/15/2 14/14/2
f 21/22/12 16/23/12 15/15/12
f 16/17/13 23/24/13 17/19/13
f 23/24/5 18/25/5 17/19/5
f 24/26/14 13/13/14 18/25/14
f 19/20/15 26/27/15 20/21/15
f 20/21/2 27/28/2 21/22/2
f 27/28/16 22/29/16 21/22/16
f 28/30/17 23/24/17 22/31/17
f 23/24/5 30/32/5 24/26/5
f 30/32/18 19/20/18 24/26/18
f 31/33/19 26/27/19 25/34/19
f 32/35/2 27/28/2 26/27/2
f 33/36/20 28/37/20 27/28/20
f 34/38/21 29/39/21 28/30/21
f 35/40/5 30/32/5 29/39/5
f 36/41/22 25/34/22 30/32/22
f 31/33/23 38/42/23 32/35/23
f 32/35/2 39/43/2 33/36/2
f 39/43/24 34/44/24 33/36/24
f 40/45/25 35/40/25 34/38/25
f 41/46/5 36/41/5 35/40/5
f 42/47/26 31/33/26 36/41/26
f 37/48/27 44/49/27 38/42/27
f 44/49/2 39/43/2 38/42/2
f 45/50/28 40/51/28 39/43/28
f 46/52/29 41/46/29 40/45/29
f 41/46/5 48/53/5 42/47/5
f 48/53/30 37/48/30 42/47/30
f 43/54/31 50/55/31 44/49/31
f 44/49/2 51/56/2 45/50/2
f 51/56/32 46/57/32 45/50/32
f 52/58/33 47/59/33 46/52/33
f 47/59/5 54/60/5 48/53/5
f 54/60/34 43/54/34 48/53/34
f 49/61/35 56/62/35 50/55/35
f 56/62/2 51/56/2 50/55/2
f 57/63/36 52/64/36 51/56/36
f 58/65/37 53/66/37 52/58/37
f 59/67/5 54/60/5 53/66/5
f 60/68/38 49/61/38 54/60/38
f 61/69/39 56/70/39 55/71/39
f 62/72/2 57/73/2 56/70/2
f 57/73/40 64/74/40 58/75/40
f 64/76/41 59/77/41 58/78/41
f 65/79/5 60/80/5 59/77/5
f 66/81/42 55/71/42 60/80/42
f 67/82/43 62/72/43 61/69/43
f 62/72/2 69/83/2 63/84/2
f 63/84/44 70/85/44 64/74/44
f 70/86/45 65/79/45 64/76/45
f 71/87/5 66/81/5 65/79/5
f 66/81/46 67/82/46 61/69/46
f 73/88/47 68/89/47 67/82/47
f 74/90/2 69/83/2 68/89/2
f 75/91/48 70/85/48 69/83/48
f 70/86/49 77/92/49 71/87/49
f 77/92/5 72/93/5 71/87/5
f 72/93/50 73/88/50 67/82/50
f 79/94/51 74/90/51 73/88/51
f 74/90/2 81/95/2 75/91/2
f 81/95/52 76/96/52 75/91/52
f 82/97/53 77/92/53 76/98/53
f 83/99/5 78/100/5 77/92/5
f 84/101/54 73/88/54 78/100/54
f 85/102/55 80/103/55 79/94/55
f 86/104/2 81/95/2 80/103/2
f 87/105/56 82/106/56 81/95/56
f 82/97/57 89/107/57 83/99/57
f 89/107/5 84/101/5 83/99/5
f 90/108/58 79/94/58 84/101/58
f 91/109/59 86/104/59 85/102/59
f 86/104/2 93/110/2 87/105/2
f 93/110/60 88/111/60 87/105/60
f 94/112/61 89/107/61 88/113/61
f 95/114/5 90/108/5 89/107/5
f 96/115/62 85/102/62 90/108/62
f 97/116/63 92/117/63 91/109/63
f 92/117/2 99/118/2 93/110/2
f 99/118/64 94/119/64 93/110/64
f 100/120/65 95/114/65 94/112/65
f 101/121/5 96/115/5 95/114/5
f 96/115/66 97/116/66 91/109/66
f 97/116/67 104/122/67 98/123/67
f 104/122/2 99/118/2 98/123/2
f 105/124/68 100/125/68 99/118/68
f 106/126/69 101/121/69 100/120/69
f 101/121/5 108/127/5 102/128/5
f 108/127/70 97/116/70 102/128/70
f 103/129/71 110/130/71 104/122/71
f 110/130/2 105/124/2 104/122/2
f 111/131/72 106/132/72 105/124/72
f 112/133/73 107/134/73 106/126/73
f 113/135/5 108/127/5 107/134/5
f 114/136/74 103/129/74 108/127/74
f 1/1/75 110/130/75 109/137/75
f 2/3/2 111/131/2 110/130/2
f 3/4/76 112/138/76 111/131/76
f 4/9/77 113/135/77 112/133/77
f 5/8/5 114/136/5 113/135/5
f 6/11/78 109/137/78 114/136/78
f 1/1/1 7/12/1 8/2/1
f 8/2/2 9/5/2 3/4/2
f 9/5/3 10/16/3 4/6/3
f 10/7/4 11/18/4 5/8/4
f 5/8/5 11/18/5 12/10/5
f 6/11/6 12/10/6 7/12/6
f 13/13/7 14/14/7 8/2/7
f 14/14/2 15/15/2 9/5/2
f 15/15/8 16/23/8 10/16/8
f 16/17/9 17/19/9 11/18/9
f 17/19/5 18/25/5 12/10/5
f 12/10/10 18/25/10 13/13/10
f 19/20/11 20/21/11 14/14/11
f 20/21/2 21/22/2 15/15/2
f 21/22/12 22/29/12 16/23/12
f 16/17/13 22/31/13 23/24/13
f 23/24/5 24/26/5 18/25/5
f 24/26/14 19/20/14 13/13/14
f 19/20/15 25/34/15 26/27/15
f 20/21/2 26/27/2 27/28/2
f 27/28/16 28/37/16 22/29/16
f 28/30/17 29/39/17 23/24/17
f 23/24/5 29/39/5 30/32/5
f 30/32/18 25/34/18 19/20/18
f 31/33/19 32/35/19 26/27/19
f 32/35/2 33/36/2 27/28/2
f 33/36/20 34/44/20 28/37/20
f 34/38/21 35/40/21 29/39/21
f 35/40/5 36/41/5 30/32/5
f 36/41/22 31/33/22 25/34/22
f 31/33/23 37/48/23 38/42/23
f 32/35/2 38/42/2 39/43/2
f 39/43/24 40/51/24 34/44/24
f 40/45/25 41/46/25 35/40/25
f 41/46/5 42/47/5 36/41/5
f 42/47/26 37/48/26 31/33/26
f 37/48/27 43/54/27 44/49/27
f 44/49/2 45/50/2 39/43/2
f 45/50/28 46/57/28 40/51/28
f 46/52/29 47/59/29 41/46/29
f 41/46/5 47/59/5 48/53/5
f 48/53/30 43/54/30 37/48/30
f 43/54/31 49/61/31 50/55/31
f 44/49/2 50/55/2 51/56/2
f 51/56/32 52/64/32 46/57/32
f 52/58/33 53/66/33 47/59/33
f 47/59/5 53/66/5 54/60/5
f 54/60/34 49/61/34 43/54/34
f 49/61/35 55/139/35 56/62/35
f 56/62/2 57/63/2 51/56/2
f 57/63/36 58/140/36 52/64/36
f 58/65/37 59/67/37 53/66/37
f 59/67/5 60/68/5 54/60/5
f 60/68/38 55/139/38 49/61/38
f 61/69/39 62/72/39 56/70/39
f 62/72/2 63/84/2 57/73/2
f 57/73/40 63/84/40 64/74/40
f 64/76/41 65/79/41 59/77/41
f 65/79/5 66/81/5 60/80/5
f 66/81/42 61/69/42 55/71/42
f 67/82/43 68/89/43 62/72/43
f 62/72/2 68/89/2 69/83/2
f 63/84/44 69/83/44 70/85/44
f 70/86/45 71/87/45 65/79/45
f 71/87/5 72/93/5 66/81/5
f 66/81/46 72/93/46 67/82/46
f 73/88/47 74/90/47 68/89/47
f 74/90/2 75/91/2 69/83/2
f 75/91/48 76/96/48 70/85/48
f 70/86/49 76/98/49 77/92/49
f 77/92/5 78/100/5 72/93/5
f 72/93/50 78/100/50 73/88/50
f 79/94/51 80/103/51 74/90/51
f 74/90/2 80/103/2 81/95/2
f 81/95/52 82/106/52 76/96/52
f 82/97/53 83/99/53 77/92/53
f 83/99/5 84/101/5 78/100/5
f 84/101/54 79/94/54 73/88/54
f 85/102/55 86/104/55 80/103/55
f 86/104/2 87/105/2 81/95/2
f 87/105/56 88/111/56 82/106/56
f 82/97/57 88/113/57 89/107/57
f 89/107/5 90/108/5 84/101/5
f 90/108/58 85/102/58 79/94/58
f 91/109/59 92/117/59 86/104/59
f 86/104/2 92/117/2 93/110/2
f 93/110/60 94/119/60 88/111/60
f 94/112/61 95/114/61 89/107/61
f 95/114/5 96/115/5 90/108/5
f 96/115/62 91/109/62 85/102/62
f 97/116/63 98/123/63 92/117/63
f 92/117/2 98/123/2 99/118/2
f 99/118/64 100/125/64 94/119/64
f 100/120/65 101/121/65 95/114/65
f 101/121/5 102/128/5 96/115/5
f 96/115/66 102/128/66 97/116/66
f 97/116/67 103/129/67 104/122/67
f 104/122/2 105/124/2 99/118/2
f 105/124/68 106/132/68 100/125/68
f 106/126/69 107/134/69 101/121/69
f 101/121/5 107/134/5 108/127/5
f 108/127/70 103/129/70 97/116/70
f 103/129/71 109/137/71 110/130/71
f 110/130/2 111/131/2 105/124/2
f 111/131/72 112/138/72 106/132/72
f 112/133/73 113/135/73 107/134/73
f 113/135/5 114/136/5 108/127/5
f 114/136/74 109/137/74 103/129/74
f 1/1/75 2/3/75 110/130/75
f 2/3/2 3/4/2 111/131/2
f 3/4/76 4/6/76 112/138/76
f 4/9/77 5/8/77 113/135/77
f 5/8/5 6/11/5 114/136/5
f 6/11/78 1/1/78 109/137/78

119
asset-srcs/models/arrow.obj Normal file
View file

@ -0,0 +1,119 @@
# Blender 4.3.2
# www.blender.org
mtllib arrow.mtl
o Cylinder.001
v 0.189969 0.008460 -1.749730
v 0.189969 0.008460 1.759116
v 0.134756 -0.124837 -1.749730
v 0.134756 -0.124837 1.759116
v 0.001459 -0.180050 -1.749730
v 0.001459 -0.180050 1.759116
v -0.131837 -0.124837 -1.749730
v -0.131837 -0.124837 1.759116
v -0.187050 0.008460 -1.749730
v -0.187050 0.008460 1.759116
v -0.131837 0.141756 -1.749730
v -0.131837 0.141756 1.759116
v 0.001459 0.196969 -1.749730
v 0.001459 0.196969 1.759116
v 0.134756 0.141756 -1.749730
v 0.134756 0.141756 1.759116
v 0.398640 0.008460 1.759116
v 0.282308 -0.272389 1.759116
v 0.001459 -0.388721 1.759116
v -0.279390 -0.272389 1.759116
v -0.395722 0.008460 1.759116
v -0.279390 0.289309 1.759116
v 0.001459 0.405641 1.759116
v 0.282308 0.289309 1.759116
v 0.001459 0.010576 2.759115
vn 0.9239 -0.3827 -0.0000
vn 0.3827 -0.9239 -0.0000
vn -0.3827 -0.9239 -0.0000
vn -0.9239 -0.3827 -0.0000
vn -0.9239 0.3827 -0.0000
vn -0.3827 0.9239 -0.0000
vn -0.0000 -0.0000 -1.0000
vn 0.3827 0.9239 -0.0000
vn 0.9239 0.3827 -0.0000
vn 0.8676 0.3594 0.3438
vn -0.3595 0.8679 0.3429
vn -0.8671 -0.3592 0.3452
vn 0.3590 -0.8668 0.3461
vn 0.3595 0.8679 0.3429
vn -0.8676 0.3594 0.3438
vn -0.3590 -0.8668 0.3461
vn 0.8671 -0.3592 0.3452
vt 1.000000 1.000000
vt 0.875000 0.500000
vt 1.000000 0.500000
vt 0.875000 1.000000
vt 0.750000 0.500000
vt 0.750000 1.000000
vt 0.625000 0.500000
vt 0.625000 1.000000
vt 0.500000 0.500000
vt 0.500000 1.000000
vt 0.375000 0.500000
vt 0.375000 1.000000
vt 0.250000 0.500000
vt 0.250000 1.000000
vt 0.125000 0.500000
vt 0.125000 1.000000
vt 0.000000 0.500000
vt 0.919706 0.080294
vt 0.580294 0.080294
vt 0.580294 0.419706
vt 0.000000 1.000000
vt 0.750000 0.490000
vt 0.919706 0.419706
vt 0.990000 0.250000
vt 0.750000 0.010000
vt 0.510000 0.250000
s 0
f 2/1/1 3/2/1 1/3/1
f 4/4/2 5/5/2 3/2/2
f 6/6/3 7/7/3 5/5/3
f 8/8/4 9/9/4 7/7/4
f 10/10/5 11/11/5 9/9/5
f 12/12/6 13/13/6 11/11/6
f 8/8/7 21/10/7 10/10/7
f 14/14/8 15/15/8 13/13/8
f 16/16/9 1/17/9 15/15/9
f 7/18/7 11/19/7 15/20/7
f 17/21/10 24/16/10 25/21/10
f 14/14/7 24/16/7 16/16/7
f 4/4/7 19/6/7 6/6/7
f 10/10/7 22/12/7 12/12/7
f 16/16/7 17/21/7 2/21/7
f 2/1/7 18/4/7 4/4/7
f 6/6/7 20/8/7 8/8/7
f 12/12/7 23/14/7 14/14/7
f 23/14/11 22/12/11 25/14/11
f 21/10/12 20/8/12 25/10/12
f 19/6/13 18/4/13 25/6/13
f 24/16/14 23/14/14 25/16/14
f 22/12/15 21/10/15 25/12/15
f 20/8/16 19/6/16 25/8/16
f 18/4/17 17/1/17 25/4/17
f 2/1/1 4/4/1 3/2/1
f 4/4/2 6/6/2 5/5/2
f 6/6/3 8/8/3 7/7/3
f 8/8/4 10/10/4 9/9/4
f 10/10/5 12/12/5 11/11/5
f 12/12/6 14/14/6 13/13/6
f 8/8/7 20/8/7 21/10/7
f 14/14/8 16/16/8 15/15/8
f 16/16/9 2/21/9 1/17/9
f 15/20/7 1/22/7 3/23/7
f 3/23/7 5/24/7 7/18/7
f 7/18/7 9/25/7 11/19/7
f 11/19/7 13/26/7 15/20/7
f 15/20/7 3/23/7 7/18/7
f 14/14/7 23/14/7 24/16/7
f 4/4/7 18/4/7 19/6/7
f 10/10/7 21/10/7 22/12/7
f 16/16/7 24/16/7 17/21/7
f 2/1/7 17/1/7 18/4/7
f 6/6/7 19/6/7 20/8/7
f 12/12/7 22/12/7 23/14/7

View file

@ -0,0 +1,36 @@
# Blender 4.3.2
# www.blender.org
mtllib cube.mtl
o Cube
v 1.000000 1.000000 -1.000000
v 1.000000 -1.000000 -1.000000
v 1.000000 1.000000 1.000000
v 1.000000 -1.000000 1.000000
v -1.000000 1.000000 -1.000000
v -1.000000 -1.000000 -1.000000
v -1.000000 1.000000 1.000000
v -1.000000 -1.000000 1.000000
vn -0.0000 1.0000 -0.0000
vn -0.0000 -0.0000 1.0000
vn -1.0000 -0.0000 -0.0000
vn -0.0000 -1.0000 -0.0000
vn 1.0000 -0.0000 -0.0000
vn -0.0000 -0.0000 -1.0000
vt 0.000000 1.000000
vt 1.000000 0.000000
vt 1.000000 1.000000
vt 0.000000 0.000000
s 0
usemtl Material
f 5/1/1 3/2/1 1/3/1
f 3/3/2 8/4/2 4/2/2
f 7/1/3 6/2/3 8/4/3
f 2/3/4 8/4/4 6/1/4
f 1/3/5 4/4/5 2/2/5
f 5/1/6 2/2/6 6/4/6
f 5/1/1 7/4/1 3/2/1
f 3/3/2 7/1/2 8/4/2
f 7/1/3 5/3/3 6/2/3
f 2/3/4 4/2/4 8/4/4
f 1/3/5 3/1/5 4/4/5
f 5/1/6 1/3/6 2/2/6

View file

@ -0,0 +1,137 @@
# Blender 4.4.1
# www.blender.org
mtllib cylinder.mtl
o Cylinder
v 0.000000 -1.000000 1.000000
v 0.000000 -1.000000 -1.000000
v 0.500000 -0.866025 1.000000
v 0.500000 -0.866025 -1.000000
v 0.866025 -0.500000 1.000000
v 0.866025 -0.500000 -1.000000
v 1.000000 0.000000 1.000000
v 1.000000 -0.000000 -1.000000
v 0.866025 0.500000 1.000000
v 0.866025 0.500000 -1.000000
v 0.500000 0.866025 1.000000
v 0.500000 0.866025 -1.000000
v 0.000000 1.000000 1.000000
v 0.000000 1.000000 -1.000000
v -0.500000 0.866025 1.000000
v -0.500000 0.866025 -1.000000
v -0.866025 0.500000 1.000000
v -0.866025 0.500000 -1.000000
v -1.000000 0.000000 1.000000
v -1.000000 -0.000000 -1.000000
v -0.866025 -0.500000 1.000000
v -0.866025 -0.500000 -1.000000
v -0.500000 -0.866025 1.000000
v -0.500000 -0.866025 -1.000000
vn 0.2588 -0.9659 -0.0000
vn 0.7071 -0.7071 -0.0000
vn 0.9659 -0.2588 -0.0000
vn 0.9659 0.2588 -0.0000
vn 0.7071 0.7071 -0.0000
vn 0.2588 0.9659 -0.0000
vn -0.2588 0.9659 -0.0000
vn -0.7071 0.7071 -0.0000
vn -0.9659 0.2588 -0.0000
vn -0.9659 -0.2588 -0.0000
vn -0.0000 -0.0000 -1.0000
vn -0.7071 -0.7071 -0.0000
vn -0.2588 -0.9659 -0.0000
vn -0.0000 -0.0000 1.0000
vt 1.000000 1.000000
vt 0.916667 0.500000
vt 1.000000 0.500000
vt 0.916667 1.000000
vt 0.833333 0.500000
vt 0.833333 1.000000
vt 0.750000 0.500000
vt 0.750000 1.000000
vt 0.666667 0.500000
vt 0.666667 1.000000
vt 0.583333 0.500000
vt 0.583333 1.000000
vt 0.500000 0.500000
vt 0.500000 1.000000
vt 0.416667 0.500000
vt 0.416667 1.000000
vt 0.333333 0.500000
vt 0.333333 1.000000
vt 0.250000 0.500000
vt 0.250000 1.000000
vt 0.166667 0.500000
vt 0.042154 0.370000
vt 0.250000 0.010000
vt 0.457846 0.370000
vt 0.166667 1.000000
vt 0.083333 0.500000
vt 0.083333 1.000000
vt 0.000000 0.500000
vt 0.990000 0.250000
vt 0.630000 0.042154
vt 0.630000 0.457846
vt 0.370000 0.457846
vt 0.250000 0.490000
vt 0.130000 0.457846
vt 0.010000 0.250000
vt 0.042154 0.130000
vt 0.130000 0.042154
vt 0.370000 0.042154
vt 0.457846 0.130000
vt 0.490000 0.250000
vt 0.000000 1.000000
vt 0.750000 0.490000
vt 0.870000 0.457846
vt 0.957846 0.370000
vt 0.957846 0.130000
vt 0.870000 0.042154
vt 0.750000 0.010000
vt 0.542154 0.130000
vt 0.510000 0.250000
vt 0.542154 0.370000
s 0
f 2/1/1 3/2/1 1/3/1
f 4/4/2 5/5/2 3/2/2
f 6/6/3 7/7/3 5/5/3
f 8/8/4 9/9/4 7/7/4
f 10/10/5 11/11/5 9/9/5
f 12/12/6 13/13/6 11/11/6
f 14/14/7 15/15/7 13/13/7
f 16/16/8 17/17/8 15/15/8
f 18/18/9 19/19/9 17/17/9
f 20/20/10 21/21/10 19/19/10
f 22/22/11 14/23/11 6/24/11
f 22/25/12 23/26/12 21/21/12
f 24/27/13 1/28/13 23/26/13
f 7/29/14 15/30/14 23/31/14
f 2/1/1 4/4/1 3/2/1
f 4/4/2 6/6/2 5/5/2
f 6/6/3 8/8/3 7/7/3
f 8/8/4 10/10/4 9/9/4
f 10/10/5 12/12/5 11/11/5
f 12/12/6 14/14/6 13/13/6
f 14/14/7 16/16/7 15/15/7
f 16/16/8 18/18/8 17/17/8
f 18/18/9 20/20/9 19/19/9
f 20/20/10 22/25/10 21/21/10
f 6/24/11 4/32/11 2/33/11
f 2/33/11 24/34/11 22/22/11
f 22/22/11 20/35/11 18/36/11
f 18/36/11 16/37/11 22/22/11
f 16/37/11 14/23/11 22/22/11
f 14/23/11 12/38/11 10/39/11
f 10/39/11 8/40/11 14/23/11
f 8/40/11 6/24/11 14/23/11
f 6/24/11 2/33/11 22/22/11
f 22/25/12 24/27/12 23/26/12
f 24/27/13 2/41/13 1/28/13
f 23/31/14 1/42/14 3/43/14
f 3/43/14 5/44/14 23/31/14
f 5/44/14 7/29/14 23/31/14
f 7/29/14 9/45/14 11/46/14
f 11/46/14 13/47/14 7/29/14
f 13/47/14 15/30/14 7/29/14
f 15/30/14 17/48/14 19/49/14
f 19/49/14 21/50/14 23/31/14
f 15/30/14 19/49/14 23/31/14

View file

@ -0,0 +1,295 @@
# Blender 4.4.0
# www.blender.org
mtllib outline.mtl
o Cube
v -1.000000 -1.000000 1.000000
v -1.000000 1.000000 1.000000
v -1.000000 -1.000000 -1.000000
v -1.000000 1.000000 -1.000000
v 1.000000 -1.000000 1.000000
v 1.000000 1.000000 1.000000
v 1.000000 -1.000000 -1.000000
v 1.000000 1.000000 -1.000000
v -0.500000 -1.000000 -1.000000
v -0.500000 1.000000 -1.000000
v -0.500000 -1.000000 1.000000
v -0.500000 1.000000 1.000000
v 0.500000 1.000000 -1.000000
v 0.500000 -1.000000 1.000000
v 0.500000 -1.000000 -1.000000
v 0.500000 1.000000 1.000000
v -1.000000 -1.000000 -0.500000
v -1.000000 1.000000 -0.500000
v 1.000000 -1.000000 -0.500000
v 1.000000 1.000000 -0.500000
v -0.500000 1.000000 -0.500000
v -0.500000 -1.000000 -0.500000
v 0.500000 1.000000 -0.500000
v 0.500000 -1.000000 -0.500000
v -1.000000 -1.000000 0.500000
v 1.000000 1.000000 0.500000
v -0.500000 1.000000 0.500000
v -0.500000 -1.000000 0.500000
v 0.500000 1.000000 0.500000
v 0.500000 -1.000000 0.500000
v -1.000000 1.000000 0.500000
v 1.000000 -1.000000 0.500000
v -1.000000 0.500000 1.000000
v -1.000000 0.500000 -1.000000
v 1.000000 0.500000 -1.000000
v 1.000000 0.500000 1.000000
v -0.500000 0.500000 1.000000
v -0.500000 0.500000 -1.000000
v 0.500000 0.500000 1.000000
v 0.500000 0.500000 -1.000000
v 1.000000 0.500000 -0.500000
v -1.000000 0.500000 -0.500000
v -1.000000 0.500000 0.500000
v 1.000000 0.500000 0.500000
v -1.000000 -0.500000 -1.000000
v 1.000000 -0.500000 -1.000000
v 1.000000 -0.500000 1.000000
v -0.500000 -0.500000 1.000000
v -0.500000 -0.500000 -1.000000
v 0.500000 -0.500000 1.000000
v 0.500000 -0.500000 -1.000000
v 1.000000 -0.500000 -0.500000
v -1.000000 -0.500000 -0.500000
v -1.000000 -0.500000 0.500000
v 1.000000 -0.500000 0.500000
v -1.000000 -0.500000 1.000000
v 0.500000 0.500000 0.500000
v -0.500000 -0.500000 0.500000
v 0.500000 0.500000 -0.500000
v 0.500000 -0.500000 -0.500000
v -0.500000 -0.500000 -0.500000
v 0.500000 -0.500000 0.500000
v -0.500000 0.500000 0.500000
v -0.500000 0.500000 -0.500000
vn -1.0000 -0.0000 -0.0000
vn -0.0000 -0.0000 -1.0000
vn 1.0000 -0.0000 -0.0000
vn -0.0000 -0.0000 1.0000
vn -0.0000 -1.0000 -0.0000
vn -0.0000 1.0000 -0.0000
vt 0.625000 0.187500
vt 0.562500 0.250000
vt 0.562500 0.187500
vt 0.625000 0.437500
vt 0.562500 0.500000
vt 0.562500 0.437500
vt 0.625000 0.687500
vt 0.562500 0.750000
vt 0.562500 0.687500
vt 0.625000 0.937500
vt 0.562500 1.000000
vt 0.562500 0.937500
vt 0.375000 0.687500
vt 0.312500 0.750000
vt 0.312500 0.687500
vt 0.875000 0.687500
vt 0.812500 0.750000
vt 0.812500 0.687500
vt 0.687500 0.750000
vt 0.687500 0.687500
vt 0.187500 0.687500
vt 0.125000 0.750000
vt 0.125000 0.687500
vt 0.625000 0.812500
vt 0.562500 0.812500
vt 0.625000 0.250000
vt 0.562500 0.312500
vt 0.625000 0.750000
vt 0.187500 0.750000
vt 0.625000 0.312500
vt 0.312500 0.500000
vt 0.187500 0.562500
vt 0.187500 0.500000
vt 0.687500 0.500000
vt 0.625000 0.562500
vt 0.625000 0.500000
vt 0.125000 0.562500
vt 0.125000 0.500000
vt 0.812500 0.500000
vt 0.687500 0.562500
vt 0.875000 0.500000
vt 0.812500 0.562500
vt 0.375000 0.500000
vt 0.312500 0.562500
vt 0.562500 0.562500
vt 0.625000 0.062500
vt 0.562500 0.062500
vt 0.625000 0.000000
vt 0.562500 0.000000
vt 0.875000 0.562500
vt 0.375000 0.562500
vt 0.437500 0.562500
vt 0.437500 0.062500
vt 0.437500 0.000000
vt 0.437500 0.500000
vt 0.437500 0.437500
vt 0.437500 0.812500
vt 0.437500 0.750000
vt 0.437500 0.312500
vt 0.437500 0.250000
vt 0.437500 1.000000
vt 0.437500 0.937500
vt 0.437500 0.687500
vt 0.437500 0.187500
vt 0.375000 0.250000
vt 0.375000 0.187500
vt 0.375000 0.437500
vt 0.375000 0.750000
vt 0.375000 1.000000
vt 0.375000 0.937500
vt 0.375000 0.812500
vt 0.375000 0.312500
vt 0.375000 0.062500
vt 0.375000 0.000000
vt 0.625000 1.000000
vt 0.875000 0.750000
s 0
f 18/1/1 34/2/1 42/3/1
f 13/4/2 35/5/2 40/6/2
f 26/7/3 36/8/3 44/9/3
f 12/10/4 33/11/4 37/12/4
f 32/13/5 14/14/5 30/15/5
f 31/16/6 12/17/6 27/18/6
f 27/18/6 16/19/6 29/20/6
f 28/21/5 1/22/5 25/23/5
f 16/24/4 37/12/4 39/25/4
f 4/26/2 38/27/2 34/2/2
f 6/28/4 39/25/4 36/8/4
f 29/20/6 6/28/6 26/7/6
f 30/15/5 11/29/5 28/21/5
f 10/30/2 40/6/2 38/27/2
f 15/31/5 22/32/5 9/33/5
f 13/34/6 20/35/6 8/36/6
f 9/33/5 17/37/5 3/38/5
f 10/39/6 23/40/6 13/34/6
f 4/41/6 21/42/6 10/39/6
f 7/43/5 24/44/5 15/31/5
f 8/36/3 41/45/3 35/5/3
f 31/46/1 42/3/1 43/47/1
f 2/48/1 43/47/1 33/49/1
f 28/21/2 62/15/2 30/15/2
f 23/40/6 26/7/6 20/35/6
f 22/32/5 25/23/5 17/37/5
f 27/18/3 64/42/3 21/42/3
f 18/50/6 27/18/6 21/42/6
f 19/51/5 30/15/5 24/44/5
f 20/35/3 44/9/3 41/45/3
f 41/45/4 60/52/4 52/52/4
f 33/49/1 54/53/1 56/54/1
f 43/47/2 58/53/2 54/53/2
f 35/5/3 52/52/3 46/55/3
f 51/56/1 59/6/1 40/6/1
f 36/8/4 50/57/4 47/58/4
f 34/2/2 49/59/2 45/60/2
f 39/25/1 62/57/1 50/57/1
f 37/12/4 56/61/4 48/62/4
f 44/9/3 47/58/3 55/63/3
f 40/6/2 46/55/2 51/56/2
f 42/3/1 45/60/1 53/64/1
f 53/64/1 3/65/1 17/66/1
f 51/56/2 7/43/2 15/67/2
f 55/63/3 5/68/3 32/13/3
f 48/62/4 1/69/4 11/70/4
f 50/57/4 11/70/4 14/71/4
f 45/60/2 9/72/2 3/65/2
f 47/58/4 14/71/4 5/68/4
f 49/59/2 15/67/2 9/72/2
f 46/55/3 19/51/3 7/43/3
f 54/53/1 17/66/1 25/73/1
f 56/54/1 25/73/1 1/74/1
f 52/52/3 32/13/3 19/51/3
f 50/57/6 58/62/6 48/62/6
f 48/62/3 63/12/3 37/12/3
f 37/12/5 57/25/5 39/25/5
f 52/52/6 62/63/6 55/63/6
f 44/9/5 59/45/5 41/45/5
f 55/63/2 57/9/2 44/9/2
f 38/27/3 61/59/3 49/59/3
f 40/6/5 64/27/5 38/27/5
f 49/59/6 60/56/6 51/56/6
f 29/20/2 63/18/2 27/18/2
f 21/42/4 59/40/4 23/40/4
f 23/40/1 57/20/1 29/20/1
f 30/15/1 60/44/1 24/44/1
f 24/44/4 61/32/4 22/32/4
f 22/32/3 58/21/3 28/21/3
f 42/3/5 63/47/5 43/47/5
f 54/53/6 61/64/6 53/64/6
f 53/64/4 64/3/4 42/3/4
f 18/1/1 4/26/1 34/2/1
f 13/4/2 8/36/2 35/5/2
f 26/7/3 6/28/3 36/8/3
f 12/10/4 2/75/4 33/11/4
f 32/13/5 5/68/5 14/14/5
f 31/16/6 2/76/6 12/17/6
f 27/18/6 12/17/6 16/19/6
f 28/21/5 11/29/5 1/22/5
f 16/24/4 12/10/4 37/12/4
f 4/26/2 10/30/2 38/27/2
f 6/28/4 16/24/4 39/25/4
f 29/20/6 16/19/6 6/28/6
f 30/15/5 14/14/5 11/29/5
f 10/30/2 13/4/2 40/6/2
f 15/31/5 24/44/5 22/32/5
f 13/34/6 23/40/6 20/35/6
f 9/33/5 22/32/5 17/37/5
f 10/39/6 21/42/6 23/40/6
f 4/41/6 18/50/6 21/42/6
f 7/43/5 19/51/5 24/44/5
f 8/36/3 20/35/3 41/45/3
f 31/46/1 18/1/1 42/3/1
f 2/48/1 31/46/1 43/47/1
f 28/21/2 58/21/2 62/15/2
f 23/40/6 29/20/6 26/7/6
f 22/32/5 28/21/5 25/23/5
f 27/18/3 63/18/3 64/42/3
f 18/50/6 31/16/6 27/18/6
f 19/51/5 32/13/5 30/15/5
f 20/35/3 26/7/3 44/9/3
f 41/45/4 59/45/4 60/52/4
f 33/49/1 43/47/1 54/53/1
f 43/47/2 63/47/2 58/53/2
f 35/5/3 41/45/3 52/52/3
f 51/56/1 60/56/1 59/6/1
f 36/8/4 39/25/4 50/57/4
f 34/2/2 38/27/2 49/59/2
f 39/25/1 57/25/1 62/57/1
f 37/12/4 33/11/4 56/61/4
f 44/9/3 36/8/3 47/58/3
f 40/6/2 35/5/2 46/55/2
f 42/3/1 34/2/1 45/60/1
f 53/64/1 45/60/1 3/65/1
f 51/56/2 46/55/2 7/43/2
f 55/63/3 47/58/3 5/68/3
f 48/62/4 56/61/4 1/69/4
f 50/57/4 48/62/4 11/70/4
f 45/60/2 49/59/2 9/72/2
f 47/58/4 50/57/4 14/71/4
f 49/59/2 51/56/2 15/67/2
f 46/55/3 52/52/3 19/51/3
f 54/53/1 53/64/1 17/66/1
f 56/54/1 54/53/1 25/73/1
f 52/52/3 55/63/3 32/13/3
f 50/57/6 62/57/6 58/62/6
f 48/62/3 58/62/3 63/12/3
f 37/12/5 63/12/5 57/25/5
f 52/52/6 60/52/6 62/63/6
f 44/9/5 57/9/5 59/45/5
f 55/63/2 62/63/2 57/9/2
f 38/27/3 64/27/3 61/59/3
f 40/6/5 59/6/5 64/27/5
f 49/59/6 61/59/6 60/56/6
f 29/20/2 57/20/2 63/18/2
f 21/42/4 64/42/4 59/40/4
f 23/40/1 59/40/1 57/20/1
f 30/15/1 62/15/1 60/44/1
f 24/44/4 60/44/4 61/32/4
f 22/32/3 61/32/3 58/21/3
f 42/3/5 64/3/5 63/47/5
f 54/53/6 58/53/6 61/64/6
f 53/64/4 61/64/4 64/3/4

1627
asset-srcs/models/sphere.obj Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,36 @@
# Blender 4.4.3
# www.blender.org
mtllib wedge.mtl
o Cube
v 1.000000 -1.000000 -1.000000
v 1.000000 1.000000 1.000000
v 1.000000 -1.000000 1.000000
v -1.000000 -1.000000 -1.000000
v -1.000000 1.000000 1.000000
v -1.000000 -1.000000 1.000000
vn -0.0000 -0.0000 1.0000
vn -1.0000 -0.0000 -0.0000
vn -0.0000 -1.0000 -0.0000
vn 1.0000 -0.0000 -0.0000
vn -0.0000 0.7071 -0.7071
vt 0.625000 0.750000
vt 0.375000 1.000000
vt 0.375000 0.750000
vt 0.375000 0.000000
vt 0.625000 0.000000
vt 0.375000 0.250000
vt 0.375000 0.500000
vt 0.125000 0.750000
vt 0.125000 0.500000
vt 0.875000 0.750000
vt 0.625000 1.000000
s 0
usemtl Material
f 2/1/1 6/2/1 3/3/1
f 6/4/2 5/5/2 4/6/2
f 1/7/3 6/8/3 4/9/3
f 1/7/4 2/1/4 3/3/4
f 5/10/5 1/7/5 4/6/5
f 2/1/1 5/11/1 6/2/1
f 1/7/3 3/3/3 6/8/3
f 5/10/5 2/1/5 1/7/5

BIN
assets/icons/message.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 413 B

View file

@ -3,10 +3,10 @@
// I/O
out vec4 fColor;
uniform vec3 aColor;
uniform vec4 aColor;
// Main
void main() {
fColor = vec4(aColor, 1.0);
fColor = aColor;
}

View file

@ -54,11 +54,14 @@ uniform int numPointLights;
uniform DirLight sunLight;
uniform Material material;
uniform sampler2DArray studs;
uniform samplerCube skybox;
uniform float transparency;
uniform float reflectance;
uniform vec3 texScale;
// Functions
// Functions
vec3 calculateReflection();
vec3 calculateDirectionalLight(DirLight light);
vec3 calculatePointLight(PointLight light);
mat3 lookAlong(vec3 pos, vec3 forward, vec3 up);
@ -68,6 +71,7 @@ mat3 lookAlong(vec3 pos, vec3 forward, vec3 up);
void main() {
vec3 result = vec3(0.0);
result += calculateReflection();
result += calculateDirectionalLight(sunLight);
for (int i = 0; i < numPointLights; i++) {
@ -95,6 +99,25 @@ mat3 lookAlong(vec3 pos, vec3 forward, vec3 up) {
return mat3(s, u, f);
}
vec3 sampleSkybox()
{
vec3 norm = normalize(vNormal);
vec3 viewDir = normalize(viewPos - vPos);
vec3 reflectDir = reflect(-viewDir, norm);
return textureLod(skybox,reflectDir, 5.0 * (1.0-material.shininess)).rgb;
}
vec3 calculateReflection() {
vec3 norm = normalize(vNormal);
vec3 viewDir = normalize(viewPos - vPos);
vec3 reflectDir = reflect(viewDir, norm);
float fresnel = (pow(1.0-max(dot(viewDir, norm), 0.0), 5.0));
vec3 result = sampleSkybox() * mix(/* TEMPORARY: will be replaced by setting */ 0 * /* /TEMPORARY */ fresnel * material.specular, vec3(1.0), reflectance);
return result;
}
vec3 calculateDirectionalLight(DirLight light) {
// Calculate diffuse
vec3 norm = normalize(vNormal);
@ -105,10 +128,13 @@ vec3 calculateDirectionalLight(DirLight light) {
vec3 viewDir = normalize(viewPos - vPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
// float fresnel = (pow(1.0-max(dot(viewDir, norm), 0.0), 5.0));
vec3 ambient = light.ambient * material.diffuse;
vec3 diffuse = light.diffuse * diff * material.diffuse;
vec3 ambient = light.ambient * (material.diffuse * (1.0-reflectance));
vec3 diffuse = light.diffuse * diff * (material.diffuse * (1.0-reflectance));
vec3 specular = light.specular * spec * material.specular;
// specular += sampleSkybox() * fresnel * material.specular;
return (ambient + diffuse + specular);
}

View file

@ -9,6 +9,7 @@ const int FaceBack = 2;
const int FaceLeft = 3;
const int FaceBottom = 4;
const int FaceFront = 5;
const int FaceNone = 6;
const int SurfaceSmooth = 0;
const int SurfaceGlue = 1;
@ -31,6 +32,8 @@ uniform mat4 projection;
uniform int surfaces[6];
uniform vec3 texScale;
const float faceThreshold = sqrt(2)/2;
void main()
{
gl_Position = projection * view * model * vec4(aPos, 1.0);
@ -38,12 +41,20 @@ void main()
lPos = aPos;
vNormal = normalMatrix * aNormal;
lNormal = aNormal;
int vFace = aNormal == vec3(0,1,0) ? FaceTop :
aNormal == vec3(0, -1, 0) ? FaceBottom :
aNormal == vec3(1, 0, 0) ? FaceRight :
aNormal == vec3(-1, 0, 0) ? FaceLeft :
aNormal == vec3(0, 0, -1) ? FaceFront :
aNormal == vec3(0, 0, 1) ? FaceBack : -1;
int vFace = FaceNone;
if (dot(vec3(0, 1, 0), aNormal) > faceThreshold)
vFace = FaceTop;
else if (dot(vec3(0, -1, 0), aNormal) > faceThreshold)
vFace = FaceBottom;
else if (dot(vec3(1, 0, 0), aNormal) > faceThreshold)
vFace = FaceRight;
else if (dot(vec3(-1, 0, 0), aNormal) > faceThreshold)
vFace = FaceLeft;
else if (dot(vec3(0, 0, -1), aNormal) > faceThreshold)
vFace = FaceFront;
else if (dot(vec3(0, 0, 1), aNormal) > faceThreshold)
vFace = FaceBack;
vSurfaceZ = surfaces[vFace];
if (surfaces[vFace] > SurfaceUniversal) vSurfaceZ = 0;

View file

@ -26,6 +26,7 @@ int processHeader(fs::path srcRoot, fs::path srcPath, fs::path outPath) {
std::string srcRootStr = string_of(srcRoot);
std::string srcPathStr = string_of(srcPath);
std::string outPathStr = string_of(outPath);
std::string logFileStr = outPathStr + ".log";
const char* cargs[] = { "-xc++", "-std=c++17", "-I", srcRootStr.c_str(), "-D__AUTOGEN__", 0 };
// THANK YOU SO MUCH THIS STACKOVERFLOW ANSWER IS SO HELPFUL
@ -42,17 +43,26 @@ int processHeader(fs::path srcRoot, fs::path srcPath, fs::path outPath) {
return 1;
}
fs::create_directories(outPath.parent_path()); // Make sure generated dir exists before we try writing to it
// We write to a special log file instead of stdout/stderr to
// 1. avoid confusion
// 2. prevent MSBuild from reading the word "error" and detecting there's a problem with the program (there isn't)
FILE* logout = fopen(logFileStr.c_str(), "w");
// Print errors
int ndiags = clang_getNumDiagnostics(unit);
for (int i = 0; i < ndiags; i++) {
CXDiagnostic diag = clang_getDiagnostic(unit, i);
CXString str = clang_formatDiagnostic(diag, 0);
fprintf(stderr, "diag: %s\n", clang_getCString(str));
fprintf(logout, "diag: %s\n", clang_getCString(str));
clang_disposeString(str);
clang_disposeDiagnostic(diag);
}
fclose(logout);
CXCursor cursor = clang_getTranslationUnitCursor(unit);
object::AnalysisState objectAnlyState;
@ -66,8 +76,6 @@ int processHeader(fs::path srcRoot, fs::path srcPath, fs::path outPath) {
data::analyzeClasses(cursor, srcRootStr, &dataAnlyState);
enum_::analyzeClasses(cursor, srcRootStr, &enumAnlyState);
fs::create_directories(outPath.parent_path()); // Make sure generated dir exists before we try writing to it
printf("[AUTOGEN] Generating file %s...\n", relpathStr.c_str());
std::ofstream outStream(outPathStr);
@ -104,5 +112,7 @@ int main(int argc, char** argv) {
fs::path srcPath = argv[2];
fs::path outPath = argv[3];
// fprintf(stderr, "Some error here\n");
// return 0;
return processHeader(srcRoot, srcPath, outPath);
}

View file

@ -1,8 +1,24 @@
find_package(SDL2 REQUIRED)
include_directories(${SDL2_INCLUDE_DIRS})
find_package(glfw3 REQUIRED)
include(${CMAKE_CURRENT_SOURCE_DIR}/deps.cmake)
add_executable(client "src/main.cpp")
target_link_libraries(client PRIVATE ${SDL2_LIBRARIES} openblocks glfw)
add_dependencies(client openblocks)
target_link_libraries(client PRIVATE openblocks glfw)
add_dependencies(client openblocks)
if(WIN32)
# Copy assets
add_custom_command(
TARGET client POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_SOURCE_DIR}/assets
$<TARGET_FILE_DIR:client>/assets)
endif()
set_target_properties(client PROPERTIES
WIN32_EXECUTABLE ON
)
# https://stackoverflow.com/a/73899349/16255372
if (WIN32)
# /ENTRY:mainCRTStartup keeps the same "main" function instead of requiring "WinMain"
target_link_options(client PRIVATE "/ENTRY:mainCRTStartup")
endif()

12
client/deps.cmake Normal file
View file

@ -0,0 +1,12 @@
# Declare/fetch packages
include(FetchContent)
FetchContent_Declare(
glfw3
GIT_REPOSITORY https://github.com/glfw/glfw
GIT_TAG 3.4
)
FetchContent_MakeAvailable(glfw3)
# Find/include packages

View file

@ -1,8 +1,11 @@
#include <GL/glew.h>
#include <glad/gl.h>
#include <GLFW/glfw3.h>
#include "objects/part.h"
#include "logger.h"
#include "objects/part/part.h"
#include "panic.h"
#include "rendering/renderer.h"
#include "common.h"
#include "version.h"
void errorCatcher(int id, const char* str);
@ -15,23 +18,34 @@ void mouseCallback(GLFWwindow* window, double xpos, double ypos);
void mouseButtonCallback(GLFWwindow* window, int button, int action, int mods);
void resizeCallback(GLFWwindow* window, int width, int height);
std::shared_ptr<Part> lastPart;
std::shared_ptr<BasePart> lastPart;
int main() {
Logger::init();
glfwSetErrorCallback(errorCatcher);
std::string title = std::string() + "Openblocks Client " + BUILD_VERSION;
glfwInit();
glfwWindowHint(GLFW_SAMPLES, 4);
GLFWwindow *window = glfwCreateWindow(1200, 900, "OpenBlocks Client ALPHA", NULL, NULL);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_COMPAT_PROFILE); // Valid only in OpenGL 3.2+, see: https://stackoverflow.com/a/70519392/16255372
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
GLFWwindow *window = glfwCreateWindow(1200, 900, title.c_str(), NULL, NULL);
glfwSetKeyCallback(window, keyCallback);
glfwSetMouseButtonCallback(window, mouseButtonCallback);
glfwSetCursorPosCallback(window, mouseCallback);
glfwSetFramebufferSizeCallback(window, resizeCallback);
glfwMakeContextCurrent(window);
glewInit();
int version = gladLoadGL(glfwGetProcAddress);
if (version == 0) {
Logger::fatalError("Failed to initialize OpenGL context");
panic();
} else {
Logger::debugf("Initialized GL context version %d.%d", GLAD_VERSION_MAJOR(version), GLAD_VERSION_MINOR(version));
}
gDataModel->Init();
renderInit(1200, 900);
@ -55,7 +69,7 @@ int main() {
for (std::shared_ptr<Instance> inst : gWorkspace()->GetChildren()) {
if (inst->GetClass()->className != "Part") continue;
std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(inst);
std::shared_ptr<BasePart> part = std::dynamic_pointer_cast<BasePart>(inst);
gWorkspace()->SyncPartPhysics(part);
}
@ -77,7 +91,7 @@ int main() {
}
void errorCatcher(int id, const char* str) {
Logger::fatalError(std::format("GLFW Error: [{}] {}", id, str));
Logger::fatalErrorf("GLFW Error: [%d] %s", id, str);
}
float lastTime;
@ -148,6 +162,7 @@ void mouseButtonCallback(GLFWwindow* window, int button, int action, int mods) {
}
}
bool doDebugRender = false;
void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) {
if (key == GLFW_KEY_F && action == GLFW_PRESS) {
gWorkspace()->AddChild(lastPart = Part::New({
@ -191,6 +206,8 @@ void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods
if (key == GLFW_KEY_M && action == GLFW_PRESS) mode = 0;
if (key == GLFW_KEY_E && action == GLFW_PRESS) mode = 1; // Enlarge
if (key == GLFW_KEY_R && action == GLFW_PRESS) mode = 2;
if (key == GLFW_KEY_LEFT_BRACKET && action == GLFW_PRESS) setDebugRendererEnabled(doDebugRender = !doDebugRender);
}
void resizeCallback(GLFWwindow* window, int width, int height) {

23
cmake/CPM.cmake Normal file
View file

@ -0,0 +1,23 @@
set(CPM_DOWNLOAD_VERSION 0.42.0)
# Patch support is broken in CPM upstream atm, so we have to use a fork
set(CPM_HASH_SUM "f7d92592a257d184fd8de6fb496a711ce393676ed68999b96043aab2e5e6f9e6")
# set(CPM_HASH_SUM "2020b4fc42dba44817983e06342e682ecfc3d2f484a581f11cc5731fbe4dce8a")
if(CPM_SOURCE_CACHE)
set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
elseif(DEFINED ENV{CPM_SOURCE_CACHE})
set(CPM_DOWNLOAD_LOCATION "$ENV{CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
else()
set(CPM_DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake")
endif()
# Expand relative path. This is important if the provided path contains a tilde (~)
get_filename_component(CPM_DOWNLOAD_LOCATION ${CPM_DOWNLOAD_LOCATION} ABSOLUTE)
file(DOWNLOAD
https://raw.githubusercontent.com/BohdanBuinich/CPM.cmake/refs/heads/feat/fix_patch_command/cmake/CPM.cmake
# https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake
${CPM_DOWNLOAD_LOCATION} EXPECTED_HASH SHA256=${CPM_HASH_SUM}
)
include(${CPM_DOWNLOAD_LOCATION})

View file

@ -0,0 +1,60 @@
# Modified from QGIS' FindQScintilla.cmake by Thomas Moenicke, Larry Schaffer
add_library(QScintilla::QScintilla UNKNOWN IMPORTED)
### NECESSARY TO PREVENT staticMetaObject ERROR!!! See qscintilla.prf AKA qmake config
if(WIN32)
add_compile_definitions(QSCINTILLA_DLL)
endif()
FIND_PATH(QSCINTILLA_INCLUDE_DIR
NAMES Qsci/qsciglobal.h
PATHS
${Qt6Core_INCLUDE_DIRS}
$ENV{LIB_DIR}/include
/usr/local/include
/usr/include
${VCPKG_INSTALLED_DIR}/x64-windows/include
PATH_SUFFIXES ${QSCINTILLA_PATH_SUFFIXES}
)
set(QSCINTILLA_LIBRARY_NAMES
qscintilla2-qt6
qscintilla2_qt6
libqt6scintilla2
libqscintilla2-qt6
qt6scintilla2
libqscintilla2-qt6.dylib
qscintilla2
)
find_library(QSCINTILLA_LIBRARY
NAMES ${QSCINTILLA_LIBRARY_NAMES}
PATHS
"${QT_LIBRARY_DIR}"
$ENV{LIB_DIR}/lib
/usr/local/lib
/usr/local/lib/qt6
/usr/lib
/usr/lib64
/usr/lib32
${VCPKG_INSTALLED_DIR}/x64-windows/lib
)
get_filename_component(QSCINTILLA_LIB_DIR ${QSCINTILLA_LIBRARY} DIRECTORY)
list(TRANSFORM QSCINTILLA_LIBRARY_NAMES APPEND ".dll" OUTPUT_VARIABLE QSCINTILLA_DLL_NAMES)
find_file(QSCINTILLA_DLLS
NAMES ${QSCINTILLA_DLL_NAMES}
PATHS
"${QT_LIBRARY_DIR}"
$ENV{LIB_DIR}/lib
/usr/local/lib
/usr/local/lib/qt6
/usr/lib64
/usr/lib32
/usr/lib
${QSCINTILLA_LIB_DIR}
${VCPKG_INSTALLED_DIR}/x64-windows/lib
)

View file

@ -1,30 +0,0 @@
# Distributed under the OSI-approved BSD 3-Clause License.
# Copyright Stefano Sinigardi
#.rst:
# FindStb
# ------------
#
# Find the Stb include headers.
#
# Result Variables
# ^^^^^^^^^^^^^^^^
#
# This module defines the following variables:
#
# ``Stb_FOUND``
# True if Stb library found
#
# ``Stb_INCLUDE_DIR``
# Location of Stb headers
#
include(${CMAKE_ROOT}/Modules/FindPackageHandleStandardArgs.cmake)
include(${CMAKE_ROOT}/Modules/SelectLibraryConfigurations.cmake)
if(NOT Stb_INCLUDE_DIR)
find_path(Stb_INCLUDE_DIR NAMES stb_image.h PATHS ${Stb_DIR} PATH_SUFFIXES include stb include/stb)
endif()
find_package_handle_standard_args(Stb DEFAULT_MSG Stb_INCLUDE_DIR)
mark_as_advanced(Stb_INCLUDE_DIR)

40
cmake/gitversion.cmake Normal file
View file

@ -0,0 +1,40 @@
# https://jonathanhamberg.com/post/cmake-embedding-git-hash/
# Detect current version from git
execute_process(
COMMAND git rev-parse HEAD
OUTPUT_VARIABLE GIT_COMMIT_HASH RESULT_VARIABLE GIT_RESULT OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET)
execute_process(
COMMAND git describe --abbrev=0
OUTPUT_VARIABLE GIT_VERSION RESULT_VARIABLE GIT_RESULT OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET)
execute_process(
COMMAND git describe --dirty
OUTPUT_VARIABLE GIT_VERSION_LONG RESULT_VARIABLE GIT_RESULT OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET)
# For some reason, CMake sets CMAKE_*_DIR all to be CMAKE_CURRENT_BINARY_DIR
# so we have to bypass this by passing in custom "orig_" variables
if (NOT GIT_STATE_WITHIN)
# Re-run this target always so that the version can be checked
add_custom_target(recheck_git_version ALL COMMAND ${CMAKE_COMMAND}
-DGIT_STATE_WITHIN=1
-DORIG_BINARY_DIR=${CMAKE_BINARY_DIR}
-DORIG_CURRENT_BINARY_DIR=${CMAKE_CURRENT_BINARY_DIR}
-DORIG_SOURCE_DIR=${CMAKE_SOURCE_DIR}
-DORIG_CURRENT_SOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR}
-P ${CMAKE_MODULE_PATH}/gitversion.cmake
BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/src/version.cpp
)
else ()
# # Set defaults if the git commands fail
if (NOT GIT_RESULT EQUAL 0)
set(GIT_COMMIT_HASH "unknown")
set(GIT_VERSION "unknown")
set(GIT_VERSION_LONG "unknown")
endif ()
# configure_file only touches the file if it has been changed, so no caching is necessary
configure_file(${ORIG_CURRENT_SOURCE_DIR}/src/version.cpp.in ${ORIG_CURRENT_BINARY_DIR}/src/version.cpp @ONLY)
endif ()

View file

@ -1,29 +1,176 @@
find_package(OpenGL REQUIRED COMPONENTS OpenGL)
include(${CMAKE_CURRENT_SOURCE_DIR}/deps.cmake)
find_package(GLEW REQUIRED)
include_directories(${GLEW_INCLUDE_DIRS})
## Sources
set(SOURCES
src/stb.cpp
src/glad.cpp
find_package(OpenGL)
find_package(glm CONFIG REQUIRED)
find_package(ReactPhysics3D REQUIRED)
find_package(pugixml 1.15 REQUIRED)
find_package(Freetype)
src/ptr_helpers.h
src/enum/part.h
src/enum/surface.cpp
src/enum/meta.h
src/enum/annotation.h
src/enum/surface.h
src/camera.cpp
src/datatypes/vector.cpp
src/datatypes/variant.h
src/datatypes/cframe.cpp
src/datatypes/signal.cpp
src/datatypes/base.h
src/datatypes/enum.h
src/datatypes/enum.cpp
src/datatypes/primitives.h
src/datatypes/cframe.h
src/datatypes/variant.cpp
src/datatypes/vector.h
src/datatypes/color3.h
src/datatypes/annotation.h
src/datatypes/color3.cpp
src/datatypes/primitives.cpp
src/datatypes/ref.cpp
src/datatypes/ref.h
src/datatypes/signal.h
src/common.cpp
src/utils.h
src/platform.h
src/math_helper.cpp
src/rendering/skybox.cpp
src/rendering/mesh2d.cpp
src/rendering/torus.cpp
src/rendering/mesh.h
src/rendering/font.cpp
src/rendering/debug/debugrenderer.cpp
src/rendering/texture.h
src/rendering/shader.h
src/rendering/defaultmeshes.cpp
src/rendering/skybox.h
src/rendering/mesh2d.h
src/rendering/light.h
src/rendering/renderer.cpp
src/rendering/texture3d.h
src/rendering/texture.cpp
src/rendering/renderer.h
src/rendering/shader.cpp
src/rendering/mesh.cpp
src/rendering/material.h
src/rendering/torus.h
src/rendering/font.h
src/rendering/defaultmeshes.h
src/rendering/texture3d.cpp
src/logger.cpp
src/handles.h
src/timeutil.h
src/error/error.h
src/error/result.h
src/error/instance.h
src/error/data.h
src/partassembly.h
src/objects/service/jointsservice.cpp
src/objects/service/script/serverscriptservice.h
src/objects/service/script/serverscriptservice.cpp
src/objects/service/script/scriptcontext.h
src/objects/service/script/scriptcontext.cpp
src/objects/service/workspace.cpp
src/objects/service/selection.cpp
src/objects/service/selection.h
src/objects/service/jointsservice.h
src/objects/service/workspace.h
src/objects/datamodel.cpp
src/objects/script.h
src/objects/joint/snap.h
src/objects/joint/jointinstance.h
src/objects/joint/rotatev.h
src/objects/joint/weld.cpp
src/objects/joint/jointinstance.cpp
src/objects/joint/rotate.cpp
src/objects/joint/rotate.h
src/objects/joint/weld.h
src/objects/joint/snap.cpp
src/objects/joint/rotatev.cpp
src/objects/base/service.h
src/objects/base/member.h
src/objects/base/instance.h
src/objects/base/service.cpp
src/objects/base/instance.cpp
src/objects/base/refstate.h
src/objects/message.h
src/objects/pvinstance.cpp
src/objects/hint.cpp
src/objects/pvinstance.h
src/objects/base.h
src/objects/folder.cpp
src/objects/model.cpp
src/objects/datamodel.h
src/objects/folder.h
src/objects/meta.cpp
src/objects/model.h
src/objects/part/part.cpp
src/objects/part/part.h
src/objects/part/wedgepart.h
src/objects/part/basepart.cpp
src/objects/part/wedgepart.cpp
src/objects/part/basepart.h
src/objects/meta.h
src/objects/hint.h
src/objects/annotation.h
src/objects/message.cpp
src/objects/script.cpp
src/partassembly.cpp
src/panic.cpp
src/logger.h
src/camera.h
src/handles.cpp
src/version.h
src/common.h
src/platform.cpp
src/panic.h
src/lua/instancelib.cpp
src/timeutil.cpp
src/physics/util.h
src/luaapis.h
src/math_helper.h
)
find_package(Stb REQUIRED)
include_directories(${Stb_INCLUDE_DIR})
set(AUTOGEN_SOURCES
# Objects
src/objects/service/script/serverscriptservice.h
src/objects/service/script/scriptcontext.h
src/objects/service/selection.h
src/objects/service/jointsservice.h
src/objects/service/workspace.h
src/objects/script.h
src/objects/joint/snap.h
src/objects/joint/jointinstance.h
src/objects/joint/rotatev.h
src/objects/joint/rotate.h
src/objects/joint/weld.h
src/objects/message.h
src/objects/pvinstance.h
src/objects/base.h
src/objects/datamodel.h
src/objects/folder.h
src/objects/model.h
src/objects/part/part.h
src/objects/part/wedgepart.h
src/objects/part/basepart.h
src/objects/meta.h
src/objects/hint.h
# Enum
src/enum/part.h
src/enum/surface.h
# Data types
src/datatypes/enum.h
src/datatypes/cframe.h
src/datatypes/vector.h
src/datatypes/color3.h
)
# PkgConfig packages
find_package(PkgConfig REQUIRED)
pkg_check_modules(LUAJIT REQUIRED luajit)
link_directories(${LUAJIT_LIBRARY_DIRS})
# Run autogen
file(GLOB_RECURSE AUTOGEN_SOURCES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}/src" "src/objects/*.h" "src/datatypes/*.h" "src/enum/*.h")
### Autogen
# https://cmake.org/cmake/help/book/mastering-cmake/chapter/Custom%20Commands.html
foreach (SRC ${AUTOGEN_SOURCES})
string(REGEX REPLACE "[.]h$" ".cpp" OUT_SRC_NAME ${SRC})
set(SRC_PATH "${CMAKE_CURRENT_SOURCE_DIR}/src/${SRC}")
set(SRC_PATH "${CMAKE_CURRENT_SOURCE_DIR}/${SRC}")
set(OUT_PATH "${CMAKE_BINARY_DIR}/generated/${OUT_SRC_NAME}")
add_custom_command(
@ -36,17 +183,22 @@ foreach (SRC ${AUTOGEN_SOURCES})
list(APPEND AUTOGEN_OUTS "${OUT_PATH}")
endforeach()
### /Autogen
# Add version info into the build
include(gitversion)
add_custom_target(autogen_build ALL
DEPENDS ${AUTOGEN_OUTS}
)
file(GLOB_RECURSE SOURCES "src/*.cpp" "src/*.h")
list(APPEND SOURCES ${AUTOGEN_OUTS})
list(APPEND SOURCES ${CMAKE_CURRENT_BINARY_DIR}/src/version.cpp)
add_library(openblocks STATIC ${SOURCES})
set_target_properties(openblocks PROPERTIES OUTPUT_NAME "openblocks")
target_link_directories(openblocks PUBLIC ${LUAJIT_LIBRARY_DIRS})
target_link_libraries(openblocks ${GLEW_LIBRARIES} ${LUAJIT_LIBRARIES} OpenGL::GL ReactPhysics3D::ReactPhysics3D pugixml::pugixml Freetype::Freetype)
target_include_directories(openblocks PUBLIC "src" "../include" ${LUAJIT_INCLUDE_DIRS})
target_link_libraries(openblocks reactphysics3d pugixml::pugixml Freetype::Freetype glm::glm libluajit ${LuaJIT_LIBRARIES})
target_include_directories(openblocks PUBLIC "src" "../include" "${CMAKE_SOURCE_DIR}/external/glad" ${ReactPhysics3D_SOURCE_DIR}/include ${LUAJIT_INCLUDE_DIRS} ${stb_SOURCE_DIR})
add_dependencies(openblocks autogen_build autogen)
# Windows-specific dependencies

32
core/deps.cmake Normal file
View file

@ -0,0 +1,32 @@
include(CPM)
# Some packages will build helper binaries. This keeps them out of our own build output
set (PREV_BIN_PATH ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
unset (CMAKE_RUNTIME_OUTPUT_DIRECTORY)
CPMAddPackage("gh:g-truc/glm#1.0.1")
CPMAddPackage(NAME reactphysics3d GITHUB_REPOSITORY "DanielChappuis/reactphysics3d" VERSION 0.10.2 PATCHES ${CMAKE_SOURCE_DIR}/patches/std_chrono.patch)
# https://github.com/StereoKit/StereoKit/blob/0be056efebcee5e58ad1438f4cf6dfdb942f6cf9/CMakeLists.txt#L205
set_property(TARGET reactphysics3d PROPERTY POSITION_INDEPENDENT_CODE ON)
CPMAddPackage("gh:zeux/pugixml@1.15")
CPMAddPackage(
NAME freetype
GIT_REPOSITORY https://github.com/aseprite/freetype2.git
GIT_TAG VER-2-10-0
VERSION 2.10.0
PATCHES ${CMAKE_SOURCE_DIR}/patches/freetype_cmakever.patch
)
if (freetype_ADDED)
add_library(Freetype::Freetype ALIAS freetype)
endif()
CPMAddPackage("gh:nothings/stb#8cfb1605c02aee9fb6eb5d8ea559017745bd9a16") # 2.14
CPMAddPackage("gh:WohlSoft/LuaJIT#a5da8f4a31972b74254f00969111b8b7a07cf584") # v2.1
set(LUAJIT_INCLUDE_DIRS ${LuaJIT_SOURCE_DIR}/src)
CPMAddPackage("gh:mackron/miniaudio#0.11.22")
set (CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PREV_BIN_PATH})

View file

@ -6,6 +6,7 @@
#include <iomanip>
#include <algorithm>
Color3::Color3() {};
Color3::Color3(float r, float g, float b) : r(std::clamp(r, 0.f, 1.f)), g(std::clamp(g, 0.f, 1.f)), b(std::clamp(b, 0.f, 1.f)) {};
Color3::Color3(const glm::vec3& vec) : r(std::clamp(vec.x, 0.f, 1.f)), g(std::clamp(vec.y, 0.f, 1.f)), b(std::clamp(vec.z, 0.f, 1.f)) {};

View file

@ -14,6 +14,7 @@ class DEF_DATA Color3 {
public:
DEF_DATA_CTOR Color3(float r, float g, float b);
Color3();
Color3(const glm::vec3&);
virtual ~Color3();

View file

@ -7,7 +7,6 @@
#include <memory>
#include <vector>
extern const char* WRAPPER_SRC; // TODO: Move this to a shared header
int script_errhandler(lua_State*); // extern
SignalSource::SignalSource() : std::shared_ptr<Signal>(std::make_shared<Signal>()) {}
@ -39,30 +38,11 @@ LuaSignalConnection::~LuaSignalConnection() {
luaL_unref(state, LUA_REGISTRYINDEX, thread);
}
#if 0
static void stackdump(lua_State* L) {
printf("%d\n", lua_gettop(L));
fflush(stdout);
lua_getfield(L, LUA_GLOBALSINDEX, "tostring");
for (int i = lua_gettop(L)-1; i >= 1; i--) {
lua_pushvalue(L, -1);
lua_pushvalue(L, i);
lua_call(L, 1, 1);
const char* str = lua_tostring(L, -1);
lua_pop(L, 1);
printf("%s: %s\n", lua_typename(L, lua_type(L, i)), str);
}
lua_pop(L, 1);
printf("\n\n");
fflush(stdout);
}
#endif
void LuaSignalConnection::Call(std::vector<Variant> args) {
lua_State* thread = lua_newthread(state);
// Push wrapepr as thread function
luaL_loadbuffer(thread, WRAPPER_SRC, strlen(WRAPPER_SRC), "=PCALL_WRAPPER");
lua_getfield(thread, LUA_REGISTRYINDEX, "LuaPCallWrapper");
// Push function as upvalue for wrapper
lua_rawgeti(thread, LUA_REGISTRYINDEX, function);

14
core/src/enum/part.h Normal file
View file

@ -0,0 +1,14 @@
#pragma once
#include "datatypes/enum.h"
#include "enum/annotation.h"
enum class DEF_ENUM PartType {
Ball = 0,
Block = 1,
};
namespace EnumType {
extern const Enum PartType;
};

2
core/src/glad.cpp Normal file
View file

@ -0,0 +1,2 @@
#define GLAD_GL_IMPLEMENTATION
#include <glad/gl.h>

View file

@ -27,11 +27,11 @@ static CFrame XYZToZXY(glm::vec3(0, 0, 0), -glm::vec3(1, 0, 0), glm::vec3(0, 0,
static rp3d::PhysicsCommon common;
static rp3d::PhysicsWorld* world = common.createPhysicsWorld();
std::shared_ptr<Part> getHandleAdornee() {
std::shared_ptr<BasePart> getHandleAdornee() {
std::shared_ptr<Selection> selection = gDataModel->GetService<Selection>();
for (std::weak_ptr<Instance> inst : selection->Get()) {
if (!inst.expired() && inst.lock()->IsA<Part>())
return inst.lock()->CastTo<Part>().expect();
if (!inst.expired() && inst.lock()->IsA<BasePart>())
return inst.lock()->CastTo<BasePart>().expect();
}
return {};
@ -58,7 +58,7 @@ std::optional<HandleFace> raycastHandle(rp3d::Ray ray) {
// Implement manual detection via boxes instead of... this shit
// This code also hardly works, and is not good at all... Hooo nope.
rp3d::RigidBody* body = world->createRigidBody(CFrame::IDENTITY + cframe.Position());
body->addCollider(common.createBoxShape(cframe.Rotation() * Vector3(handleSize(face) / 2.f)), rp3d::Transform::identity());
body->addCollider(common.createBoxShape((cframe.Rotation() * Vector3(handleSize(face) / 2.f)).Abs()), rp3d::Transform::identity());
rp3d::RaycastInfo info;
if (body->raycast(ray, info)) {
@ -82,8 +82,8 @@ static int getAABBOfSelection(glm::vec3& pos, glm::vec3& size, glm::vec3& min, g
int count = 0;
std::shared_ptr<Selection> selection = gDataModel->GetService<Selection>();
for (std::weak_ptr<Instance> inst : selection->Get()) {
if (inst.expired() || !inst.lock()->IsA<Part>()) continue;
std::shared_ptr<Part> part = inst.lock()->CastTo<Part>().expect();
if (inst.expired() || !inst.lock()->IsA<BasePart>()) continue;
std::shared_ptr<BasePart> part = inst.lock()->CastTo<BasePart>().expect();
if (count == 0)
min = part->position(), max = part->position();
@ -99,12 +99,12 @@ static int getAABBOfSelection(glm::vec3& pos, glm::vec3& size, glm::vec3& min, g
return count;
}
static std::shared_ptr<Part> getFirstSelectedPart() {
static std::shared_ptr<BasePart> getFirstSelectedPart() {
std::shared_ptr<Selection> selection = gDataModel->GetService<Selection>();
for (std::weak_ptr<Instance> inst : selection->Get()) {
if (inst.expired() || !inst.lock()->IsA<Part>()) continue;
if (inst.expired() || !inst.lock()->IsA<BasePart>()) continue;
return inst.lock()->CastTo<Part>().expect();
return inst.lock()->CastTo<BasePart>().expect();
}
return {};

View file

@ -1,7 +1,7 @@
#pragma once
#include "datatypes/cframe.h"
#include "objects/part.h"
#include "objects/part/part.h"
#include <array>
#include <memory>
@ -36,7 +36,7 @@ struct Handles {
bool worldMode = false;
};
std::shared_ptr<Part> getHandleAdornee();
std::shared_ptr<BasePart> getHandleAdornee();
CFrame getHandleCFrame(HandleFace face);
CFrame partCFrameFromHandlePos(HandleFace face, Vector3 newPos);
Vector3 handleSize(HandleFace face);

View file

@ -9,26 +9,33 @@
static std::ofstream logStream;
static std::vector<Logger::LogListener> logListeners;
static std::vector<Logger::TraceLogListener> traceLogListeners;
std::string Logger::currentLogDir = "NULL";
static std::stringstream* rawOutputBuffer = nullptr;
void Logger::init() {
initProgramLogsDir();
const auto now = std::chrono::system_clock::now();
const auto nows = std::chrono::floor<std::chrono::seconds>(now);
std::string fileName = std::format("log_{0:%Y%m%d}_{0:%H%M%S}.txt", now);
std::string fileName = std::format("log_{0:%Y%m%d}_{0:%H%M%S}.txt", nows);
logStream = std::ofstream(currentLogDir = (getProgramLogsDir() + "/" + fileName));
Logger::debug("Logger initialized");
}
// Initializes the logger in a "void" mode for testing.
// It is not necessary to call Logger::finish
void Logger::initTest(std::stringstream* outputBuffer) {
rawOutputBuffer = outputBuffer;
}
void Logger::finish() {
Logger::debug("Closing logger...");
logStream.close();
}
void Logger::log(std::string message, Logger::LogLevel logLevel) {
void Logger::log(std::string message, Logger::LogLevel logLevel, ScriptSource source) {
std::string logLevelStr = logLevel == Logger::LogLevel::INFO ? "INFO" :
logLevel == Logger::LogLevel::DEBUG ? "DEBUG" :
logLevel == Logger::LogLevel::TRACE ? "TRACE" :
@ -42,9 +49,10 @@ void Logger::log(std::string message, Logger::LogLevel logLevel) {
logStream << formattedLogLine << std::endl;
printf("%s\n", formattedLogLine.c_str());
if (rawOutputBuffer != nullptr) *rawOutputBuffer << logLevelStr << ": " << message << "\n";
for (Logger::LogListener listener : logListeners) {
listener(logLevel, message);
listener(logLevel, message, source);
}
if (logLevel == Logger::LogLevel::FATAL_ERROR) {
@ -52,20 +60,10 @@ void Logger::log(std::string message, Logger::LogLevel logLevel) {
}
}
void Logger::trace(std::string source, int line, void* userData) {
std::string message = "'" + source + "' Line " + std::to_string(line);
log(message, Logger::LogLevel::TRACE);
for (Logger::TraceLogListener listener : traceLogListeners) {
listener(message, source, line, userData);
}
}
void Logger::addLogListener(Logger::LogListener listener) {
logListeners.push_back(listener);
}
void Logger::addLogListener(Logger::TraceLogListener listener) {
traceLogListeners.push_back(listener);
void Logger::resetLogListeners() {
logListeners.clear();
}

View file

@ -1,9 +1,12 @@
#pragma once
#include <format>
#include <functional>
#include <memory>
#include <ostream>
#include <string>
class Script;
namespace Logger {
enum class LogLevel {
INFO,
@ -14,34 +17,41 @@ namespace Logger {
FATAL_ERROR,
};
typedef std::function<void(LogLevel logLevel, std::string message)> LogListener;
typedef std::function<void(std::string message, std::string source, int line, void* userData)> TraceLogListener;
struct ScriptSource {
std::shared_ptr<Script> script;
int line;
};
typedef std::function<void(LogLevel logLevel, std::string message, ScriptSource source)> LogListener;
extern std::string currentLogDir;
void init();
void initTest(std::stringstream* out); // Testing only!
void finish();
void addLogListener(LogListener);
void addLogListener(TraceLogListener);
void resetLogListeners(); // Testing only!
void log(std::string message, LogLevel logLevel);
void log(std::string message, LogLevel logLevel, ScriptSource source = {});
inline void info(std::string message) { log(message, LogLevel::INFO); }
inline void debug(std::string message) { log(message, LogLevel::DEBUG); }
inline void warning(std::string message) { log(message, LogLevel::WARNING); }
inline void error(std::string message) { log(message, LogLevel::ERROR); }
inline void fatalError(std::string message) { log(message, LogLevel::FATAL_ERROR); }
inline void trace(std::string message) { log(message, LogLevel::TRACE); };
inline void traceStart() { log("Stack start", LogLevel::TRACE); }
inline void traceEnd() { log("Stack end", LogLevel::TRACE); }
void trace(std::string source, int line, void* userData = nullptr);
template <typename ...Args>
void scriptLogf(std::string format, LogLevel logLevel, ScriptSource source, Args&&... args) {
char message[200];
sprintf(message, format.c_str(), args...);
log(message, logLevel, source);
}
template <typename ...Args>
void logf(std::string format, LogLevel logLevel, Args&&... args) {
char message[200];
sprintf(message, format.c_str(), args...);
log(message, logLevel);
scriptLogf(format, logLevel, {}, args...);
}
template <typename ...Args> inline void infof(std::string format, Args&&... args) { logf(format, LogLevel::INFO, args...); }
template <typename ...Args> inline void debugf(std::string format, Args&&... args) { logf(format, LogLevel::DEBUG, args...); }
template <typename ...Args> inline void warningf(std::string format, Args&&... args) { logf(format, LogLevel::WARNING, args...); }

View file

@ -6,6 +6,8 @@ extern "C" {
#include <lua.h>
}
LUALIB_API void *(luaL_testudata) (lua_State *L, int ud, const char *tname);
inline const char* x_luaL_udatatname (lua_State *L, int ud) {
void *p = lua_touserdata(L, ud);
if (p != NULL) {
@ -16,4 +18,6 @@ inline const char* x_luaL_udatatname (lua_State *L, int ud) {
return str;
}
return NULL;
}
}
#define LUA_OK 0

View file

@ -4,7 +4,7 @@
#include "panic.h"
#include <memory>
Service::Service(const InstanceType* type) : Instance(type){}
Service::Service(const InstanceType* type) : Instance(type) {}
// Fail if parented to non-datamodel, otherwise lock parent
void Service::OnParentUpdated(std::optional<std::shared_ptr<Instance>> oldParent, std::optional<std::shared_ptr<Instance>> newParent) {

View file

@ -0,0 +1,4 @@
#include "hint.h"
Hint::Hint(): Message(&TYPE) {}
Hint::~Hint() = default;

18
core/src/objects/hint.h Normal file
View file

@ -0,0 +1,18 @@
#pragma once
#include "objects/annotation.h"
#include "objects/base/instance.h"
#include "objects/message.h"
#include <memory>
// Dims the player's screen and displays some centered text
class DEF_INST_(explorer_icon="message") Hint : public Message {
AUTOGEN_PREAMBLE
public:
Hint();
~Hint();
static inline std::shared_ptr<Hint> New() { return std::make_shared<Hint>(); };
static inline std::shared_ptr<Instance> Create() { return std::make_shared<Hint>(); };
};

View file

@ -4,7 +4,7 @@
#include "datatypes/ref.h"
#include "objects/datamodel.h"
#include "objects/service/jointsservice.h"
#include "objects/part.h"
#include "objects/part/part.h"
#include "objects/service/workspace.h"
#include <memory>
#include <reactphysics3d/constraint/FixedJoint.h>
@ -54,6 +54,6 @@ void JointInstance::onUpdated(std::string property) {
oldPart1 = part1;
}
std::optional<std::shared_ptr<Workspace>> JointInstance::workspaceOfPart(std::shared_ptr<Part> part) {
std::optional<std::shared_ptr<Workspace>> JointInstance::workspaceOfPart(std::shared_ptr<BasePart> part) {
return part->workspace();
}

View file

@ -8,31 +8,31 @@
//this is necessary ebcause we use std::weak_ptr<Part> without including it in this file
#ifdef __AUTOGEN_EXTRA_INCLUDES__
#include "../part.h"
#include "objects/part/part.h"
#endif
class Part;
class BasePart;
class Workspace;
class DEF_INST_ABSTRACT JointInstance : public Instance {
AUTOGEN_PREAMBLE
std::weak_ptr<Part> oldPart0;
std::weak_ptr<Part> oldPart1;
std::weak_ptr<BasePart> oldPart0;
std::weak_ptr<BasePart> oldPart1;
protected:
// The workspace the joint was created in, if it exists
std::weak_ptr<Workspace> jointWorkspace;
void OnAncestryChanged(std::optional<std::shared_ptr<Instance>>, std::optional<std::shared_ptr<Instance>>) override;
std::optional<std::shared_ptr<Workspace>> workspaceOfPart(std::shared_ptr<Part>);
std::optional<std::shared_ptr<Workspace>> workspaceOfPart(std::shared_ptr<BasePart>);
void onUpdated(std::string property);
virtual void buildJoint() = 0;
virtual void breakJoint() = 0;
public:
DEF_PROP_(on_update=onUpdated) std::weak_ptr<Part> part0;
DEF_PROP_(on_update=onUpdated) std::weak_ptr<Part> part1;
DEF_PROP_(on_update=onUpdated) std::weak_ptr<BasePart> part0;
DEF_PROP_(on_update=onUpdated) std::weak_ptr<BasePart> part1;
DEF_PROP_(on_update=onUpdated) CFrame c0;
DEF_PROP_(on_update=onUpdated) CFrame c1;

View file

@ -1,6 +1,6 @@
#include "rotate.h"
#include "objects/service/jointsservice.h"
#include "objects/part.h"
#include "objects/part/part.h"
#include "objects/service/workspace.h"
#include "rendering/renderer.h"
#include <reactphysics3d/constraint/HingeJoint.h>

View file

@ -1,6 +1,6 @@
#include "rotatev.h"
#include "objects/service/jointsservice.h"
#include "objects/part.h"
#include "objects/part/part.h"
#include "objects/service/workspace.h"
#include "rendering/renderer.h"
#include <reactphysics3d/constraint/HingeJoint.h>

View file

@ -4,7 +4,7 @@
#include "objects/datamodel.h"
#include "objects/joint/jointinstance.h"
#include "objects/service/jointsservice.h"
#include "objects/part.h"
#include "objects/part/part.h"
#include "objects/service/workspace.h"
#include <memory>
#include <reactphysics3d/constraint/FixedJoint.h>

View file

@ -4,7 +4,7 @@
#include "objects/datamodel.h"
#include "objects/joint/jointinstance.h"
#include "objects/service/jointsservice.h"
#include "objects/part.h"
#include "objects/part/part.h"
#include "objects/service/workspace.h"
#include <memory>
#include <reactphysics3d/constraint/FixedJoint.h>

View file

@ -0,0 +1,5 @@
#include "message.h"
Message::Message(const InstanceType* type) : Instance(type) {}
Message::Message(): Instance(&TYPE) {}
Message::~Message() = default;

View file

@ -0,0 +1,21 @@
#pragma once
#include "objects/annotation.h"
#include "objects/base/instance.h"
#include <memory>
// Dims the player's screen and displays some centered text
class DEF_INST_(explorer_icon="message") Message : public Instance {
AUTOGEN_PREAMBLE
protected:
Message(const InstanceType* type);
public:
Message();
~Message();
DEF_PROP std::string text;
static inline std::shared_ptr<Message> New() { return std::make_shared<Message>(); };
static inline std::shared_ptr<Instance> Create() { return std::make_shared<Message>(); };
};

View file

@ -1,12 +1,15 @@
#include "meta.h"
#include "objects/folder.h"
#include "objects/hint.h"
#include "objects/joint/jointinstance.h"
#include "objects/joint/rotate.h"
#include "objects/joint/rotatev.h"
#include "objects/joint/weld.h"
#include "objects/message.h"
#include "objects/part/wedgepart.h"
#include "objects/service/jointsservice.h"
#include "objects/model.h"
#include "objects/part.h"
#include "objects/part/part.h"
#include "objects/joint/snap.h"
#include "objects/script.h"
#include "objects/service/script/scriptcontext.h"
@ -19,7 +22,9 @@ std::map<std::string, const InstanceType*> INSTANCE_MAP = {
{ "Instance", &Instance::TYPE },
{ "DataModel", &DataModel::TYPE },
{ "BasePart", &BasePart::TYPE },
{ "Part", &Part::TYPE },
{ "WedgePart", &WedgePart::TYPE },
{ "Snap", &Snap::TYPE },
{ "Weld", &Weld::TYPE },
{ "Rotate", &Rotate::TYPE },
@ -27,6 +32,8 @@ std::map<std::string, const InstanceType*> INSTANCE_MAP = {
{ "JointInstance", &JointInstance::TYPE },
{ "Script", &Script::TYPE },
{ "Model", &Model::TYPE },
{ "Message", &Message::TYPE },
{ "Hint", &Hint::TYPE },
// { "Folder", &Folder::TYPE },
// Services

View file

@ -1,5 +1,5 @@
#include "part.h"
#include "base/instance.h"
#include "basepart.h"
#include "objects/base/instance.h"
#include "common.h"
#include "datatypes/base.h"
#include "datatypes/cframe.h"
@ -19,15 +19,14 @@
#include <memory>
#include <optional>
Part::Part(): Part(PartConstructParams { .size = glm::vec3(2, 1.2, 4), .color = Color3(0.639216f, 0.635294f, 0.647059f) }) {
BasePart::BasePart(const InstanceType* type): BasePart(type, PartConstructParams { .size = glm::vec3(4, 1.2, 2), .color = Color3(0.639216f, 0.635294f, 0.647059f) }) {
}
Part::Part(PartConstructParams params): PVInstance(&TYPE), cframe(CFrame::FromEulerAnglesXYZ((Vector3)params.rotation) + params.position),
size(params.size), color(params.color), anchored(params.anchored), locked(params.locked) {
BasePart::BasePart(const InstanceType* type, PartConstructParams params): PVInstance(type), cframe(CFrame::FromEulerAnglesXYZ((Vector3)params.rotation) + params.position),
size(params.size), color(params.color), anchored(params.anchored), locked(params.locked) {
}
Part::~Part() {
BasePart::~BasePart() {
// This relies on physicsCommon still existing. Be very careful.
if (this->rigidBody && workspace()) {
workspace().value()->DestroyRigidBody(rigidBody);
@ -36,12 +35,12 @@ Part::~Part() {
}
void Part::OnAncestryChanged(std::optional<std::shared_ptr<Instance>> child, std::optional<std::shared_ptr<Instance>> newParent) {
void BasePart::OnAncestryChanged(std::optional<std::shared_ptr<Instance>> child, std::optional<std::shared_ptr<Instance>> newParent) {
if (this->rigidBody)
this->rigidBody->setIsActive(workspace().has_value());
if (workspace())
workspace().value()->SyncPartPhysics(std::dynamic_pointer_cast<Part>(this->shared_from_this()));
workspace().value()->SyncPartPhysics(std::dynamic_pointer_cast<BasePart>(this->shared_from_this()));
// Destroy joints
if (!workspace()) BreakJoints();
@ -49,25 +48,23 @@ void Part::OnAncestryChanged(std::optional<std::shared_ptr<Instance>> child, std
// TODO: Sleeping bodies that touch this one also need to be updated
}
void Part::OnWorkspaceAdded(std::optional<std::shared_ptr<Workspace>> oldWorkspace, std::shared_ptr<Workspace> newWorkspace) {
newWorkspace->AddBody(shared<Part>());
void BasePart::OnWorkspaceAdded(std::optional<std::shared_ptr<Workspace>> oldWorkspace, std::shared_ptr<Workspace> newWorkspace) {
newWorkspace->AddBody(shared<BasePart>());
}
void Part::OnWorkspaceRemoved(std::shared_ptr<Workspace> oldWorkspace) {
void BasePart::OnWorkspaceRemoved(std::shared_ptr<Workspace> oldWorkspace) {
if (simulationTicket.has_value())
oldWorkspace->RemoveBody(shared<Part>());
oldWorkspace->RemoveBody(shared<BasePart>());
}
void Part::onUpdated(std::string property) {
// Reset velocity
if (property != "Velocity")
velocity = Vector3::ZERO;
void BasePart::onUpdated(std::string property) {
bool reset = property == "Position" || property == "Rotation" || property == "CFrame" || property == "Size" || property == "Shape";
if (workspace())
workspace().value()->SyncPartPhysics(std::dynamic_pointer_cast<Part>(this->shared_from_this()));
workspace().value()->SyncPartPhysics(std::dynamic_pointer_cast<BasePart>(this->shared_from_this()));
// When position/rotation/size is manually edited, break all joints, they don't apply anymore
if (property != "Anchored")
if (reset)
BreakJoints();
}
@ -88,7 +85,7 @@ static Vector3 verts[8] {
{1, 1, 1},
};
Vector3 Part::GetAABB() {
Vector3 BasePart::GetAABB() {
Vector3 min(0, 0, 0);
Vector3 max(0, 0, 0);
for (Vector3 vert : verts) {
@ -99,7 +96,7 @@ Vector3 Part::GetAABB() {
return (min - max).Abs() / 2;
}
void Part::BreakJoints() {
void BasePart::BreakJoints() {
for (std::weak_ptr<JointInstance> joint : primaryJoints) {
if (joint.expired()) continue;
joint.lock()->Destroy();
@ -120,7 +117,7 @@ static Vector3 FACES[6] = {
{0, 0, -1},
};
SurfaceType Part::surfaceFromFace(NormalId face) {
SurfaceType BasePart::surfaceFromFace(NormalId face) {
switch (face) {
case Top: return topSurface;
case Bottom: return bottomSurface;
@ -132,7 +129,7 @@ SurfaceType Part::surfaceFromFace(NormalId face) {
return SurfaceType::Smooth; // Unreachable
}
float Part::GetSurfaceParamA(Vector3 face) {
float BasePart::GetSurfaceParamA(Vector3 face) {
switch (faceFromNormal(face)) {
case Top: return topParamA;
case Bottom: return bottomParamA;
@ -144,7 +141,7 @@ float Part::GetSurfaceParamA(Vector3 face) {
return 0; // Unreachable
}
float Part::GetSurfaceParamB(Vector3 face) {
float BasePart::GetSurfaceParamB(Vector3 face) {
switch (faceFromNormal(face)) {
case Top: return topParamB;
case Bottom: return bottomParamB;
@ -156,14 +153,18 @@ float Part::GetSurfaceParamB(Vector3 face) {
return 0; // Unreachable
}
bool Part::checkJointContinuity(std::shared_ptr<Part> otherPart) {
Vector3 BasePart::GetEffectiveSize() {
return size;
}
bool BasePart::checkJointContinuity(std::shared_ptr<BasePart> otherPart) {
// Make sure that the two parts don't depend on one another
return checkJointContinuityUp(otherPart) && checkJointContinuityDown(otherPart);
}
bool Part::checkJointContinuityDown(std::shared_ptr<Part> otherPart) {
if (shared<Part>() == otherPart) return false;
bool BasePart::checkJointContinuityDown(std::shared_ptr<BasePart> otherPart) {
if (shared<BasePart>() == otherPart) return false;
for (auto joint : primaryJoints) {
if (joint.expired() || joint.lock()->part1.expired()) continue;
if (!joint.lock()->part1.lock()->checkJointContinuityDown(otherPart))
@ -172,8 +173,8 @@ bool Part::checkJointContinuityDown(std::shared_ptr<Part> otherPart) {
return true;
}
bool Part::checkJointContinuityUp(std::shared_ptr<Part> otherPart) {
if (shared<Part>() == otherPart) return false;
bool BasePart::checkJointContinuityUp(std::shared_ptr<BasePart> otherPart) {
if (shared<BasePart>() == otherPart) return false;
for (auto joint : secondaryJoints) {
if (joint.expired() || joint.lock()->part0.expired()) continue;
if (!joint.lock()->part0.lock()->checkJointContinuityUp(otherPart))
@ -182,7 +183,7 @@ bool Part::checkJointContinuityUp(std::shared_ptr<Part> otherPart) {
return true;
}
bool Part::checkSurfacesTouching(CFrame surfaceFrame, Vector3 size, Vector3 myFace, Vector3 otherFace, std::shared_ptr<Part> otherPart) {
bool BasePart::checkSurfacesTouching(CFrame surfaceFrame, Vector3 size, Vector3 myFace, Vector3 otherFace, std::shared_ptr<BasePart> otherPart) {
Vector3 farCorner0 = surfaceFrame.Inverse() * otherPart->cframe * (Vector3::ONE * (otherPart->size / 2.f));
Vector3 farCorner1 = surfaceFrame.Inverse() * otherPart->cframe * (-Vector3::ONE * (otherPart->size / 2.f));
@ -218,7 +219,7 @@ std::optional<std::shared_ptr<JointInstance>> makeJointFromSurfaces(SurfaceType
return std::nullopt;
}
void Part::MakeJoints() {
void BasePart::MakeJoints() {
// Algorithm: Find nearby parts
// Make sure parts are not dependant on each other (via primary/secondaryJoints)
// Find matching surfaces (surface normal dot product < -0.999)
@ -233,8 +234,8 @@ void Part::MakeJoints() {
for (auto it = workspace().value()->GetDescendantsStart(); it != workspace().value()->GetDescendantsEnd(); it++) {
std::shared_ptr<Instance> obj = *it;
if (obj == shared_from_this()) continue; // Skip ourselves
if (obj->GetClass()->className != "Part") continue; // TODO: Replace this with a .IsA call instead of comparing the class name directly
std::shared_ptr<Part> otherPart = obj->CastTo<Part>().expect();
if (!obj->IsA<BasePart>()) continue;
std::shared_ptr<BasePart> otherPart = obj->CastTo<BasePart>().expect();
for (Vector3 myFace : FACES) {
Vector3 myWorldNormal = cframe.Rotation() * myFace;
@ -278,8 +279,8 @@ void Part::MakeJoints() {
auto joint_ = makeJointFromSurfaces(mySurface, otherSurface);
if (!joint_) continue;
std::shared_ptr<JointInstance> joint = joint_.value();
joint->part0 = shared<Part>();
joint->part1 = otherPart->shared<Part>();
joint->part0 = shared<BasePart>();
joint->part1 = otherPart->shared<BasePart>();
joint->c0 = contact0;
joint->c1 = contact1;
dataModel().value()->GetService<JointsService>()->AddChild(joint);
@ -291,7 +292,7 @@ void Part::MakeJoints() {
}
}
void Part::trackJoint(std::shared_ptr<JointInstance> joint) {
void BasePart::trackJoint(std::shared_ptr<JointInstance> joint) {
if (!joint->part0.expired() && joint->part0.lock() == shared_from_this()) {
for (auto it = primaryJoints.begin(); it != primaryJoints.end();) {
// Clean expired refs
@ -325,7 +326,7 @@ void Part::trackJoint(std::shared_ptr<JointInstance> joint) {
}
}
void Part::untrackJoint(std::shared_ptr<JointInstance> joint) {
void BasePart::untrackJoint(std::shared_ptr<JointInstance> joint) {
for (auto it = primaryJoints.begin(); it != primaryJoints.end();) {
// Clean expired refs
if (it->expired() || it->lock() == joint) {

View file

@ -12,9 +12,11 @@
#include "enum/surface.h"
#include <mutex>
#include <optional>
#include <reactphysics3d/body/RigidBody.h>
#include <reactphysics3d/engine/PhysicsCommon.h>
#include <reactphysics3d/reactphysics3d.h>
#include <vector>
#include "annotation.h"
#include "objects/annotation.h"
#include "objects/pvinstance.h"
namespace rp = reactphysics3d;
@ -34,11 +36,11 @@ class Workspace;
#ifndef __SIMULATION_TICKET
#define __SIMULATION_TICKET
class Part;
typedef std::list<std::shared_ptr<Part>>::iterator SimulationTicket;
class BasePart;
typedef std::list<std::shared_ptr<BasePart>>::iterator SimulationTicket;
#endif
class DEF_INST_(explorer_icon="part") Part : public PVInstance {
class DEF_INST_ABSTRACT_(explorer_icon="part") BasePart : public PVInstance {
AUTOGEN_PREAMBLE
protected:
// Joints where this part is Part0
@ -50,10 +52,10 @@ protected:
void untrackJoint(std::shared_ptr<JointInstance>);
SurfaceType surfaceFromFace(NormalId);
bool checkJointContinuity(std::shared_ptr<Part>);
bool checkJointContinuityUp(std::shared_ptr<Part>);
bool checkJointContinuityDown(std::shared_ptr<Part>);
bool checkSurfacesTouching(CFrame surfaceFrame, Vector3 size, Vector3 myFace, Vector3 otherFace, std::shared_ptr<Part> otherPart);
bool checkJointContinuity(std::shared_ptr<BasePart>);
bool checkJointContinuityUp(std::shared_ptr<BasePart>);
bool checkJointContinuityDown(std::shared_ptr<BasePart>);
bool checkSurfacesTouching(CFrame surfaceFrame, Vector3 size, Vector3 myFace, Vector3 otherFace, std::shared_ptr<BasePart> otherPart);
friend JointInstance;
friend Workspace;
@ -62,6 +64,11 @@ protected:
virtual void OnWorkspaceRemoved(std::shared_ptr<Workspace> oldWorkspace) override;
void OnAncestryChanged(std::optional<std::shared_ptr<Instance>> child, std::optional<std::shared_ptr<Instance>> newParent) override;
void onUpdated(std::string);
virtual void updateCollider(rp::PhysicsCommon* common) = 0;
BasePart(const InstanceType*);
BasePart(const InstanceType*, PartConstructParams params);
public:
DEF_PROP_CATEGORY(DATA)
DEF_PROP_(on_update=onUpdated) Vector3 velocity;
@ -76,6 +83,7 @@ public:
DEF_PROP_CATEGORY(APPEARANCE)
DEF_PROP Color3 color;
DEF_PROP float transparency = 0.f;
DEF_PROP float reflectance = 0.f;
DEF_PROP_CATEGORY(BEHAVIOR)
DEF_PROP_(on_update=onUpdated) bool anchored = false;
@ -115,14 +123,9 @@ public:
inline SurfaceType GetSurfaceFromFace(NormalId face) { return surfaceFromFace(face); }
float GetSurfaceParamA(Vector3 face);
float GetSurfaceParamB(Vector3 face);
virtual Vector3 GetEffectiveSize();
Part();
Part(PartConstructParams params);
~Part() override;
static inline std::shared_ptr<Part> New() { return std::make_shared<Part>(); };
static inline std::shared_ptr<Part> New(PartConstructParams params) { return std::make_shared<Part>(params); };
static inline std::shared_ptr<Instance> Create() { return std::make_shared<Part>(); };
~BasePart() override;
inline Vector3 position() { return cframe.Position(); }

View file

@ -0,0 +1,42 @@
#include "part.h"
#include "enum/part.h"
#include "physics/util.h"
#include <glm/common.hpp>
Part::Part(): BasePart(&TYPE) {
_lastShape = shape;
_lastSize = size;
}
Part::Part(PartConstructParams params): BasePart(&TYPE, params) {
_lastShape = shape;
_lastSize = size;
}
void Part::updateCollider(rp::PhysicsCommon* common) {
rp::CollisionShape* physShape;
if (shape == PartType::Ball) {
physShape = common->createSphereShape(glm::min(size.X(), size.Y(), size.Z()) * 0.5f);
} else if (shape == PartType::Block) {
physShape = common->createBoxShape(glmToRp(size * glm::vec3(0.5f)));
}
// Recreate the rigidbody if the shape changes
if (rigidBody->getNbColliders() > 0 && (_lastShape != shape || _lastSize != size)) {
// TODO: This causes Touched to get called twice. Fix this.
rigidBody->removeCollider(rigidBody->getCollider(0));
rigidBody->addCollider(physShape, rp::Transform());
}
if (rigidBody->getNbColliders() == 0)
rigidBody->addCollider(physShape, rp::Transform());
_lastShape = shape;
_lastSize = size;
}
Vector3 Part::GetEffectiveSize() {
return shape == PartType::Ball ? (Vector3)glm::vec3(glm::min(size.X(), size.Y(), size.Z())) : size;
}

View file

@ -0,0 +1,26 @@
#pragma once
#include "basepart.h"
#include "enum/part.h"
#include "objects/annotation.h"
class DEF_INST Part : public BasePart {
AUTOGEN_PREAMBLE
PartType _lastShape;
Vector3 _lastSize;
protected:
void updateCollider(rp::PhysicsCommon* common) override;
public:
Part();
Part(PartConstructParams params);
DEF_PROP_(on_update=onUpdated) PartType shape = PartType::Block;
Vector3 GetEffectiveSize() override;
static inline std::shared_ptr<Part> New() { return std::make_shared<Part>(); };
static inline std::shared_ptr<Part> New(PartConstructParams params) { return std::make_shared<Part>(params); };
static inline std::shared_ptr<Instance> Create() { return std::make_shared<Part>(); };
};

View file

@ -0,0 +1,92 @@
#include "wedgepart.h"
#include "physics/util.h"
#include <reactphysics3d/collision/ConvexMesh.h>
#include <reactphysics3d/collision/shapes/ConvexMeshShape.h>
rp::ConvexMesh* wedgePhysMesh;
WedgePart::WedgePart(): BasePart(&TYPE) {
}
WedgePart::WedgePart(PartConstructParams params): BasePart(&TYPE, params) {
}
void WedgePart::updateCollider(rp::PhysicsCommon* common) {
Logger::fatalError("Wedges are currently disabled! Please do not use them or your editor may crash\n");
rp::ConvexMeshShape* shape = common->createConvexMeshShape(wedgePhysMesh, glmToRp(size * glm::vec3(0.5f)));
// Recreate the rigidbody if the shape changes
if (rigidBody->getNbColliders() > 0
&& dynamic_cast<rp::ConvexMeshShape*>(rigidBody->getCollider(0)->getCollisionShape())->getScale() != shape->getScale()) {
// TODO: This causes Touched to get called twice. Fix this.
rigidBody->removeCollider(rigidBody->getCollider(0));
rigidBody->addCollider(shape, rp::Transform());
}
if (rigidBody->getNbColliders() == 0)
rigidBody->addCollider(shape, rp::Transform());
}
void WedgePart::createWedgeShape(rp::PhysicsCommon* common) {
// https://www.reactphysics3d.com/documentation/index.html#creatingbody
float vertices[] = {
// X Y Z
/*0*/ -1, 1, 1, // 0
/*1*/ -1, -1, 1, // |
/*2*/ -1, -1, -1, // 1---2
/*3*/ 1, 1, 1,
/*4*/ 1, -1, 1,
/*5*/ 1, -1, -1,
};
// -x +x
// +z 1----------4
// | bottom |
// -z 2----------5
// -x +x
// +y 0----------3
// | front |
// -y 1----------4
// -x +x
// +yz 0----------3
// | slope |
// -yz 2----------5
int indices[] = {
// Base
1, 2, 5, 4,
// Back-face
0, 1, 4, 3,
// 4, 1, 0, 3,
// Slope
0, 2, 5, 3,
// 3, 5, 2, 0,
// Sides
0, 1, 2,
3, 4, 5,
};
// Description of the six faces of the convex mesh
rp::PolygonVertexArray::PolygonFace* polygonFaces = new rp::PolygonVertexArray::PolygonFace[5];
polygonFaces[0] = { 4, 0 }; // Bottom
polygonFaces[1] = { 4, 4 }; // Front
polygonFaces[2] = { 4, 8 }; // Slope
polygonFaces[3] = { 3, 12 }; // Side
polygonFaces[4] = { 3, 15 }; // Side
// Create the polygon vertex array
rp::PolygonVertexArray polygonVertexArray(6, vertices, 3 * sizeof(float), indices, sizeof(int), 5, polygonFaces,
rp::PolygonVertexArray::VertexDataType::VERTEX_FLOAT_TYPE,
rp::PolygonVertexArray::IndexDataType::INDEX_INTEGER_TYPE);
// Create the convex mesh
std::vector<rp3d::Message> messages;
// wedgePhysMesh = common->createConvexMesh(polygonVertexArray, messages);
}

View file

@ -0,0 +1,21 @@
#pragma once
#include "basepart.h"
#include "objects/annotation.h"
class DEF_INST_(hidden) WedgePart : public BasePart {
AUTOGEN_PREAMBLE
protected:
void updateCollider(rp::PhysicsCommon* common) override;
static void createWedgeShape(rp::PhysicsCommon* common);
friend Workspace;
public:
WedgePart();
WedgePart(PartConstructParams params);
static inline std::shared_ptr<WedgePart> New() { return std::make_shared<WedgePart>(); };
static inline std::shared_ptr<WedgePart> New(PartConstructParams params) { return std::make_shared<WedgePart>(params); };
static inline std::shared_ptr<Instance> Create() { return std::make_shared<WedgePart>(); };
};

View file

@ -1,5 +1,6 @@
#include "script.h"
#include "common.h"
#include "datatypes/variant.h"
#include "lauxlib.h"
#include "logger.h"
#include "objects/base/instance.h"
@ -12,13 +13,8 @@
#include <algorithm>
#include <memory>
int script_wait(lua_State*);
int script_delay(lua_State*);
int script_errhandler(lua_State*);
// TODO: Move this to a shared header
const char* WRAPPER_SRC = "local func, errhandler = ... return function(...) local args = {...} xpcall(function() func(unpack(args)) end, errhandler) end";
Script::Script(): Instance(&TYPE) {
source = "print(\"Hello, world!\")";
}
@ -36,30 +32,8 @@ void Script::Run() {
this->thread = lua_newthread(L);
lua_State* Lt = thread;
// Initialize script globals
lua_getglobal(Lt, "_G");
InstanceRef(shared_from_this()).PushLuaValue(Lt);
lua_setfield(Lt, -2, "script");
InstanceRef(dataModel().value()).PushLuaValue(Lt);
lua_setfield(Lt, -2, "game");
InstanceRef(dataModel().value()->GetService<Workspace>()).PushLuaValue(Lt);
lua_setfield(Lt, -2, "workspace");
lua_pushlightuserdata(Lt, scriptContext.get());
lua_pushcclosure(Lt, script_wait, 1);
lua_setfield(Lt, -2, "wait");
lua_pushlightuserdata(Lt, scriptContext.get());
lua_pushcclosure(Lt, script_delay, 1);
lua_setfield(Lt, -2, "delay");
lua_pop(Lt, 1); // _G
// Push wrapper as thread function
luaL_loadbuffer(Lt, WRAPPER_SRC, strlen(WRAPPER_SRC), "=PCALL_WRAPPER");
lua_getfield(Lt, LUA_REGISTRYINDEX, "LuaPCallWrapper");
// Load source code and push onto thread as upvalue for wrapper
int status = luaL_loadbuffer(Lt, source.c_str(), source.size(), this->GetFullName().c_str());
@ -71,6 +45,21 @@ void Script::Run() {
return;
}
// Initialize script globals
scriptContext->NewEnvironment(Lt); // Pushes envtable, metatable
// Set script in metatable source
InstanceRef(shared_from_this()).PushLuaValue(Lt);
lua_setfield(Lt, -2, "source");
lua_pop(Lt, 1); // Pop metatable
// Set script in environment
InstanceRef(shared_from_this()).PushLuaValue(Lt);
lua_setfield(Lt, -2, "script");
lua_setfenv(Lt, -2); // Set env of loaded function
// Push our error handler and then generate the wrapped function
lua_pushcfunction(Lt, script_errhandler);
lua_call(Lt, 2, 1);
@ -86,33 +75,32 @@ void Script::Stop() {
// TODO:
}
int script_wait(lua_State* L) {
ScriptContext* scriptContext = (ScriptContext*)lua_touserdata(L, lua_upvalueindex(1));
float secs = lua_gettop(L) == 0 ? 0.03 : std::max(luaL_checknumber(L, 1), 0.03);
if (lua_gettop(L) > 0) lua_pop(L, 1); // pop secs
static std::shared_ptr<Script> getfsource(lua_State* L, lua_Debug* dbg) {
int top = lua_gettop(L);
scriptContext->PushThreadSleep(L, secs);
lua_getinfo(L, "f", dbg);
lua_getfenv(L, -1); // Get fenv of stack pos
if (lua_isnil(L, -1)) { // No env could be found
lua_settop(L, top);
return nullptr;
}
// Yield
return lua_yield(L, 0);
}
// Get source from metatable
lua_getmetatable(L, -1);
lua_getfield(L, -1, "source");
int script_delay(lua_State* L) {
ScriptContext* scriptContext = (ScriptContext*)lua_touserdata(L, lua_upvalueindex(1));
float secs = std::max(luaL_checknumber(L, 1), 0.03);
luaL_checktype(L, 2, LUA_TFUNCTION);
auto result = InstanceRef::FromLuaValue(L, -1);
if (!result) {
lua_settop(L, top);
return nullptr;
}
lua_State* Lt = lua_newthread(L); // Create a new thread
// I think this is memory abuse??
// Wouldn't popping the thread in this case make it eligible for garbage collection?
lua_pop(L, 1); // pop the newly created thread so that xmove moves func instead of it into itself
lua_xmove(L, Lt, 1); // move func
lua_pop(L, 1); // pop secs
lua_settop(L, top);
// Schedule next run
scriptContext->PushThreadSleep(Lt, secs);
std::shared_ptr<Instance> ref = result.expect().get<InstanceRef>();
if (!ref->IsA<Script>()) return nullptr;
return 0;
return ref->CastTo<Script>().expect();
}
int script_errhandler(lua_State* L) {
@ -121,7 +109,7 @@ int script_errhandler(lua_State* L) {
// Traceback
Logger::traceStart();
Logger::trace("Stack start");
lua_Debug dbg;
int stack = 1;
@ -131,10 +119,13 @@ int script_errhandler(lua_State* L) {
if (strcmp(dbg.what, "C") == 0 || strcmp(dbg.source, "=PCALL_WRAPPER") == 0)
continue;
Logger::trace(dbg.source, dbg.currentline);
// Find script source
std::shared_ptr<Script> source = getfsource(L, &dbg);
Logger::scriptLogf("'%s', Line %d", Logger::LogLevel::TRACE, {source, dbg.currentline}, dbg.source, dbg.currentline);
}
Logger::traceEnd();
Logger::trace("Stack end");
return 0;
}

View file

@ -3,7 +3,7 @@
#include "objects/annotation.h"
#include "objects/base/service.h"
class DEF_INST_SERVICE JointsService : public Service {
class DEF_INST_SERVICE_(hidden) JointsService : public Service {
AUTOGEN_PREAMBLE
private:
std::optional<std::shared_ptr<Workspace>> jointWorkspace();

View file

@ -1,22 +1,30 @@
#include "scriptcontext.h"
#include "datatypes/cframe.h"
#include "datatypes/color3.h"
#include "datatypes/ref.h"
#include "datatypes/vector.h"
#include "logger.h"
#include "objects/datamodel.h"
#include "objects/service/workspace.h"
#include "timeutil.h"
#include <ctime>
#include <string>
#include "luaapis.h" // IWYU pragma: keep
const char* WRAPPER_SRC = "local func, errhandler = ... return function(...) local args = {...} xpcall(function() func(unpack(args)) end, errhandler) end";
int g_wait(lua_State*);
int g_delay(lua_State*);
static int g_print(lua_State*);
static int g_require(lua_State*);
static const struct luaL_Reg luaglobals [] = {
static const luaL_Reg luaglobals [] = {
{"print", g_print},
{"require", g_require},
{NULL, NULL} /* end of array */
};
std::string unsafe_globals[] = {
// Todo implement our own "safe" setfenv/getfenv
"loadfile", "loadstring", "load", "dofile", "getfenv", "setfenv"
};
@ -48,6 +56,29 @@ void ScriptContext::InitService() {
Color3::PushLuaLibrary(state);
Instance::PushLuaLibrary(state);
// Add other globals
lua_getglobal(state, "_G");
InstanceRef(dataModel().value()).PushLuaValue(state);
lua_setfield(state, -2, "game");
InstanceRef(dataModel().value()->GetService<Workspace>()).PushLuaValue(state);
lua_setfield(state, -2, "workspace");
lua_pushlightuserdata(state, this);
lua_pushcclosure(state, g_wait, 1);
lua_setfield(state, -2, "wait");
lua_pushlightuserdata(state, this);
lua_pushcclosure(state, g_delay, 1);
lua_setfield(state, -2, "delay");
lua_pop(state, 1); // _G
// Add wrapper function
luaL_loadbuffer(state, WRAPPER_SRC, strlen(WRAPPER_SRC), "=PCALL_WRAPPER");
lua_setfield(state, LUA_REGISTRYINDEX, "LuaPCallWrapper");
// TODO: custom os library
// Override print
@ -131,6 +162,27 @@ void ScriptContext::RunSleepingThreads() {
schedTime = tu_clock_micros() - startTime;
}
void ScriptContext::NewEnvironment(lua_State* L) {
lua_newtable(L); // Env table
lua_newtable(L); // Metatable
// Push __index
lua_pushvalue(L, LUA_GLOBALSINDEX);
lua_setfield(L, -2, "__index");
// Push __metatable
lua_pushstring(L, "metatable is locked");
lua_setfield(L, -2, "__metatable");
// Copy metatable and set the env table's metatable
lua_pushvalue(L, -1);
lua_setmetatable(L, -3);
// Remainder on stack:
// 1. Env table
// 2. Metatable
}
// https://www.lua.org/source/5.1/lbaselib.c.html
static int g_print(lua_State* L) {
std::string buf;
@ -161,4 +213,33 @@ static int g_require(lua_State* L) {
if (nargs < 1) return luaL_error(L, "expected argument module");
return luaL_error(L, "require is not yet implemented");
}
int g_wait(lua_State* L) {
ScriptContext* scriptContext = (ScriptContext*)lua_touserdata(L, lua_upvalueindex(1));
float secs = lua_gettop(L) == 0 ? 0.03 : std::max(luaL_checknumber(L, 1), 0.03);
if (lua_gettop(L) > 0) lua_pop(L, 1); // pop secs
scriptContext->PushThreadSleep(L, secs);
// Yield
return lua_yield(L, 0);
}
int g_delay(lua_State* L) {
ScriptContext* scriptContext = (ScriptContext*)lua_touserdata(L, lua_upvalueindex(1));
float secs = std::max(luaL_checknumber(L, 1), 0.03);
luaL_checktype(L, 2, LUA_TFUNCTION);
lua_State* Lt = lua_newthread(L); // Create a new thread
// I think this is memory abuse??
// Wouldn't popping the thread in this case make it eligible for garbage collection?
lua_pop(L, 1); // pop the newly created thread so that xmove moves func instead of it into itself
lua_xmove(L, Lt, 1); // move func
lua_pop(L, 1); // pop secs
// Schedule next run
scriptContext->PushThreadSleep(Lt, secs);
return 0;
}

View file

@ -15,7 +15,7 @@ struct SleepingThread {
class Script;
class DEF_INST_SERVICE ScriptContext : public Service {
class DEF_INST_SERVICE_(hidden) ScriptContext : public Service {
AUTOGEN_PREAMBLE
std::vector<SleepingThread> sleepingThreads;
@ -32,5 +32,8 @@ public:
void PushThreadSleep(lua_State* thread, float delay);
void RunSleepingThreads();
// Generates an environment with a metatable and pushes it both the env table and metatable in order onto the stack
void NewEnvironment(lua_State* state);
static inline std::shared_ptr<Instance> Create() { return std::make_shared<ScriptContext>(); };
};

View file

@ -5,7 +5,7 @@
// Container class for server scripts
// Also handles/manages running server scripts on run
class DEF_INST_SERVICE_(explorer_icon="server-scripts") ServerScriptService : public Service {
class DEF_INST_SERVICE_(explorer_icon="server-scripts", hidden) ServerScriptService : public Service {
AUTOGEN_PREAMBLE
protected:
void InitService() override;

View file

@ -6,7 +6,7 @@
#include <memory>
#include <vector>
class DEF_INST_SERVICE Selection : public Service {
class DEF_INST_SERVICE_(hidden) Selection : public Service {
AUTOGEN_PREAMBLE
private:
std::vector<std::shared_ptr<Instance>> selection;

View file

@ -4,7 +4,8 @@
#include "datatypes/vector.h"
#include "logger.h"
#include "objects/base/instance.h"
#include "objects/part.h"
#include "objects/part/part.h"
#include "objects/part/wedgepart.h"
#include "objects/service/jointsservice.h"
#include "objects/joint/jointinstance.h"
#include "objects/datamodel.h"
@ -29,32 +30,34 @@ Workspace::~Workspace() {
PhysicsEventListener::PhysicsEventListener(Workspace* parent) : workspace(parent) {}
void PhysicsEventListener::onContact(const rp::CollisionCallback::CallbackData& data) {
workspace->contactQueueLock.lock();
for (size_t i = 0; i < data.getNbContactPairs(); i++) {
auto pair = data.getContactPair(i);
auto type = pair.getEventType();
if (type == rp::CollisionCallback::ContactPair::EventType::ContactStay) continue;
auto part0 = reinterpret_cast<Part*>(pair.getBody1()->getUserData())->shared<Part>();
auto part1 = reinterpret_cast<Part*>(pair.getBody2()->getUserData())->shared<Part>();
if (type == reactphysics3d::CollisionCallback::ContactPair::EventType::ContactStay)
continue;
if (type == reactphysics3d::CollisionCallback::ContactPair::EventType::ContactStart) {
part0->Touched->Fire({ (Variant)InstanceRef(part1) });
part1->Touched->Fire({ (Variant)InstanceRef(part0) });
} else if (type == reactphysics3d::CollisionCallback::ContactPair::EventType::ContactExit) {
part0->TouchEnded->Fire({ (Variant)InstanceRef(part1) });
part1->TouchEnded->Fire({ (Variant)InstanceRef(part0) });
}
ContactItem contact;
contact.part0 = reinterpret_cast<BasePart*>(pair.getBody1()->getUserData())->shared<BasePart>();
contact.part1 = reinterpret_cast<BasePart*>(pair.getBody2()->getUserData())->shared<BasePart>();
contact.action = type == reactphysics3d::CollisionCallback::ContactPair::EventType::ContactStart ? ContactItem::CONTACTITEM_TOUCHED : ContactItem::CONTACTITEM_TOUCHENDED;
workspace->contactQueue.push(contact);
}
workspace->contactQueueLock.unlock();
}
void PhysicsEventListener::onTrigger(const rp::OverlapCallback::CallbackData& data) {
for (size_t i = 0; i < data.getNbOverlappingPairs(); i++) {
workspace->contactQueueLock.lock();
for (size_t i = 0; i < data.getNbOverlappingPairs(); i++) {
auto pair = data.getOverlappingPair(i);
auto type = pair.getEventType();
if (type == rp::OverlapCallback::OverlapPair::EventType::OverlapStay) continue;
auto part0 = reinterpret_cast<Part*>(pair.getBody1()->getUserData())->shared<Part>();
auto part1 = reinterpret_cast<Part*>(pair.getBody2()->getUserData())->shared<Part>();
auto part0 = reinterpret_cast<BasePart*>(pair.getBody1()->getUserData())->shared<BasePart>();
auto part1 = reinterpret_cast<BasePart*>(pair.getBody2()->getUserData())->shared<BasePart>();
if (type == reactphysics3d::OverlapCallback::OverlapPair::EventType::OverlapStart) {
part0->Touched->Fire({ (Variant)InstanceRef(part1) });
@ -64,6 +67,7 @@ for (size_t i = 0; i < data.getNbOverlappingPairs(); i++) {
part1->TouchEnded->Fire({ (Variant)InstanceRef(part0) });
}
}
workspace->contactQueueLock.unlock();
}
void Workspace::InitService() {
@ -72,18 +76,22 @@ void Workspace::InitService() {
physicsWorld->setGravity(rp::Vector3(0, -196.2, 0));
// world->setContactsPositionCorrectionTechnique(rp3d::ContactsPositionCorrectionTechnique::BAUMGARTE_CONTACTS);
physicsWorld->setNbIterationsPositionSolver(2000);
physicsWorld->setNbIterationsVelocitySolver(2000);
// physicsWorld->setNbIterationsPositionSolver(2000);
// physicsWorld->setNbIterationsVelocitySolver(2000);
// physicsWorld->setSleepLinearVelocity(10);
// physicsWorld->setSleepAngularVelocity(5);
physicsWorld->setEventListener(&physicsEventListener);
// Sync all parts
// Create meshes
WedgePart::createWedgeShape(physicsCommon);
}
void Workspace::OnRun() {
// Make joints
for (auto it = this->GetDescendantsStart(); it != this->GetDescendantsEnd(); it++) {
std::shared_ptr<Instance> obj = *it;
if (!obj->IsA<Part>()) continue;
std::shared_ptr<Part> part = obj->CastTo<Part>().expect();
if (!it->IsA<BasePart>()) continue;
std::shared_ptr<BasePart> part = it->CastTo<BasePart>().expect();
part->MakeJoints();
}
@ -102,7 +110,7 @@ void Workspace::InitService() {
}
}
void Workspace::updatePartPhysics(std::shared_ptr<Part> part) {
void Workspace::updatePartPhysics(std::shared_ptr<BasePart> part) {
rp::Transform transform = part->cframe;
if (!part->rigidBody) {
part->rigidBody = physicsWorld->createRigidBody(transform);
@ -110,18 +118,7 @@ void Workspace::updatePartPhysics(std::shared_ptr<Part> part) {
part->rigidBody->setTransform(transform);
}
rp::BoxShape* shape = physicsCommon->createBoxShape(glmToRp(part->size * glm::vec3(0.5f)));
// Recreate the rigidbody if the shape changes
if (part->rigidBody->getNbColliders() > 0
&& dynamic_cast<rp::BoxShape*>(part->rigidBody->getCollider(0)->getCollisionShape())->getHalfExtents() != shape->getHalfExtents()) {
// TODO: This causes Touched to get called twice. Fix this.
part->rigidBody->removeCollider(part->rigidBody->getCollider(0));
part->rigidBody->addCollider(shape, rp::Transform());
}
if (part->rigidBody->getNbColliders() == 0)
part->rigidBody->addCollider(shape, rp::Transform());
part->updateCollider(physicsCommon);
part->rigidBody->setType(part->anchored ? rp::BodyType::STATIC : rp::BodyType::DYNAMIC);
part->rigidBody->getCollider(0)->setCollisionCategoryBits(0b11);
@ -143,7 +140,23 @@ void Workspace::updatePartPhysics(std::shared_ptr<Part> part) {
part->rigidBody->setUserData(&*part);
}
void Workspace::SyncPartPhysics(std::shared_ptr<Part> part) {
void Workspace::ProcessContactEvents() {
contactQueueLock.lock();
while (!contactQueue.empty()) {
ContactItem& contact = contactQueue.front();
contactQueue.pop();
if (contact.action == ContactItem::CONTACTITEM_TOUCHED) {
contact.part0->Touched->Fire({ (Variant)InstanceRef(contact.part1) });
contact.part1->Touched->Fire({ (Variant)InstanceRef(contact.part0) });
} else if (contact.action == ContactItem::CONTACTITEM_TOUCHENDED) {
contact.part0->TouchEnded->Fire({ (Variant)InstanceRef(contact.part1) });
contact.part1->TouchEnded->Fire({ (Variant)InstanceRef(contact.part0) });
}
}
contactQueueLock.unlock();
}
void Workspace::SyncPartPhysics(std::shared_ptr<BasePart> part) {
if (globalPhysicsLock.try_lock()) {
updatePartPhysics(part);
globalPhysicsLock.unlock();
@ -173,7 +186,7 @@ void Workspace::PhysicsStep(float deltaTime) {
queueLock.unlock();
// TODO: Add list of tracked parts in workspace based on their ancestry using inWorkspace property of Instance
for (std::shared_ptr<Part> part : simulatedBodies) {
for (std::shared_ptr<BasePart> part : simulatedBodies) {
// If the part's body is dirty, update it now instead
if (part->rigidBodyDirty) {
updatePartPhysics(part);
@ -242,7 +255,7 @@ class NearestRayHit : public rp::RaycastCallback {
return 1;
}
std::shared_ptr<Part> part = partFromBody(raycastInfo.body);
std::shared_ptr<BasePart> part = partFromBody(raycastInfo.body);
FilterResult result = filter.value()(part);
if (result == FilterResult::BLOCK) {
nearestHit = std::nullopt;
@ -286,14 +299,14 @@ rp::Joint* Workspace::CreateJoint(const rp::JointInfo& jointInfo) {
return joint;
}
void Workspace::AddBody(std::shared_ptr<Part> part) {
void Workspace::AddBody(std::shared_ptr<BasePart> part) {
queueLock.lock();
bodyQueue.push_back({part, QueueItem::QUEUEITEM_ADD});
part->rigidBodyDirty = true;
queueLock.unlock();
}
void Workspace::RemoveBody(std::shared_ptr<Part> part) {
void Workspace::RemoveBody(std::shared_ptr<BasePart> part) {
queueLock.lock();
bodyQueue.push_back({part, QueueItem::QUEUEITEM_REMOVE});
queueLock.unlock();

View file

@ -7,6 +7,7 @@
#include <list>
#include <memory>
#include <mutex>
#include <queue>
#include <reactphysics3d/body/RigidBody.h>
#include <reactphysics3d/engine/EventListener.h>
#include <reactphysics3d/engine/PhysicsCommon.h>
@ -31,7 +32,7 @@ enum FilterResult {
PASS, // The object is transparent, ignore it
};
class Part;
class BasePart;
class Snap;
class Weld;
class Rotate;
@ -39,19 +40,28 @@ class RotateV;
#ifndef __SIMULATION_TICKET
#define __SIMULATION_TICKET
typedef std::list<std::shared_ptr<Part>>::iterator SimulationTicket;
typedef std::list<std::shared_ptr<BasePart>>::iterator SimulationTicket;
#endif
typedef std::function<FilterResult(std::shared_ptr<Part>)> RaycastFilter;
typedef std::function<FilterResult(std::shared_ptr<BasePart>)> RaycastFilter;
struct QueueItem {
std::shared_ptr<Part> part;
std::shared_ptr<BasePart> part;
enum {
QUEUEITEM_ADD,
QUEUEITEM_REMOVE,
} action;
};
struct ContactItem {
std::shared_ptr<BasePart> part0;
std::shared_ptr<BasePart> part1;
enum {
CONTACTITEM_TOUCHED,
CONTACTITEM_TOUCHENDED,
} action;
};
class Workspace;
class PhysicsEventListener : public rp::EventListener {
friend Workspace;
@ -66,15 +76,20 @@ class PhysicsEventListener : public rp::EventListener {
class DEF_INST_SERVICE_(explorer_icon="workspace") Workspace : public Service {
AUTOGEN_PREAMBLE
std::list<std::shared_ptr<Part>> simulatedBodies;
friend PhysicsEventListener;
std::list<std::shared_ptr<BasePart>> simulatedBodies;
std::list<QueueItem> bodyQueue;
std::queue<ContactItem> contactQueue;
std::mutex contactQueueLock;
rp::PhysicsWorld* physicsWorld;
static rp::PhysicsCommon* physicsCommon;
PhysicsEventListener physicsEventListener;
void updatePartPhysics(std::shared_ptr<Part> part);
void updatePartPhysics(std::shared_ptr<BasePart> part);
protected:
void InitService() override;
void OnRun() override;
bool initialized = false;
public:
@ -89,14 +104,15 @@ public:
// static inline std::shared_ptr<Workspace> New() { return std::make_shared<Workspace>(); };
static inline std::shared_ptr<Instance> Create() { return std::make_shared<Workspace>(); };
void AddBody(std::shared_ptr<Part> part);
void RemoveBody(std::shared_ptr<Part> part);
void AddBody(std::shared_ptr<BasePart> part);
void RemoveBody(std::shared_ptr<BasePart> part);
void DestroyRigidBody(rp::RigidBody* rigidBody);
void SyncPartPhysics(std::shared_ptr<Part> part);
void SyncPartPhysics(std::shared_ptr<BasePart> part);
rp::Joint* CreateJoint(const rp::JointInfo& jointInfo);
void DestroyJoint(rp::Joint* joint);
void ProcessContactEvents();
void PhysicsStep(float deltaTime);
std::optional<const RaycastResult> CastRayNearest(glm::vec3 point, glm::vec3 rotation, float maxLength, std::optional<RaycastFilter> filter = std::nullopt, unsigned short categoryMaskBits = 0xFFFF);
};

View file

@ -5,13 +5,13 @@
#include "datatypes/vector.h"
#include "math_helper.h"
#include "objects/base/instance.h"
#include "objects/part.h"
#include "objects/part/part.h"
#include "objects/service/selection.h"
#include <glm/common.hpp>
#include <memory>
#include <vector>
PartAssembly::PartAssembly(std::vector<std::shared_ptr<Part>> parts, bool worldMode) : parts(parts) {
PartAssembly::PartAssembly(std::vector<std::shared_ptr<BasePart>> parts, bool worldMode) : parts(parts) {
if (parts.size() == 0) return;
if (parts.size() == 1 && !worldMode) {
_assemblyOrigin = parts[0]->cframe;
@ -36,19 +36,19 @@ PartAssembly::PartAssembly(std::vector<std::shared_ptr<Part>> parts, bool worldM
}
PartAssembly PartAssembly::FromSelection(std::vector<std::shared_ptr<Instance>> newSelection) {
std::vector<std::shared_ptr<Part>> selection;
std::vector<std::shared_ptr<BasePart>> selection;
for (std::shared_ptr<Instance> obj : newSelection) {
if (!obj->IsA<PVInstance>()) continue;
if (obj->IsA<Part>())
selection.push_back(obj->CastTo<Part>().expect());
if (obj->IsA<BasePart>())
selection.push_back(obj->CastTo<BasePart>().expect());
// Add object descendants
for (DescendantsIterator it = obj->GetDescendantsStart(); it != obj->GetDescendantsEnd(); it++) {
if (!(*it)->IsA<Part>()) continue;
if (!(*it)->IsA<BasePart>()) continue;
selection.push_back((*it)->CastTo<Part>().expect());
selection.push_back((*it)->CastTo<BasePart>().expect());
}
}
@ -68,7 +68,9 @@ void PartAssembly::SetCollisionsEnabled(bool enabled) {
void PartAssembly::SetOrigin(CFrame newOrigin) {
for (auto part : parts) {
part->cframe = newOrigin * (_assemblyOrigin.Inverse() * part->cframe);
part->velocity = 0; // Reset velocity
part->UpdateProperty("CFrame");
part->UpdateProperty("Velocity");
// sendPropertyUpdatedSignal(part, "CFrame", Variant(part->cframe));
}
@ -78,19 +80,22 @@ void PartAssembly::SetOrigin(CFrame newOrigin) {
void PartAssembly::TransformBy(CFrame transform) {
for (auto part : parts) {
part->cframe = transform * part->cframe;
part->velocity = 0; // Reset velocity
part->UpdateProperty("CFrame");
part->UpdateProperty("Position");
part->UpdateProperty("Rotation");
part->UpdateProperty("Velocity");
sendPropertyUpdatedSignal(part, "CFrame", Variant(part->cframe));
sendPropertyUpdatedSignal(part, "Position", Variant(part->cframe));
sendPropertyUpdatedSignal(part, "Rotation", Variant(part->cframe));
sendPropertyUpdatedSignal(part, "Velocity", Variant(part->cframe));
}
_assemblyOrigin = transform * _assemblyOrigin;
}
void PartAssembly::Scale(Vector3 newSize, bool scaleUp) {
if (parts.size() == 1) {
if (parts.size() == 1 && (!parts[0]->IsA<Part>() || parts[0]->CastTo<Part>().expect()->shape != PartType::Ball)) {
parts[0]->size = newSize;
parts[0]->UpdateProperty("Size");
sendPropertyUpdatedSignal(parts[0], "Size", Variant(parts[0]->size));
@ -127,6 +132,7 @@ std::vector<PartTransformState> PartAssembly::GetCurrentTransforms() {
t.part = part;
t.cframe = part->cframe;
t.size = part->size;
t.velocity = part->velocity;
transforms.push_back(t);
}

View file

@ -4,13 +4,14 @@
#include <memory>
#include <vector>
class Part;
class BasePart;
class Instance;
class Selection;
struct PartTransformState {
std::shared_ptr<Part> part;
std::shared_ptr<BasePart> part;
Vector3 size;
Vector3 velocity;
CFrame cframe;
};
@ -19,9 +20,9 @@ class PartAssembly {
Vector3 _bounds;
Vector3 _size;
std::vector<std::shared_ptr<Part>> parts;
std::vector<std::shared_ptr<BasePart>> parts;
public:
PartAssembly(std::vector<std::shared_ptr<Part>>, bool worldMode = false);
PartAssembly(std::vector<std::shared_ptr<BasePart>>, bool worldMode = false);
static PartAssembly FromSelection(std::vector<std::shared_ptr<Instance>> selection);
static PartAssembly FromSelection(std::shared_ptr<Selection> selection);

View file

@ -7,7 +7,7 @@
#include <reactphysics3d/mathematics/Vector3.h>
#include <reactphysics3d/mathematics/mathematics.h>
#include <glm/ext.hpp>
#include "objects/part.h"
#include "objects/part/part.h"
namespace rp = reactphysics3d;
@ -32,8 +32,8 @@ inline glm::quat rpToGlm(rp::Quaternion quat) {
}
// Make this std::optional
inline std::shared_ptr<Part> partFromBody(rp::Body* body) {
Part* raw = reinterpret_cast<Part*>(body->getUserData());
std::shared_ptr<Part> shared = std::dynamic_pointer_cast<Part>(raw->shared_from_this());
inline std::shared_ptr<BasePart> partFromBody(rp::Body* body) {
BasePart* raw = reinterpret_cast<BasePart*>(body->getUserData());
std::shared_ptr<BasePart> shared = std::dynamic_pointer_cast<BasePart>(raw->shared_from_this());
return shared;
}

View file

@ -1,8 +1,7 @@
#include "rendering/shader.h"
#include "rendering/texture.h"
#include "timeutil.h"
#include <GL/glew.h>
#include <GL/gl.h>
#include <glad/gl.h>
#include <glm/ext/vector_float4.hpp>
#include <string>
@ -11,6 +10,8 @@ extern Texture* debugFontTexture;
extern Shader* debugFontShader;
extern Shader* identityShader;
void drawRect(int x, int y, int width, int height, glm::vec4 color);
void drawChar(char c, int x, int y, float scale=1.f) {
debugFontShader->use();
debugFontTexture->activate(1);
@ -41,22 +42,6 @@ void drawString(std::string str, int x, int y, float scale=1.f) {
}
}
void drawRect(int x, int y, int w, int h, glm::vec4 color) {
identityShader->use();
identityShader->set("aColor", color);
float x0 = 2*float(x)/viewportWidth-1, y0 = 2*float(y)/viewportHeight-1, x1 = 2*float(x + w)/viewportWidth-1, y1 = 2*float(y + h)/viewportHeight-1;
float tmp;
tmp = -y0, y0 = -y1, y1 = tmp;
glBegin(GL_QUADS);
glVertex3f(x0, y0, 0);
glVertex3f(x1, y0, 0);
glVertex3f(x1, y1, 0);
glVertex3f(x0, y1, 0);
glEnd();
}
static tu_time_t lastTime;
extern tu_time_t renderTime;
extern tu_time_t physTime;

File diff suppressed because it is too large Load diff

View file

@ -2,6 +2,7 @@
#include "mesh.h"
extern Mesh* CUBE_MESH;
extern Mesh* WEDGE_MESH;
extern Mesh* SPHERE_MESH;
extern Mesh* ARROW_MESH;
extern Mesh* OUTLINE_MESH;

View file

@ -3,13 +3,13 @@
#include "panic.h"
#include "rendering/shader.h"
#include <GL/glew.h>
#include <GL/gl.h>
#include <glad/gl.h>
#include <glm/ext/matrix_clip_space.hpp>
#include <memory>
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_STROKER_H
// https://learnopengl.com/In-Practice/Text-Rendering
@ -52,6 +52,34 @@ void fontFinish() {
}
}
static void loadCharTexture(FT_Face& face, FT_BitmapGlyph& glyph_bitmap, Character& character) {
// Generate texture
unsigned int texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(
GL_TEXTURE_2D,
0,
GL_RED,
glyph_bitmap->bitmap.width,
glyph_bitmap->bitmap.rows,
0,
GL_RED,
GL_UNSIGNED_BYTE,
glyph_bitmap->bitmap.buffer
);
// set texture options
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// now store character for later use
character.ID = texture;
character.size = glm::ivec2(glyph_bitmap->bitmap.width, glyph_bitmap->bitmap.rows);
character.bearing = glm::ivec2(glyph_bitmap->left, glyph_bitmap->top);
character.advance = (unsigned int)face->glyph->advance.x;
}
std::shared_ptr<Font> loadFont(std::string fontName) {
std::string fontPath = "assets/font/" + fontName;
@ -63,52 +91,56 @@ std::shared_ptr<Font> loadFont(std::string fontName) {
std::shared_ptr<Font> font = std::make_shared<Font>();
FT_Set_Pixel_Sizes(face, 0, 48);
FT_Set_Pixel_Sizes(face, 0, 16);
font->height = face->size->metrics.y_ppem;
FT_Stroker stroker;
FT_Stroker_New(freetype, &stroker);
FT_Stroker_Set(stroker, 2 * 64, FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);
// Load each glyph
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
for (unsigned char c = 0; c < 128; c++) {
// load character glyph
if (FT_Error err = FT_Load_Char(face, c, FT_LOAD_RENDER)) {
if (FT_Error err = FT_Load_Char(face, c, FT_LOAD_DEFAULT)) {
Logger::errorf("Failed to load glyph %d in font '%s': [%d]", c, fontName.c_str(), err);
continue;
}
// Generate texture
unsigned int texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(
GL_TEXTURE_2D,
0,
GL_RED,
face->glyph->bitmap.width,
face->glyph->bitmap.rows,
0,
GL_RED,
GL_UNSIGNED_BYTE,
face->glyph->bitmap.buffer
);
// set texture options
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// now store character for later use
FT_Glyph glyph;
FT_BitmapGlyph glyph_bitmap;
Character character;
character.ID = texture;
character.size = glm::ivec2(face->glyph->bitmap.width, face->glyph->bitmap.rows);
character.bearing = glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top);
character.advance = (unsigned int)face->glyph->advance.x;
font->characters[c] = character;
}
// Render base
FT_Get_Glyph(face->glyph, &glyph);
FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, nullptr, true);
glyph_bitmap = (FT_BitmapGlyph)glyph;
loadCharTexture(face, glyph_bitmap, character);
font->characters[c] = character;
FT_Done_Glyph(glyph);
// TODO: Find out how to clear FT_BitmapGlyph... I cant import FT_Bitmap_Done for some reason
// Render stroked
// https://stackoverflow.com/a/28078293/16255372
FT_Get_Glyph(face->glyph, &glyph);
FT_Glyph_StrokeBorder(&glyph, stroker, false, true);
FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, nullptr, true);
glyph_bitmap = reinterpret_cast<FT_BitmapGlyph>(glyph);
loadCharTexture(face, glyph_bitmap, character);
font->strokeCharacters[c] = character;
FT_Done_Glyph(glyph);
}
FT_Stroker_Done(stroker);
FT_Done_Face(face);
return font;
}
void drawText(std::shared_ptr<Font> font, std::string text, float x, float y, float scale, glm::vec3 color) {
void drawText(std::shared_ptr<Font> font, std::string text, float x, float y, float scale, glm::vec3 color, bool drawStroke) {
// activate corresponding render state
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
@ -128,10 +160,10 @@ void drawText(std::shared_ptr<Font> font, std::string text, float x, float y, fl
// iterate through all characters
for (size_t i = 0; i < text.size(); i++) {
unsigned char c = text[i];
Character ch = font->characters[c];
Character ch = drawStroke ? font->strokeCharacters[c] : font->characters[c];
float xpos = x + ch.bearing.x * scale;
float ypos = y - (ch.size.y - ch.bearing.y) * scale;
float ypos = viewportHeight - y - font->height - (ch.size.y - ch.bearing.y) * scale;
float w = ch.size.x * scale;
float h = ch.size.y * scale;
@ -159,4 +191,17 @@ void drawText(std::shared_ptr<Font> font, std::string text, float x, float y, fl
x += (ch.advance >> 6) * scale; // bitshift by 6 to get value in pixels (2^6 = 64)
}
glBindTexture(GL_TEXTURE_2D, 0);
}
float calcTextWidth(std::shared_ptr<Font> font, std::string text, float scale, bool stroke) {
float x = 0;
// iterate through all characters
for (size_t i = 0; i < text.size(); i++) {
unsigned char c = text[i];
Character ch = stroke ? font->strokeCharacters[c] : font->characters[c];
x += (ch.advance >> 6) * scale;
}
return x;
}

View file

@ -14,10 +14,13 @@ struct Character {
};
struct Font {
unsigned int height;
Character characters[128];
Character strokeCharacters[128];
};
void fontInit();
void fontFinish();
std::shared_ptr<Font> loadFont(std::string fontName);
void drawText(std::shared_ptr<Font> font, std::string text, float x, float y, float scale=1.f, glm::vec3 color = glm::vec3(1,1,1));
void drawText(std::shared_ptr<Font> font, std::string text, float x, float y, float scale=1.f, glm::vec3 color = glm::vec3(1,1,1), bool drawStroke = false);
float calcTextWidth(std::shared_ptr<Font> font, std::string text, float scale = 1.f, bool stroke = false);

View file

@ -1,5 +1,4 @@
#include <GL/glew.h>
#include <GL/gl.h>
#include <glad/gl.h>
#include "mesh.h"

View file

@ -1,5 +1,4 @@
#include <GL/glew.h>
#include <GL/gl.h>
#include <glad/gl.h>
#include "mesh2d.h"

View file

@ -1,5 +1,4 @@
#include <GL/glew.h>
#include <GL/gl.h>
#include <glad/gl.h>
#include <cmath>
#include <cstdio>
#include <glm/ext.hpp>
@ -18,8 +17,12 @@
#include "datatypes/cframe.h"
#include "datatypes/color3.h"
#include "datatypes/vector.h"
#include "enum/part.h"
#include "handles.h"
#include "math_helper.h"
#include "objects/hint.h"
#include "objects/message.h"
#include "objects/part/wedgepart.h"
#include "objects/service/selection.h"
#include "partassembly.h"
#include "rendering/font.h"
@ -29,9 +32,9 @@
#include "shader.h"
#include "mesh.h"
#include "defaultmeshes.h"
#include "../camera.h"
#include "../common.h"
#include "../objects/part.h"
#include "camera.h"
#include "common.h"
#include "objects/part/part.h"
#include "skybox.h"
#include "enum/surface.h"
#include "texture3d.h"
@ -62,7 +65,8 @@ bool wireframeRendering = false;
int viewportWidth, viewportHeight;
void renderDebugInfo();
void drawRect(int x, int y, int width, int height, glm::vec3 color);
void drawRect(int x, int y, int width, int height, glm::vec4 color);
inline void drawRect(int x, int y, int width, int height, glm::vec3 color) { return drawRect(x, y, width, height, glm::vec4(color, 1)); };
void renderInit(int width, int height) {
viewportWidth = width, viewportHeight = height;
@ -73,6 +77,7 @@ void renderInit(int width, int height) {
glEnable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glEnable(GL_MULTISAMPLE);
glFrontFace(GL_CW);
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
// glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
@ -118,30 +123,58 @@ void renderInit(int width, int height) {
sansSerif = loadFont("LiberationSans-Regular.ttf");
}
static void renderPart(std::shared_ptr<BasePart> part) {
glm::mat4 model = part->cframe;
Vector3 size = part->GetEffectiveSize();
model = glm::scale(model, (glm::vec3)size);
shader->set("model", model);
shader->set("material", Material {
.diffuse = part->color,
.specular = glm::vec3(0.5f, 0.5f, 0.5f),
.shininess = 16.0f,
});
glm::mat3 normalMatrix = glm::mat3(glm::transpose(glm::inverse(model)));
shader->set("normalMatrix", normalMatrix);
shader->set("texScale", size);
shader->set("transparency", part->transparency);
shader->set("reflectance", part->reflectance);
shader->set("surfaces[" + std::to_string(NormalId::Right) + "]", (int)part->rightSurface);
shader->set("surfaces[" + std::to_string(NormalId::Top) + "]", (int)part->topSurface);
shader->set("surfaces[" + std::to_string(NormalId::Back) + "]", (int)part->backSurface);
shader->set("surfaces[" + std::to_string(NormalId::Left) + "]", (int)part->leftSurface);
shader->set("surfaces[" + std::to_string(NormalId::Bottom) + "]", (int)part->bottomSurface);
shader->set("surfaces[" + std::to_string(NormalId::Front) + "]", (int)part->frontSurface);
PartType shape = part->IsA<Part>() ? part->CastTo<Part>().expect()->shape : PartType::Block;
if (part->IsA<WedgePart>()) {
WEDGE_MESH->bind();
glDrawArrays(GL_TRIANGLES, 0, WEDGE_MESH->vertexCount);
} else if (shape == PartType::Ball) { // Part
SPHERE_MESH->bind();
glDrawArrays(GL_TRIANGLES, 0, SPHERE_MESH->vertexCount);
} else if (shape == PartType::Block) {
glFrontFace(GL_CW);
CUBE_MESH->bind();
glDrawArrays(GL_TRIANGLES, 0, CUBE_MESH->vertexCount);
}
}
void renderParts() {
glDepthMask(GL_TRUE);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
glFrontFace(GL_CW);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
// Use shader
shader->use();
// shader->set("objectColor", glm::vec3(1.0f, 0.5f, 0.31f));
// shader->set("lightColor", glm::vec3(1.0f, 1.0f, 1.0f));
// view/projection transformations
glm::mat4 projection = glm::perspective(glm::radians(45.f), (float)viewportWidth / (float)viewportHeight, 0.1f, 1000.0f);
glm::mat4 view = camera.getLookAt();
shader->set("projection", projection);
shader->set("view", view);
// shader->set("material", Material {
// // .ambient = glm::vec3(1.0f, 0.5f, 0.31f),
// .diffuse = glm::vec3(0.639216f, 0.635294f, 0.647059f),
// .specular = glm::vec3(0.5f, 0.5f, 0.5f),
// .shininess = 16.0f,
// });
shader->set("sunLight", DirLight {
.direction = glm::vec3(-0.2f, -1.0f, -0.3f),
.ambient = glm::vec3(0.2f, 0.2f, 0.2f),
@ -149,86 +182,36 @@ void renderParts() {
.specular = glm::vec3(1.0f, 1.0f, 1.0f),
});
shader->set("numPointLights", 0);
// shader->set("pointLights[0]", PointLight {
// .position = lightPos,
// .ambient = glm::vec3(0.4f, 0.4f, 0.4f),
// .diffuse = glm::vec3(1.0f, 1.0f, 1.0f),
// .specular = glm::vec3(1.0f, 1.0f, 1.0f),
// .constant = 1.0,
// .linear = 0.9,
// .quadratic = 0.32,
// });
studsTexture->activate(0);
shader->set("studs", 0);
skyboxTexture->activate(1);
shader->set("skybox", 1);
// Pre-calculate the normal matrix for the shader
// Pass in the camera position
shader->set("viewPos", camera.cameraPos);
// Sort by nearest
std::map<float, std::shared_ptr<Part>> sorted;
std::map<float, std::shared_ptr<BasePart>> sorted;
for (auto it = gWorkspace()->GetDescendantsStart(); it != gWorkspace()->GetDescendantsEnd(); it++) {
std::shared_ptr<Instance> inst = *it;
if (inst->GetClass()->className != "Part") continue;
std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(inst);
if (!it->IsA<BasePart>()) continue;
std::shared_ptr<BasePart> part = std::dynamic_pointer_cast<BasePart>(*it);
if (part->transparency > 0.00001) {
float distance = glm::length(glm::vec3(Vector3(camera.cameraPos) - part->position()));
sorted[distance] = part;
} else {
glm::mat4 model = part->cframe;
// if (part->name == "camera") model = camera.getLookAt();
model = glm::scale(model, (glm::vec3)part->size);
shader->set("model", model);
shader->set("material", Material {
.diffuse = part->color,
.specular = glm::vec3(0.5f, 0.5f, 0.5f),
.shininess = 16.0f,
});
glm::mat3 normalMatrix = glm::mat3(glm::transpose(glm::inverse(model)));
shader->set("normalMatrix", normalMatrix);
shader->set("texScale", part->size);
shader->set("transparency", part->transparency);
shader->set("surfaces[" + std::to_string(NormalId::Right) + "]", (int)part->rightSurface);
shader->set("surfaces[" + std::to_string(NormalId::Top) + "]", (int)part->topSurface);
shader->set("surfaces[" + std::to_string(NormalId::Back) + "]", (int)part->backSurface);
shader->set("surfaces[" + std::to_string(NormalId::Left) + "]", (int)part->leftSurface);
shader->set("surfaces[" + std::to_string(NormalId::Bottom) + "]", (int)part->bottomSurface);
shader->set("surfaces[" + std::to_string(NormalId::Front) + "]", (int)part->frontSurface);
CUBE_MESH->bind();
glDrawArrays(GL_TRIANGLES, 0, CUBE_MESH->vertexCount);
renderPart(part);
}
}
// TODO: Same as todo in src/physics/simulation.cpp
// According to LearnOpenGL, std::map automatically sorts its contents.
for (std::map<float, std::shared_ptr<Part>>::reverse_iterator it = sorted.rbegin(); it != sorted.rend(); it++) {
std::shared_ptr<Part> part = it->second;
glm::mat4 model = part->cframe;
// if (part->name == "camera") model = camera.getLookAt();
model = glm::scale(model, (glm::vec3)part->size);
shader->set("model", model);
shader->set("material", Material {
.diffuse = part->color,
.specular = glm::vec3(0.5f, 0.5f, 0.5f),
.shininess = 16.0f,
});
glm::mat3 normalMatrix = glm::mat3(glm::transpose(glm::inverse(model)));
shader->set("normalMatrix", normalMatrix);
shader->set("texScale", part->size);
shader->set("transparency", part->transparency);
shader->set("surfaces[" + std::to_string(NormalId::Right) + "]", (int)part->rightSurface);
shader->set("surfaces[" + std::to_string(NormalId::Top) + "]", (int)part->topSurface);
shader->set("surfaces[" + std::to_string(NormalId::Back) + "]", (int)part->backSurface);
shader->set("surfaces[" + std::to_string(NormalId::Left) + "]", (int)part->leftSurface);
shader->set("surfaces[" + std::to_string(NormalId::Bottom) + "]", (int)part->bottomSurface);
shader->set("surfaces[" + std::to_string(NormalId::Front) + "]", (int)part->frontSurface);
CUBE_MESH->bind();
glDrawArrays(GL_TRIANGLES, 0, CUBE_MESH->vertexCount);
for (std::map<float, std::shared_ptr<BasePart>>::reverse_iterator it = sorted.rbegin(); it != sorted.rend(); it++) {
std::shared_ptr<BasePart> part = it->second;
renderPart(part);
}
}
@ -245,7 +228,6 @@ void renderSurfaceExtras() {
glDepthMask(GL_TRUE);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
glFrontFace(GL_CCW);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
@ -265,7 +247,7 @@ void renderSurfaceExtras() {
for (auto it = gWorkspace()->GetDescendantsStart(); it != gWorkspace()->GetDescendantsEnd(); it++) {
std::shared_ptr<Instance> inst = *it;
if (!inst->IsA("Part")) continue;
std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(inst);
std::shared_ptr<BasePart> part = std::dynamic_pointer_cast<BasePart>(inst);
for (int i = 0; i < 6; i++) {
NormalId face = (NormalId)i;
SurfaceType type = part->GetSurfaceFromFace(face);
@ -286,7 +268,6 @@ void renderSurfaceExtras() {
void renderSkyBox() {
glDepthMask(GL_FALSE);
glCullFace(GL_FRONT);
glFrontFace(GL_CW);
skyboxShader->use();
@ -313,7 +294,6 @@ void renderHandles() {
glDepthMask(GL_TRUE);
glCullFace(GL_BACK);
glFrontFace(GL_CCW); // This is right... Probably.....
// Use shader
handleShader->use();
@ -393,7 +373,7 @@ void renderAABB() {
// Sort by nearest
for (std::shared_ptr<Instance> inst : gWorkspace()->GetChildren()) {
if (inst->GetClass()->className != "Part") continue;
std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(inst);
std::shared_ptr<BasePart> part = std::dynamic_pointer_cast<BasePart>(inst);
glm::mat4 model = CFrame::IDENTITY + part->cframe.Position();
printf("AABB is supposedly (%f, %f, %f)\n", part->GetAABB().X(), part->GetAABB().Y(), part->GetAABB().Z());
model = glm::scale(model, (glm::vec3)part->GetAABB());
@ -433,7 +413,7 @@ void renderWireframe() {
// Sort by nearest
for (std::shared_ptr<Instance> inst : gWorkspace()->GetChildren()) {
if (inst->GetClass()->className != "Part") continue;
std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(inst);
std::shared_ptr<BasePart> part = std::dynamic_pointer_cast<BasePart>(inst);
glm::mat4 model = part->cframe;
model = glm::scale(model, (glm::vec3)part->size);
wireframeShader->set("model", model);
@ -451,7 +431,6 @@ void renderOutlines() {
glDepthMask(GL_TRUE);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
glFrontFace(GL_CCW);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
@ -475,8 +454,8 @@ void renderOutlines() {
std::shared_ptr<Selection> selection = gDataModel->GetService<Selection>();
for (auto inst : selection->Get()) {
if (inst->GetClass() != &Part::TYPE) continue;
std::shared_ptr<Part> part = std::dynamic_pointer_cast<Part>(inst);
if (inst->GetClass() != &BasePart::TYPE) continue;
std::shared_ptr<BasePart> part = std::dynamic_pointer_cast<BasePart>(inst);
if (first)
min = part->position(), max = part->position();
@ -515,7 +494,6 @@ void renderSelectionAssembly() {
glDepthMask(GL_TRUE);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
glFrontFace(GL_CCW);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
@ -603,7 +581,6 @@ std::vector<std::pair<CFrame, Color3>> DEBUG_CFRAMES;
void renderDebugCFrames() {
glDepthMask(GL_TRUE);
glCullFace(GL_BACK);
glFrontFace(GL_CCW); // This is right... Probably.....
// Use shader
handleShader->use();
@ -649,6 +626,35 @@ void addDebugRenderCFrame(CFrame frame, Color3 color) {
DEBUG_CFRAMES.push_back(std::make_pair(frame, color));
}
void renderMessages() {
glDisable(GL_DEPTH_TEST);
// glEnable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
// glEnable(GL_BLEND);
// glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
for (auto it = gWorkspace()->GetDescendantsStart(); it != gWorkspace()->GetDescendantsEnd(); it++) {
if (!it->IsA<Message>()) continue;
std::shared_ptr<Message> message = it->CastTo<Message>().expect();
float textWidth = calcTextWidth(sansSerif, message->text);
// Render hint
if (message->GetClass() == &Hint::TYPE) {
drawRect(0, 0, viewportWidth, 20, glm::vec4(0,0,0,1));
drawText(sansSerif, message->text, (viewportWidth - textWidth) / 2, 0);
} else {
// Don't draw if text is empty
if (message->text == "") continue;
float strokedTextWidth = calcTextWidth(sansSerif, message->text, true);
drawRect(0, 0, viewportWidth, viewportHeight, glm::vec4(0.5));
drawText(sansSerif, message->text, ((float)viewportWidth - textWidth) / 2, ((float)viewportHeight - sansSerif->height) / 2, 1.f, glm::vec3(0), true);
drawText(sansSerif, message->text, ((float)viewportWidth - strokedTextWidth) / 2, ((float)viewportHeight - sansSerif->height) / 2, 1.f, glm::vec3(1), false);
}
}
}
tu_time_t renderTime;
void render() {
tu_time_t startTime = tu_clock_micros();
@ -668,13 +674,14 @@ void render() {
renderWireframe();
if (debugRendererEnabled)
renderDebugInfo();
renderMessages();
// TODO: Make this a debug flag
// renderAABB();
renderTime = tu_clock_micros() - startTime;
}
void drawRect(int x, int y, int width, int height, glm::vec3 color) {
void drawRect(int x, int y, int width, int height, glm::vec4 color) {
// GL_CULL_FACE has to be disabled as we are flipping the order of the vertices here, besides we don't really care about it
glDisable(GL_CULL_FACE);
glm::mat4 model(1.0f); // Same applies to this VV

View file

@ -1,6 +1,5 @@
#include <fstream>
#include <GL/glew.h>
#include <GL/gl.h>
#include <glad/gl.h>
#include <glm/gtc/type_ptr.hpp>
#include "logger.h"

View file

@ -1,5 +1,4 @@
#include <GL/glew.h>
#include <GL/gl.h>
#include <glad/gl.h>
#include <stb_image.h>
#include "logger.h"
@ -23,6 +22,7 @@ Skybox::Skybox(std::array<std::string, 6> faces, unsigned int format) {
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, format, width, height, 0, format,
GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
stbi_image_free(data);
}
@ -32,7 +32,7 @@ Skybox::Skybox(std::array<std::string, 6> faces, unsigned int format) {
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
// Interpolation
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
@ -42,5 +42,5 @@ Skybox::~Skybox() {
void Skybox::activate(unsigned int textureIdx) {
glActiveTexture(GL_TEXTURE0 + textureIdx);
glBindTexture(GL_TEXTURE_2D, this->ID);
glBindTexture(GL_TEXTURE_CUBE_MAP, this->ID);
}

View file

@ -1,7 +1,6 @@
#include "texture.h"
#include <GL/glew.h>
#include <GL/gl.h>
#include <glad/gl.h>
#include <stb_image.h>
#include "panic.h"

View file

@ -1,7 +1,6 @@
#include "texture3d.h"
#include <GL/glew.h>
#include <GL/gl.h>
#include <glad/gl.h>
#include <stb_image.h>
#include "panic.h"

View file

@ -1,7 +1,7 @@
#include "torus.h"
#include <cmath>
#include <GL/glew.h>
#include <glad/gl.h>
#define PI 3.1415926535f

View file

@ -4,9 +4,15 @@
tu_time_t TIME_STARTED_MICROS = std::chrono::time_point_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now()).time_since_epoch().count();
static tu_time_t timeOverride = -1UL;
tu_time_t tu_clock_micros() {
if (timeOverride != -1UL) return timeOverride;
tu_time_t now = std::chrono::time_point_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now()).time_since_epoch().count();;
return now - TIME_STARTED_MICROS;
}
void tu_set_override(tu_time_t destTime) {
timeOverride = destTime;
}

View file

@ -5,4 +5,8 @@
typedef uint64_t tu_time_t;
// Provides a high-accuracy time since the program started in microseconds (via std::chrono)
tu_time_t tu_clock_micros();
tu_time_t tu_clock_micros();
#ifdef TU_TIME_EXPOSE_TEST
void tu_set_override(tu_time_t destTime);
#endif

5
core/src/version.cpp.in Normal file
View file

@ -0,0 +1,5 @@
#include "version.h"
const char* BUILD_COMMIT_HASH = "@GIT_COMMIT_HASH@"; // Commit hash of the current build
const char* BUILD_VERSION = "@GIT_VERSION@"; // Short form of the build version v1.2.3
const char* BUILD_VERSION_LONG = "@GIT_VERSION_LONG@"; // Long form of the build version v1.2.3-12-g1234567

8
core/src/version.h Normal file
View file

@ -0,0 +1,8 @@
#pragma once
// Allows files to read the version of the current build from git
// https://jonathanhamberg.com/post/cmake-embedding-git-hash/
extern const char* BUILD_COMMIT_HASH; // Commit hash of the current build
extern const char* BUILD_VERSION; // Short form of the build version v1.2.3
extern const char* BUILD_VERSION_LONG; // Long form of the build version v1.2.3-12-g1234567

View file

@ -1,8 +1,7 @@
opengl (Linux: glvnd, Windows: [built-in/none])
glfw
glew
glad
glm
sdl2
stb
qt6
reactphysics3d

View file

@ -105,7 +105,7 @@ However, Autogen cannot tell where this class came from to automatically include
block via `__AUTOGEN_EXTRA_INCLUDES__`:
#ifdef __AUTOGEN_EXTRA_INCLUDES__
#include "objects/part.h"
#include "objects/part/part.h"
#endif
class Part;

View file

@ -4,7 +4,7 @@ To do this, first download the source archive from [`https://www.riverbankcomput
Next, launch the *x64 Native Tools Command Prompt for VS 2022*, and cd into the directory that you extracted the archive to
Now, run `qmake` from your Qt's bin directory to configure it
Now, run `qmake src` from your Qt's bin directory to configure it
Once that's done, build and install the project using `nmake install`

View file

@ -1,21 +1,12 @@
cmake_minimum_required(VERSION 3.30.0)
project(editor VERSION 0.1 LANGUAGES CXX)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(QT_VERSION_MAJOR 6)
find_package(Qt6 REQUIRED COMPONENTS Widgets OpenGLWidgets REQUIRED)
find_package(QScintilla6 REQUIRED)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets Multimedia LinguistTools)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets Multimedia LinguistTools)
find_package(QScintilla REQUIRED)
set(TS_FILES editor_en_US.ts)
include(${CMAKE_CURRENT_SOURCE_DIR}/deps.cmake)
set(PROJECT_SOURCES
main.cpp
@ -38,6 +29,8 @@ set(PROJECT_SOURCES
panes/outputtextview.cpp
script/scriptdocument.h
script/scriptdocument.cpp
script/commandedit.h
script/commandedit.cpp
aboutdialog.ui
aboutdialog.h
aboutdialog.cpp
@ -45,59 +38,25 @@ set(PROJECT_SOURCES
${TS_FILES}
)
if(${QT_VERSION_MAJOR} GREATER_EQUAL 6)
qt_add_executable(editor
MANUAL_FINALIZATION
${PROJECT_SOURCES}
)
# Define target properties for Android with Qt 6 as:
# set_property(TARGET editor APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR
# ${CMAKE_CURRENT_SOURCE_DIR}/android)
# For more information, see https://doc.qt.io/qt-6/qt-add-executable.html#target-creation
qt_add_executable(editor MANUAL_FINALIZATION ${PROJECT_SOURCES})
target_include_directories(editor PRIVATE .)
target_link_libraries(editor PRIVATE openblocks Qt6::Widgets Qt6::OpenGLWidgets ${QSCINTILLA_LIBRARY} miniaudio QCursorConstraints)
qt_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES})
else()
if(ANDROID)
add_library(editor SHARED
${PROJECT_SOURCES}
)
# Define properties for Android with Qt 5 after find_package() calls as:
# set(ANDROID_PACKAGE_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/android")
else()
add_executable(editor
${PROJECT_SOURCES}
mainglwidget.h mainglwidget.cpp
)
endif()
qt5_create_translation(QM_FILES ${CMAKE_SOURCE_DIR} ${TS_FILES})
endif()
target_include_directories(editor PUBLIC "../core/src" "../include" ${QSCINTILLA_INCLUDE_DIR})
target_link_libraries(editor PRIVATE openblocks Qt${QT_VERSION_MAJOR}::Widgets Qt${QT_VERSION_MAJOR}::Multimedia ${QSCINTILLA_LIBRARY})
add_dependencies(editor openblocks)
# Qt6 does not include QOpenGLWidgets as part of Widgets base anymore, so
# we have to include it manually
if (${QT_VERSION} GREATER_EQUAL 6)
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS OpenGL OpenGLWidgets)
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS OpenGL OpenGLWidgets)
target_include_directories(editor PUBLIC Qt6::OpenGL Qt6::OpenGLWidgets)
target_link_libraries(editor PRIVATE Qt6::OpenGL Qt6::OpenGLWidgets)
endif()
# Copy assets
add_custom_command(
TARGET editor POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_SOURCE_DIR}/assets
$<TARGET_FILE_DIR:editor>/assets)
set_target_properties(editor PROPERTIES
WIN32_EXECUTABLE ON
)
# Copy Qt files
if (WIN32)
#include("${QT_DEPLOY_SUPPORT}")
# Copy assets
add_custom_command(
TARGET editor POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
${CMAKE_SOURCE_DIR}/assets
$<TARGET_FILE_DIR:editor>/assets)
# Copy over QScintilla DLLs
# TODO: Use a better approach?
add_custom_command(
@ -105,45 +64,37 @@ if (WIN32)
COMMAND ${CMAKE_COMMAND} -E copy ${QSCINTILLA_DLLS} $<TARGET_FILE_DIR:editor>
)
set(WINDEPLOYQT_OPTIONS
--dir $<TARGET_FILE_DIR:editor>
--translations en
--no-compiler-runtime
--no-opengl-sw # No sense adding opengl-sw given that hardware acceleration is necessary, anyway
--no-system-d3d-compiler
--plugindir $<TARGET_FILE_DIR:editor>/qtplugins
# Also don't want to clutter with plugins, add only needed ones
)
# We split these into two commands because
# we might build a debug binary against a release qscintilla library
# TODO: Add other translations
add_custom_command(
TARGET editor POST_BUILD
COMMAND ${WINDEPLOYQT_EXECUTABLE} $<TARGET_FILE:editor> ${QSCINTILLA_DLLS} --dir $<TARGET_FILE_DIR:editor> --translations en --no-compiler-runtime --no-opengl-sw --no-system-d3d-compiler --plugindir $<TARGET_FILE_DIR:editor>/qtplugins
COMMAND ${WINDEPLOYQT_EXECUTABLE} $<TARGET_FILE:editor>
${WINDEPLOYQT_OPTIONS}
)
# No sense adding opengl-sw given that hardware acceleration is necessary, anyway
# Also don't want to clutter with plugins, add only needed ones
# Copy QScintilla dependencies too
add_custom_command(
TARGET editor POST_BUILD
COMMAND ${WINDEPLOYQT_EXECUTABLE} ${QSCINTILLA_DLLS}
${WINDEPLOYQT_OPTIONS}
)
# Copy qt.conf to override default plugins location
add_custom_command(
TARGET editor POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/qt.conf $<TARGET_FILE_DIR:editor>/qt.conf
)
endif ()
# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1.
# If you are developing for iOS or macOS you should consider setting an
# explicit, fixed bundle identifier manually though.
if(${QT_VERSION} VERSION_LESS 6.1.0)
set(BUNDLE_ID_OPTION MACOSX_BUNDLE_GUI_IDENTIFIER com.example.qtbasic)
endif()
set_target_properties(editor PROPERTIES
${BUNDLE_ID_OPTION}
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
MACOSX_BUNDLE TRUE
WIN32_EXECUTABLE TRUE
)
include(GNUInstallDirs)
install(TARGETS editor
BUNDLE DESTINATION .
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
)
install(FILES $<TARGET_RUNTIME_DLLS:editor> TYPE BIN)
if(QT_VERSION_MAJOR EQUAL 6)
qt_finalize_executable(editor)
endif()
qt_finalize_executable(editor)

View file

@ -1,5 +1,6 @@
#include "aboutdialog.h"
#include "./ui_aboutdialog.h"
#include "version.h"
#include <qdialogbuttonbox.h>
#include <qnamespace.h>
#include <qplaintextedit.h>
@ -40,6 +41,9 @@ AboutDialog::AboutDialog(QWidget *parent)
{
ui->setupUi(this);
ui->titleString->setText(QString() + "Openblocks Engine " + BUILD_VERSION);
ui->versionString->setText(BUILD_VERSION_LONG);
connect(ui->viewLicense, &QLabel::linkActivated, [this]() {
(new LicenseDialog(this))->open();
});

View file

@ -7,19 +7,19 @@
<x>0</x>
<y>0</y>
<width>582</width>
<height>162</height>
<height>200</height>
</rect>
</property>
<property name="minimumSize">
<size>
<width>582</width>
<height>162</height>
<height>200</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>582</width>
<height>162</height>
<height>200</height>
</size>
</property>
<property name="windowTitle">
@ -29,7 +29,7 @@
<property name="geometry">
<rect>
<x>220</x>
<y>110</y>
<y>140</y>
<width>341</width>
<height>32</height>
</rect>
@ -57,7 +57,7 @@
<pixmap resource="editor.qrc">:/placeholder-logo.png</pixmap>
</property>
</widget>
<widget class="QLabel" name="label_2">
<widget class="QLabel" name="titleString">
<property name="geometry">
<rect>
<x>110</x>
@ -67,7 +67,7 @@
</rect>
</property>
<property name="text">
<string>Openblocks Engine ALPHA SNAPSHOT</string>
<string>Openblocks Engine</string>
</property>
</widget>
<widget class="QLabel" name="label_3">
@ -96,6 +96,32 @@
<string>&lt;a href=&quot;/license&quot;&gt;View license...&lt;/a&gt;</string>
</property>
</widget>
<widget class="QLabel" name="label_4">
<property name="geometry">
<rect>
<x>20</x>
<y>110</y>
<width>81</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>Version:</string>
</property>
</widget>
<widget class="QLabel" name="versionString">
<property name="geometry">
<rect>
<x>110</x>
<y>110</y>
<width>451</width>
<height>16</height>
</rect>
</property>
<property name="text">
<string>unknown</string>
</property>
</widget>
</widget>
<resources>
<include location="editor.qrc"/>

4
editor/deps.cmake Normal file
View file

@ -0,0 +1,4 @@
include(CPM)
CPMAddPackage("gh:m-doescode/qcursorconstraints#cef1a31c0afad8ed3c95ee1a6bc531090805b510")

View file

@ -7,23 +7,40 @@
#include <QStyleFactory>
#include <QBasicTimer>
#include <QSurfaceFormat>
#include <cstdio>
#include <qfont.h>
#include <qsurfaceformat.h>
#include <miniaudio.h>
#include <qcursorconstraints.h>
ma_engine miniaudio;
int main(int argc, char *argv[])
{
Logger::init();
// Has to happen before Qt application initializes or we get an error in WASAPI initialization
ma_result res = ma_engine_init(NULL, &miniaudio);
if (res != MA_SUCCESS) {
Logger::fatalErrorf("Failed to initialize Miniaudio withe error [%d]", res);
panic();
}
QSurfaceFormat format;
format.setSamples(4);
format.setRenderableType(QSurfaceFormat::OpenGL);
format.setVersion(3, 3);
format.setProfile(QSurfaceFormat::CompatibilityProfile); // Valid only in OpenGL 3.2+, see: https://stackoverflow.com/a/70519392/16255372
QSurfaceFormat::setDefaultFormat(format);
QApplication a(argc, argv);
Logger::init();
QCursorConstraints::init();
MainWindow w;
w.show();
int result = a.exec();
ma_engine_uninit(&miniaudio);
Logger::finish();
return result;
}

View file

@ -1,13 +1,16 @@
#include <GL/glew.h>
#include <glad/gl.h>
#include <glm/common.hpp>
#include <glm/vector_relational.hpp>
#include <memory>
#include <miniaudio.h>
#include <qcursorconstraints.h>
#include <qnamespace.h>
#include <qsoundeffect.h>
#include <qguiapplication.h>
#include <string>
#include "./ui_mainwindow.h"
#include "mainglwidget.h"
#include "datatypes/vector.h"
#include "enum/surface.h"
#include "handles.h"
#include "logger.h"
#include "mainwindow.h"
@ -34,15 +37,20 @@ MainGLWidget::MainGLWidget(QWidget* parent): QOpenGLWidget(parent), contextMenu(
}
void MainGLWidget::initializeGL() {
glewInit();
int version = gladLoaderLoadGL();
if (version == 0) {
Logger::fatalError("Failed to initialize OpenGL context");
panic();
} else {
Logger::debugf("Initialized GL context version %d.%d", GLAD_VERSION_MAJOR(version), GLAD_VERSION_MINOR(version));
}
renderInit(width(), height());
}
extern ma_engine miniaudio;
inline void playSound(QString path) {
QSoundEffect *sound = new QSoundEffect;
sound->setSource(QUrl::fromLocalFile(path));
sound->play();
sound->connect(sound, &QSoundEffect::playingChanged, [=]() { /* Thank you QSound source code! */ sound->deleteLater(); return false; });
ma_engine_stop(&miniaudio);
ma_engine_play_sound(&miniaudio, path.toStdString().c_str(), NULL);
}
extern int vpx, vpy;
@ -59,7 +67,7 @@ void MainGLWidget::resizeGL(int w, int h) {
glm::vec2 firstPoint;
glm::vec2 secondPoint;
extern std::weak_ptr<Part> draggingObject;
extern std::weak_ptr<BasePart> draggingObject;
extern std::optional<HandleFace> draggingHandle;
extern Shader* shader;
void MainGLWidget::paintGL() {
@ -67,14 +75,11 @@ void MainGLWidget::paintGL() {
}
bool isMouseRightDragging = false;
QPoint lastMousePos;
QPoint mouseLockedPos;
void MainGLWidget::handleCameraRotate(QMouseEvent* evt) {
if (!isMouseRightDragging) return;
camera.processRotation(evt->pos().x() - lastMousePos.x(), evt->pos().y() - lastMousePos.y());
lastMousePos = evt->pos();
// QCursor::setPos(lastMousePos);
camera.processRotation(evt->pos().x() - mouseLockedPos.x(), evt->pos().y() - mouseLockedPos.y());
}
@ -125,7 +130,7 @@ std::vector<PartTransformState> initialTransforms;
bool tryMouseContextMenu = false;
bool isMouseDragging = false;
std::weak_ptr<Part> draggingObject;
std::weak_ptr<BasePart> draggingObject;
std::optional<HandleFace> draggingHandle;
Vector3 initialHitPos;
Vector3 initialHitNormal;
@ -238,6 +243,7 @@ void MainGLWidget::handleLinearTransform(QMouseEvent* evt) {
if (glm::any(glm::lessThan(glm::vec3(selectionAssembly.size() + abs(draggingHandle->normal) * diff), glm::vec3(0.001f))))
return;
// This causes the velocity to be reset even though it shouldn't, but it's not a huge deal, so whatevs.
selectionAssembly.TransformBy(CFrame() + absDiff * 0.5f);
selectionAssembly.Scale(selectionAssembly.size() + abs(draggingHandle->normal) * diff, diff > 0);
}
@ -321,6 +327,7 @@ std::optional<HandleFace> MainGLWidget::raycastHandle(glm::vec3 pointDir) {
}
void MainGLWidget::handleCursorChange(QMouseEvent* evt) {
if (isMouseRightDragging) return; // Don't change the cursor while it is intentionally blank
QPoint position = evt->pos();
glm::vec3 pointDir = camera.getScreenDirection(glm::vec2(position.x(), position.y()), glm::vec2(width(), height()));
@ -371,8 +378,10 @@ void MainGLWidget::mousePressEvent(QMouseEvent* evt) {
switch(evt->button()) {
// Camera drag
case Qt::RightButton: {
lastMousePos = evt->pos();
mouseLockedPos = evt->pos();
isMouseRightDragging = true;
setCursor(Qt::BlankCursor);
QCursorConstraints::lockCursor(window()->windowHandle());
return;
// Clicking on objects
} case Qt::LeftButton: {
@ -396,7 +405,7 @@ void MainGLWidget::mousePressEvent(QMouseEvent* evt) {
std::shared_ptr<Selection> selection = gDataModel->GetService<Selection>();
std::optional<const RaycastResult> rayHit = gWorkspace()->CastRayNearest(camera.cameraPos, pointDir, 50000);
if (!rayHit || !partFromBody(rayHit->body)) { selection->Set({}); return; }
std::shared_ptr<Part> part = partFromBody(rayHit->body);
std::shared_ptr<BasePart> part = partFromBody(rayHit->body);
if (part->locked) { selection->Set({}); return; }
std::shared_ptr<PVInstance> selObject = part;
@ -419,16 +428,14 @@ void MainGLWidget::mousePressEvent(QMouseEvent* evt) {
Vector3 localNormal = part->cframe.Inverse().Rotation() * rayHit->worldNormal;
NormalId face = faceFromNormal(localNormal);
SurfaceType surface = SurfaceType(mainWindow()->selectedTool - TOOL_SMOOTH);
std::string surfacePropertyName = EnumType::NormalId.FromValue(face)->Name() + "Surface";
switch (face) {
case Right: part->rightSurface = surface; break;
case Top: part->topSurface = surface; break;
case Back: part->backSurface = surface; break;
case Left: part->leftSurface = surface; break;
case Bottom: part->bottomSurface = surface; break;
case Front: part->frontSurface = surface; break;
default: return;
}
// Get old surface and set new surface
EnumItem newSurface = EnumType::SurfaceType.FromValue((int)surface).value();
EnumItem oldSurface = part->GetProperty(surfacePropertyName).expect().get<EnumItem>();
part->SetProperty(surfacePropertyName, newSurface).expect();
M_mainWindow->undoManager.PushState({UndoStatePropertyChanged { part, surfacePropertyName, oldSurface, newSurface }});
if (mainWindow()->editSoundEffects && QFile::exists("./assets/excluded/electronicpingshort.wav"))
playSound("./assets/excluded/electronicpingshort.wav");
@ -459,11 +466,13 @@ void MainGLWidget::mousePressEvent(QMouseEvent* evt) {
}
void MainGLWidget::mouseReleaseEvent(QMouseEvent* evt) {
QCursorConstraints::unlockCursor(window()->windowHandle());
// if (isMouseDragging) draggingObject.lock()->rigidBody->getCollider(0)->setCollisionCategoryBits(0b11);
isMouseRightDragging = false;
isMouseDragging = false;
draggingObject = {};
draggingHandle = std::nullopt;
setCursor(Qt::ArrowCursor);
if (!initialTransforms.empty()) {
UndoState historyState;
@ -471,6 +480,7 @@ void MainGLWidget::mouseReleaseEvent(QMouseEvent* evt) {
for (auto t : initialTransforms) {
historyState.push_back(UndoStatePropertyChanged { t.part, "CFrame", t.cframe, t.part->cframe });
historyState.push_back(UndoStatePropertyChanged { t.part, "Size", t.size, t.part->size });
historyState.push_back(UndoStatePropertyChanged { t.part, "Velocity", t.velocity, t.part->velocity });
}
M_mainWindow->undoManager.PushState(historyState);

Some files were not shown because too many files have changed in this diff Show more