diff --git a/.gitignore b/.gitignore index b9aaf88..f7b5476 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,7 @@ .DS_Store build src/resources.c* +MarbleMarcher.app +winBuild +MarbleMarcher.exe +MarbleMarcher diff --git a/CMakeLists.txt b/CMakeLists.txt index af453ab..98240a4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,19 +1,129 @@ cmake_minimum_required(VERSION 3.0) project(MarbleMarcher) -set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) + +# +# Try to find AntTweakBar library and include path. +# Once done this will define +# +# ANT_TWEAK_BAR_FOUND +# ANT_TWEAK_BAR_INCLUDE_DIR +# ANT_TWEAK_BAR_LIBRARY +# + +IF (WIN32) + IF( CMAKE_SIZEOF_VOID_P EQUAL 8 ) + SET( BITS "64" ) + ELSE( CMAKE_SIZEOF_VOID_P EQUAL 8 ) + SET( BITS "" ) + ENDIF( CMAKE_SIZEOF_VOID_P EQUAL 8 ) + + FIND_PATH( ANT_TWEAK_BAR_INCLUDE_DIR AntTweakBar.h + PATHS + ${PROJECT_SOURCE_DIR}/../../external/AntTweakBar/include + ${PROJECT_SOURCE_DIR}/../external/AntTweakBar/include + ${PROJECT_SOURCE_DIR}/external/AntTweakBar/include + $ENV{ANT_TWEAK_BAR_ROOT}/include + DOC "The directory where AntTweakBar.h resides") + + FIND_LIBRARY( ANT_TWEAK_BAR_LIBRARY AntTweakBar${BITS} + PATHS + ${PROJECT_SOURCE_DIR}/../../external/AntTweakBar/lib + ${PROJECT_SOURCE_DIR}/../external/AntTweakBar/lib + ${PROJECT_SOURCE_DIR}/external/AntTweakBar/lib + $ENV{ANT_TWEAK_BAR_ROOT}/lib + DOC "The AntTweakBar library") +ELSE (WIN32) + +FIND_PATH(ANT_TWEAK_BAR_INCLUDE_DIR AntTweakBar.h + PATHS + ${LIBIGL_INCLUDE_DIR}/../external/AntTweakBar/include/ + ${PROJECT_SOURCE_DIR}/../../external/AntTweakBar/include/ + ${PROJECT_SOURCE_DIR}/../external/AntTweakBar/include/ + ${PROJECT_SOURCE_DIR}/external/AntTweakBar/include/ + /usr/local/include + /usr/X11/include + /usr/include + NO_DEFAULT_PATH) + +FIND_LIBRARY( ANT_TWEAK_BAR_LIBRARY AntTweakBar + PATHS + ${LIBIGL_INCLUDE_DIR}/../external/AntTweakBar/lib + ${PROJECT_SOURCE_DIR}/../../external/AntTweakBar/lib + ${PROJECT_SOURCE_DIR}/../external/AntTweakBar/lib + ${PROJECT_SOURCE_DIR}/external/AntTweakBar/lib + /usr/local + /usr/X11 + /usr + PATH_SUFFIXES + a + lib64 + lib + dylib + NO_DEFAULT_PATH +) + +ENDIF (WIN32) + + +SET(ANTTWEAKBAR_FOUND "NO") +IF (ANT_TWEAK_BAR_INCLUDE_DIR AND ANT_TWEAK_BAR_LIBRARY) + SET(ANTTWEAKBAR_FOUND "YES") +ENDIF (ANT_TWEAK_BAR_INCLUDE_DIR AND ANT_TWEAK_BAR_LIBRARY) + +set(ANT_TWEAK_BAR_INCLUDE_DIR ${ANT_TWEAK_BAR_INCLUDE_DIR} ${ANT_TWEAK_BAR_INCLUDE_DIR}/../src/) + +# message(FATAL_ERROR ${ANT_TWEAK_BAR_LIBRARY}) + +if(ANT_TWEAK_BAR_INCLUDE_DIR AND ANT_TWEAK_BAR_LIBRARY) + message(STATUS "Found ANTTWEAKBAR: ${ANT_TWEAK_BAR_INCLUDE_DIR}") +else(ANT_TWEAK_BAR_INCLUDE_DIR AND ANT_TWEAK_BAR_LIBRARY) + if (NOT ANTTWEAKBAR_FIND_QUIETLY) + message(FATAL_ERROR "could NOT find ANTTWEAKBAR") + endif (NOT ANTTWEAKBAR_FIND_QUIETLY) + + +endif(ANT_TWEAK_BAR_INCLUDE_DIR AND ANT_TWEAK_BAR_LIBRARY) + +FILE(GLOB + ANT_TWEAK_BAR_SOURCES + ${ANT_TWEAK_BAR_INCLUDE_DIR}/../src/*.c + ${ANT_TWEAK_BAR_INCLUDE_DIR}/../src/*.cpp) ## DEPENDENCIES -find_package(Eigen3 3.3 REQUIRED) +IF (WIN32) +set(SFML_DIR C:/Libraries/SFML-2.5.1_64/lib/cmake/SFML) +set(SFML_INCLUDE_DIR C:/Libraries/SFML-2.5.1_64/include) +set(SDK_DIRECTORY "C:/Program Files (x86)/Microsoft SDKs") +set(EIGEN3_INCLUDE_DIR C:/Libraries/eigen3/) +set(GLEW_INCLUDE_DIR C:/Libraries/GLEW) +set(GLM_INCLUDE_DIR C:/Libraries/glm) +set(SFML_STATIC_LIBRARIES TRUE) +ELSE (WIN32) +set(SFML_INCLUDE_DIR /usr/local/include) +set(EIGEN3_INCLUDE_DIR /usr/local/include/eigen3) +ENDIF (WIN32) + find_package(SFML 2.5 COMPONENTS system window graphics audio REQUIRED) ## TARGETS add_subdirectory(src) target_include_directories(MarbleMarcherSources PUBLIC + src ${EIGEN3_INCLUDE_DIR} ${SFML_INCLUDE_DIR} + ${GLEW_INCLUDE_DIR}/include + ${GLM_INCLUDE_DIR} + ${ANT_TWEAK_BAR_INCLUDE_DIR} ) + +include_directories(${ANT_TWEAK_BAR_LIBRARY}) +IF (WIN32) +include_directories(${SDK_DIRECTORY}/Windows/v7.1/Include) +ENDIF (WIN32) + target_compile_definitions(MarbleMarcherSources PRIVATE SFML_STATIC) if(WIN32) @@ -22,9 +132,17 @@ if(WIN32) else() add_executable(MarbleMarcher src/Main.cpp) endif() + +add_custom_command(TARGET MarbleMarcher PRE_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_directory + ${CMAKE_SOURCE_DIR}/game_folder $) + + target_compile_definitions(MarbleMarcher PRIVATE SFML_STATIC) target_link_libraries(MarbleMarcher MarbleMarcherSources + ${ANT_TWEAK_BAR_LIBRARY} + ${GLEW_INCLUDE_DIR}/lib/Release/x64/glew32.lib sfml-system sfml-window sfml-graphics diff --git a/README.md b/README.md index f7719a8..0c95bbc 100644 --- a/README.md +++ b/README.md @@ -1,65 +1,167 @@ -# Marble Marcher - -[![Build Status](https://travis-ci.org/jgoldfar/MarbleMarcher.svg?branch=master)](https://travis-ci.org/jgoldfar/MarbleMarcher) - -Marble Marcher is a video game demo that uses a fractal physics engine and fully procedural rendering to produce beautiful and unique gameplay unlike anything you've seen before. - -The goal of the game is to reach the flag as quickly as possible. But be careful not to -fall off the level or get crushed by the fractal! There are 24 levels to unlock. - -Download Link: https://codeparade.itch.io/marblemarcher - -Video Explanation: https://youtu.be/9U0XVdvQwAI +# Marble Marcher: Community Edition + +### Version 1.4.0 beta + +![Logo](https://github.com/MichaelMoroz/MarbleMarcher/blob/master/doc/LOGO.PNG) + +This is the community edition of Marble Marcher, a procedurally rendered fractal physics marble game in which you must get to the flag in each level as fast as you can. With 24 levels to unlock and an active [speedrunning community](https://www.speedrun.com/marblemarcher), along with a rudimentary level editor, there's always new features being developed. If you complete all levels, you can use cheats to create and enhance a more exploratory experience. + +Because version 1.1.0 was the last feature update of Marble Marcher, we (the Marble Marcher Speedrunning Community) opted to create a community edition to keep the community around the game alive and continuously improve the experience. +Currently this is maintained mainly by members of the [Marble Marcher Speedrunning Community on Discord](https://discord.gg/r3XrJxH), in addition to the members of the [subreddit](https://www.reddit.com/r/marblemarcher) and anyone else who has contributed. + +All credit goes to [HackerPoet](https://github.com/HackerPoet) (aka [CodeParade](https://www.youtube.com/channel/UCrv269YwJzuZL3dH5PCgxUw)) for the [original game](https://github.com/HackerPoet/MarbleMarcher). + +## Original Summary +*Marble Marcher is a video game demo that uses a fractal physics engine and fully procedural rendering to produce beautiful and unique gameplay unlike anything you've seen before.* + +*The goal of the game is to reach the flag as quickly as possible. But be careful not to* +*fall off the level or get crushed by the fractal! There are 24 levels to unlock.* + +*Download Link: https://codeparade.itch.io/marblemarcher* + +*Video Explanation: https://youtu.be/9U0XVdvQwAI* + +## Table of Contents +- [Changes](https://github.com/WAUthethird/Marble-Marcher-Community-Edition/blob/master/README.md#changes) +- [Proposed changes](https://github.com/WAUthethird/Marble-Marcher-Community-Edition/blob/master/README.md#proposed-changes) +- [System Dependencies](https://github.com/WAUthethird/Marble-Marcher-Community-Edition/blob/master/README.md#system-dependencies) + - [macOS](https://github.com/WAUthethird/Marble-Marcher-Community-Edition/blob/master/README.md#macos) + - [Arch Linux](https://github.com/WAUthethird/Marble-Marcher-Community-Edition/blob/master/README.md#arch-linux) +- [Building](https://github.com/WAUthethird/Marble-Marcher-Community-Edition/blob/master/README.md#building) + - [macOS](https://github.com/WAUthethird/Marble-Marcher-Community-Edition/blob/master/README.md#macos-1) + - [Arch Linux](https://github.com/WAUthethird/Marble-Marcher-Community-Edition/blob/master/README.md#arch-linux-1) + - [Compiling on Windows](https://github.com/WAUthethird/Marble-Marcher-Community-Edition/blob/master/README.md#compiling-on-windows) + - [Cross-Compile for Windows (macOS)](https://github.com/WAUthethird/Marble-Marcher-Community-Edition/blob/master/README.md#cross-compile-for-windows-on-macos) +- [Launching](https://github.com/WAUthethird/Marble-Marcher-Community-Edition/blob/master/README.md#launching) + - [macOS](https://github.com/WAUthethird/Marble-Marcher-Community-Edition/blob/master/README.md#macos-2) + - [Other OSes](https://github.com/WAUthethird/Marble-Marcher-Community-Edition/blob/master/README.md#windowslinuxanything-else) +- [Other](https://github.com/WAUthethird/Marble-Marcher-Community-Edition/blob/master/README.md#other) + - [Shortcuts](https://github.com/WAUthethird/Marble-Marcher-Community-Edition/blob/master/README.md#shortcuts) + +## Changes +- numerous miscellaneous fixes have been applied +- easier compilation for macOS +- feasible Windows compilation +- README.md overhaul +- new rendering engine +- ingame statistics added (marble speed, ground/air state, ...). Press `o` to toggle. +- AntTweakBar editor added. Press `F4` to toggle. +- Level Editor and custom level support +![Editor](https://github.com/MichaelMoroz/MarbleMarcher/blob/master/doc/EDITOR.PNG) +![Levels](https://github.com/MichaelMoroz/MarbleMarcher/blob/master/doc/LEVELS.PNG) + + +## Proposed Changes +Though Marble Marcher CE is a largely finished game, there are still several improvements we'd like to implement. Here are our proposed changes: + +- ### Fixes + - make sure game runs at the same speed everywhere + - mouse/camera speed is too fast while skipping cutscenes + - figure out why so many people get `Failed to compile vertex shader` +- ### User Experience Improvements + - pre-built versions for the common operating systems and make them available as GitHub releases (in progress) + - make the fractal recoloring from cheats persistent + - more efficient anti-aliasing modes + - toggle to always activate fast cutscenes + - better autosplitter integration + - #### UI Redesign + - confirmation, exit buttons in end screens + - change "controls" menu to "settings" and have audio, controls, sensitivity and fullscreen options there, also allow custom input bindings + - improve text rendering + - better layout +- ### New Features + - cheat: no drag + - anaglyph/stereoscopic mode + - screenshot mode (temporarily higher resolution and AA) + - native controller support (+deadzone) + - custom marble designs + - add a script to build for all platforms at once + - have MarbleMarcher added to package managers +- ### Currently WIP + - in-game level editor + - metal marble skin + - custom map support + - recording/replay functionality + - debug info screen + - cheat: unlock all levels + - add creation of macOS `.dmg` for easy distribution. ## System Dependencies * [Eigen](http://eigen.tuxfamily.org/index.php?title=Main_Page) * [SFML 2.5.0](https://www.sfml-dev.org) +* [AntTweakBar](http://anttweakbar.sourceforge.net/) +* [CMake](https://cmake.org/) +* [OpenAL](https://www.openal.org/) (in case you get an "OpenAL DLL not found" error while trying to run the software, seems to be Windows-specific) ### MacOS -On macOS these can be conveniently installed using [Homebrew](https://brew.sh): +On macOS these can be conveniently installed using [HomeBrew](https://brew.sh): -`brew install eigen sfml` +`brew install cmake eigen sfml anttweakbar` -(Note that SFML might require a newer version than the one from Homebrew, in which case a manual installation is required) +The version of SFML required is 2.5.1 or newer. It has come to my attention that HomeBrew does now have this version (unlike when these instructions where first written) so installing via HomeBrew should work but you can still [download manually](https://www.sfml-dev.org/download/sfml/2.5.1/) if you wish and install using [these instructions](https://www.sfml-dev.org/tutorials/2.5/start-osx.php). You must install the Frameworks option not the dylib option or the build script may fail. +**Note that if HomeBrew installed a version of SFML older than 2.5.1 for some reason or you wish to install manually, you must remove the version of SFML that Brew installed using `brew remove sfml`.** -Alternatively, [vcpkg](https://github.com/Microsoft/vcpkg) can be used: +Alternatively, [vcpkg](https://github.com/Microsoft/vcpkg) can be used though it is unsupported: -`vcpkg install eigen3 sfml` +`vcpkg install cmake eigen3 sfml anttweakbar` +r +It may also be possible to use MacPorts which is also unsupported and untested. ### Arch Linux -`sudo pacman -S eigen sfml git cmake make` - +`sudo pacman -S eigen sfml anttweakbar git cmake make` ## Building ### MacOS +#### Build Script +**Note for the current version: the macOS build is not yet entirely working properly so revert to the previous prerelease as required. SFML is properly included but as of yet, AntTweakBar is not so this executable will not work on machines without it installed.** +Simply run `./macOSBuild.sh`. This will generate the full Application bundle that can be used like any other application. It can even be used on systems without SFML as SFML is included in the bundle and the binary is relinked to these versions. Currently the script will only do the relinking part properly if you use SFML 2.5.1 specifically however it is planned to allow for any version. If you have another version, the script will still work, the app just won't work on a machine without SFML. +#### Manual * `mkdir build && cd build` * `cmake ..` +* `cmake -DCMAKE_CXX_FLAGS="-I/usr/local/include" ..` * If you use `vcpkg`, add the flag `-DCMAKE_TOOLCHAIN_FILE=[path/to/vcpkg]/scripts/buildsystems/vcpkg.cmake` * `cd ..` * `cmake --build build` +Note that this just builds a binary and not an Application bundle like you might be used to seeing. To run properly, you must move the binary (which, after building, is `build/MarbleMarcher`) to the same folder as the assets folder. It is not recommended to build the Application bundle manually so no instructions for that are provided however you may peek in `macOSBuild.sh` to see how it is done. + Alternatively, one can use the platform-dependent build system, for example `Make`: * `make -C build` + ### Arch Linux * `cd ~` -* `git clone https://github.com/HackerPoet/MarbleMarcher.git` -* `cd MarbleMarcher` +* `git clone https://github.com/WAUthethird/Marble-Marcher-Community-Edition.git` +* `cd Marble-Marcher-Community-Edition` * `mkdir build && cd build` * `cmake ..` * `cd ..` * `cmake --build build` * `cp build/MarbleMarcher ./` +### Compiling on Windows +Windows compilation should work just fine now. It's relatively easy to do without help, but in case you'd like them, [here are some configuring and compiling instructions](https://www.reddit.com/r/Marblemarcher/comments/bamqyh/how_to_configure_and_compile_source_for_windows/). A [copy of the instructions](build_on_windows.md) is also in the root. + +### Cross-Compile for Windows on macOS +This requires you to install wget, mingw-w64, and git (which you probably already have) either with HomeBrew (recommended) or otherwise. Theoretically, you should be able to just run `winMacOSBuild.sh`. There are no manual instructions because due to issues I had to just compile the thing manually which is annoying and has too many steps. ## Launching +### macOS +If the macOS build script was used, simply launch the app as normal, otherwise: * Make sure that the current working directory contains the `assets` folder * Run the executable generated by CMake, located in `build` (or a subdirectory) * If running MarbleMarcher from a tarball and you see a message like > ./MarbleMarcher: error while loading shared libraries: libsfml-graphics.so.2.5: cannot open shared object file: No such file or directory - You'll just need to run MarbleMarcher with the correct `LD_LIBRARY_PATH`: ```shell LD_LIBRARY_PATH=`pwd`/usr/lib ./MarbleMarcher ``` +### Windows/Linux/Anything Else +Launching should be self-explanatory for these systems, just run the excecutable file relevant to your system that is generated by the build process. + +## Special Controls +* Press `o` to toggle the debug screen. +* Press `F5` to take a screenshot. +* Press `F4` to open AntTweakBar. + diff --git a/build_on_windows.md b/build_on_windows.md new file mode 100644 index 0000000..30b1fd4 --- /dev/null +++ b/build_on_windows.md @@ -0,0 +1,13 @@ +This is a short and easy tutorial for building Marble Marcher: Community Edition on Windows. The online version of the tutorial can be found [here](https://www.reddit.com/r/Marblemarcher/comments/bamqyh/how_to_configure_and_compile_source_for_windows/). (both this and the one on Reddit are kept up-to-date with each other) This tutorial (the command line parts) can also be followed with Git Bash, though you'll need a developer CMD in administrator mode for at least one process. + +1. Make sure you have [CMake](https://cmake.org/), [OpenAL](https://www.openal.org/), and [Visual Studio 2019](https://visualstudio.microsoft.com/vs/) (in addition to the 2015 and/or 2017 VS build tools, if one does not work, try installing the other) installed. +2. Download the CE edition to a folder of your choice and unzip it: [https://github.com/WAUthethird/Marble-Marcher-Community-Edition](https://github.com/WAUthethird/Marble-Marcher-Community-Edition) +3. Create a folder in your `C:` drive root called `Libraries`. Alternatively, feel free to configure the paths in `CMakeLists.txt`, in the MMCE root. +4. Download and unzip Eigen to the `Libraries` folder. I used the .zip version of the 3.3.7 release: [http://eigen.tuxfamily.org/](http://eigen.tuxfamily.org/) Rename the unzipped folder to `eigen3`. +5. Download and unzip SFML to the `Libraries` folder. I used the 2.5.1 Visual C++ 15 (2017) - 32-bit version: [https://www.sfml-dev.org/download.php](https://www.sfml-dev.org/download.php) Inside the resulting unzipped folder should be just a folder called `SFML-2.5.1`. Move this folder outside its parent folder, into `Libraries`. Delete the now empty parent folder. +6. Download and unzip AntTweakBar to the `Libraries` folder: [http://anttweakbar.sourceforge.net](http://anttweakbar.sourceforge.net) Inside the resulting unzipped folder should be just a folder called `AntTweakBar`. Move this folder outside its parent folder, into `Libraries`. Delete the now empty parent folder. +7. Now that you have all libraries in the proper places, open a developer command prompt with administrator privileges and CD into the MMCE root. Run `cmake .` +8. Open the resulting .sln file in Visual Studio and build the solution. + +You're done! +If this did not work for you, please open an issue in the repository, or comment on the online Reddit post linked above. diff --git a/doc/AntTweakBar.PNG b/doc/AntTweakBar.PNG new file mode 100644 index 0000000..76148cc Binary files /dev/null and b/doc/AntTweakBar.PNG differ diff --git a/doc/EDITOR.PNG b/doc/EDITOR.PNG new file mode 100644 index 0000000..8206ddc Binary files /dev/null and b/doc/EDITOR.PNG differ diff --git a/doc/LEVELS.PNG b/doc/LEVELS.PNG new file mode 100644 index 0000000..c8361ae Binary files /dev/null and b/doc/LEVELS.PNG differ diff --git a/doc/LOGO.PNG b/doc/LOGO.PNG new file mode 100644 index 0000000..fa3f03d Binary files /dev/null and b/doc/LOGO.PNG differ diff --git a/game_folder/AntTweakBar.dll b/game_folder/AntTweakBar.dll new file mode 100644 index 0000000..eeeff2a Binary files /dev/null and b/game_folder/AntTweakBar.dll differ diff --git a/game_folder/AntTweakBar64.dll b/game_folder/AntTweakBar64.dll new file mode 100644 index 0000000..276b2c4 Binary files /dev/null and b/game_folder/AntTweakBar64.dll differ diff --git a/assets/Inconsolata-Bold.ttf b/game_folder/assets/Inconsolata-Bold.ttf similarity index 100% rename from assets/Inconsolata-Bold.ttf rename to game_folder/assets/Inconsolata-Bold.ttf diff --git a/game_folder/assets/NotoSansJP-Bold.otf b/game_folder/assets/NotoSansJP-Bold.otf new file mode 100644 index 0000000..94ab639 Binary files /dev/null and b/game_folder/assets/NotoSansJP-Bold.otf differ diff --git a/game_folder/assets/NotoSansJP-Regular.otf b/game_folder/assets/NotoSansJP-Regular.otf new file mode 100644 index 0000000..de78e6d Binary files /dev/null and b/game_folder/assets/NotoSansJP-Regular.otf differ diff --git a/assets/OFL.txt b/game_folder/assets/OFL.txt similarity index 100% rename from assets/OFL.txt rename to game_folder/assets/OFL.txt diff --git a/assets/Orbitron-Bold.ttf b/game_folder/assets/Orbitron-Bold.ttf similarity index 100% rename from assets/Orbitron-Bold.ttf rename to game_folder/assets/Orbitron-Bold.ttf diff --git a/game_folder/assets/Roboto-Bold.ttf b/game_folder/assets/Roboto-Bold.ttf new file mode 100644 index 0000000..e612852 Binary files /dev/null and b/game_folder/assets/Roboto-Bold.ttf differ diff --git a/game_folder/assets/Roboto-Regular.ttf b/game_folder/assets/Roboto-Regular.ttf new file mode 100644 index 0000000..cb8ffcf Binary files /dev/null and b/game_folder/assets/Roboto-Regular.ttf differ diff --git a/game_folder/assets/config.txt b/game_folder/assets/config.txt new file mode 100644 index 0000000..27b4d1b --- /dev/null +++ b/game_folder/assets/config.txt @@ -0,0 +1,6 @@ +#Screenshot width +2560 +#Screenshot height +1440 +#Language(write name in English) +English \ No newline at end of file diff --git a/game_folder/glew32.dll b/game_folder/glew32.dll new file mode 100644 index 0000000..699fb65 Binary files /dev/null and b/game_folder/glew32.dll differ diff --git a/game_folder/images/MarbleMarcher.png b/game_folder/images/MarbleMarcher.png new file mode 100644 index 0000000..f53717a Binary files /dev/null and b/game_folder/images/MarbleMarcher.png differ diff --git a/game_folder/images/add.png b/game_folder/images/add.png new file mode 100644 index 0000000..ea78104 Binary files /dev/null and b/game_folder/images/add.png differ diff --git a/assets/arrow.png b/game_folder/images/arrow.png similarity index 100% rename from assets/arrow.png rename to game_folder/images/arrow.png diff --git a/game_folder/images/box_0.png b/game_folder/images/box_0.png new file mode 100644 index 0000000..a48ee29 Binary files /dev/null and b/game_folder/images/box_0.png differ diff --git a/game_folder/images/box_1.png b/game_folder/images/box_1.png new file mode 100644 index 0000000..61c4aec Binary files /dev/null and b/game_folder/images/box_1.png differ diff --git a/game_folder/images/clear.png b/game_folder/images/clear.png new file mode 100644 index 0000000..95a48e8 Binary files /dev/null and b/game_folder/images/clear.png differ diff --git a/game_folder/images/delete.png b/game_folder/images/delete.png new file mode 100644 index 0000000..00386b3 Binary files /dev/null and b/game_folder/images/delete.png differ diff --git a/game_folder/images/done.png b/game_folder/images/done.png new file mode 100644 index 0000000..359293b Binary files /dev/null and b/game_folder/images/done.png differ diff --git a/game_folder/images/edit.png b/game_folder/images/edit.png new file mode 100644 index 0000000..c75942f Binary files /dev/null and b/game_folder/images/edit.png differ diff --git a/game_folder/images/icon.ico b/game_folder/images/icon.ico new file mode 100644 index 0000000..d96e58c Binary files /dev/null and b/game_folder/images/icon.ico differ diff --git a/game_folder/images/outline_delete_black_36dp.png b/game_folder/images/outline_delete_black_36dp.png new file mode 100644 index 0000000..aa7379c Binary files /dev/null and b/game_folder/images/outline_delete_black_36dp.png differ diff --git a/game_folder/images/priority.png b/game_folder/images/priority.png new file mode 100644 index 0000000..5c5d3ff Binary files /dev/null and b/game_folder/images/priority.png differ diff --git a/game_folder/images/star.png b/game_folder/images/star.png new file mode 100644 index 0000000..9fd595c Binary files /dev/null and b/game_folder/images/star.png differ diff --git a/game_folder/levels/Around_The_Citadel.lvl b/game_folder/levels/Around_The_Citadel.lvl new file mode 100644 index 0000000..75aac4d Binary files /dev/null and b/game_folder/levels/Around_The_Citadel.lvl differ diff --git a/game_folder/levels/Around_The_World.lvl b/game_folder/levels/Around_The_World.lvl new file mode 100644 index 0000000..f7ce4f0 Binary files /dev/null and b/game_folder/levels/Around_The_World.lvl differ diff --git a/game_folder/levels/Asteroid_Extreme.lvl b/game_folder/levels/Asteroid_Extreme.lvl new file mode 100644 index 0000000..47f999d Binary files /dev/null and b/game_folder/levels/Asteroid_Extreme.lvl differ diff --git a/game_folder/levels/Asteroid_Field.lvl b/game_folder/levels/Asteroid_Field.lvl new file mode 100644 index 0000000..b9496a4 Binary files /dev/null and b/game_folder/levels/Asteroid_Field.lvl differ diff --git a/game_folder/levels/Beware_Of_Bumps.lvl b/game_folder/levels/Beware_Of_Bumps.lvl new file mode 100644 index 0000000..0484bae Binary files /dev/null and b/game_folder/levels/Beware_Of_Bumps.lvl differ diff --git a/game_folder/levels/Build_Up_Speed.lvl b/game_folder/levels/Build_Up_Speed.lvl new file mode 100644 index 0000000..5d77f8e Binary files /dev/null and b/game_folder/levels/Build_Up_Speed.lvl differ diff --git a/game_folder/levels/Building_Bridges.lvl b/game_folder/levels/Building_Bridges.lvl new file mode 100644 index 0000000..e4b0505 Binary files /dev/null and b/game_folder/levels/Building_Bridges.lvl differ diff --git a/game_folder/levels/Bunny_Hops.lvl b/game_folder/levels/Bunny_Hops.lvl new file mode 100644 index 0000000..172400b Binary files /dev/null and b/game_folder/levels/Bunny_Hops.lvl differ diff --git a/game_folder/levels/Don't_Get_Crushed.lvl b/game_folder/levels/Don't_Get_Crushed.lvl new file mode 100644 index 0000000..29a2b14 Binary files /dev/null and b/game_folder/levels/Don't_Get_Crushed.lvl differ diff --git a/game_folder/levels/Expressways.lvl b/game_folder/levels/Expressways.lvl new file mode 100644 index 0000000..abc40c4 Binary files /dev/null and b/game_folder/levels/Expressways.lvl differ diff --git a/game_folder/levels/Fatal_Fissures.lvl b/game_folder/levels/Fatal_Fissures.lvl new file mode 100644 index 0000000..93a7b24 Binary files /dev/null and b/game_folder/levels/Fatal_Fissures.lvl differ diff --git a/game_folder/levels/First_Kaizo.lvl b/game_folder/levels/First_Kaizo.lvl new file mode 100644 index 0000000..e204a56 Binary files /dev/null and b/game_folder/levels/First_Kaizo.lvl differ diff --git a/game_folder/levels/Hole_In_One.lvl b/game_folder/levels/Hole_In_One.lvl new file mode 100644 index 0000000..a8209e3 Binary files /dev/null and b/game_folder/levels/Hole_In_One.lvl differ diff --git a/game_folder/levels/Jump_The_Crater.lvl b/game_folder/levels/Jump_The_Crater.lvl new file mode 100644 index 0000000..46c783b Binary files /dev/null and b/game_folder/levels/Jump_The_Crater.lvl differ diff --git a/game_folder/levels/Lily_Pads.lvl b/game_folder/levels/Lily_Pads.lvl new file mode 100644 index 0000000..f0abf34 Binary files /dev/null and b/game_folder/levels/Lily_Pads.lvl differ diff --git a/game_folder/levels/Mind_The_Gap.lvl b/game_folder/levels/Mind_The_Gap.lvl new file mode 100644 index 0000000..9233f08 Binary files /dev/null and b/game_folder/levels/Mind_The_Gap.lvl differ diff --git a/game_folder/levels/Mountain_Climbing.lvl b/game_folder/levels/Mountain_Climbing.lvl new file mode 100644 index 0000000..e5c428d Binary files /dev/null and b/game_folder/levels/Mountain_Climbing.lvl differ diff --git a/game_folder/levels/Planet_Crusher.lvl b/game_folder/levels/Planet_Crusher.lvl new file mode 100644 index 0000000..8c40689 Binary files /dev/null and b/game_folder/levels/Planet_Crusher.lvl differ diff --git a/game_folder/levels/Platform_Extreme.lvl b/game_folder/levels/Platform_Extreme.lvl new file mode 100644 index 0000000..9af3333 Binary files /dev/null and b/game_folder/levels/Platform_Extreme.lvl differ diff --git a/game_folder/levels/Pylon_Palace.lvl b/game_folder/levels/Pylon_Palace.lvl new file mode 100644 index 0000000..97ae04d Binary files /dev/null and b/game_folder/levels/Pylon_Palace.lvl differ diff --git a/game_folder/levels/Ride_The_Gecko.lvl b/game_folder/levels/Ride_The_Gecko.lvl new file mode 100644 index 0000000..4f74c1c Binary files /dev/null and b/game_folder/levels/Ride_The_Gecko.lvl differ diff --git a/game_folder/levels/The_Catwalk.lvl b/game_folder/levels/The_Catwalk.lvl new file mode 100644 index 0000000..b4957bc Binary files /dev/null and b/game_folder/levels/The_Catwalk.lvl differ diff --git a/game_folder/levels/The_Crown_Jewels.lvl b/game_folder/levels/The_Crown_Jewels.lvl new file mode 100644 index 0000000..6bef43f Binary files /dev/null and b/game_folder/levels/The_Crown_Jewels.lvl differ diff --git a/game_folder/levels/The_Hills_Are_Alive.lvl b/game_folder/levels/The_Hills_Are_Alive.lvl new file mode 100644 index 0000000..58109f0 Binary files /dev/null and b/game_folder/levels/The_Hills_Are_Alive.lvl differ diff --git a/game_folder/levels/The_Sponge.lvl b/game_folder/levels/The_Sponge.lvl new file mode 100644 index 0000000..c814098 Binary files /dev/null and b/game_folder/levels/The_Sponge.lvl differ diff --git a/game_folder/levels/Too_Many_Trees.lvl b/game_folder/levels/Too_Many_Trees.lvl new file mode 100644 index 0000000..49d8120 Binary files /dev/null and b/game_folder/levels/Too_Many_Trees.lvl differ diff --git a/game_folder/levels/Top_Of_The_Citadel.lvl b/game_folder/levels/Top_Of_The_Citadel.lvl new file mode 100644 index 0000000..5801e80 Binary files /dev/null and b/game_folder/levels/Top_Of_The_Citadel.lvl differ diff --git a/game_folder/levels/World_tree.lvl b/game_folder/levels/World_tree.lvl new file mode 100644 index 0000000..a92037a Binary files /dev/null and b/game_folder/levels/World_tree.lvl differ diff --git a/game_folder/levels/scores.bin b/game_folder/levels/scores.bin new file mode 100644 index 0000000..e69de29 diff --git a/game_folder/locals/English.loc b/game_folder/locals/English.loc new file mode 100644 index 0000000..e52bd12 --- /dev/null +++ b/game_folder/locals/English.loc @@ -0,0 +1,188 @@ +#English +English +#font_1 +Orbitron-Bold.ttf +#font_2 +Inconsolata-Bold.ttf +#Marble_Marcher +Marble +Marcher +#Play +Play +#Levels +Levels +#Controls +Controls +#Settings +Settings +#Exit +Exit +#OK +OK +#Yes +Yes +#No +No +#Best_time +Best time +#Edit +Edit +#Remove +Remove +#You_sure +Are you sure? +#Graphics +Graphics +#MM_about_short +Marble Marcher Community Edition +#MM_about_long +Marble Marcher Community Edition. +Original made by CodeParade. +Music by PettyTheft. + +Community Edition development team: +Michael Moroz(michael0884) +WAUthethird +Bryce AS202313 +Selicre + +Translators: +KaMa2 - polish language +Michael Moroz - english, ukrainian and russian languages +#Window +Window +#Button +Button +#Screen_Saver +Screen Saver +#Back2Main +Back To Main Menu +#CreateNewLvl +Create a new level +#About +©2019 Original by CodeParade, 1.4.0 beta, Community Edition +Music by PettyTheft +#DetailControls +Roll WASD or Arrows +Camera Mouse +Zoom Scroll Wheel +Restart R or Right-Click +Pause Esc +#Back +Back +#Paused +Paused +#Continue +Continue +#Restart +Restart +#Quit +Quit +#Music +Music +#On +On +#Off +Off +#High +High +#Medium +Medium +#Low +Low +#CongratsEnd +Congratulations, you beat all the levels! + + + +As a reward, cheats have been unlocked! +Activate them with the F1 key during gameplay. +Thanks for playing! +#CongratsMid +You've done well so far. + + + +But this is only the beginning. +If you need a quick break, take it now. +The challenge levels are coming up... +#CheatsON +Cheats Enabled +#CheatsInfo +[ C ] Color change +[ F ] Free camera +[ G ] Gravity strength +[ H ] Hyperspeed toggle +[ I ] Ignore goal +[ M ] Motion disable +[ P ] Planet toggle +[ Z ] Zoom to scale +[ 1 - 9 ] Scroll fractal parameter +#Statistics +Statistics +#Marble_velocity +Marble velocity +#Marble_position +Marble position +#Settings +Settings +#Sun_direction +Sun direction +#Shadows +Shadows +#Reflection_Refraction +Reflection and Refraction +#Glass +Glass +#Metal +Metal +#Marble_type +Marble type +#Mouse_sensitivity +Mouse sensitivity +#Wheel_sensitivity +Wheel sensitivity +#Music_volume +Music volume +#Target_FPS +Target FPS +#Camera_size +Camera size +#Camera_speed_free +Camera speed(Free mode) +#Level_Name +Level Name +#Level_Description +Level Description +#Save +Save Level +#Set_Marble +Set Marble Position +#Set_Marble_help +Click on the fractal to place +#Set_Flag +Set Flag Position +#Flag_Position +Flag Position +#Marble_Position +Marble Position +#Marble_scale +Marble Radius(Scale) +#Level_music +Level music +#Play_Music +Play/Stop current music +#None +None +#Fractal_Iterations +Fractal Iterations +#Fractal_Scale +Fractal Scale +#Fractal_Angle1 +Fractal Angle1 +#Fractal_Angle2 +Fractal Angle2 +#Fractal_Shift +Fractal Shift +#Fractal_Color +Fractal Color \ No newline at end of file diff --git a/game_folder/locals/German.loc b/game_folder/locals/German.loc new file mode 100644 index 0000000..3a6f30b --- /dev/null +++ b/game_folder/locals/German.loc @@ -0,0 +1,189 @@ +#German +Deutsch +#font_1 +Orbitron-Bold.ttf +#font_2 +Inconsolata-Bold.ttf +#Marble_Marcher +Marble +Marcher +#Play +Spielen +#Levels +Levels +#Controls +Steuerung +#Settings +Einstellungen +#Exit +Beenden +#OK +OK +#Yes +Ja +#No +Nein +#Best_time +Rekord +#Edit +Bearbeiten +#Remove +Entfernen +#You_sure +Bist du sicher? +#Graphics +Steuerung +#MM_about_short +Marble Marcher Community Edition +#MM_about_long +Marble Marcher Community Edition. +Original made by CodeParade. +Music by PettyTheft. + +Community Edition development team: +Michael Moroz(michael0884) +WAUthethird +Bryce AS202313 +Selicre + +Translators: +KaMa2 - polish language +Michael Moroz - english, ukrainian and russian languages +Mobilfan - german language +#Window +Fenster +#Button +Taste +#Screen_Saver +Bildschirmschoner +#Back2Main +Zurück zum Hauptmenü +#CreateNewLvl +Erstelle ein neues Level +#About +©2019 Original by CodeParade, 1.4.0 beta, Community Edition +Music von PettyTheft +#DetailControls +Rollen WASD oder Pfeiltasten +Kamera Maus +Zoom Mausrad +Neustart R oder Rechtsklick +Pause Esc +#Back +Zurück +#Paused +Pausiert +#Continue +Fortfahren +#Restart +Neustart +#Quit +Verlassen +#Music +Musik +#On +An +#Off +Aus +#High +Hoch +#Medium +Mittel +#Low +Niedrig +#CongratsEnd +Gratulation, du hast alle Levels geschafft! + + + +Als Belohnung hast du Cheats erhalten! +Aktiviere sie mit F1, während du in einem Level bist. +Danke fürs Spielen! +#CongratsMid +Bisher recht gut. + + + +Aber es hat erst begonnen. +Nimm dir eine kurze Pause, wenn du sie brauchst. +Die schweren Levels beginnen... +#CheatsON +Cheats aktiv +#CheatsInfo +[ C ] Farbänderung +[ F ] Freie Kamera +[ G ] Schwerkraftstärke +[ H ] Ultrageschwindigkeit umschalten +[ I ] Ziel ignorieren +[ M ] Bewegung umschalten +[ P ] Planet umschalten +[ Z ] Zoom für Größe +[ 1 - 9 ] Scrollen um Fraktal zu verändern +#Statistics +Statistiken +#Marble_velocity +Kugelgeschwindigkeit +#Marble_position +Kugelposition +#Settings +Einstellungen +#Sun_direction +Sonnenrichtung +#Shadows +Schatten +#Reflection_Refraction +Reflektion und Refraktion +#Glass +Glas +#Metal +Metall +#Marble_type +Murmel Art +#Mouse_sensitivity +Maus Empfindlichkeit +#Wheel_sensitivity +Mausrad Empfindlichkeit +#Music_volume +Musik Lautstärke +#Target_FPS +Ziel FPS +#Camera_size +Kamera Größe +#Camera_speed_free +Kamera Geschwindigkeit (Freie Kamera) +#Level_Name +Level Name +#Level_Description +Level Beschreibung +#Save +Level speichern +#Set_Marble +Kugel Position setzen +#Set_Marble_help +Klicke auf das Fraktal zum setzen +#Set_Flag +Setze Ziel Position +#Flag_Position +Ziel Position +#Marble_Position +Kugel Position +#Marble_scale +Kugel Größe (Radius) +#Level_music +Level Musik +#Play_Music +Starte/Stoppe aktuelle Musik +#None +Nichts +#Fractal_Iterations +Fraktal Iterationen +#Fractal_Scale +Fraktal Größe +#Fractal_Angle1 +Fraktal Winkel1 +#Fractal_Angle2 +Fraktal Winkel2 +#Fractal_Shift +Fraktal Verschiebung +#Fractal_Color +Fraktal Farbe \ No newline at end of file diff --git a/game_folder/locals/Japanese.loc b/game_folder/locals/Japanese.loc new file mode 100644 index 0000000..09db30d --- /dev/null +++ b/game_folder/locals/Japanese.loc @@ -0,0 +1,188 @@ +#Japanese +日本語 +#font_1 +NotoSansJP-Bold.otf +#font_2 +NotoSansJP-Regular.otf +#Marble_Marcher +Marble +Marcher +#Play +プレイ +#Levels +レベル +#Controls +コントロール +#Settings +セッティング +#Exit +出口 +#OK +OK +#Yes +はい +#No +いいえ +#Best_time +Best time +#Edit +Edit +#Remove +Remove +#You_sure +Are you sure? +#Graphics +Graphics +#MM_about_short +Marble Marcher Community Edition +#MM_about_long +Marble Marcher Community Edition. +Original made by CodeParade. +Music by PettyTheft. + +Community Edition development team: +Michael Moroz(michael0884) +WAUthethird +Bryce AS202313 +Selicre + +Translators: +KaMa2 - polish language +Michael Moroz - english, ukrainian and russian languages +#Window +Window +#Button +Button +#Screen_Saver +スクリーンセーバー +#Back2Main +Back To Main Menu +#CreateNewLvl +Create a new level +#About +©2019 Original by CodeParade, 1.4.0 beta, Community Edition +Music by PettyTheft +#DetailControls +Roll WASD or Arrows +Camera Mouse +Zoom Scroll Wheel +Restart R or Right-Click +Pause Esc +#Back +Back +#Paused +Paused +#Continue +Continue +#Restart +Restart +#Quit +Quit +#Music +Music +#On +On +#Off +Off +#High +High +#Medium +Medium +#Low +Low +#CongratsEnd +Congratulations, you beat all the levels! + + + +As a reward, cheats have been unlocked! +Activate them with the F1 key during gameplay. +Thanks for playing! +#CongratsMid +You've done well so far. + + + +But this is only the beginning. +If you need a quick break, take it now. +The challenge levels are coming up... +#CheatsON +Cheats Enabled +#CheatsInfo +[ C ] Color change +[ F ] Free camera +[ G ] Gravity strength +[ H ] Hyperspeed toggle +[ I ] Ignore goal +[ M ] Motion disable +[ P ] Planet toggle +[ Z ] Zoom to scale +[ 1 - 9 ] Scroll fractal parameter +#Statistics +Statistics +#Marble_velocity +Marble velocity +#Marble_position +Marble position +#Settings +Settings +#Sun_direction +Sun direction +#Shadows +Shadows +#Reflection_Refraction +Reflection and Refraction +#Glass +Glass +#Metal +Metal +#Marble_type +Marble type +#Mouse_sensitivity +Mouse sensitivity +#Wheel_sensitivity +Wheel sensitivity +#Music_volume +Music volume +#Target_FPS +Target FPS +#Camera_size +Camera size +#Camera_speed_free +Camera speed(Free mode) +#Level_Name +Level Name +#Level_Description +Level Description +#Save +Save Level +#Set_Marble +Set Marble Position +#Set_Marble_help +Click on the fractal to place +#Set_Flag +Set Flag Position +#Flag_Position +Flag Position +#Marble_Position +Marble Position +#Marble_scale +Marble Radius(Scale) +#Level_music +Level music +#Play_Music +Play/Stop current music +#None +None +#Fractal_Iterations +Fractal Iterations +#Fractal_Scale +Fractal Scale +#Fractal_Angle1 +Fractal Angle1 +#Fractal_Angle2 +Fractal Angle2 +#Fractal_Shift +Fractal Shift +#Fractal_Color +Fractal Color \ No newline at end of file diff --git a/game_folder/locals/Polish.loc b/game_folder/locals/Polish.loc new file mode 100644 index 0000000..e0f1ec6 --- /dev/null +++ b/game_folder/locals/Polish.loc @@ -0,0 +1,188 @@ +#Polish +Polski +#font_1 +Roboto-Bold.ttf +#font_2 +Roboto-Regular.ttf +#Marble_Marcher +Marble +Marcher +#Play +Graj +#Levels +Poziomy +#Controls +Sterowanie +#Settings +Ustawienia +#Exit +Wyjście +#OK +OK +#Yes +Tak +#No +Nie +#Best_time +Najlepszy czas +#Edit +Edytuj +#Remove +Usuń +#You_sure +Na pewno? +#Graphics +Grafika +#MM_about_short +Marble Marcher Community Edition +#MM_about_long +Marble Marcher Community Edition. +Oryginał stworzony przez CodeParade +Muzyka zrobiona przez PettyTheft. + +Deweloperzy Community Edition: +Michael Moroz(michael0884) +WAUthethird +Bryce AS202313 +Selicre + +Tłumacze: +KaMa2 - polski język +Michael Moroz - angielski, ukraiński i rosyjski język +#Window +Okno +#Button +Przycisk +#Screen_Saver +Wygaszacz Ekranu +#Back2Main +Powrót do menu +#CreateNewLvl +Stwórz nowy poziom +#About +©2019 Original by CodeParade, 1.4.0 beta, Community Edition +Muzyka zrobiona przez PettyTheft +#DetailControls +Toczenie się WASD lub Strzałki +Kamera Mysz +Zoom Scroll +Restart R lub Przwy przycisk myszy +Pauza Esc +#Back +Powrót +#Paused +Pauza +#Continue +Kontynuuj +#Restart +Restart +#Quit +Wyjdź +#Music +Muzyka +#On +Włączona +#Off +Wyłączona +#High +Wysoka +#Medium +Średnia +#Low +Niska +#CongratsEnd +Gratulacje, przeszedłeś wszystkie poziomy! + + + +W ramach nagrody odblokowano kody! +Aktywuj je naciskając F1 podczas gry. +Dziękujemy za grę! +#CongratsMid +Dobrze ci idzie! + + + +Ale to dopiero początek. +Jeśli potrzebujesz przerwy to zrób ją teraz. +Prawdziwe wyzwania dopiero nadchodzą... +#CheatsON +Kody Aktywowane +#CheatsInfo +[ C ] Zmiana koloru +[ F ] Tryb widza +[ G ] Zmiana siły grawitacji +[ H ] Włączenie hiperprędkości +[ I ] Ignoruj flagę +[ M ] Wyłączenie ruchu poziomu +[ P ] Przełączenie trybu grawitacji +[ Z ] Zmiana wielkości +[ 1 - 9 ] Edycja Fraktala (scrollem) +#Statistics +Statystyki +#Marble_velocity +Prędkość kulki +#Marble_position +Pozycja kulki +#Settings +Ustawienia +#Sun_direction +Kierunek słońca +#Shadows +Cienie +#Reflection_Refraction +Odbicia i refrakcja +#Glass +Szkło +#Metal +Metal +#Marble_type +Materiał +#Mouse_sensitivity +Czułość myszy +#Wheel_sensitivity +Czułość scrolla +#Music_volume +Głośność +#Target_FPS +Zamierzona ilość FPS +#Camera_size +Wielkość kamery +#Camera_speed_free +Prędkość kamery (tryb widza) +#Level_Name +Nazwa Poziomu +#Level_Description +Opis +#Save +Zapisz +#Set_Marble +Ustaw położenie początkowe +#Set_Marble_help +Naciśnij w odpowiednim miejscu na fraktalu, aby umieścić położenie +#Set_Flag +Umieść flagę +#Flag_Position +Położenie flagi +#Marble_Position +Położenie kulki +#Marble_scale +Wielkość kulki(skala) +#Level_music +Muzyka +#Play_Music +Graj/Zatrzymaj muzykę +#None +Żadne +#Fractal_Iterations +Iteracje fraktala +#Fractal_Scale +Skala fraktala +#Fractal_Angle1 +Kąt 1 fraktala +#Fractal_Angle2 +Kąt 2 fraktala +#Fractal_Shift +Przesunięcie fraktala +#Fractal_Color +Kolor fraktala \ No newline at end of file diff --git a/game_folder/locals/Russian.loc b/game_folder/locals/Russian.loc new file mode 100644 index 0000000..d489aea --- /dev/null +++ b/game_folder/locals/Russian.loc @@ -0,0 +1,188 @@ +#font_1 +Roboto-Bold.ttf +#font_2 +Roboto-Regular.ttf +#Ukrainian +Українська +#Marble_Marcher +Marble +Marcher +#Play +Грати +#Levels +Рівні +#Controls +Керування +#Settings +Налаштування +#Exit +Вихід +#OK +OK +#Yes +Так +#No +Ні +#Best_time +Найкращий час +#Edit +Редагувати +#Remove +Видалити +#You_sure +Ви впевнені? +#Graphics +Графіка +#MM_about_short +Marble Marcher Community Edition +#MM_about_long +Marble Marcher Community Edition. +Оригінал зроблений CodeParade. +Музика PettyTheft. + +Команда розробки Community Edition: +Michael Moroz(michael0884) +WAUthethird +Bryce AS202313 +Selicre + +Перекладачі: +KaMa2 - польська мова +Michael Moroz - англійська, українська та російська +#Window +Вікно +#Button +Кнопка +#Screen_Saver +Заставка +#Back2Main +Назад в Головне Меню +#CreateNewLvl +Створити новий рівень +#About +©2019 Original by CodeParade, 1.4.0 beta, Community Edition +Музика PettyTheft +#DetailControls +Котитися WASD or Arrows +Камера Mouse +Приближення Scroll Wheel +Перезапуск рівня R or Right-Click +Пауза Esc +#Back +Назад +#Paused +Пауза +#Continue +Продовжити +#Restart +Перезапустити рівень +#Quit +Вийти +#Music +Музика +#On +Включено +#Off +Виключено +#High +Висока +#Medium +Середня +#Low +Низька +#CongratsEnd +Вітаю, ти пройшов всі рівні! + + + +Твоя нагорода - розблоковані чіти! +Ти можеш активувати їх за допомогою F1 під час гри. +Дякую за те що грали! +#CongratsMid +Непогано - непогано... + + + +Але це тільки початок. +Якщо ти потребуєш паузи, то можеш зробити її зараз. +Наступні рівні будуть набагато складнішими... +#CheatsON +Чіти включені +#CheatsInfo +[ C ] Зміна кольору +[ F ] Режим вільної камери +[ G ] Сила тяжіння +[ H ] Режим гіпершвидкості +[ I ] Ігнорувати ціль(флаг) +[ M ] Виключити рух фрактала +[ P ] Включити режим планети +[ Z ] Режим зміни розміру кульки +[ 1 - 9 ] Зміна параметрів фрактала +#Statistics +Статистика +#Marble_velocity +Швидкість кульки +#Marble_position +Розташування кульки +#Settings +Налаштування +#Sun_direction +Напрямок сонця +#Shadows +Тіні +#Reflection_Refraction +Відбивання та заломлення +#Glass +Скло +#Metal +Метал +#Marble_type +Тип кульки +#Mouse_sensitivity +Чутливість мишки +#Wheel_sensitivity +Чутливість колеса мишки +#Music_volume +Гучність музики +#Target_FPS +Цільовий FPS +#Camera_size +Розмір камери +#Camera_speed_free +Швидкість камери(режим вільної камери) +#Level_Name +Назва рівня +#Level_Description +Опис рівня +#Save +Запам'ятати рівень +#Set_Marble +Поставити кульку +#Set_Marble_help +Натисність на фрактал щоб поставити +#Set_Flag +Поставити флаг +#Flag_Position +Розташування флага +#Marble_Position +Розташування кульки +#Marble_scale +Радіус кульки(масштаб) +#Level_music +Музика рівня +#Play_Music +Ввімкнути/вимкнути музику +#None +Нічого +#Fractal_Iterations +Кількість ітерації фрактала +#Fractal_Scale +Масштаб фрактала +#Fractal_Angle1 +Кут 1 фрактала +#Fractal_Angle2 +Кут 2 фрактала +#Fractal_Shift +Зсув фрактала +#Fractal_Color +Колір фрактала \ No newline at end of file diff --git a/game_folder/locals/Ukrainian.loc b/game_folder/locals/Ukrainian.loc new file mode 100644 index 0000000..27ad5cd --- /dev/null +++ b/game_folder/locals/Ukrainian.loc @@ -0,0 +1,188 @@ +#Ukrainian +Українська +#font_1 +Roboto-Bold.ttf +#font_2 +Roboto-Regular.ttf +#Marble_Marcher +Marble +Marcher +#Play +Грати +#Levels +Рівні +#Controls +Керування +#Settings +Налаштування +#Exit +Вихід +#OK +OK +#Yes +Так +#No +Ні +#Best_time +Найкращий час +#Edit +Редагувати +#Remove +Видалити +#You_sure +Ви впевнені? +#Graphics +Графіка +#MM_about_short +Marble Marcher Community Edition +#MM_about_long +Marble Marcher Community Edition. +Оригінал зроблений CodeParade. +Музика PettyTheft. + +Команда розробки Community Edition: +Michael Moroz(michael0884) +WAUthethird +Bryce AS202313 +Selicre + +Перекладачі: +KaMa2 - польська мова +Michael Moroz - англійська, українська та російська +#Window +Вікно +#Button +Кнопка +#Screen_Saver +Заставка +#Back2Main +Назад в Головне Меню +#CreateNewLvl +Створити новий рівень +#About +©2019 Original by CodeParade, 1.4.0 beta, Community Edition +Музика PettyTheft +#DetailControls +Котитися WASD or Arrows +Камера Mouse +Приближення Scroll Wheel +Перезапуск рівня R or Right-Click +Пауза Esc +#Back +Назад +#Paused +Пауза +#Continue +Продовжити +#Restart +Перезапуск +#Quit +Вийти +#Music +Музика +#On +Включено +#Off +Виключено +#High +Висока +#Medium +Середня +#Low +Низька +#CongratsEnd +Вітаю, ти пройшов всі рівні! + + + +Твоя нагорода - розблоковані чіти! +Ти можеш активувати їх за допомогою F1 під час гри. +Дякую за те що грали! +#CongratsMid +Непогано - непогано... + + + +Але це тільки початок. +Якщо ти потребуєш паузи, то можеш зробити її зараз. +Наступні рівні будуть набагато складнішими... +#CheatsON +Чіти включені +#CheatsInfo +[ C ] Зміна кольору +[ F ] Режим вільної камери +[ G ] Сила тяжіння +[ H ] Режим гіпершвидкості +[ I ] Ігнорувати ціль(флаг) +[ M ] Виключити рух фрактала +[ P ] Включити режим планети +[ Z ] Режим зміни розміру кульки +[ 1 - 9 ] Зміна параметрів фрактала +#Statistics +Статистика +#Marble_velocity +Швидкість кульки +#Marble_position +Розташування кульки +#Settings +Налаштування +#Sun_direction +Напрямок сонця +#Shadows +Тіні +#Reflection_Refraction +Відбивання та заломлення +#Glass +Скло +#Metal +Метал +#Marble_type +Тип кульки +#Mouse_sensitivity +Чутливість мишки +#Wheel_sensitivity +Чутливість колеса мишки +#Music_volume +Гучність музики +#Target_FPS +Цільовий FPS +#Camera_size +Розмір камери +#Camera_speed_free +Швидкість камери(режим вільної камери) +#Level_Name +Назва рівня +#Level_Description +Опис рівня +#Save +Запам'ятати рівень +#Set_Marble +Поставити кульку +#Set_Marble_help +Натисність на фрактал щоб поставити +#Set_Flag +Поставити флаг +#Flag_Position +Розташування флага +#Marble_Position +Розташування кульки +#Marble_scale +Радіус кульки(масштаб) +#Level_music +Музика рівня +#Play_Music +Ввімкнути/вимкнути музику +#None +Нічого +#Fractal_Iterations +Кількість ітерацій фрактала +#Fractal_Scale +Масштаб фрактала +#Fractal_Angle1 +Кут 1 фрактала +#Fractal_Angle2 +Кут 2 фрактала +#Fractal_Shift +Зсув фрактала +#Fractal_Color +Колір фрактала \ No newline at end of file diff --git a/game_folder/screenshots/readme.txt b/game_folder/screenshots/readme.txt new file mode 100644 index 0000000..c137eb2 --- /dev/null +++ b/game_folder/screenshots/readme.txt @@ -0,0 +1 @@ +To change the screenshot resolution go to assets folder and open config.txt \ No newline at end of file diff --git a/game_folder/shaders/compute/Illumination_step.glsl b/game_folder/shaders/compute/Illumination_step.glsl new file mode 100644 index 0000000..4a16f0a --- /dev/null +++ b/game_folder/shaders/compute/Illumination_step.glsl @@ -0,0 +1,55 @@ +#version 430 +//4*4 ray bundle +#define group_size 8 +#define block_size 64 + +layout(local_size_x = group_size, local_size_y = group_size) in; +layout(rgba32f, binding = 0) uniform image2D illumination; +layout(rgba32f, binding = 1) uniform image2D DE_input; +layout(rgba32f, binding = 2) uniform image2D color_HDR; //calculate final color + +//make all the local distance estimator spheres shared +shared vec4 de_sph[group_size][group_size]; + +#include +#include + +///Half-resolution illumination step + +void main() { + + ivec2 global_pos = ivec2(gl_GlobalInvocationID.xy); + ivec2 local_indx = ivec2(gl_LocalInvocationID.xy); + + vec2 img_size = vec2(imageSize(illumination)); + vec2 pimg_size = vec2(imageSize(DE_input)); + vec2 step_scale = img_size/pimg_size; + + ivec2 prev_pos = min(ivec2((vec2(global_pos)/step_scale) + 0.5),ivec2(pimg_size)-1); + + vec4 sph = imageLoad(DE_input, prev_pos); + + ray rr = get_ray(vec2(global_pos)/img_size); + vec4 pos = vec4(rr.pos,0); + vec4 dir = vec4(rr.dir,0); + vec4 var = vec4(0); + + float td = dot(dir.xyz, sph.xyz - pos.xyz);//traveled distance + + pos = sph; + dir.w += td; + + vec4 illum = vec4(0); + + if(pos.w < max(2*fovray*td, MIN_DIST) && SHADOWS_ENABLED) + { + vec4 norm = calcNormal(pos.xyz, td*fovray/8); + norm.xyz = normalize(norm.xyz); + pos.xyz += norm.xyz*5*td*fovray; + illum.x = shadow_march(pos, normalize(vec4(LIGHT_DIRECTION,0)), MAX_DIST, 0.08); + + //illum.y = ambient_occlusion(pos, norm); + } + + imageStore(illumination, global_pos, illum); +} \ No newline at end of file diff --git a/game_folder/shaders/compute/MAIN.cfg b/game_folder/shaders/compute/MAIN.cfg new file mode 100644 index 0000000..23ad2eb --- /dev/null +++ b/game_folder/shaders/compute/MAIN.cfg @@ -0,0 +1,42 @@ +#number of main textures(HDR color and position map) // this should be first +2 +#shader name +MRRM1.glsl +#global size +width/32 +height/32 +#texture resolution +width/4 +height/4 +#output texture number +3 +#shader name +MRRM3.glsl +#global size +width/8 +height/8 +#texture resolution +width +height +#output texture number +0 +#shader name//half res shadows and AO +Illumination_step.glsl +#global size +width/16 +height/16 +#texture resolution +width/2 +height/2 +#output texture number +1 +#shader name +Shading_step.glsl +#global size +width/8 +height/8 +#texture resolution +width +height +#output texture number +1 \ No newline at end of file diff --git a/game_folder/shaders/compute/MRRM1.glsl b/game_folder/shaders/compute/MRRM1.glsl new file mode 100644 index 0000000..8a3400a --- /dev/null +++ b/game_folder/shaders/compute/MRRM1.glsl @@ -0,0 +1,41 @@ +#version 430 +//4*4 ray bundle +#define group_size 8 +#define block_size 64 + +layout(local_size_x = group_size, local_size_y = group_size) in; +layout(rgba32f, binding = 0) uniform image2D DE_output; //calculate final DE spheres +layout(rgba32f, binding = 1) uniform image2D DE2_output; +layout(rgba32f, binding = 2) uniform image2D var_output; + +//make all the local distance estimator spheres shared +shared vec4 de_sph[group_size][group_size]; + +#include +#include + +///The first step of multi resolution ray marching + +void main() { + ivec2 global_pos = ivec2(gl_GlobalInvocationID.x,gl_GlobalInvocationID.y); + int local_indx = int(gl_LocalInvocationIndex); + vec2 img_size = vec2(imageSize(DE_output)); + //if within the texture + + //initialize the ray + ray rr = get_ray(vec2(global_pos)/img_size); + vec4 pos = vec4(rr.pos,0); + vec4 dir = vec4(rr.dir,0); + vec4 var = vec4(0); + fovray *= 4; + ray_march(pos, dir, var, fovray, fovray); + + vec4 pos1 = pos; + + normarch(pos1); + + //save the DE spheres + imageStore(DE_output, global_pos, pos); + imageStore(DE2_output, global_pos, pos1); + imageStore(var_output, global_pos, var); +} \ No newline at end of file diff --git a/game_folder/shaders/compute/MRRM2.glsl b/game_folder/shaders/compute/MRRM2.glsl new file mode 100644 index 0000000..f430862 --- /dev/null +++ b/game_folder/shaders/compute/MRRM2.glsl @@ -0,0 +1,76 @@ +#version 430 +//4*4 ray bundle +#define group_size 8 +#define block_size 64 +#define RBM1 0 + +layout(local_size_x = group_size, local_size_y = group_size) in; +layout(rgba32f, binding = 0) uniform image2D DE_input; +layout(rgba32f, binding = 1) uniform image2D DE2_input; +layout(rgba32f, binding = 2) uniform image2D var_input; +layout(rgba32f, binding = 3) uniform image2D DE_output; //calculate final DE spheres +layout(rgba32f, binding = 4) uniform image2D DE2_output; +layout(rgba32f, binding = 5) uniform image2D var_output; + +//make all the local distance estimator spheres shared +shared vec4 de_sph[group_size][group_size]; + +#include +#include + +///The second step of multi resolution ray marching + +void main() { + ivec2 global_pos = ivec2(gl_GlobalInvocationID.xy); + ivec2 local_indx = ivec2(gl_LocalInvocationID.xy); + vec2 img_size = vec2(imageSize(DE_output)); + vec2 pimg_size = vec2(imageSize(DE_input)); + vec2 MRRM_step_scale = img_size/pimg_size; + //if within the texture + + ivec2 prev_pos = min(ivec2((vec2(global_pos)/MRRM_step_scale) + 0.5),ivec2(pimg_size)-1); + //initialize the ray + vec4 sph = imageLoad(DE_input, prev_pos); + //vec4 sph_norm = imageLoad(DE2_input, prev_pos); + + #if(RBM1) + de_sph[local_indx.x][local_indx.y] = sph; + memoryBarrierShared(); + #endif + + ray rr = get_ray(vec2(global_pos)/img_size); + vec4 pos = vec4(rr.pos,0); + vec4 dir = vec4(rr.dir,0); + vec4 var = imageLoad(var_input, prev_pos); + + float td = dot(dir.xyz, sph.xyz - pos.xyz);//traveled distance + + //first order, MRRM + pos.xyz += dir.xyz*td;//move local ray beginning inside the DE sphere + dir.w += td; + + //calculate new best pos, second order, MRRBM + #if(RBM1) + barrier(); + float d = find_furthest_intersection(dir.xyz, pos.xyz, local_indx); + #else + float d = sphere_intersection(dir.xyz, pos.xyz, sph); + //d = max(d, sphere_intersection(dir.xyz, pos.xyz, sph_norm)); + #endif + + pos.w = d; + var.w = 1; + + fovray = 1.2*Camera.FOV/img_size.x; + + ray_march(pos, dir, var, fovray, fovray); + + vec4 pos1 = pos; + + normarch(pos1); + + //save the DE spheres + imageStore(DE_output, global_pos, pos); + imageStore(DE2_output, global_pos, pos1); + imageStore(var_output, global_pos, var); +} \ No newline at end of file diff --git a/game_folder/shaders/compute/MRRM3.glsl b/game_folder/shaders/compute/MRRM3.glsl new file mode 100644 index 0000000..e8504b6 --- /dev/null +++ b/game_folder/shaders/compute/MRRM3.glsl @@ -0,0 +1,66 @@ +#version 430 +//4*4 ray bundle +#define group_size 8 +#define block_size 64 +#define RBM1 0 + +layout(local_size_x = group_size, local_size_y = group_size) in; +layout(rgba32f, binding = 0) uniform image2D DE_input; +layout(rgba32f, binding = 1) uniform image2D DE2_input; +layout(rgba32f, binding = 2) uniform image2D var_input; +layout(rgba32f, binding = 3) uniform image2D DE_output; //calculate final DE spheres + +//make all the local distance estimator spheres shared +shared vec4 de_sph[group_size][group_size]; + +#include +#include + +///The second step of multi resolution ray marching + +void main() { + ivec2 global_pos = ivec2(gl_GlobalInvocationID.xy); + ivec2 local_indx = ivec2(gl_LocalInvocationID.xy); + vec2 img_size = vec2(imageSize(DE_output)); + vec2 pimg_size = vec2(imageSize(DE_input)); + vec2 MRRM_step_scale = img_size/pimg_size; + //if within the texture + + ivec2 prev_pos = min(ivec2((vec2(global_pos)/MRRM_step_scale) + 0.5),ivec2(pimg_size)-1); + //initialize the ray + vec4 sph = imageLoad(DE_input, prev_pos); + vec4 sph_norm = imageLoad(DE2_input, prev_pos); + + #if(RBM1) + de_sph[local_indx.x][local_indx.y] = sph; + memoryBarrierShared(); + #endif + + ray rr = get_ray(vec2(global_pos)/img_size); + vec4 pos = vec4(rr.pos,0); + vec4 dir = vec4(rr.dir,0); + vec4 var = imageLoad(var_input, prev_pos); + + float td = dot(dir.xyz, sph.xyz - pos.xyz);//traveled distance + + //first order, MRRM + pos.xyz += dir.xyz*td;//move local ray beginning inside the DE sphere + dir.w += td; + + //calculate new best pos, second order, MRRBM + #if(RBM1) + barrier(); + float d = find_furthest_intersection(dir.xyz, pos.xyz, local_indx); + #else + float d = sphere_intersection(dir.xyz, pos.xyz, sph); + d = max(d, sphere_intersection(dir.xyz, pos.xyz, sph_norm)); + #endif + + pos.w = d; + var.w = 1; + + ray_march(pos, dir, var, fovray, 0); + + //save the DE spheres + imageStore(DE_output, global_pos, pos); +} \ No newline at end of file diff --git a/game_folder/shaders/compute/Shading_step.glsl b/game_folder/shaders/compute/Shading_step.glsl new file mode 100644 index 0000000..6d44164 --- /dev/null +++ b/game_folder/shaders/compute/Shading_step.glsl @@ -0,0 +1,58 @@ +#version 430 +//4*4 ray bundle +#define group_size 8 +#define block_size 64 + +layout(local_size_x = group_size, local_size_y = group_size) in; +layout(rgba32f, binding = 0) uniform image2D illumination; //calculate final color +layout(rgba8, binding = 1) uniform image2D color_output; //calculate final color +layout(rgba32f, binding = 2) uniform image2D DE_input; +layout(rgba32f, binding = 3) uniform image2D color_HDR; //calculate final color + + +//make all the local distance estimator spheres shared +shared vec4 de_sph[group_size][group_size]; + +#include +#include + +#define VIGNETTE_STRENGTH 0.2 + + +void main() { + ivec2 global_pos = ivec2(gl_GlobalInvocationID.xy); + ivec2 local_indx = ivec2(gl_LocalInvocationID.xy); + vec2 img_size = vec2(imageSize(color_output)); + + vec4 sph = imageLoad(DE_input, global_pos); + vec4 illum = interp(illumination, vec2(global_pos)*0.5f); + + ray rr = get_ray(vec2(global_pos)/img_size); + vec4 pos = vec4(rr.pos,0); + vec4 dir = vec4(rr.dir,0); + vec4 var = vec4(0); + + float td = dot(dir.xyz, sph.xyz - pos.xyz);//traveled distance + + pos = sph; + dir.w += td; + + vec4 color; + if(pos.w < max(2*fovray*td, MIN_DIST) ) + { + color = shading(pos, dir, fovray, illum.x); + } + else + { + color = vec4(BACKGROUND_COLOR*BACKGROUND_COLOR*BACKGROUND_COLOR,0); + } + + vec4 prev_color = imageLoad(color_HDR, global_pos); + if(!isnan(color.x) && !isnan(color.y) && !isnan(color.z)) + { + color = prev_color*Camera.mblur + (1-Camera.mblur)*color; //blur + float vignette = 1.0 - VIGNETTE_STRENGTH * length(vec2(global_pos)/img_size - 0.5); + imageStore(color_HDR, global_pos, vec4(color.xyz, 1)); + imageStore(color_output, global_pos, vec4(HDRmapping(color.xyz, Camera.exposure, 2.2)*vignette, 1)); + } +} \ No newline at end of file diff --git a/game_folder/shaders/compute/camera.glsl b/game_folder/shaders/compute/camera.glsl new file mode 100644 index 0000000..c13449c --- /dev/null +++ b/game_folder/shaders/compute/camera.glsl @@ -0,0 +1,47 @@ + +struct ray +{ + vec3 pos; + vec3 dir; +}; + +struct gl_camera +{ + vec3 position; + vec3 dirx; + vec3 diry; + vec3 dirz; + vec2 resolution; + float aspect_ratio; + float FOV; + float focus; + float bokeh; + float exposure; + float mblur; + float speckle; + float size; + int stepN; + int step; +}; + + +ivec2 getGpos(int index) +{ + int y = index/group_size; + int x = index%group_size; + return ivec2(x,y); +} + +uniform gl_camera Camera; +float fovray; + +ray get_ray(vec2 screen_pos) +{ + vec2 shift = Camera.FOV*(2.f*screen_pos - 1.f)*vec2(Camera.aspect_ratio, 1.f); + ray cray; + cray.pos = Camera.position + Camera.size*(Camera.dirx*shift.x + Camera.diry*shift.y); + cray.dir = normalize(Camera.dirx*shift.x + Camera.diry*shift.y + Camera.dirz); + float aspect_ratio_ratio = Camera.aspect_ratio/(Camera.resolution.x/Camera.resolution.y); + fovray = 1.41*Camera.FOV*max(1.f/aspect_ratio_ratio, aspect_ratio_ratio)/Camera.resolution.x; //pixel FOV + return cray; +} \ No newline at end of file diff --git a/game_folder/shaders/compute/distance_estimators.glsl b/game_folder/shaders/compute/distance_estimators.glsl new file mode 100644 index 0000000..c4e7b04 --- /dev/null +++ b/game_folder/shaders/compute/distance_estimators.glsl @@ -0,0 +1,179 @@ +#define COL col_scene +#define DE de_scene + +uniform float iFracScale; +uniform float iFracAng1; +uniform float iFracAng2; +uniform vec3 iFracShift; +uniform vec3 iFracCol; +uniform vec3 iMarblePos; +uniform float iMarbleRad; +uniform float iFlagScale; +uniform vec3 iFlagPos; +#define FRACTAL_ITER 16 + +///Original MM distance estimators + +float s1 = sin(iFracAng1), c1 = cos(iFracAng1), s2 = sin(iFracAng2), c2 = cos(iFracAng2); + +//########################################## +// Space folding +//########################################## +void planeFold(inout vec4 z, vec3 n, float d) { + z.xyz -= 2.0 * min(0.0, dot(z.xyz, n) - d) * n; +} +void sierpinskiFold(inout vec4 z) { + z.xy -= min(z.x + z.y, 0.0); + z.xz -= min(z.x + z.z, 0.0); + z.yz -= min(z.y + z.z, 0.0); +} +void mengerFold(inout vec4 z) { + float a = min(z.x - z.y, 0.0); + z.x -= a; + z.y += a; + a = min(z.x - z.z, 0.0); + z.x -= a; + z.z += a; + a = min(z.y - z.z, 0.0); + z.y -= a; + z.z += a; +} +void boxFold(inout vec4 z, vec3 r) { + z.xyz = clamp(z.xyz, -r, r) * 2.0 - z.xyz; +} +void rotX(inout vec4 z, float s, float c) { + z.yz = vec2(c*z.y + s*z.z, c*z.z - s*z.y); +} +void rotY(inout vec4 z, float s, float c) { + z.xz = vec2(c*z.x - s*z.z, c*z.z + s*z.x); +} +void rotZ(inout vec4 z, float s, float c) { + z.xy = vec2(c*z.x + s*z.y, c*z.y - s*z.x); +} +void rotX(inout vec4 z, float a) { + rotX(z, sin(a), cos(a)); +} +void rotY(inout vec4 z, float a) { + rotY(z, sin(a), cos(a)); +} +void rotZ(inout vec4 z, float a) { + rotZ(z, sin(a), cos(a)); +} + +//########################################## +// Primitive DEs +//########################################## +float de_sphere(vec4 p, float r) { + return (length(p.xyz) - r) / p.w; +} +float de_box(vec4 p, vec3 s) { + vec3 a = abs(p.xyz) - s; + return (min(max(max(a.x, a.y), a.z), 0.0) + length(max(a, 0.0))) / p.w; +} +float de_tetrahedron(vec4 p, float r) { + float md = max(max(-p.x - p.y - p.z, p.x + p.y - p.z), + max(-p.x + p.y + p.z, p.x - p.y + p.z)); + return (md - r) / (p.w * sqrt(3.0)); +} +float de_capsule(vec4 p, float h, float r) { + p.y -= clamp(p.y, -h, h); + return (length(p.xyz) - r) / p.w; +} + +//########################################## +// Main DEs +//########################################## +float de_fractal(vec4 p) +{ + for (int i = 0; i < FRACTAL_ITER; ++i) { + p.xyz = abs(p.xyz); + rotZ(p, s1, c1); + mengerFold(p); + rotX(p, s2, c2); + p *= iFracScale; + p.xyz += iFracShift; + } + return de_box(p, vec3(6.0)); +} + +vec4 col_fractal(vec4 p) +{ + vec3 orbit = vec3(0.0); + for (int i = 0; i < FRACTAL_ITER; ++i) { + p.xyz = abs(p.xyz); + rotZ(p, s1, c1); + mengerFold(p); + rotX(p, s2, c2); + p *= iFracScale; + p.xyz += iFracShift; + orbit = max(orbit, p.xyz*iFracCol); + } + return vec4(orbit, de_box(p, vec3(6.0))); +} + +float de_marble(vec4 p) +{ + return de_sphere(p - vec4(iMarblePos, 0), iMarbleRad); +} + +vec4 col_marble(vec4 p) +{ + vec4 col = vec4(0, 0, 0, de_sphere(p - vec4(iMarblePos, 0), iMarbleRad)); + return vec4(col.x, col.y, col.z, de_sphere(p - vec4(iMarblePos, 0), iMarbleRad)); +} + +float de_flag(vec4 p) +{ + vec3 f_pos = iFlagPos + vec3(1.5, 4, 0)*iFlagScale; + float d = de_box(p - vec4(f_pos, 0), vec3(1.5, 0.8, 0.08)*iMarbleRad); + d = min(d, de_capsule(p - vec4(iFlagPos + vec3(0, iFlagScale*2.4, 0), 0), iMarbleRad*2.4, iMarbleRad*0.18)); + return d; +} + +vec4 col_flag(vec4 p) +{ + vec3 f_pos = iFlagPos + vec3(1.5, 4, 0)*iFlagScale; + float d1 = de_box(p - vec4(f_pos, 0), vec3(1.5, 0.8, 0.08)*iMarbleRad); + float d2 = de_capsule(p - vec4(iFlagPos + vec3(0, iFlagScale*2.4, 0), 0), iMarbleRad*2.4, iMarbleRad*0.18); + if (d1 < d2) { + return vec4(1.0, 0.2, 0.1, d1); + } else { + return vec4(0.9, 0.9, 0.1, d2); + } +} + +//float DE_count = 0; + +float de_scene(vec3 pos) +{ + //DE_count = DE_count+1; + vec4 p = vec4(pos,1.f); + float d = de_fractal(p); + d = min(d, de_marble(p)); + d = min(d, de_flag(p)); + return d; +} + +vec4 col_scene(vec3 pos) +{ + //DE_count = DE_count+1; + vec4 p = vec4(pos,1.f); + vec4 col = col_fractal(p); + vec4 col_f = col_flag(p); + if (col_f.w < col.w) { col = col_f; } + vec4 col_m = col_marble(p); + if (col_m.w < col.w) { + return vec4(col_m.xyz, 1.0); + } + return vec4(min(col.xyz,1), 0.0); +} + +//A faster formula to find the gradient/normal direction of the DE +//credit to http://www.iquilezles.org/www/articles/normalsSDF/normalsSDF.htm +vec4 calcNormal(vec3 p, float dx) { + const vec3 k = vec3(1,-1,0); + return (k.xyyx*DE(p + k.xyy*dx) + + k.yyxx*DE(p + k.yyx*dx) + + k.yxyx*DE(p + k.yxy*dx) + + k.xxxx*DE(p + k.xxx*dx))/vec4(4*dx,4*dx,4*dx,4); +} \ No newline at end of file diff --git a/game_folder/shaders/compute/main.glsl b/game_folder/shaders/compute/main.glsl new file mode 100644 index 0000000..7076bac --- /dev/null +++ b/game_folder/shaders/compute/main.glsl @@ -0,0 +1,80 @@ +#version 430 +//4*4 ray bundle +#define bundle_size 1 +#define bundle_length 1 +#define bundle_center 0 +#define group_size 8 +#define block_size 64 +#define RBM 0 +#include + +//4*4 bundle of ray bundles(oh lol) +layout(local_size_x = group_size, local_size_y = group_size) in; +layout(rgba8, binding = 0) uniform image2D img_output; + +//make all the local distance estimator spheres shared +shared vec4 de_sph[group_size][group_size]; + +#include + + +//each shader invocation will march a ray bundle, +//all invocations will have all the DE data from neighbore +//ray bundles to improve marching efficiency +//since it will give a larger volume of space in which the rays can safely march +//(not only a single DE sphere) + +void main() { + ivec2 global_pos = ivec2(gl_GlobalInvocationID.x,gl_GlobalInvocationID.y); + int local_indx = int(gl_LocalInvocationIndex); + ivec2 block_pos = bundle_size*global_pos; + vec2 img_size = vec2(imageSize(img_output)); + if(block_pos.x < img_size.x && block_pos.y < img_size.y) + { + //ray bundle array + vec4 pos[bundle_length]; + vec4 dir[bundle_length]; + vec4 var[bundle_length]; + + //initialize the ray bundle + ivec2 pix = block_pos; + #pragma unroll + for(int i = 0; i < bundle_length; i++) + { + ray rr = get_ray(vec2(pix)/img_size); + pos[i] = vec4(rr.pos,0); + dir[i] = vec4(rr.dir,0); + var[i] = vec4(0); + } + + //de_sph[local_indx] = pos[bundle_center]; + + + #pragma unroll + for(int i = 0; i < bundle_length; i++) + { + ray_march(pos[i], dir[i], var[i], fovray, 0); + } + + // output to the specified image block + + pix = block_pos; + #pragma unroll + for(int i = 0; i < bundle_length; i++) + { + //ray rr = get_ray(vec2(pix)/vec2(imageSize(img_output))); + + /* pos[i] = vec4(rr.pos,0); + dir[i] = vec4(rr.dir,0); + ray_march(pos[i], dir[i]);*/ + // float DE_per_pix = DE_count/float(bundle_length*MAX_MARCHES); + float ao = (1 - var[i].x/MAX_MARCHES); + float td = (1 - dir[i].w*0.06f); + vec4 norm = calcNormal(pos[i].xyz, pos[i].w/16); + pos[i].xyz -= norm.xyz*norm.w; + vec4 color = COL(pos[i].xyz); + //memoryBarrierImage(); + imageStore(img_output, pix, vec4(color.xyz*ao,1)); + } + } +} \ No newline at end of file diff --git a/game_folder/shaders/compute/pipeline.cfg b/game_folder/shaders/compute/pipeline.cfg new file mode 100644 index 0000000..98161b5 --- /dev/null +++ b/game_folder/shaders/compute/pipeline.cfg @@ -0,0 +1,20 @@ +#shader name +MRRM1.glsl +#global size +width/32 +height/32 +#texture resolution +width/4 +height/4 +#output texture number +3 +#shader name +MRRM3.glsl +#global size +width/8 +height/8 +#texture resolution +width +height +#output texture number +1 \ No newline at end of file diff --git a/game_folder/shaders/compute/ray_marching.glsl b/game_folder/shaders/compute/ray_marching.glsl new file mode 100644 index 0000000..b130e40 --- /dev/null +++ b/game_folder/shaders/compute/ray_marching.glsl @@ -0,0 +1,133 @@ +#include + +#define MAX_DIST 50 +#define MIN_DIST 1e-7 +#define MAX_MARCHES 512 +#define NORMARCHES 2 + + +void ray_march(inout vec4 pos, inout vec4 dir, inout vec4 var, float fov, float d0) +{ + //March the ray + for (; var.x < MAX_MARCHES; var.x += 1.0) { + //if the distance from the surface is less than the distance per pixel we stop + if(dir.w > MAX_DIST || pos.w<0) + { + break; + } + + if(pos.w < max(fov*dir.w, MIN_DIST) && var.x > 0 && var.w < 1) + { + break; + } + + dir.w += pos.w; + pos.xyz += pos.w*dir.xyz; + pos.w = DE(pos.xyz)-d0*dir.w; + var.w = 0; + } + + pos.w += d0*dir.w; +} + +// Polynomial smooth minimum by iq +float smin(float a, float b, float k) { + float h = clamp(0.5 + 0.5*(a-b)/k, 0.0, 1.0); + return mix(a, b, h) - k*h*(1.0-h); +} + +float shadow_march(vec4 pos, vec4 dir, float distance2light, float light_angle) +{ + float light_visibility=1; + float ph = 1e5; + pos.w = DE(pos.xyz); + int i = 0; + for (; i < MAX_MARCHES; i++) { + + dir.w += pos.w; + pos.xyz += pos.w*dir.xyz; + pos.w = DE(pos.xyz); + + float y = pos.w*pos.w/(2.0*ph); + float d = sqrt(pos.w*pos.w-y*y); + float angle = d/(max(MIN_DIST,dir.w-y)*light_angle); + light_visibility = min(light_visibility, angle); + ph = pos.w; + + + if(dir.w >= distance2light) + { + return light_visibility*light_visibility; + } + + if(dir.w > MAX_DIST || pos.w < max(fovray*dir.w, MIN_DIST)) + { + return 0; + } + } + return light_visibility*light_visibility; //smoothens out the shadow, and is more physically accurate +} + +float sphere_intersection(vec3 r, vec3 p, vec4 sphere) +{ + p = p - sphere.xyz; + if(p == vec3(0)) return sphere.w; + + float b = dot(p, r); + float c = sphere.w*sphere.w - dot(p,p); + float d = b*b + c; + + if((d <= 0) || (c <= 0)) //if no intersection + { + return 0; + } + else + { + return sqrt(d) - b; //use furthest solution in the direction of the ray + } +} + +float find_furthest_intersection(vec3 r, vec3 p, ivec2 id) +{ + float d = 0; + ivec2 idR = min(id+1, group_size-1); + ivec2 idL = max(id-1, 0); + for(int i = idL.x; i <= idR.x; i++) + { + for(int j = idL.y; j <= idR.y; j++) + { + d = max(d, sphere_intersection(r,p,de_sph[i][j])); + } + } + return d; +} + + +float find_furthest_intersection_all(vec3 r, vec3 p, ivec2 id) +{ + float d = 0; + for(int i = 0; i < group_size; i++) + { + for(int j = 0; j < group_size; j++) + { + d = max(d, sphere_intersection(r,p,de_sph[i][j])); + } + } + return d; +} + +void normarch(inout vec4 pos) +{ + //calculate the normal + vec4 norm = calcNormal(pos.xyz, pos.w/8); + norm.xyz = normalize(norm.xyz); + pos.w = norm.w; + + //march in the direction of the normal + #pragma unroll + for(int i = 0; i < NORMARCHES; i++) + { + pos.xyz += pos.w*norm.xyz; + pos.w = DE(pos.xyz); + } +} diff --git a/game_folder/shaders/compute/shading.glsl b/game_folder/shaders/compute/shading.glsl new file mode 100644 index 0000000..4efed0c --- /dev/null +++ b/game_folder/shaders/compute/shading.glsl @@ -0,0 +1,210 @@ +#include + +#define PI 3.14159265 +#define AMBIENT_MARCHES 5 +#define AMBIENT_COLOR 2*vec4(1,1,1,1) + +uniform vec3 BACKGROUND_COLOR; +uniform vec3 LIGHT_DIRECTION; +uniform float PBR_METALLIC; +uniform float PBR_ROUGHNESS; +uniform vec3 LIGHT_COLOR; + +uniform bool SHADOWS_ENABLED; + +//better to use a sampler though +vec4 interp(layout (rgba32f) image2D text, vec2 coord) +{ + //coord *= 0.99; + ivec2 ci = ivec2(coord); + vec2 d = coord - floor(coord); + return imageLoad(text, ci)*(1-d.x)*(1-d.y) + + imageLoad(text, ci+ivec2(1,0))*d.x*(1-d.y) + + imageLoad(text, ci+ivec2(0,1))*(1-d.x)*d.y + + imageLoad(text, ci+ivec2(1))*d.x*d.y; +} + + +///PBR functions +vec3 fresnelSchlick(float cosTheta, vec3 F0) +{ + return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0); +} + +float DistributionGGX(vec3 N, vec3 H, float roughness) +{ + float a = roughness*roughness; + float a2 = a*a; + float NdotH = max(dot(N, H), 0.0); + float NdotH2 = NdotH*NdotH; + + float num = a2; + float denom = (NdotH2 * (a2 - 1.0) + 1.0); + denom = PI * denom * denom; + + return num / denom; +} + +float GeometrySchlickGGX(float NdotV, float roughness) +{ + float r = (roughness + 1.0); + float k = (r*r) / 8.0; + + float num = NdotV; + float denom = NdotV * (1.0 - k) + k; + + return num / denom; +} + +float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) +{ + float NdotV = max(dot(N, V), 0.0); + float NdotL = max(dot(N, L), 0.0); + float ggx2 = GeometrySchlickGGX(NdotV, roughness); + float ggx1 = GeometrySchlickGGX(NdotL, roughness); + + return ggx1 * ggx2; +} +///END PBR functions + +float ambient_occlusion(in vec4 pos, in vec4 norm) +{ + + pos.w = iMarbleRad/2; + float dcoef = 0.02/iMarbleRad; + float occlusion_angle = 0; + float integral = 0; + float i_coef = 0; + + //march in the direction of the normal + #pragma unroll + for(int i = 0; i < AMBIENT_MARCHES; i++) + { + norm.w += pos.w; + pos.xyz += pos.w*norm.xyz; + pos.w = DE(pos.xyz); + + i_coef = 1/(dcoef*norm.w+1);//importance + occlusion_angle += i_coef*pos.w/norm.w; + integral += i_coef; + } + + occlusion_angle /= integral; // average weighted by importance + + return pow(occlusion_angle,2); + +} + +vec4 shading(in vec4 pos, in vec4 dir, float fov, float shadow) +{ + //calculate the normal + float error = fov*dir.w; + vec4 norm = calcNormal(pos.xyz, error/2); + norm.xyz = normalize(norm.xyz); + + //optimize color sampling + vec3 cpos = pos.xyz - norm.w*norm.xyz; + cpos = cpos - DE(cpos)*norm.xyz; + cpos = cpos - DE(cpos)*norm.xyz; + vec3 albedo = COL(cpos).xyz; + albedo *= albedo; + + //square because its a surface + float ao = ambient_occlusion(pos, norm); + vec4 ambient_color = vec4(BACKGROUND_COLOR,1)*ao; + + float metallic = PBR_METALLIC; + vec3 F0 = vec3(0.04); + F0 = mix(F0, albedo, metallic); + + //reflectance equation + vec3 Lo = vec3(0.0); + vec3 V = -dir.xyz; + vec3 N = norm.xyz; + + { //ambient occlusion contribution + float roughness = 0.6; + vec3 L = normalize(N); + vec3 H = normalize(V + L); + vec3 radiance = ambient_color.xyz; + + // cook-torrance brdf + float NDF = DistributionGGX(N, H, roughness); + float G = GeometrySmith(N, V, L, roughness); + vec3 F = fresnelSchlick(max(dot(H, V), 0.0), F0); + + vec3 kS = F; + vec3 kD = vec3(1.0) - kS; + kD *= 1.0 - metallic; + + vec3 numerator = NDF * G * F; + float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0); + vec3 specular = numerator / max(denominator, 0.001); + + // add to outgoing radiance Lo + float NdotL = max(dot(N, L), 0.0); + Lo += (kD * albedo / PI + specular) * radiance * NdotL; + } + + if(SHADOWS_ENABLED) + { + { //light contribution + float roughness = PBR_ROUGHNESS; + vec3 L = normalize(LIGHT_DIRECTION); + vec3 H = normalize(V + L); + vec3 radiance = 10*LIGHT_COLOR*shadow*(0.6+0.4*ao); + + // cook-torrance brdf + float NDF = DistributionGGX(N, H, roughness); + float G = GeometrySmith(N, V, L, roughness); + vec3 F = fresnelSchlick(max(dot(H, V), 0.0), F0); + + vec3 kS = F; + vec3 kD = vec3(1.0) - kS; + kD *= 1.0 - metallic; + + vec3 numerator = NDF * G * F; + float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0); + vec3 specular = numerator / max(denominator, 0.001); + + // add to outgoing radiance Lo + float NdotL = max(dot(N, L), 0.0); + Lo += (kD * albedo / PI + specular) * radiance * NdotL; + } + + { //light reflection, GI imitation + float roughness = 0.6; + vec3 L = normalize(-LIGHT_DIRECTION); + vec3 H = normalize(V + L); + vec3 radiance = 6*LIGHT_COLOR*ao*(1-ao); + + // cook-torrance brdf + float NDF = DistributionGGX(N, H, roughness); + float G = GeometrySmith(N, V, L, roughness); + vec3 F = fresnelSchlick(max(dot(H, V), 0.0), F0); + + vec3 kS = F; + vec3 kD = vec3(1.0) - kS; + kD *= 1.0 - metallic; + + vec3 numerator = NDF * G * F; + float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0); + vec3 specular = numerator / max(denominator, 0.001); + + // add to outgoing radiance Lo + float NdotL = max(dot(N, L), 0.0); + Lo += (kD * albedo / PI + specular) * radiance * NdotL; + } + } + + return vec4(Lo,1); +} + + +vec3 HDRmapping(vec3 color, float exposure, float gamma) +{ + // Exposure tone mapping + vec3 mapped = vec3(1.0) - exp(-color * exposure); + // Gamma correction + return pow(mapped, vec3(1.0 / gamma)); +} \ No newline at end of file diff --git a/game_folder/shaders/compute/test.glsl b/game_folder/shaders/compute/test.glsl new file mode 100644 index 0000000..ca76038 --- /dev/null +++ b/game_folder/shaders/compute/test.glsl @@ -0,0 +1,32 @@ +#version 430 +layout(local_size_x = 1, local_size_y = 1) in; +layout(rgba8, binding = 0) uniform image2D img_output; + +//placeholder shader +void main() { + ivec2 pixel_coords = ivec2(gl_GlobalInvocationID.xy); + + vec4 pixel = vec4(0.0, 0.0, 0.0, 1.0); + + float max_x = 5.0; + float max_y = 5.0; + ivec2 dims = imageSize(img_output); // fetch image dimensions + float x = (float(pixel_coords.x * 2 - dims.x) / dims.x); + float y = (float(pixel_coords.y * 2 - dims.y) / dims.y); + vec3 ray_o = vec3(x * max_x, y * max_y, 0.0); + vec3 ray_d = vec3(0.0, 0.0, -1.0); // ortho + + vec3 sphere_c = vec3(0.0, 0.0, -10.0); + float sphere_r = 1.0; + + vec3 omc = ray_o - sphere_c; + float b = dot(ray_d, omc); + float c = dot(omc, omc) - sphere_r * sphere_r; + float bsqmc = b * b - c; + // hit one or both sides + if (bsqmc >= 0.0) { + pixel = vec4(0.4, 0.4, 1.0, 1.0); + } + // output to a specific pixel in the image + imageStore(img_output, pixel_coords, pixel); +} \ No newline at end of file diff --git a/game_folder/shaders/frag.glsl b/game_folder/shaders/frag.glsl new file mode 100644 index 0000000..fa58588 --- /dev/null +++ b/game_folder/shaders/frag.glsl @@ -0,0 +1,9 @@ +#version 120 + +uniform vec2 iResolution; +uniform sampler2D render_texture; + +void main() { + vec2 screen_pos = gl_FragCoord.xy / iResolution.xy; + gl_FragColor = texture2D(render_texture, screen_pos); +} diff --git a/assets/frag.glsl b/game_folder/shaders/frag_old.glsl similarity index 62% rename from assets/frag.glsl rename to game_folder/shaders/frag_old.glsl index 06ec34a..59fbf3c 100644 --- a/assets/frag.glsl +++ b/game_folder/shaders/frag_old.glsl @@ -15,10 +15,9 @@ * along with this program.If not, see . */ #version 120 -#define AMBIENT_OCCLUSION_COLOR_DELTA vec3(0.7) +#define AMBIENT_OCCLUSION_COLOR_DELTA vec3(0.5) #define AMBIENT_OCCLUSION_STRENGTH 0.008 #define ANTIALIASING_SAMPLES 1 -#define BACKGROUND_COLOR vec3(0.6,0.8,1.0) #define COL col_scene #define DE de_scene #define DIFFUSE_ENABLED 0 @@ -26,14 +25,10 @@ #define FILTERING_ENABLE 0 #define FOCAL_DIST 1.73205080757 #define FOG_ENABLED 0 -#define FRACTAL_ITER 16 -#define LIGHT_COLOR vec3(1.0,0.95,0.8) -#define LIGHT_DIRECTION vec3(-0.36, 0.8, 0.48) #define MAX_DIST 30.0 -#define MAX_MARCHES 1000 +#define MAX_MARCHES 512 #define MIN_DIST 1e-5 #define PI 3.14159265358979 -#define SHADOWS_ENABLED 1 #define SHADOW_DARKNESS 0.7 #define SHADOW_SHARPNESS 10.0 #define SPECULAR_HIGHLIGHT 40 @@ -58,7 +53,20 @@ uniform float iFlagScale; uniform vec3 iFlagPos; uniform float iExposure; +uniform vec3 LIGHT_DIRECTION; +uniform bool PBR_ENABLED; +uniform float PBR_METALLIC; +uniform float PBR_ROUGHNESS; +uniform bool SHADOWS_ENABLED; +uniform float CAMERA_SIZE; +uniform int FRACTAL_ITER; +uniform bool REFL_REFR_ENABLED; +uniform int MARBLE_MODE; +uniform vec3 BACKGROUND_COLOR; +uniform vec3 LIGHT_COLOR; + float FOVperPixel; +float s1, c1, s2, c2; vec3 refraction(vec3 rd, vec3 n, float p) { float dot_nd = dot(rd, n); @@ -135,21 +143,22 @@ float de_capsule(vec4 p, float h, float r) { float de_fractal(vec4 p) { for (int i = 0; i < FRACTAL_ITER; ++i) { p.xyz = abs(p.xyz); - rotZ(p, iFracAng1); + rotZ(p, s1, c1); mengerFold(p); - rotX(p, iFracAng2); + rotX(p, s2, c2); p *= iFracScale; p.xyz += iFracShift; } return de_box(p, vec3(6.0)); } + vec4 col_fractal(vec4 p) { vec3 orbit = vec3(0.0); for (int i = 0; i < FRACTAL_ITER; ++i) { p.xyz = abs(p.xyz); - rotZ(p, iFracAng1); + rotZ(p, s1, c1); mengerFold(p); - rotX(p, iFracAng2); + rotX(p, s2, c2); p *= iFracScale; p.xyz += iFracShift; orbit = max(orbit, p.xyz*iFracCol); @@ -160,7 +169,12 @@ float de_marble(vec4 p) { return de_sphere(p - vec4(iMarblePos, 0), iMarbleRad); } vec4 col_marble(vec4 p) { - return vec4(0, 0, 0, de_sphere(p - vec4(iMarblePos, 0), iMarbleRad)); + vec4 col = vec4(0, 0, 0, de_sphere(p - vec4(iMarblePos, 0), iMarbleRad)); + if(!REFL_REFR_ENABLED) + { + col.xyz = vec3(0.7,0.7,0.7); + } + return vec4(col.x, col.y, col.z, de_sphere(p - vec4(iMarblePos, 0), iMarbleRad)); } float de_flag(vec4 p) { vec3 f_pos = iFlagPos + vec3(1.5, 4, 0)*iFlagScale; @@ -199,7 +213,7 @@ vec4 col_scene(vec4 p) { // Main code //########################################## -//A faster formula to find the gradient/normal direction of the DE(the w component is the average DE) +//A faster formula to find the gradient/normal direction of the DE //credit to http://www.iquilezles.org/www/articles/normalsSDF/normalsSDF.htm vec3 calcNormal(vec4 p, float dx) { const vec3 k = vec3(1,-1,0); @@ -220,35 +234,73 @@ vec4 smoothColor(vec4 p, vec3 s1, vec3 s2, float dx) { vec4 ray_march(inout vec4 p, vec4 ray, float sharpness) { //March the ray float d = DE(p); - if (d < 0.0 && sharpness == 1.0) { - vec3 v; - if (abs(iMarblePos.x) >= 999.0f) { - v = (-20.0 * iMarbleRad) * iMat[2].xyz; - } else { - v = iMarblePos.xyz - iMat[3].xyz; - } - d = dot(v, v) / dot(v, ray.xyz) - iMarbleRad; - } float s = 0.0; float td = 0.0; float min_d = 1.0; for (; s < MAX_MARCHES; s += 1.0) { //if the distance from the surface is less than the distance per pixel we stop float min_dist = max(FOVperPixel*td, MIN_DIST); - if (d < min_dist) { - s += d / min_dist; - break; - } else if (td > MAX_DIST) { + if(d < -min_dist || td > MAX_DIST) + { break; } - td += d; - p += ray * d; + else if (d < min_dist) + { + s += d / min_dist; + break; + } + td += d+min_dist*0.1; + p += ray * (d+min_dist*0.1); min_d = min(min_d, sharpness * d / td); d = DE(p); } return vec4(d, s, td, min_d); } + +///PBR functions +vec3 fresnelSchlick(float cosTheta, vec3 F0) +{ + return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0); +} + +float DistributionGGX(vec3 N, vec3 H, float roughness) +{ + float a = roughness*roughness; + float a2 = a*a; + float NdotH = max(dot(N, H), 0.0); + float NdotH2 = NdotH*NdotH; + + float num = a2; + float denom = (NdotH2 * (a2 - 1.0) + 1.0); + denom = PI * denom * denom; + + return num / denom; +} + +float GeometrySchlickGGX(float NdotV, float roughness) +{ + float r = (roughness + 1.0); + float k = (r*r) / 8.0; + + float num = NdotV; + float denom = NdotV * (1.0 - k) + k; + + return num / denom; +} + +float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) +{ + float NdotV = max(dot(N, V), 0.0); + float NdotL = max(dot(N, L), 0.0); + float ggx2 = GeometrySchlickGGX(NdotV, roughness); + float ggx1 = GeometrySchlickGGX(NdotL, roughness); + + return ggx1 * ggx2; +} +///END PBR functions + + vec4 scene(inout vec4 p, inout vec4 ray, float vignette) { //Trace the ray vec4 d_s_td_m = ray_march(p, ray, 1.0f); @@ -264,7 +316,7 @@ vec4 scene(inout vec4 p, inout vec4 ray, float vignette) { vec3 n = calcNormal(p, min_dist*0.5); //find closest surface point, without this we get weird coloring artifacts - p.xyz -= n*d; + if(s>0) p.xyz -= n*d; //Get coloring #if FILTERING_ENABLE @@ -279,44 +331,111 @@ vec4 scene(inout vec4 p, inout vec4 ray, float vignette) { #endif col.w = orig_col.w; - //Get if this point is in shadow - float k = 1.0; - #if SHADOWS_ENABLED - vec4 light_pt = p; - light_pt.xyz += n * MIN_DIST * 100; - vec4 rm = ray_march(light_pt, vec4(LIGHT_DIRECTION, 0.0), SHADOW_SHARPNESS); - k = rm.w * min(rm.z, 1.0); - #endif - - //Get specular - #if SPECULAR_HIGHLIGHT > 0 - vec3 reflected = ray.xyz - 2.0*dot(ray.xyz, n) * n; - float specular = max(dot(reflected, LIGHT_DIRECTION), 0.0); - specular = pow(specular, SPECULAR_HIGHLIGHT); - col.xyz += specular * LIGHT_COLOR * (k * SPECULAR_MULT); - #endif - - //Get diffuse lighting - #if DIFFUSE_ENHANCED_ENABLED - k = min(k, SHADOW_DARKNESS * 0.5 * (dot(n, LIGHT_DIRECTION) - 1.0) + 1.0); - #elif DIFFUSE_ENABLED - k = min(k, dot(n, LIGHT_DIRECTION)); - #endif - - //Don't make shadows entirely dark - k = max(k, 1.0 - SHADOW_DARKNESS); - col.xyz += orig_col.xyz * LIGHT_COLOR * k; + if(PBR_ENABLED) + { + vec3 albedo = orig_col.xyz; + float metallic = PBR_METALLIC; + float roughness = PBR_ROUGHNESS; + + //reflectance equation + vec3 Lo = vec3(0.0); + vec3 V = -ray.xyz; + vec3 N = n; + + vec3 F0 = vec3(0.04); + F0 = mix(F0, albedo, metallic); + float attenuation = 1; + + if(SHADOWS_ENABLED) + { + //saves 1 march + if(dot(n, LIGHT_DIRECTION)>0) + { + vec4 light_pt = p; + light_pt.xyz += n * MIN_DIST * 100; + vec4 rm = ray_march(light_pt, vec4(LIGHT_DIRECTION, 0.0), SHADOW_SHARPNESS); + attenuation *= rm.w * min(rm.z, 1.0); + } + else + { + attenuation = 0; + } + } + + + float ao0 = 1.f/(2.5*AMBIENT_OCCLUSION_STRENGTH*s + 1); + + + vec3 L = normalize(LIGHT_DIRECTION); + vec3 H = normalize(V + L); + vec3 radiance = 3.5*LIGHT_COLOR * attenuation; + + // cook-torrance brdf + float NDF = DistributionGGX(N, H, roughness); + float G = GeometrySmith(N, V, L, roughness); + vec3 F = fresnelSchlick(max(dot(H, V), 0.0), F0); + + vec3 kS = F; + vec3 kD = vec3(1.0) - kS; + kD *= 1.0 - metallic; + + vec3 numerator = NDF * G * F; + float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0); + vec3 specular = numerator / max(denominator, 0.001); + + // add to outgoing radiance Lo + float NdotL = max(dot(N, L), 0.0); + Lo += (kD * albedo / PI + specular) * radiance * NdotL; + + //background AO + vec3 ambient = 0.9*AMBIENT_OCCLUSION_COLOR_DELTA * albedo * (0.7*BACKGROUND_COLOR+0.3*LIGHT_COLOR) * ao0; + + col.xyz = clamp(Lo+ambient,0,1); + + } + else + { + //Get if this point is in shadow + float k = 1.0; + if(SHADOWS_ENABLED) + { + vec4 light_pt = p; + light_pt.xyz += n * MIN_DIST * 100; + vec4 rm = ray_march(light_pt, vec4(LIGHT_DIRECTION, 0.0), SHADOW_SHARPNESS); + k = rm.w * min(rm.z, 1.0); + } - //Add small amount of ambient occlusion - float a = 1.0 / (1.0 + s * AMBIENT_OCCLUSION_STRENGTH); - col.xyz += (1.0 - a) * AMBIENT_OCCLUSION_COLOR_DELTA; + //Get specular + #if SPECULAR_HIGHLIGHT > 0 + vec3 reflected = ray.xyz - 2.0*dot(ray.xyz, n) * n; + float specular = max(dot(reflected, LIGHT_DIRECTION), 0.0); + specular = pow(specular, SPECULAR_HIGHLIGHT); + col.xyz += specular * LIGHT_COLOR * (k * SPECULAR_MULT); + #endif + + //Get diffuse lighting + #if DIFFUSE_ENHANCED_ENABLED + k = min(k, SHADOW_DARKNESS * 0.5 * (dot(n, LIGHT_DIRECTION) - 1.0) + 1.0); + #elif DIFFUSE_ENABLED + k = min(k, dot(n, LIGHT_DIRECTION)); + #endif + + //Don't make shadows entirely dark + k = max(k, 1.0 - SHADOW_DARKNESS); + col.xyz += orig_col.xyz * LIGHT_COLOR * k; + + //Add small amount of ambient occlusion + float a = 1.0 / (1.0 + s * AMBIENT_OCCLUSION_STRENGTH); + col.xyz += (1.0 - a) * AMBIENT_OCCLUSION_COLOR_DELTA; + + } //Add fog effects #if FOG_ENABLED - a = td / MAX_DIST; - col.xyz = (1.0 - a) * col.xyz + a * BACKGROUND_COLOR; + float b = td / MAX_DIST; + col.xyz = (1.0 - b) * col.xyz + b * BACKGROUND_COLOR; #endif - + //Return normal through ray ray = vec4(n, 0.0); } else { @@ -338,7 +457,11 @@ vec4 scene(inout vec4 p, inout vec4 ray, float vignette) { void main() { //Calculate the view angle per pixel, with a minimum quality level FOVperPixel = 1.0 / max(iResolution.x, 900.0); - + s1 = sin(iFracAng1); + c1 = cos(iFracAng1); + s2 = sin(iFracAng2); + c2 = cos(iFracAng2); + vec3 col = vec3(0.0); for (int i = 0; i < ANTIALIASING_SAMPLES; ++i) { for (int j = 0; j < ANTIALIASING_SAMPLES; ++j) { @@ -351,7 +474,7 @@ void main() { //Convert screen coordinate to 3d ray vec4 ray = iMat * normalize(vec4(uv.x, uv.y, -FOCAL_DIST, 0.0)); - vec4 p = iMat[3]; + vec4 p = iMat * vec4(CAMERA_SIZE*uv.x, CAMERA_SIZE*uv.y, 0, 1); //Reflect light if needed float vignette = 1.0 - VIGNETTE_STRENGTH * length(screen_pos - 0.5); @@ -359,7 +482,8 @@ void main() { vec4 col_r = scene(p, ray, vignette); //Check if this is the glass marble - if (col_r.w > 0.5) { + if (col_r.w > 0.5 && REFL_REFR_ENABLED) + { //Calculate refraction vec3 n = normalize(iMarblePos - p.xyz); vec3 q = refraction(r, n, 1.0 / 1.5); @@ -370,15 +494,42 @@ void main() { vec4 r_temp = vec4(q, 0.0); vec3 refr = scene(p_temp, r_temp, 0.8).xyz; - //Calculate refraction + //Calculate reflection n = normalize(p.xyz - iMarblePos); q = r - n*(2*dot(r,n)); p_temp = vec4(p.xyz + n * (MIN_DIST * 10), 1.0); r_temp = vec4(q, 0.0); vec3 refl = scene(p_temp, r_temp, 0.8).xyz; - + + //PBR reflections/refractions + vec3 V = -r; + vec3 N = n; + //Combine for final marble color - col += refr * 0.6f + refl * 0.4f + col_r.xyz; + if(MARBLE_MODE == 0) + { + //glass + vec3 F0 = vec3(0.03); + vec3 L = normalize(q.xyz); + vec3 H = normalize(V + L); + vec3 F = fresnelSchlick(max(dot(H, V), 0.0), F0); + + vec3 kS = F; + vec3 kD = vec3(1.0) - kS; + col += kS*refl + kD*refr + col_r.xyz; + } + else + { + //metal + vec3 F0 = vec3(0.6); + vec3 L = normalize(q.xyz); + vec3 H = normalize(V + L); + vec3 F = fresnelSchlick(max(dot(H, V), 0.0), F0); + + vec3 kS = F; + vec3 kD = vec3(1.0) - kS; + col += kS*refl + col_r.xyz; + } } else { col += col_r.xyz; } diff --git a/assets/vert.glsl b/game_folder/shaders/vert.glsl similarity index 100% rename from assets/vert.glsl rename to game_folder/shaders/vert.glsl diff --git a/assets/bounce1.wav b/game_folder/sound/fx/bounce1.wav similarity index 100% rename from assets/bounce1.wav rename to game_folder/sound/fx/bounce1.wav diff --git a/assets/bounce2.wav b/game_folder/sound/fx/bounce2.wav similarity index 100% rename from assets/bounce2.wav rename to game_folder/sound/fx/bounce2.wav diff --git a/assets/bounce3.wav b/game_folder/sound/fx/bounce3.wav similarity index 100% rename from assets/bounce3.wav rename to game_folder/sound/fx/bounce3.wav diff --git a/assets/count_down.wav b/game_folder/sound/fx/count_down.wav similarity index 100% rename from assets/count_down.wav rename to game_folder/sound/fx/count_down.wav diff --git a/assets/count_go.wav b/game_folder/sound/fx/count_go.wav similarity index 100% rename from assets/count_go.wav rename to game_folder/sound/fx/count_go.wav diff --git a/assets/goal.wav b/game_folder/sound/fx/goal.wav similarity index 100% rename from assets/goal.wav rename to game_folder/sound/fx/goal.wav diff --git a/assets/menu_click.wav b/game_folder/sound/fx/menu_click.wav similarity index 100% rename from assets/menu_click.wav rename to game_folder/sound/fx/menu_click.wav diff --git a/assets/menu_hover.wav b/game_folder/sound/fx/menu_hover.wav similarity index 100% rename from assets/menu_hover.wav rename to game_folder/sound/fx/menu_hover.wav diff --git a/assets/shatter.wav b/game_folder/sound/fx/shatter.wav similarity index 100% rename from assets/shatter.wav rename to game_folder/sound/fx/shatter.wav diff --git a/game_folder/sound/music/LICENSE.txt b/game_folder/sound/music/LICENSE.txt new file mode 100644 index 0000000..9f4b50a --- /dev/null +++ b/game_folder/sound/music/LICENSE.txt @@ -0,0 +1,20 @@ +This font is licensed under a Creative Commons Attribution Share Alike license +(http://creativecommons.org/licenses/by-sa/3.0/). + +Attribution-Share Alike 3.0 Unported + +You are free: + +to Share — to copy, distribute and transmit the work +to Remix — to adapt the work + +Under the following conditions: + +Attribution. You must attribute the work in the manner specified by the author or licensor (but not in any way that suggests that they endorse you or your use of the work). + +Share Alike. If you alter, transform, or build upon this work, you may distribute the resulting work only under the same, similar or a compatible license. + +For any reuse or distribution, you must make clear to others the license terms of this work. The best way to do this is with a link to this web page. + +Any of the above conditions can be waived if you get permission from the copyright holder. +Nothing in this license impairs or restricts the author's moral rights. \ No newline at end of file diff --git a/assets/credits.ogg b/game_folder/sound/music/credits.ogg similarity index 100% rename from assets/credits.ogg rename to game_folder/sound/music/credits.ogg diff --git a/assets/level1.ogg b/game_folder/sound/music/level1.ogg similarity index 100% rename from assets/level1.ogg rename to game_folder/sound/music/level1.ogg diff --git a/assets/level2.ogg b/game_folder/sound/music/level2.ogg similarity index 100% rename from assets/level2.ogg rename to game_folder/sound/music/level2.ogg diff --git a/assets/level3.ogg b/game_folder/sound/music/level3.ogg similarity index 100% rename from assets/level3.ogg rename to game_folder/sound/music/level3.ogg diff --git a/assets/level4.ogg b/game_folder/sound/music/level4.ogg similarity index 100% rename from assets/level4.ogg rename to game_folder/sound/music/level4.ogg diff --git a/assets/menu.ogg b/game_folder/sound/music/menu.ogg similarity index 100% rename from assets/menu.ogg rename to game_folder/sound/music/menu.ogg diff --git a/macOS/MarbleMarcher.icns b/macOS/MarbleMarcher.icns new file mode 100644 index 0000000..88cd645 Binary files /dev/null and b/macOS/MarbleMarcher.icns differ diff --git a/macOS/MarbleMarcher.iconset/icon_128x128.png b/macOS/MarbleMarcher.iconset/icon_128x128.png new file mode 100644 index 0000000..270eb94 Binary files /dev/null and b/macOS/MarbleMarcher.iconset/icon_128x128.png differ diff --git a/macOS/MarbleMarcher.iconset/icon_128x128@2x.png b/macOS/MarbleMarcher.iconset/icon_128x128@2x.png new file mode 100644 index 0000000..8c50200 Binary files /dev/null and b/macOS/MarbleMarcher.iconset/icon_128x128@2x.png differ diff --git a/macOS/MarbleMarcher.iconset/icon_16x16.png b/macOS/MarbleMarcher.iconset/icon_16x16.png new file mode 100644 index 0000000..1e4b576 Binary files /dev/null and b/macOS/MarbleMarcher.iconset/icon_16x16.png differ diff --git a/macOS/MarbleMarcher.iconset/icon_16x16@2x.png b/macOS/MarbleMarcher.iconset/icon_16x16@2x.png new file mode 100644 index 0000000..585911f Binary files /dev/null and b/macOS/MarbleMarcher.iconset/icon_16x16@2x.png differ diff --git a/macOS/MarbleMarcher.iconset/icon_256x256.png b/macOS/MarbleMarcher.iconset/icon_256x256.png new file mode 100644 index 0000000..8c50200 Binary files /dev/null and b/macOS/MarbleMarcher.iconset/icon_256x256.png differ diff --git a/macOS/MarbleMarcher.iconset/icon_256x256@2x.png b/macOS/MarbleMarcher.iconset/icon_256x256@2x.png new file mode 100644 index 0000000..e74030c Binary files /dev/null and b/macOS/MarbleMarcher.iconset/icon_256x256@2x.png differ diff --git a/macOS/MarbleMarcher.iconset/icon_32x32.png b/macOS/MarbleMarcher.iconset/icon_32x32.png new file mode 100644 index 0000000..585911f Binary files /dev/null and b/macOS/MarbleMarcher.iconset/icon_32x32.png differ diff --git a/macOS/MarbleMarcher.iconset/icon_32x32@2x.png b/macOS/MarbleMarcher.iconset/icon_32x32@2x.png new file mode 100644 index 0000000..b50f803 Binary files /dev/null and b/macOS/MarbleMarcher.iconset/icon_32x32@2x.png differ diff --git a/macOS/MarbleMarcher.iconset/icon_512x512.png b/macOS/MarbleMarcher.iconset/icon_512x512.png new file mode 100644 index 0000000..e74030c Binary files /dev/null and b/macOS/MarbleMarcher.iconset/icon_512x512.png differ diff --git a/macOS/MarbleMarcher.iconset/icon_512x512@2x.png b/macOS/MarbleMarcher.iconset/icon_512x512@2x.png new file mode 100644 index 0000000..5931dc4 Binary files /dev/null and b/macOS/MarbleMarcher.iconset/icon_512x512@2x.png differ diff --git a/macOS/sfml_license.md b/macOS/sfml_license.md new file mode 100644 index 0000000..0e6d7b1 --- /dev/null +++ b/macOS/sfml_license.md @@ -0,0 +1,20 @@ +# SFML + +SFML - Copyright (C) 2007-2018 Laurent Gomila - laurent@sfml-dev.org + +This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + +## External libraries used by SFML + + * _OpenAL-Soft_ is under the LGPL license + * _stb_image_ and _stb_image_write_ are public domain + * _freetype_ is under the FreeType license or the GPL license + * _libogg_ is under the BSD license + * _libvorbis_ is under the BSD license + * _libflac_ is under the BSD license diff --git a/macOS/template/Contents/Info.plist b/macOS/template/Contents/Info.plist new file mode 100644 index 0000000..a7d3b42 --- /dev/null +++ b/macOS/template/Contents/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleGetInfoString + MarbleMarcher + CFBundleExecutable + MarbleMarcher.sh + CFBundleIdentifier + ca.thenetworknerds + CFBundleName + MarbleMarcher + CFBundleIconFile + MarbleMarcher.icns + CFBundleShortVersionString + 1.1.0 + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + APPL + IFMajorVersion + 0 + IFMinorVersion + 1 + + diff --git a/macOS/template/Contents/MacOS/MarbleMarcher.sh b/macOS/template/Contents/MacOS/MarbleMarcher.sh new file mode 100755 index 0000000..a14b770 --- /dev/null +++ b/macOS/template/Contents/MacOS/MarbleMarcher.sh @@ -0,0 +1,6 @@ +#! /bin/bash + +cd "${0%/*}" +cd ../Resources +echo `pwd` +./MarbleMarcher "$@" diff --git a/macOS/template/Contents/Resources/sfml-audio b/macOS/template/Contents/Resources/sfml-audio new file mode 100755 index 0000000..0d8bdf1 Binary files /dev/null and b/macOS/template/Contents/Resources/sfml-audio differ diff --git a/macOS/template/Contents/Resources/sfml-graphics b/macOS/template/Contents/Resources/sfml-graphics new file mode 100755 index 0000000..4c9fb10 Binary files /dev/null and b/macOS/template/Contents/Resources/sfml-graphics differ diff --git a/macOS/template/Contents/Resources/sfml-system b/macOS/template/Contents/Resources/sfml-system new file mode 100755 index 0000000..f5fd6c3 Binary files /dev/null and b/macOS/template/Contents/Resources/sfml-system differ diff --git a/macOS/template/Contents/Resources/sfml-window b/macOS/template/Contents/Resources/sfml-window new file mode 100755 index 0000000..6b13886 Binary files /dev/null and b/macOS/template/Contents/Resources/sfml-window differ diff --git a/macOSBuild.sh b/macOSBuild.sh new file mode 100755 index 0000000..16fe27d --- /dev/null +++ b/macOSBuild.sh @@ -0,0 +1,26 @@ +#! /bin/bash + +mkdir -p build && cd build +cmake .. +cd .. +cmake --build build + +rm -R -f MarbleMarcher.app +cp -R macOS/template MarbleMarcher.app +cp macOS/MarbleMarcher.icns MarbleMarcher.app/Contents/Resources/ +cp -R assets MarbleMarcher.app/Contents/Resources/ +mv build/MarbleMarcher MarbleMarcher.app/Contents/Resources/ +cp -R game_folder/* MarbleMarcher.app/Contents/Resources +cp build/AntTweakBar.dll MarbleMarcher.app/Contents/Resources/ +cp -R build/assets MarbleMarcher.app/Contents/Resources +cp -R build/images MarbleMarcher.app/Contents/Resources +cp -R build/screenshots MarbleMarcher.app/Contents/Resources +cp -R build/shaders MarbleMarcher.app/Contents/Resources +cp -R build/sound MarbleMarcher.app/Contents/Resources + + +cd MarbleMarcher.app/Contents/Resources +install_name_tool -change @rpath/sfml-graphics.framework/Versions/2.5.1/sfml-graphics @executable_path/sfml-graphics MarbleMarcher +install_name_tool -change @rpath/sfml-audio.framework/Versions/2.5.1/sfml-audio @executable_path/sfml-audio MarbleMarcher +install_name_tool -change @rpath/sfml-window.framework/Versions/2.5.1/sfml-window @executable_path/sfml-window MarbleMarcher +install_name_tool -change @rpath/sfml-system.framework/Versions/2.5.1/sfml-system @executable_path/sfml-system MarbleMarcher diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 44ad224..7bdac62 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,14 +1,41 @@ add_library(MarbleMarcherSources Level.cpp Level.h + Overlays.cpp Overlays.h + Res.h + Scene.cpp Scene.h + Scores.cpp Scores.h + SelectRes.cpp SelectRes.h + + Interface.h + Interface.cpp + + Localization.h + Localization.cpp + Settings.h + + Gamemodes.h + Gamemodes.cpp + + Renderer.h + Renderer.cpp + + Shaders.h + Shaders.cpp + + Camera.h + Camera.cpp + + ExprParser.h + ExprParser.cpp ) diff --git a/src/Camera.cpp b/src/Camera.cpp new file mode 100644 index 0000000..83d196b --- /dev/null +++ b/src/Camera.cpp @@ -0,0 +1,228 @@ +#include + +void Camera::SetPosition(vec3 pos) +{ + position = pos; +} + +void Camera::RotateLR(float a) +{ + //rotate around UP direction + quat rotation = angleAxis(degrees(a), GetDirY()); + dirx = rotation*dirx; + dirz = rotation*dirz; +} + +void Camera::RotateUD(float a) +{ + //rotate around sideways direction + quat rotation = angleAxis(degrees(a), GetDirZ()); + dirx = rotation*dirx; + diry = rotation*diry; +} + +void Camera::RotateRoll(float a) +{ + //rotate around look direction + quat rotation = angleAxis(degrees(a), GetDirX()); + diry = rotation*diry; + dirz = rotation*dirz; +} + +void Camera::SetRotation(float a, float b, float c) +{ + vec3 Euler(a, b, c); + quat rotation(Euler); + dirx = rotation*quat(0, 1, 0, 0); + diry = rotation*quat(0, 0, 1, 0); + dirz = rotation*quat(0, 0, 0, 1); +} + +//k = 0 - absolutely sharp movements, 1 - infinitely smooth movements, best ~0.1 +void Camera::SetSmoothness(float k) +{ + smooth = k; +} + +void Camera::Shift(vec3 dx) +{ + position += dx; +} + +void Camera::Move(vec3 dv) +{ + velocity += dv.x*GetDirX() + dv.y*GetDirY() + dv.z*GetDirZ(); +} + +void Camera::RotateX(float a) +{ + alpha += a; +} + +void Camera::RotateY(float a) +{ + beta += a; +} + +void Camera::Roll(float a) +{ + gamma += a; +} + +void Camera::Update(float dt) +{ + //interpolate + dt = (dt*smooth + 1 - smooth); + + Shift(velocity*dt); + RotateLR(alpha*dt); + RotateUD(beta*dt); + RotateRoll(gamma*dt); + + //exponential decay of our velocities, if smooth = 1 - they won't decay at all + velocity -= (1 - smooth)*velocity*dt; + alpha -= (1 - smooth)*alpha*dt; + beta -= (1 - smooth)*beta*dt; + gamma -= (1 - smooth)*gamma*dt; +} + +void Camera::SetDirY(vec3 dir) +{ + diry.x = dir.x; + diry.y = dir.y; + diry.z = dir.z; +} + +void Camera::SetDirZ(vec3 dir) +{ + dirz.x = dir.x; + dirz.y = dir.y; + dirz.z = dir.z; +} + +vec3 Camera::GetPosition() +{ + if (cur_mode == ThirdPerson) + { + return position - radius*GetDirX(); + } + + return position; +} + +vec3 Camera::GetDirX() +{ + return normalize(vec3(dirx.x, dirx.y, dirx.z)); +} + +vec3 Camera::GetDirY() +{ + return normalize(vec3(diry.x, diry.y, diry.z)); +} + +vec3 Camera::GetDirZ() +{ + return normalize(vec3(dirz.x, dirz.y, dirz.z)); +} + +vec4 Camera::GetCameraProperties() +{ + return vec4(FOV, exposure, focus, bokeh); +} + +vec4 Camera::GetCameraProperties2() +{ + return vec4(size, mblur, speckle, 0); +} + +gl_camera Camera::GetGLdata() +{ + gl_camera cam; + cam.position = GetPosition(); + cam.dirx = GetDirX(); + cam.diry = GetDirY(); + cam.dirz = GetDirZ(); + + cam.aspect_ratio = aspect_ratio; + cam.FOV = tan(FOV*3.14159 / 360); + cam.focus = focus; + cam.bokeh = bokeh; + cam.exposure = exposure; + cam.mblur = mblur; + cam.speckle = speckle; + cam.size = size; + + //data for the renderer + cam.stepN = 0; + cam.step = 0; + cam.resolution = resolution; //not a property of the camera, but of the renderer + + return cam; +} + +void Camera::SetMode(CameraMode mode) +{ + cur_mode = mode; +} + +void Camera::SetRadius(float r) +{ + radius = r; +} + +void Camera::SetFOV(float fov) +{ + FOV = fov; +} + +void Camera::SetAspectRatio(float asr) +{ + aspect_ratio = asr; +} + +void Camera::SetBokehRadius(float b) +{ + bokeh = b; +} + +void Camera::SetMotionBlur(float mb) +{ + mblur = mb; +} + +void Camera::SetExposure(float e) +{ + exposure = e; +} + +void Camera::SetFocus(float f) +{ + focus = f; +} + +void Camera::SetSpeckleRadius(float s) +{ + speckle = s; +} + +void Camera::SetCameraSize(float s) +{ + size = s; +} + +void Camera::SetResolution(vec2 res) +{ + resolution = res; +} + +void Camera::LookAt(vec3 pos) +{ + //todo +} + +void Camera::SetDirX(vec3 dir) +{ + dirx.x = dir.x; + dirx.y = dir.y; + dirx.z = dir.z; +} diff --git a/src/Camera.h b/src/Camera.h new file mode 100644 index 0000000..862a14a --- /dev/null +++ b/src/Camera.h @@ -0,0 +1,120 @@ +#pragma once + +#include +#include + +using namespace glm; + +typedef struct +{ + vec3 position; + vec3 dirx; + vec3 diry; + vec3 dirz; + vec2 resolution; + float aspect_ratio; + float FOV; + float focus; + float bokeh; + float exposure; + float mblur; + float speckle; + float size; + int stepN; + int step; +} gl_camera; + +class Camera +{ +public: + enum CameraMode + { + Free, + ThirdPerson, + FirstPerson + }; + + Camera(): alpha(0), beta(0), gamma(0), cur_mode(Free), radius(1.f), + smooth(0.3f), FOV(60.f), focus(1e10), bokeh(0), mblur(0.05), speckle(10), size(0), exposure(1.5f) + { + //camera directions + dirx = quat(0, 1, 0, 0); + diry = quat(0, 0, 1, 0); + dirz = quat(0, 0, 0, 1); + //camera inertial velocity + velocity = vec3(0); + position = vec3(0); + } + + void SetPosition(vec3 pos); + void RotateLR(float a); + void RotateUD(float a); + void RotateRoll(float a); + void SetRotation(float a, float b, float c); + void SetSmoothness(float k); + void SetMode(CameraMode mode); + void SetRadius(float r); + void SetFOV(float fov); + void SetAspectRatio(float asr); + void SetBokehRadius(float b); + void SetMotionBlur(float mb); + void SetExposure(float e); + void SetFocus(float f); + void SetSpeckleRadius(float s); + void SetCameraSize(float s); + void SetResolution(vec2 res); + void LookAt(vec3 pos); + void SetDirX(vec3 dir); + void SetDirY(vec3 dir); + void SetDirZ(vec3 dir); + + vec3 GetPosition(); + vec3 GetDirX(); + vec3 GetDirY(); + vec3 GetDirZ(); + vec4 GetCameraProperties(); + vec4 GetCameraProperties2(); + + gl_camera GetGLdata(); + + void Shift(vec3 dx); + void Move(vec3 dv); + void RotateX(float a); + void RotateY(float a); + void Roll(float a); + + void Update(float dt); + + //exposure, motion blur and speckle radius + float exposure, mblur, speckle; +private: + CameraMode cur_mode; + + //camera position + vec3 position; + + // camera directions + quat dirx, diry, dirz; + + //camera inertial speed + vec3 velocity; + + vec2 resolution; + + //camera directional inertial angular velocities + float alpha, beta, gamma, radv; + + //field of view, focus distance, bokeh radius + float FOV, focus, bokeh; + + //camera size + float size; + + // 3 person view radius + float radius; + + //movement smoothness parameter + float smooth; + + float aspect_ratio; +}; \ No newline at end of file diff --git a/src/ExprParser.cpp b/src/ExprParser.cpp new file mode 100644 index 0000000..45c0a0f --- /dev/null +++ b/src/ExprParser.cpp @@ -0,0 +1,530 @@ +#include + +bool replace(std::string& str, const std::string& from, const std::string& to) { + + int pos = str.find(from,0); + if (pos == std::string::npos) + return false; + while (pos != std::string::npos) + { + str.replace(pos, from.length(), to); + pos = str.find(from,pos+1); + } + return true; +} + + +/* +* checks if the given char c is a minus +*/ +bool isMinus(const char c) +{ + if (c == 0) return 0; + return c == '-'; +} + +// Get variable type and number +int ExprParser::AddVariable() +{ + std::string tokenn(token); + + if(variable_ids.count(tokenn) == 0) + { + variables.push_back(tokenn); + variable_ids[tokenn] = variables.size(); + } + + return variable_ids[tokenn]; +} + +/* +* checks if the given char c is whitespace +* whitespace when space chr(32) or tab chr(9) +*/ +bool isWhiteSpace(const char c) +{ + if (c == 0) return 0; + return c == 32 || c == 9; // space or tab +} + +/* +* checks if the given char c is a delimiter +* minus is checked apart, can be unary minus +*/ +bool isdelimiter(const char c) +{ + if (c == 0) return 0; + return strchr("&|<>=+/*%^!", c) != 0; +} + +/* +* checks if the given char c is NO delimiter +*/ +bool isNotdelimiter(const char c) +{ + if (c == 0) return 0; + return strchr("&|<>=+-/*%^!()", c) != 0; +} + +/* +* checks if the given char c is a letter or undersquare +*/ +bool isAlpha(const char c) +{ + if (c == 0) return 0; + return strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ_", toupper(c)) != 0; +} + +/* +* checks if the given char c is a digit or dot +*/ +bool isDigitDot(const char c) +{ + if (c == 0) return 0; + return strchr("0123456789.", c) != 0; +} + +/* +* checks if the given char c is a digit +*/ +bool isDigit(const char c) +{ + if (c == 0) return 0; + return strchr("0123456789", c) != 0; +} + + +/** +* Get next token in the current string expr. +* Uses the Parser data expr, e, token, t, token_type and err +*/ +void ExprParser::getToken() +{ + token_type = NOTHING; + char* t; // points to a character in token + t = token; // let t point to the first character in token + *t = '\0'; // set token empty + + + + // skip over whitespaces + while (*e == ' ' || *e == '\t') // space or tab + { + e++; + } + + // check for end of expression + if (*e == '\0') + { + // token is still empty + token_type = delimiter; + return; + } + + // check for minus + if (*e == '-') + { + token_type = delimiter; + *t = *e; + e++; + t++; + *t = '\0'; // add a null character at the end of token + return; + } + + // check for parentheses + if (*e == '(' || *e == ')') + { + token_type = delimiter; + *t = *e; + e++; + t++; + *t = '\0'; + return; + } + + // check for operators (delimiters) + if (isdelimiter(*e)) + { + token_type = delimiter; + while (isdelimiter(*e)) + { + *t = *e; + e++; + t++; + } + *t = '\0'; // add a null character at the end of token + return; + } + + // check for a value + if (isDigitDot(*e)) + { + token_type = NUMBER; + while (isDigitDot(*e)) + { + *t = *e; + e++; + t++; + } + + // check for scientific notation like "2.3e-4" or "1.23e50" + if (toupper(*e) == 'E') + { + *t = *e; + e++; + t++; + + if (*e == '+' || *e == '-') + { + *t = *e; + e++; + t++; + } + + while (isDigit(*e)) + { + *t = *e; + e++; + t++; + } + } + + *t = '\0'; + return; + } + + // check for variables or functions + if (isAlpha(*e)) + { + while (isAlpha(*e) || isDigit(*e)) + //while (isNotdelimiter(*e)) + { + *t = *e; + e++; + t++; + } + *t = '\0'; // add a null character at the end of token + + // check if this is a variable or a function. + // a function has a parentesis '(' open after the name + char* e2 = NULL; + e2 = e; + + // skip whitespaces + while (*e2 == ' ' || *e2 == '\t') // space or tab + { + e2++; + } + + if (*e2 == '(') + { + token_type = FUNCTION; + } + else + { + token_type = VARIABLE; + } + return; + } + + // something unknown is found, wrong characters -> a syntax error + token_type = UNKNOWN; + while (*e != '\0') + { + *t = *e; + e++; + t++; + } + *t = '\0'; + + return; +} + +void ExprParser::clear() +{ + op_stack.clear(); + IPN.clear(); + variables.clear(); + variable_ids.clear(); +} + +//convert number token to float +float ExprParser::token2float() +{ + std::string numb(token); + + std::istringstream io(numb); + float number; + io >> number; + return number; +} + +int ExprParser::getOperatorPrecedenceLevel(int op) +{ + if (op == PLUS || op == MINUS) + return 1; + else if (op == MULTIPLY || op == DIVIDE || op == INV) + return 2; + else if (op == POW) + return 3; + else if (op == FACTORIAL) + return 4; + else if (op == ABS || EXP == op || SIGN == op || SQRT == op + || LOG == op || SIN == op || COS == op || TANH == op || THETA == op) + return 5; + else if (op == LBRACK) + return 6; +} + +//get operator id from the token +int ExprParser::token2operation() +{ + std::string fnc(token); + std::transform(fnc.begin(), fnc.end(), fnc.begin(), ::toupper); + if (fnc == "+") + return PLUS; + else if (fnc == "-") + return MINUS; + else if (fnc == "*") + return MULTIPLY; + else if (fnc == "/") + return DIVIDE; + else if (fnc == "^") + return POW; + else if (fnc == "!") + return FACTORIAL; + else if (fnc == "ABS") + return ABS; + else if (fnc == "EXP") + return EXP; + else if (fnc == "SIGN") + return SIGN; + else if (fnc == "SQRT") + return SQRT; + else if (fnc == "LOG") + return LOG; + else if (fnc == "SIN") + return SIN; + else if (fnc == "COS") + return COS; + else if (fnc == "TANH") + return TANH; + else if (fnc == "THETA") + return THETA; + else if (fnc == "(") + return LBRACK; + + return PLUS; +} + +bool ExprParser::tokenIsRBRACK() +{ + std::string fnc(token); + if (fnc == ")") + return 1; + else + return 0; +} + + +void ExprParser::load_expr(std::string expr) +{ + replace(expr, "**", "^"); + expr.copy(expression, expr.length()); + expression[expr.length()] = '\0'; + expr.copy(e, expr.length()); + e[expr.length()] = '\0'; +} + +void ExprParser::Parse(std::string expr) +{ + clear(); + load_expr(expr); + int vari = 0, cvari = 0; + token_type = NUMBER; + int prev_token = delimiter; + //shunting yard algorithm + while (*e != '\0') + { + getToken(); + + if (token_type == NUMBER) + { + std::vector num; + cvari++; + vari++; + num.push_back(0); + num.push_back(cvari - 1); + + IPN.push_back(-1); + + IPN.push_back(token2float()); + } + + if (token_type == VARIABLE) + { + int AA = AddVariable(); + + vari++; + + IPN.push_back(-AA-1); + IPN.push_back(0); + } + + if ((token_type == FUNCTION || token_type == delimiter) && !tokenIsRBRACK()) + { + int operation = token2operation(); + + while (op_stack.size()>0 && getOperatorPrecedenceLevel(op_stack[op_stack.size() - 1]) > + getOperatorPrecedenceLevel(operation) && (op_stack[op_stack.size() - 1] != LBRACK)) + { + int pop_oper = op_stack[op_stack.size() - 1]; + op_stack.pop_back(); + IPN.push_back(pop_oper); + } + + //if token is a minus sign + if (operation == MINUS && (prev_token == delimiter || prev_token == FUNCTION)) + { + operation = INV; + } + + op_stack.push_back(operation); + } + + if (tokenIsRBRACK()) + { + while (op_stack.size()>0 && op_stack[op_stack.size() - 1] != LBRACK) + { + int pop_oper = op_stack[op_stack.size() - 1]; + op_stack.pop_back(); + IPN.push_back(pop_oper); + } + op_stack.pop_back(); + } + + prev_token = token_type; + } + while (op_stack.size()>0) + { + int pop_oper = op_stack[op_stack.size() - 1]; + op_stack.pop_back(); + IPN.push_back(pop_oper); + } +} + + +float ExprParser::Evaluate(std::map variable_map) +{ + variable_map["pi"] = 3.141592653589f; + variable_map["e"] == 2.718281828459f; + + //code written in such a way to be compatible with OpenCL + float varstack[64]; + int stack_size = 0; + int N = IPN.size(); + + float result = 0; + + for (int i = 0; i < N; i++) + { + //if operation + if (IPN[i] >= 0) + { + if (stack_size>0) + { + float a = 0; + if (stack_size>1) + { + a = varstack[stack_size - 2]; + } + float b = varstack[stack_size - 1]; + switch ((int)(IPN[i])) + { + //plus + case 0: + stack_size--; + varstack[stack_size - 1] = a + b; + break; + //minus + case 1: + stack_size--; + varstack[stack_size - 1] = a - b; + break; + //minus sign + case 2: + varstack[stack_size - 1] = -b; + break; + //multiplication + case 3: + stack_size--; + varstack[stack_size - 1] = a * b; + break; + case 4: + stack_size--; + varstack[stack_size - 1] = a / b; + break; + case 5: + stack_size--; + varstack[stack_size - 1] = pow(a, b); + break; + case 6: + varstack[stack_size - 1] = b/*factorial*/; + break; + case 7: + varstack[stack_size - 1] = abs(b); + break; + case 8: + varstack[stack_size - 1] = exp(b); + break; + case 9: + varstack[stack_size - 1] = (b >= 0) ? (1) : (-1); + break; + case 10: + varstack[stack_size - 1] = sqrt(b); + break; + case 11: + varstack[stack_size - 1] = log(b); + break; + case 12: + varstack[stack_size - 1] = sin(b); + break; + case 13: + varstack[stack_size - 1] = cos(b); + break; + case 14: + varstack[stack_size - 1] = tanh(b); + break; + case 15: + varstack[stack_size - 1] = (b > 0) ? b : 0; + break; + default: + break; + } + } + } + //if variable + else + { + int vartype = -IPN[i]-1; + i++; + stack_size++; + + if (vartype == 0) + { + varstack[stack_size - 1] = IPN[i]; + } + else + { + std::string varname = variables[vartype-1]; + varstack[stack_size - 1] = variable_map[varname]; + } + } + } + + result = varstack[0]; + + return result; +} + diff --git a/src/ExprParser.h b/src/ExprParser.h new file mode 100644 index 0000000..c73f2b2 --- /dev/null +++ b/src/ExprParser.h @@ -0,0 +1,62 @@ +#pragma once +#include +#include +#include +#include +#include + +bool replace(std::string& str, const std::string& from, const std::string& to); + +class ExprParser +{ +public: + + enum TOKENTYPE { NOTHING = -1, delimiter, NUMBER, VARIABLE, FUNCTION, UNKNOWN }; + + enum OPERATION_ID { + PLUS, MINUS, // level 1 + INV, MULTIPLY, DIVIDE, // level 2 + POW, // level 3 + FACTORIAL, //level 4 + ABS, EXP, SIGN, SQRT, LOG, SIN, COS, TANH, THETA, //level 5 + LBRACK + }; // level 6 + + TOKENTYPE token_type; + + char token[256]; + char* expression; + char* e; + int N; + + std::vector op_stack; + std::vector IPN; + std::vector variables; + std::map variable_ids; + + ExprParser() + { + expression = new char[2 << 13]; + e = new char[2 << 13]; + } + + void getToken(); + + void clear(); + + int AddVariable(); + + float token2float(); + + void Parse(std::string expr); + + int getOperatorPrecedenceLevel(int); + + int token2operation(); + + bool tokenIsRBRACK(); + + void load_expr(std::string expr); + + float Evaluate(std::map variable_map); +}; \ No newline at end of file diff --git a/src/Gamemodes.cpp b/src/Gamemodes.cpp new file mode 100644 index 0000000..b13333c --- /dev/null +++ b/src/Gamemodes.cpp @@ -0,0 +1,455 @@ +#include "Gamemodes.h" + +//Global variables +sf::Vector2i mouse_pos, mouse_prev_pos; +InputState io_state; + +bool all_keys[sf::Keyboard::KeyCount] = { 0 }; +bool mouse_clicked = false; +bool show_cheats = false; + +//Constants +float mouse_sensitivity = 0.005f; +float wheel_sensitivity = 0.2f; +float music_vol = 75.0f; +float target_fps = 60.0f; + +GameMode game_mode = MAIN_MENU; + +void OpenMainMenu(Scene * scene, Overlays * overlays) +{ + RemoveAllObjects(); + game_mode = MAIN_MENU; + scene->SetExposure(1.0f); + scene->SetMode(Scene::INTRO); + sf::Vector2f wsize = default_view.getSize(); + + MenuBox mainmenu(1000, wsize.y*0.95f, wsize.x*0.025, wsize.y*0.025f); + mainmenu.SetBackgroundColor(sf::Color::Transparent); + //make the menu static + mainmenu.static_object = true; + + //TITLE + Text ttl("Marble\nMarcher", LOCAL("default"), 120, sf::Color::White); + ttl.SetBorderColor(sf::Color::Black); + ttl.SetBorderWidth(4); + mainmenu.AddObject(&ttl, Object::Allign::LEFT); + + Text CE("Community Edition", LOCAL("default"), 60, sf::Color::White); + CE.SetBorderColor(sf::Color::Black); + CE.SetBorderWidth(4); + mainmenu.AddObject(&CE, Object::Allign::LEFT); + + Box margin(800, 80); + margin.SetBackgroundColor(sf::Color::Transparent); + mainmenu.AddObject(&margin, Object::Allign::LEFT); + + //PLAY + Box playbtn(600, 50); + Text button1(LOCAL["Play"], LOCAL("default"), 40, sf::Color::White); + playbtn.hoverstate.color_main = sf::Color(200, 40, 0, 255); + playbtn.SetCallbackFunction([scene, overlays](sf::RenderWindow * window, InputState & state) + { + PlayNewGame(scene, window, 0); + overlays->sound_click.play(); + }, true); + playbtn.AddObject(&button1, Object::Allign::CENTER); + mainmenu.AddObject(&playbtn, Object::Allign::LEFT); + + //LEVELS + Box lvlsbtn(600, 50); + Text button2(LOCAL["Levels"], LOCAL("default"), 40, sf::Color::White); + lvlsbtn.hoverstate.color_main = sf::Color(200, 40, 0, 255); + lvlsbtn.SetCallbackFunction([scene, overlays](sf::RenderWindow * window, InputState & state) + { + OpenLevelMenu(scene, overlays); + overlays->sound_click.play(); + }, true); + lvlsbtn.AddObject(&button2, Object::Allign::CENTER); + mainmenu.AddObject(&lvlsbtn, Object::Allign::LEFT); + + //Settings + Box sttbtn(600, 50); + Text buttonstt(LOCAL["Settings"], LOCAL("default"), 40, sf::Color::White); + sttbtn.hoverstate.color_main = sf::Color(200, 40, 0, 255); + sttbtn.SetCallbackFunction([scene, overlays](sf::RenderWindow * window, InputState & state) + { + overlays->TWBAR_ENABLED = !overlays->TWBAR_ENABLED; + overlays->sound_click.play(); + }, true); + sttbtn.AddObject(&buttonstt, Object::Allign::CENTER); + mainmenu.AddObject(&sttbtn, Object::Allign::LEFT); + + //Controls + Box cntrlbtn(600, 50); + Text button3(LOCAL["Controls"], LOCAL("default"), 40, sf::Color::White); + cntrlbtn.hoverstate.color_main = sf::Color(200, 40, 0, 255); + cntrlbtn.SetCallbackFunction([scene, overlays](sf::RenderWindow * window, InputState & state) + { + OpenControlMenu(scene, overlays); + overlays->sound_click.play(); + }, true); + cntrlbtn.AddObject(&button3, Object::Allign::CENTER); + mainmenu.AddObject(&cntrlbtn, Object::Allign::LEFT); + + //Screen Saver + Box ssbtn(600, 50); + Text button4(LOCAL["Screen_Saver"], LOCAL("default"), 40, sf::Color::White); + ssbtn.hoverstate.color_main = sf::Color(200, 40, 0, 255); + ssbtn.SetCallbackFunction([scene, overlays](sf::RenderWindow * window, InputState & state) + { + OpenScreenSaver(scene, overlays); + overlays->sound_click.play(); + }, true); + ssbtn.AddObject(&button4, Object::Allign::CENTER); + mainmenu.AddObject(&ssbtn, Object::Allign::LEFT); + + //Exit + Box exitbtn(600, 50); + Text button5(LOCAL["Exit"], LOCAL("default"), 40, sf::Color::White); + exitbtn.hoverstate.color_main = sf::Color(200, 40, 0, 255); + exitbtn.SetCallbackFunction([scene, overlays](sf::RenderWindow * window, InputState & state) + { + overlays->sound_click.play(); + window->close(); + }, true); + exitbtn.AddObject(&button5, Object::Allign::CENTER); + mainmenu.AddObject(&exitbtn, Object::Allign::LEFT); + + Text about(LOCAL["About"], LOCAL("mono"), 30, sf::Color::White); + about.SetBorderColor(sf::Color::Black); + about.SetBorderWidth(3); + mainmenu.AddObject(&about, Object::Allign::LEFT); + + + AddGlobalObject(mainmenu); +} + +void OpenEditor(Scene * scene, Overlays * overlays, int level) +{ + RemoveAllObjects(); + //go to level editor + game_mode = LEVEL_EDITOR; + scene->SetExposure(1.0f); + overlays->TWBAR_ENABLED = true; + TwDefine("LevelEditor visible=true position='20 20'"); + TwDefine("FractalEditor visible=true position='20 500'"); + TwDefine("Settings iconified=true"); + TwDefine("Statistics iconified=true"); + scene->StartLevelEditor(level); +} + +void PlayLevel(Scene * scene, sf::RenderWindow * window, int level) +{ + RemoveAllObjects(); + //play level + game_mode = PLAYING; + scene->SetExposure(1.0f); + scene->levels.GetLevelMusic(level)->setVolume(GetVol()); + scene->levels.GetLevelMusic(level)->play(); + scene->StartSingle(level); + LockMouse(*window); +} + + +void OpenControlMenu(Scene * scene, Overlays * overlays) +{ + RemoveAllObjects(); + game_mode = CONTROLS; +} + +void OpenScreenSaver(Scene * scene, Overlays * overlays) +{ + RemoveAllObjects(); + game_mode = SCREEN_SAVER; + scene->SetMode(Scene::SCREEN_SAVER); +} + +void PlayNewGame(Scene * scene, sf::RenderWindow * window, int level) +{ + RemoveAllObjects(); + game_mode = PLAYING; + scene->StartNewGame(); + scene->GetCurMusic().setVolume(GetVol()); + scene->GetCurMusic().play(); + LockMouse(*window); +} + +void OpenTestWindow() +{ + Window test(200, 200, 500, 500, sf::Color(0, 0, 0, 128), LOCAL["Window"], LOCAL("default")); + Text button(LOCAL["Button"], LOCAL("default"), 30, sf::Color::White); + Box sbox(0, 0, 420, 200, sf::Color(128, 128, 128, 240)); + Box sbox2(0, 0, 240, 40, sf::Color(0, 64, 128, 240)); + Box sbox3(0, 0, 30, 30, sf::Color(0, 64, 128, 240)); + + sbox2.hoverstate.color_main = sf::Color(230, 40, 20, 200); + sbox2.AddObject(&button, Box::CENTER); + button.hoverstate.font_size = 40; + test.Add(&sbox, Box::CENTER); + test.Add(&sbox2, Box::CENTER); + test.Add(&sbox, Box::CENTER); + test.Add(&sbox2, Box::CENTER); + test.Add(&sbox, Box::CENTER); + test.Add(&sbox2, Box::CENTER); + + AddGlobalObject(test); +} + +void OpenLevelMenu(Scene* scene, Overlays* overlays) +{ + RemoveAllObjects(); + sf::Vector2f wsize = default_view.getSize(); + + MenuBox levels(wsize.x*0.95f, wsize.y*0.95f, wsize.x*0.025f, wsize.y*0.025f); + levels.SetBackgroundColor(sf::Color(32,32,32,160)); + //make the menu static + levels.static_object = true; + + scene->SetExposure(0.7f); + scene->SetMode(Scene::INTRO); + game_mode = LEVELS; + + std::map names = scene->levels.getLevelNames(); + std::map desc = scene->levels.getLevelDesc(); + std::vector ids = scene->levels.getLevelIds(); + std::map scores = scene->levels.getLevelScores(); + Text lvl(LOCAL["Levels"], LOCAL("default"), 60, sf::Color::White); + levels.AddObject(&lvl, Object::Allign::CENTER); + Box Bk2Menu(600, 50); + Bk2Menu.SetBackgroundColor(sf::Color(128, 128, 128, 128)); + Text button(LOCAL["Back2Main"], LOCAL("default"), 40, sf::Color::White); + Bk2Menu.hoverstate.color_main = sf::Color(200, 40, 0, 255); + Bk2Menu.SetCallbackFunction([scene, overlays](sf::RenderWindow * window, InputState & state) + { + OpenMainMenu(scene, overlays); + overlays->sound_click.play(); + }); + Bk2Menu.AddObject(&button, Object::Allign::CENTER); + levels.AddObject(&Bk2Menu, Object::Allign::LEFT); + + Box Newlvl(600, 50); + Newlvl.SetBackgroundColor(sf::Color(128, 128, 128, 128)); + Text newlvl(LOCAL["CreateNewLvl"], LOCAL("default"), 40, sf::Color::White); + Newlvl.hoverstate.color_main = sf::Color(200, 40, 0, 255); + Newlvl.SetCallbackFunction([scene, overlays](sf::RenderWindow * window, InputState & state) + { + OpenEditor(scene, overlays, -1); + overlays->sound_click.play(); + }); + Newlvl.AddObject(&newlvl, Object::Allign::CENTER); + levels.AddObject(&Newlvl, Object::Allign::LEFT); + + sf::Image edit; edit.loadFromFile(edit_png); + sf::Texture edittxt; edittxt.loadFromImage(edit); + edittxt.setSmooth(true); + + sf::Image remove; remove.loadFromFile(delete_png); + sf::Texture removetxt; removetxt.loadFromImage(remove); + removetxt.setSmooth(true); + + for (int i = 0; i < scene->levels.GetLevelNum(); i++) + { + Box lvlbtton(wsize.x*0.95f - 60, 60); + lvlbtton.SetBackgroundColor(sf::Color(128, 128, 128, 128)); + lvlbtton.hoverstate.border_thickness = 3; + + Box lvltext(500, 60); + lvltext.SetBackgroundColor(sf::Color::Transparent); + Box lvltitle(500, 40); + lvltitle.SetBackgroundColor(sf::Color::Transparent); + Text lvlname(utf8_to_wstring(names[ids[i]]), LOCAL("default"), 35, sf::Color::White); + Text lvldescr(utf8_to_wstring(desc[ids[i]]), LOCAL("default"), 18, sf::Color::White); + lvlname.hoverstate.color_main = sf::Color(255, 0, 0, 255); + lvlname.SetCallbackFunction([scene, overlays, selected = ids[i]](sf::RenderWindow * window, InputState & state) + { + PlayLevel(scene, window, selected); + overlays->sound_click.play(); + }); + lvltitle.AddObject(&lvlname, Object::Allign::LEFT); + lvltext.AddObject(&lvltitle, Object::Allign::LEFT); + lvltext.AddObject(&lvldescr, Object::Allign::LEFT); + lvlbtton.AddObject(&lvltext, Object::Allign::LEFT); + + Box lvlscore(500, 40); + lvlscore.SetBackgroundColor(sf::Color::Transparent); + std::string score_text = "--:--:--"; + if (scores[ids[i]].best_time != 0) + { + float time = scores[ids[i]].best_time; + float minutes = floor(time / 60.f); + float seconds = floor(time) - minutes*60; + float mili = floor(time*100) - seconds*100 - minutes*6000; + //convrt mili to frames + score_text = num2str(minutes) + ":" + num2str(seconds) + ":" + num2str(floor(mili*0.6f)); + } + Text lvlscorev(score_text, LOCAL("default"), 35, sf::Color::White); + lvlscorev.SetBackgroundColor(sf::Color::Green); + lvlscore.AddObject(&lvlscorev, Object::Allign::CENTER); + lvlbtton.AddObject(&lvlscore, Object::Allign::LEFT); + + Box lvlavg(500, 40); + lvlavg.SetBackgroundColor(sf::Color::Transparent); + score_text = "--:--:--"; + if (scores[ids[i]].best_time != 0) + { + float time = scores[ids[i]].all_time/scores[ids[i]].played_num; + float minutes = floor(time / 60.f); + float seconds = floor(time) - minutes * 60; + float mili = floor(time * 100) - seconds * 100 - minutes * 6000; + score_text = num2str(minutes) + ":" + num2str(seconds) + ":" + num2str(floor(mili*0.6f)); + } + Text lvlavgtxt(score_text, LOCAL("default"), 35, sf::Color::White); + lvlavgtxt.SetBackgroundColor(sf::Color::White); + lvlavg.AddObject(&lvlavgtxt, Object::Allign::CENTER); + lvlbtton.AddObject(&lvlavg, Object::Allign::LEFT); + + Box buttons(120, 60); + buttons.SetBackgroundColor(sf::Color::Transparent); + Box bedit(60, 60); + bedit.defaultstate.color_main = sf::Color(255, 255, 255, 255); + bedit.hoverstate.color_main = sf::Color(0, 255, 0, 255); + bedit.SetBackground(edittxt); + bedit.SetCallbackFunction([scene, overlays, id = ids[i]](sf::RenderWindow * window, InputState & state) + { + OpenEditor(scene, overlays, id); + overlays->sound_click.play(); + }, true); + + Box bremove(60, 60); + bremove.defaultstate.color_main = sf::Color(255, 255, 255, 255); + bremove.hoverstate.color_main = sf::Color(255, 0, 0, 255); + bremove.SetBackground(removetxt); + + bremove.SetCallbackFunction([scene, overlays, id = ids[i]](sf::RenderWindow * window, InputState & state) + { + ConfirmLevelDeletion(id, scene, overlays); + overlays->sound_click.play(); + }, true); + + buttons.AddObject(&bremove, Object::Allign::RIGHT); + buttons.AddObject(&bedit, Object::Allign::RIGHT); + lvlbtton.AddObject(&buttons, Object::Allign::RIGHT); + levels.AddObject(&lvlbtton, Object::Allign::LEFT); + } + + AddGlobalObject(levels); +} + +void ConfirmLevelDeletion(int lvl, Scene* scene, Overlays* overlays) +{ + sf::Vector2f wsize = default_view.getSize(); + Window confirm(wsize.x*0.4f, wsize.y*0.4f, 500, 215, sf::Color(0, 0, 0, 128), LOCAL["You_sure"], LOCAL("default")); + Text button1(LOCAL["Yes"], LOCAL("default"), 30, sf::Color::White); + Text button2(LOCAL["No"], LOCAL("default"), 30, sf::Color::White); + Text text(LOCAL["You_sure"], LOCAL("default"), 30, sf::Color::White); + + Box but1(0, 0, 240, 40, sf::Color(0, 64, 128, 240)); + Box but2(0, 0, 240, 40, sf::Color(0, 64, 128, 240)); + + but1.hoverstate.color_main = sf::Color(230, 40, 20, 200); + but2.hoverstate.color_main = sf::Color(40, 230, 20, 200); + but1.AddObject(&button1, Box::CENTER); + but2.AddObject(&button2, Box::CENTER); + + confirm.Add(&text, Box::CENTER); + confirm.Add(&but1, Box::RIGHT); + confirm.Add(&but2, Box::RIGHT); + + int id = AddGlobalObject(confirm); + + get_glob_obj(id).objects[1].get()->objects[0].get()->objects[1].get()->SetCallbackFunction([scene, overlays, id, lvl](sf::RenderWindow * window, InputState & state) + { + //remove lvl + scene->levels.DeleteLevel(lvl); + overlays->sound_click.play(); + OpenLevelMenu(scene, overlays); + }); + + get_glob_obj(id).objects[1].get()->objects[0].get()->objects[2].get()->SetCallbackFunction([scene, overlays, id](sf::RenderWindow * window, InputState & state) + { + Add2DeleteQueue(id); + overlays->sound_click.play(); + }); +} + +void ConfirmEditorExit(Scene* scene, Overlays* overlays) +{ + sf::Vector2f wsize = default_view.getSize(); + Window confirm(wsize.x*0.4f, wsize.y*0.4f, 500, 215, sf::Color(0, 0, 0, 128), LOCAL["You_sure"], LOCAL("default")); + Text button1(LOCAL["Yes"], LOCAL("default"), 30, sf::Color::White); + Text button2(LOCAL["No"], LOCAL("default"), 30, sf::Color::White); + Text text(LOCAL["You_sure"], LOCAL("default"), 30, sf::Color::White); + + Box but1(0, 0, 240, 40, sf::Color(0, 64, 128, 240)); + Box but2(0, 0, 240, 40, sf::Color(0, 64, 128, 240)); + + but1.hoverstate.color_main = sf::Color(230, 40, 20, 200); + but2.hoverstate.color_main = sf::Color(40, 230, 20, 200); + but1.AddObject(&button1, Box::CENTER); + but2.AddObject(&button2, Box::CENTER); + + confirm.Add(&text, Box::CENTER); + confirm.Add(&but1, Box::RIGHT); + confirm.Add(&but2, Box::RIGHT); + + int id = AddGlobalObject(confirm); + + get_glob_obj(id).objects[1].get()->objects[0].get()->objects[1].get()->SetCallbackFunction([scene, overlays, id](sf::RenderWindow * window, InputState & state) + { + OpenLevelMenu(scene, overlays); + scene->ExitEditor(); + scene->StopAllMusic(); + overlays->TWBAR_ENABLED = false; + TwDefine("LevelEditor visible=false"); + TwDefine("FractalEditor visible=false"); + overlays->sound_click.play(); + }); + + get_glob_obj(id).objects[1].get()->objects[0].get()->objects[2].get()->SetCallbackFunction([scene, overlays, id](sf::RenderWindow * window, InputState & state) + { + Add2DeleteQueue(id); + overlays->sound_click.play(); + }); +} + + +float GetVol() { + if (game_settings.mute) { + return 0.0f; + } + else if (game_mode == PAUSED) { + return music_vol / 4; + } + else { + return music_vol; + } +} + +void LockMouse(sf::RenderWindow& window) { + window.setMouseCursorVisible(false); + const sf::Vector2u size = window.getSize(); + mouse_pos = sf::Vector2i(size.x / 2, size.y / 2); + sf::Mouse::setPosition(mouse_pos); +} +void UnlockMouse(sf::RenderWindow& window) { + window.setMouseCursorVisible(true); +} + +void PauseGame(sf::RenderWindow& window, Scene& scene) { + game_mode = PAUSED; + scene.GetCurMusic().setVolume(GetVol()); + UnlockMouse(window); + scene.SetExposure(0.5f); +} + +int DirExists(const char *path) { + struct stat info; + if (stat(path, &info) != 0) { + return 0; + } + else if (info.st_mode & S_IFDIR) { + return 1; + } + return 0; +} \ No newline at end of file diff --git a/src/Gamemodes.h b/src/Gamemodes.h new file mode 100644 index 0000000..0808597 --- /dev/null +++ b/src/Gamemodes.h @@ -0,0 +1,73 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +enum GameMode { + MAIN_MENU, + PLAYING, + PAUSED, + SCREEN_SAVER, + CONTROLS, + LEVELS, + LEVEL_EDITOR, + CREDITS, + MIDPOINT +}; + +//Global variables +extern sf::Vector2i mouse_pos, mouse_prev_pos; +extern bool all_keys[sf::Keyboard::KeyCount]; +extern bool mouse_clicked; +extern bool show_cheats; +extern InputState io_state; + +//Constants +extern float mouse_sensitivity; +extern float wheel_sensitivity; +extern float music_vol; +extern float target_fps; + +extern GameMode game_mode; + +void OpenMainMenu(Scene * scene, Overlays * overlays); + +void OpenEditor(Scene * scene, Overlays * overlays, int level); +void PlayLevel(Scene * scene, sf::RenderWindow * window, int level); + +void OpenControlMenu(Scene * scene, Overlays * overlays); + +void OpenScreenSaver(Scene * scene, Overlays * overlays); + +void PlayNewGame(Scene * scene, sf::RenderWindow * window, int level); + +void OpenTestWindow(); + +void OpenLevelMenu(Scene* scene, Overlays* overlays); +void ConfirmLevelDeletion(int lvl, Scene* scene, Overlays* overlays); + +void ConfirmEditorExit(Scene * scene, Overlays * overlays); + +float GetVol(); +void LockMouse(sf::RenderWindow& window); +void UnlockMouse(sf::RenderWindow& window); +void PauseGame(sf::RenderWindow& window, Scene& scene); +int DirExists(const char *path); + +template < typename T > std::string num2str(const T& n) +{ + std::ostringstream stm; + if (n < 10) stm << "0"; + stm << n; + return stm.str(); +} + + +class Sounds +{ + +}; \ No newline at end of file diff --git a/src/Interface.cpp b/src/Interface.cpp new file mode 100644 index 0000000..81767e7 --- /dev/null +++ b/src/Interface.cpp @@ -0,0 +1,1012 @@ +#include + +std::map> global_objects; +std::vector z_value; //rendering order +std::map z_index; +std::vector del; // deletion stack +int all_obj_id = 0; +std::map active; +int focused = 0; +int cursor = 0; +int global_focus = 0; + +sf::Color default_main_color = sf::Color(64, 64, 64, 128); +sf::Color default_hover_main_color = sf::Color(200, 128, 128, 128); +sf::Color default_active_main_color = sf::Color(255, 128, 128, 255); +float default_margin =0; +sf::View default_view = sf::View(sf::FloatRect(0, 0, 1920, 1080)); + +float animation_sharpness = 5.f; +float action_dt = 0.3; + + +sf::FloatRect overlap(sf::FloatRect a, sf::FloatRect b) +{ + sf::FloatRect c; + c.top = std::max(a.top, b.top); + c.left = std::max(a.left, b.left); + c.height = std::max(std::min(a.top + a.height, b.top + b.height) - c.top, 0.f); + c.width = std::max(std::min(a.left + a.width, b.left + b.width) - c.left, 0.f); + return c; +} + +ColorFloat operator+(ColorFloat a, ColorFloat b) +{ + ColorFloat c; + c.r = a.r + b.r; + c.g = a.g + b.g; + c.b = a.b + b.b; + c.a = a.a + b.a; + return c; +} + +ColorFloat operator-(ColorFloat a, ColorFloat b) +{ + ColorFloat c; + c.r = a.r - b.r; + c.g = a.g - b.g; + c.b = a.b - b.b; + c.a = a.a - b.a; + return c; +} + +ColorFloat operator*(ColorFloat a, float b) +{ + ColorFloat c; + c.r = a.r * b; + c.g = a.g * b; + c.b = a.b * b; + c.a = a.a * b; + return c; +} + +sf::Color ToColor(ColorFloat a) +{ + return sf::Color(a.r, a.g, a.b, a.a); +} + +ColorFloat ToColorF(sf::Color a) +{ + return ColorFloat(a.r, a.g, a.b, a.a); +} + +Object& get_glob_obj(int id) +{ + return *global_objects[id].get(); +} + +void UpdateAspectRatio(float width, float heigth) +{ + sf::Vector2f size = sf::Vector2f(default_view.getSize().x, default_view.getSize().x * heigth / width); + default_view.reset(sf::FloatRect(sf::Vector2f(0, 0), size)); +} + +int AddGlobalObject(Object & a) +{ + Object* copy = a.GetCopy(); + global_objects[copy->id] = std::unique_ptr(copy);//add a copy to the global list + global_objects[copy->id].get()->id = copy->id; + z_value.push_back(copy->id); + global_focus = copy->id; + focused = copy->id; + return copy->id; +} + +bool global_exists(int id) +{ + for (auto &obj : global_objects) + { + if (obj.first == id) + { + return true; + } + } + return false; +} + +int z_val(int id) +{ + std::vector::iterator it = std::find(z_value.begin(), z_value.end(), id); + return std::distance(z_value.begin(), it); +} + +void RemoveGlobalObject(int id) +{ + if (global_objects.count(id) != 0) + { + global_objects.erase(id); + int z_id = z_val(id); + z_value.erase(z_value.begin() + z_id); + } +} + +void RemoveAllObjects1() +{ + global_objects.clear(); + z_value.clear(); +} + + +void RemoveAllObjects() +{ + for (auto &obj:global_objects) + { + del.push_back(obj.first); + } +} + +void Add2DeleteQueue(int id) +{ + if (global_objects.count(id) != 0) + { + del.push_back(id); + } +} + +void UpdateAllObjects(sf::RenderWindow * window, InputState& state) +{ + for (auto &id : del) + { + RemoveGlobalObject(id); + } + + del.clear(); + + //render stuff in the following order + for (auto &z : z_value) + { + if (global_objects.count(z) != 0) + { + global_objects[z].get()->Update(window, state); + } + } + + if (global_objects.count(global_focus) != 0) + { + //put the focused global object to the top + int top_id = z_value[z_value.size() - 1]; + + //if not the top object and not a static object + if (top_id != global_focus) + { + if (global_objects.count(top_id) != 0) + { + global_objects[top_id]->UpdateAction(window, state); + if (!global_objects[global_focus].get()->static_object) + { + int global_z = z_val(global_focus); + int top_z = z_value.size() - 1; + std::swap(z_value[top_z], z_value[global_z]); + } + } + } + + //update callbacks in the focused object + global_objects[global_focus]->UpdateAction(window, state); + } +} + + +State interpolate(State a, State b, float t) +{ + State C; + C.position.x = a.position.x*(1.f - t) + b.position.x*t; + C.position.y = a.position.y*(1.f - t) + b.position.y*t; + C.size.x = a.size.x*(1.f - t) + b.size.x*t; + C.size.y = a.size.y*(1.f - t) + b.size.y*t; + C.border_thickness = a.border_thickness*(1.f - t) + b.border_thickness*t; + C.margin = a.margin*(1.f - t) + b.margin*t; + C.scroll = a.scroll*(1.f - t) + b.scroll*t; + C.font_size = a.font_size*(1.f - t) + b.font_size*t; + C.color_main = a.color_main*(1.f - t) + b.color_main*t; + C.color_second = a.color_second*(1.f - t) + b.color_second*t; + C.color_border = a.color_border*(1.f - t) + b.color_border*t; + return C; +} + +void Object::SetPosition(float x, float y) +{ + defaultstate.position = sf::Vector2f(x, y); + curstate.position = sf::Vector2f(x, y); + activestate.position = sf::Vector2f(x, y); + hoverstate.position = sf::Vector2f(x, y); +} + +void Object::SetSize(float x, float y) +{ + defaultstate.size = sf::Vector2f(x, y); + activestate.size = sf::Vector2f(x, y); + hoverstate.size = sf::Vector2f(x, y); +} + +void Object::SetHeigth(float x) +{ + defaultstate.size.y = x; + activestate.size.y = x; + hoverstate.size.y = x; +} + +void Object::SetWidth(float x) +{ + defaultstate.size.x = x; + activestate.size.x = x; + hoverstate.size.x = x; +} + +void Object::SetBackgroundColor(sf::Color color) +{ + defaultstate.color_main = color; + activestate.color_main = color; + hoverstate.color_main = color; +} + +void Object::SetBorderColor(sf::Color color) +{ + defaultstate.color_border = color; + activestate.color_border = color; + hoverstate.color_border = color; +} + +void Object::SetBorderWidth(float S) +{ + defaultstate.border_thickness = S; + activestate.border_thickness = S; + hoverstate.border_thickness = S; +} + +void Object::SetMargin(float x) +{ + defaultstate.margin = x; + activestate.margin = x; + hoverstate.margin = x; +} + +void Object::SetInsideSize(float x) +{ + defaultstate.inside_size = x; + activestate.inside_size = x; + hoverstate.inside_size = x; +} + +void Object::SetScroll(float x) +{ + defaultstate.scroll = x; + activestate.scroll = x; + hoverstate.scroll = x; + curstate.scroll = x; +} + +void Object::ApplyScroll(float x) +{ + defaultstate.scroll = x; + activestate.scroll = x; + hoverstate.scroll = x; +} + +void Object::Move(sf::Vector2f dx) +{ + if (!isnan(dx.x) && !isnan(dx.y) && (dx.x != 0 || dx.y != 0)) + { + defaultstate.position += dx; + curstate.position += dx; + activestate.position += dx; + hoverstate.position += dx; + } +} + +void Object::SetDefaultFunction(std::function fun) +{ + defaultfn = fun; +} + +void Object::SetCallbackFunction(std::function fun, bool limit_repeat) +{ + callback = fun; + limiter = limit_repeat; +} + +void Object::SetHoverFunction(std::function fun) +{ + hoverfn = fun; +} + +bool Object::RunCallback(sf::RenderWindow * window, InputState & state) +{ + if (callback != NULL) + { + callback(window, state); //run callback with state info + return true; + } + else + { + //try to run a callback inside the object + for (auto &obj : objects) + { + if (obj.get()->RunCallback(window, state)) + { + return true; + } + } + + //mission failed. we'll get 'em next time + return false; + } +} + +void Object::clone_states() +{ + curstate = defaultstate; + activestate = defaultstate; + hoverstate = defaultstate; +} + +void Object::Update(sf::RenderWindow * window, InputState& state) +{ + worldPos = window->mapPixelToCoords(sf::Vector2i(state.mouse_pos.x, state.mouse_pos.y)); + obj= sf::FloatRect(curstate.position.x, curstate.position.y, curstate.size.x, curstate.size.y); + + window->setView(used_view); + state.mouse_speed = window->mapPixelToCoords(sf::Vector2i(state.mouse_pos.x, state.mouse_pos.y)) - + window->mapPixelToCoords(sf::Vector2i(state.mouse_prev.x, state.mouse_prev.y)); + + if ( ((state.mouse[0] || state.mouse[2]) && !limiter) || ((state.mouse_press[0] || state.mouse_press[2]) && limiter) ) //if clicked + { + if (obj.contains(worldPos)) // if inside object + { + active[id] = true; //set as active + cursor = id; + if (defaultfn != NULL) + focused = id; // save this object as the last focused if it has a default callback + + if (global_exists(id)) + { + global_focus = id; + } + } + } + else + { + active[id] = false; //deactivate + } + + + float t = 1.f - exp(-animation_sharpness * state.dt); + + //update animation + switch (curmode) + { + case DEFAULT: + curstate = interpolate(curstate, defaultstate, t); + break; + case ACTIVE: + curstate = interpolate(curstate, activestate, t); + break; + case ONHOVER: + curstate = interpolate(curstate, hoverstate, t); + break; + } + + Draw(window, state); +} + +void Object::UpdateAction(sf::RenderWindow * window, InputState & state) +{ + state.mouse_speed = window->mapPixelToCoords(sf::Vector2i(state.mouse_pos.x, state.mouse_pos.y)) - + window->mapPixelToCoords(sf::Vector2i(state.mouse_prev.x, state.mouse_prev.y)); + + curmode = DEFAULT; + //if mouse is inside the object + if (obj.contains(worldPos)) + { + if(!(state.mouse[0] || state.mouse[2])) // if hover + { + if (hoverfn != NULL) + hoverfn(window, state); //run callback with state info + curmode = ONHOVER; + } + } + + if (cursor == id) + { + curmode = ONHOVER; + } + + if (active[id]) + { + if (callback != NULL) + { + callback(window, state); //run callback with state info + } + curmode = ACTIVE; + } + + if (action_time <= 0.f) //limit the repetability + { + if (focused == id) //if this object is the one that is currently in focus + { + if (defaultfn != NULL) //the function existance check may seem unnecessery, but it is + defaultfn(window, state); //run callback with state info + } + } + else + { + action_time -= state.dt; + } + + //update callbacks for all functions inside + for (auto &obj : objects) + { + obj.get()->UpdateAction(window, state); + } +} + + +Object::Object() : callback(NULL), hoverfn(NULL), defaultfn(NULL), curmode(DEFAULT), action_time(0.2), obj_allign(LEFT), static_object(false), limiter(false) +{ + curstate.color_main = default_main_color; + activestate.color_main = default_active_main_color; + hoverstate.color_main = default_hover_main_color; + defaultstate.color_main = default_main_color; + + used_view = default_view; + + id = all_obj_id; + active[id] = false; + all_obj_id++; +} + +Object::Object(Object & A) +{ + *this = A; +} + +Object::Object(Object && A) +{ + *this = A; +} + +Object::Object(Object * A) +{ + *this = *A; +} + +void Object::operator=(Object & A) +{ + copy(A); +} + +void Object::operator=(Object && A) +{ + copy(A); +} + +void Object::copy(Object & A) +{ + curstate = A.curstate; + activestate = A.activestate; + hoverstate = A.hoverstate; + defaultstate = A.defaultstate; + curmode = A.curmode; + static_object = A.static_object; + limiter = A.limiter; + used_view = A.used_view; + obj_allign = A.obj_allign; + callback = A.callback; + hoverfn = A.hoverfn; + defaultfn = A.hoverfn; + + id = all_obj_id; + all_obj_id++; + active[id] = false; + + action_time = A.action_time; + + for (auto &element : A.objects) + Object::AddObject(element.get(), element.get()->obj_allign); +} + +void Object::copy(Object && A) +{ + std::swap(curstate, A.curstate); + std::swap(activestate, A.activestate); + std::swap(hoverstate, A.hoverstate); + std::swap(defaultstate, A.defaultstate); + std::swap(curmode, A.curmode); + std::swap(limiter, A.limiter); + std::swap(static_object, A.static_object); + std::swap(used_view, A.used_view); + std::swap(obj_allign, A.obj_allign); + std::swap(id, A.id); + std::swap(action_time, A.action_time); + std::swap(objects, A.objects); +} + +Object * Object::GetCopy() +{ + return new Object(*this); +} + +void Object::Draw(sf::RenderWindow * window, InputState & state) +{ + //nothing to draw +} + +void Object::AddObject(Object * something, Allign a) +{ + something->obj_allign = a; + objects.push_back(std::unique_ptr(something->GetCopy())); + this->SetInsideSize(defaultstate.inside_size + something->defaultstate.size.y); +} + +void Box::SetBackground(const sf::Texture & texture) +{ + image = texture; + rect.setTexture(&image); +} + +void Box::Draw(sf::RenderWindow * window, InputState& state) +{ + //update the box itself + if ( !(ToColor(defaultstate.color_main) == sf::Color::Transparent && curmode == DEFAULT) + && !(ToColor(hoverstate.color_main) == sf::Color::Transparent && curmode == ONHOVER) + && !(ToColor(activestate.color_main) == sf::Color::Transparent && curmode == ACTIVE) ) + { + rect.setPosition(curstate.position); + rect.setSize(curstate.size); + rect.setFillColor(ToColor(curstate.color_main)); + rect.setOutlineThickness(curstate.border_thickness); + rect.setOutlineColor(ToColor(curstate.color_border)); + window->draw(rect); + } + + sf::Vector2f thisone = this->used_view.getSize(); + sf::Vector2f default_size = default_view.getSize(); + sf::View gview = window->getView(); + sf::FloatRect global_view = sf::FloatRect(gview.getCenter() - gview.getSize()*0.5f, gview.getSize()); + sf::FloatRect this_view = overlap(global_view, sf::FloatRect(curstate.position, curstate.size)); + boxView.reset(this_view); + sf::FloatRect global_viewport = gview.getViewport(); + sf::FloatRect local_viewport = sf::FloatRect(curstate.position.x / default_size.x, curstate.position.y / default_size.y, curstate.size.x / default_size.x, curstate.size.y / default_size.y); + sf::FloatRect this_viewport = overlap(global_viewport, local_viewport); + boxView.setViewport(this_viewport); + + if (this_viewport.width > 0.f && this_viewport.height > 0.f) + { + float line_height = 0; + float cur_shift_x1 = curstate.margin, cur_shift_x2 = curstate.margin; + float cur_shift_y = curstate.margin + curstate.scroll; + + //update all the stuff inside the box + for (auto &obj : objects) + { + Allign A = obj.get()->obj_allign; + bool not_placed = true; + int tries = 0; + int obj_id = obj.get()->id; + float obj_h = obj.get()->curstate.size.y; + while (not_placed && tries < 2) //try to place the object somewhere + { + float space_left = defaultstate.size.x - cur_shift_x1 - cur_shift_x2; + float obj_width = obj.get()->curstate.size.x; + + if (space_left >= obj_width || space_left >= defaultstate.size.x - 2 * curstate.margin) + { + not_placed = false; + switch (A) + { + case LEFT: + obj.get()->SetPosition(curstate.position.x + cur_shift_x1, curstate.position.y + cur_shift_y); + cur_shift_x1 += obj_width + curstate.margin; + line_height = std::max(obj_h, line_height); + break; + case CENTER: + obj.get()->SetPosition(curstate.position.x + defaultstate.size.x * 0.5f - obj_width * 0.5f, curstate.position.y + cur_shift_y); + cur_shift_y += std::max(obj_h, line_height) + curstate.margin; + line_height = 0; + cur_shift_x1 = curstate.margin; + cur_shift_x2 = curstate.margin; + break; + case RIGHT: + obj.get()->SetPosition(curstate.position.x + defaultstate.size.x - obj_width - cur_shift_x2, curstate.position.y + cur_shift_y); + cur_shift_x2 += obj_width + curstate.margin; + line_height = std::max(obj_h, line_height); + break; + } + } + else + { + cur_shift_y += line_height + curstate.margin; + line_height = 0; + cur_shift_x1 = curstate.margin; + cur_shift_x2 = curstate.margin; + tries++; + } + + } + if (tries >= 2) + { + //ERROR_MSG("Object does not fit in the box."); + } + sf::FloatRect obj_box(obj.get()->curstate.position, obj.get()->curstate.size); + sf::FloatRect seen_part = overlap(obj_box, this_view); + //if the object is seen in the view, then update it + if (seen_part.width > 0.f && seen_part.height > 0.f) + { + obj.get()->used_view = boxView; + obj.get()->Update(window, state); + } + + } + this->SetInsideSize(cur_shift_y - curstate.scroll - curstate.margin); + } +} + +Box::Box(float x, float y, float dx, float dy, sf::Color color_main) +{ + defaultstate.position.x = x; + defaultstate.position.y = y; + defaultstate.size.x = dx; + defaultstate.size.y = dy; + defaultstate.color_main = ToColorF(color_main); + clone_states(); +} + +Box::Box(float dx, float dy) +{ + defaultstate.position.x = 0; + defaultstate.position.y = 0; + defaultstate.size.x = dx; + defaultstate.size.y = dy; + defaultstate.color_main = ToColorF(default_main_color); + clone_states(); +} + + +Box::Box() +{ + +} + +Box::Box(Box & A) +{ + *this = A; +} + +Box::Box(Box && A) +{ + *this = A; +} + +void Box::operator=(Box & A) +{ + copy(A); + + rect = A.rect; + boxView = A.boxView; + SetBackground(A.image); +} + +void Box::operator=(Box && A) +{ + copy(A); + + std::swap(image, A.image); + std::swap(rect, A.rect); + std::swap(boxView, A.boxView); +} + +Object * Box::GetCopy() +{ + return static_cast(new Box(*this)); +} + +Box::~Box() +{ + +} + +ColorFloat::ColorFloat(float red, float green, float blue, float alpha): r(red), g(green), b(blue), a(alpha) +{ + +} + +void ColorFloat::operator=(sf::Color a) +{ + *this = ToColorF(a); +} + +void Text::Draw(sf::RenderWindow * window, InputState& state) +{ + text.setPosition(curstate.position); + text.setCharacterSize(curstate.font_size); + text.setFillColor(ToColor(curstate.color_main)); + text.setOutlineThickness(curstate.border_thickness); + text.setOutlineColor(ToColor(curstate.color_border)); + + window->draw(text); + SetSize(text.getLocalBounds().width+ text.getLocalBounds().left, text.getLocalBounds().height+ text.getLocalBounds().top); +} + +Text::Text(sf::Text t) +{ + text = t; + defaultstate.font_size = t.getCharacterSize(); + defaultstate.color_main = t.getFillColor(); + clone_states(); +} + + +Text::Text(Text & A) +{ + *this = A; +} + +Text::Text(Text && A) +{ + *this = A; +} + +void Text::operator=(Text & A) +{ + copy(A); + + text = A.text; +} + +void Text::operator=(Text && A) +{ + copy(A); + + std::swap(text, A.text); +} + +Object * Text::GetCopy() +{ + return static_cast(new Text(*this)); +} + +void Window::Add(Object* something, Allign a) +{ + objects[1].get()->AddObject(something, a); +} + +Window::Window(Window & A) +{ + *this = A; +} + +Window::Window(Window && A) +{ + *this = A; +} + +void Window::CreateCallbacks() +{ + //use lambda funtion + this->objects[0].get()->objects[1].get()->SetCallbackFunction([parent = this](sf::RenderWindow * window, InputState & state) + { + Add2DeleteQueue(parent->id); + }); + + //delete callback + this->SetDefaultFunction([parent = this](sf::RenderWindow * window, InputState & state) + { + if (state.keys[sf::Keyboard::Escape] == true) + { + Add2DeleteQueue(parent->id); + parent->action_time = action_dt; + } + }); + + //drag callback + this->objects[0].get()->SetCallbackFunction([parent = this](sf::RenderWindow * window, InputState & state) + { + parent->Move(state.mouse_speed); + }); +} + +void Window::operator=(Window & A) +{ + Box::operator=(A); + CreateCallbacks(); +} + +void Window::operator=(Window && A) +{ + Box::operator=(A); + CreateCallbacks(); +} + +Object * Window::GetCopy() +{ + return static_cast(new Window(*this)); +} + +InputState::InputState(): mouse_pos(sf::Vector2f(0,0)), mouse_speed(sf::Vector2f(0, 0)) +{ + std::fill(keys, keys + sf::Keyboard::KeyCount - 1, false); + std::fill(mouse, mouse + 2, false); +} + +InputState::InputState(bool a[sf::Keyboard::KeyCount], bool b[3], sf::Vector2f c, sf::Vector2f d): + mouse_pos(c), mouse_speed(d) +{ + std::copy(a, a + sf::Keyboard::KeyCount - 1, keys); + std::copy(b, b + 2, mouse); +} + +void MenuBox::AddObject(Object * something, Allign a) +{ + this->objects[0].get()->AddObject(something, a); + + //update the slider + float inside_size = this->objects[0].get()->defaultstate.inside_size; + float height = this->objects[0].get()->defaultstate.size.y; + float height_1 = height - 2 * this->objects[1].get()->defaultstate.margin; + float new_h = std::min(height_1 * (height / inside_size), height_1); + if (new_h == height_1) + { + //remove the slider + this->objects[1].get()->SetWidth(0); + this->objects[1].get()->objects[0].get()->SetWidth(0); + this->objects[0].get()->SetWidth(this->defaultstate.size.x); + } + else + { + this->objects[0].get()->SetWidth(this->defaultstate.size.x - 30); + this->objects[1].get()->SetWidth(30); + this->objects[1].get()->objects[0].get()->SetWidth(26); + this->objects[1].get()->objects[0].get()->SetHeigth(new_h); + } +} + +void MenuBox::Cursor(int d) +{ + if (d == -1) // up + { + if (cursor_id > 0) + { + cursor_id--; + } + } + else if(d == 1) + { + if (cursor_id < this->objects[0].get()->objects.size() - 1) + { + cursor_id++; + } + } + cursor = this->objects[0].get()->objects[cursor_id].get()->id; + + //apply scroll to chosen object + float ybox = this->curstate.position.y; + float yobj = this->objects[0].get()->objects[cursor_id].get()->curstate.position.y; + float dy = yobj - ybox; + ScrollTo(dy); +} + +void MenuBox::ScrollBy(float dx) +{ + float inside_size = this->objects[0].get()->defaultstate.inside_size; + float cur_scroll = -this->objects[0].get()->defaultstate.scroll + dx; + float height_1 = this->objects[1].get()->defaultstate.size.y - 2 * this->objects[1].get()->defaultstate.margin; + float height_2 = this->objects[1].get()->objects[0].get()->defaultstate.size.y; + //only scroll within the appropriate range + if (cur_scroll <= inside_size - height_1 + 100 && cur_scroll >= 0) + { + this->objects[0].get()->ApplyScroll(-cur_scroll); + float max_slide_scroll = height_1 - height_2; + //relative scroll of the scroll button + float scroll_a = max_slide_scroll * cur_scroll / (inside_size - height_1); + this->objects[1].get()->SetScroll(scroll_a); + } +} + +void MenuBox::ScrollTo(float scroll) +{ + float cur_scroll = this->objects[0].get()->defaultstate.scroll; + float ds = scroll - this->objects[0].get()->defaultstate.margin; + ScrollBy(ds); +} + +MenuBox::MenuBox(float dx, float dy, float x, float y, sf::Color color_main): cursor_id(0) +{ + defaultstate.position.x = x; + defaultstate.position.y = y; + defaultstate.size.x = dx; + defaultstate.size.y = dy; + defaultstate.color_main = ToColorF(color_main); + clone_states(); + + Box Inside(0, 0, dx - 30, dy, sf::Color(100, 100, 100, 128)), + Scroll(0, 0, 30, dy, sf::Color(150, 150, 150, 128)), + Scroll_Slide(0, 0, 26, 60, sf::Color(255, 150, 0, 128)); + + Scroll_Slide.hoverstate.color_main = sf::Color(255, 50, 0, 128); + Scroll_Slide.activestate.color_main = sf::Color(255, 100, 100, 255); + Inside.SetBackgroundColor(sf::Color::Transparent); + Scroll.SetMargin(2); + Inside.SetMargin(12); + Scroll.AddObject(&Scroll_Slide, Box::CENTER); + this->Object::AddObject(&Inside, Box::LEFT); + this->Object::AddObject(&Scroll, Box::RIGHT); + + CreateCallbacks(); +} + +MenuBox::MenuBox(MenuBox & A): cursor_id(0) +{ + *this = A; +} + +MenuBox::MenuBox(MenuBox && A): cursor_id(0) +{ + *this = A; +} + +void MenuBox::CreateCallbacks() +{ + //use lambda funtion + this->objects[1].get()->objects[0].get()->SetCallbackFunction([parent = this](sf::RenderWindow * window, InputState & state) + { + float inside_size = parent->objects[0].get()->defaultstate.inside_size; + float height_1 = parent->objects[1].get()->defaultstate.size.y - 2 * parent->objects[1].get()->defaultstate.margin; + float height_2 = parent->objects[1].get()->objects[0].get()->defaultstate.size.y; + float max_slide_scroll = height_1 - height_2; + //relative scroll coefficient + float rel_coef = (inside_size - height_1) / max_slide_scroll; + parent->ScrollBy(state.mouse_speed.y*rel_coef); + }); + + this->SetHoverFunction([parent = this](sf::RenderWindow * window, InputState & state) + { + //wheel scroll + if (state.wheel != 0.f) + { + float ds = 20.f; + parent->ScrollBy(-state.wheel*ds); + } + }); + + this->SetDefaultFunction([parent = this](sf::RenderWindow * window, InputState & state) + { + bool A = false; + + if (state.keys[sf::Keyboard::Up]) + { + parent->Cursor(-1); + A = 1; + } + + if (state.keys[sf::Keyboard::Down]) + { + parent->Cursor(1); + A = 1; + } + + if (state.keys[sf::Keyboard::Enter]) + { + //run the callback function of the chosen object + A = parent->objects[0].get()->objects[parent->cursor_id].get()->RunCallback(window, state); + } + + if (A) parent->action_time = action_dt; + }); +} + +void MenuBox::operator=(MenuBox & A) +{ + Box::operator=(A); + CreateCallbacks(); +} + +void MenuBox::operator=(MenuBox && A) +{ + Box::operator=(A); + CreateCallbacks(); +} + +Object * MenuBox::GetCopy() +{ + return static_cast(new MenuBox(*this)); +} diff --git a/src/Interface.h b/src/Interface.h new file mode 100644 index 0000000..62c841c --- /dev/null +++ b/src/Interface.h @@ -0,0 +1,320 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#define ERROR_MSG(x) MessageBox(nullptr, TEXT(x), TEXT("ERROR"), MB_OK); +#else +#define ERROR_MSG(x) std::cerr << x << std::endl; +#endif + +//default interface parameters +extern sf::Color default_main_color; +extern sf::Color default_hover_main_color; +extern sf::Color default_active_main_color; +extern float default_margin; +extern sf::View default_view ; + +extern float animation_sharpness; +extern float action_dt; + +static const std::string close_png = "images/clear.png"; +static const std::string delete_png = "images/delete.png"; +static const std::string edit_png = "images/edit.png"; +static const std::string priority_png = "images/priority.png"; +static const std::string done_png = "images/done.png"; +extern int focused; + + +struct ColorFloat +{ + float r, g, b, a; + ColorFloat(float red = 0.f, float green = 0.f, float blue = 0.f, float alpha = 255.f); + + void operator=(sf::Color a); +}; + +ColorFloat operator+(ColorFloat a, ColorFloat b); + +ColorFloat operator-(ColorFloat a, ColorFloat b); + +ColorFloat operator*(ColorFloat a, float b); + +sf::Color ToColor(ColorFloat a); +ColorFloat ToColorF(sf::Color a); + + +struct InputState +{ + bool keys[sf::Keyboard::KeyCount] = { false }; + bool mouse[3] = { false }; + bool mouse_press[3] = { false }; + float wheel = 0.f; + sf::Vector2f mouse_pos = sf::Vector2f(0,0); + sf::Vector2f mouse_prev = sf::Vector2f(0, 0); + sf::Vector2f mouse_speed = sf::Vector2f(0, 0); + sf::Vector2f window_size = sf::Vector2f(0, 0); + float time = 0, dt = 0; + + InputState(); + InputState(bool keys[sf::Keyboard::KeyCount], bool mouse[3], sf::Vector2f mouse_pos, sf::Vector2f mouse_speed); +}; + + +//the object parameters +struct State +{ + sf::Vector2f position = sf::Vector2f(0,0); + sf::Vector2f size = sf::Vector2f(0.1f, 0.1f); + float border_thickness = 0.f; + float margin = default_margin; + float font_size = 1.f; + float scroll = 0.f; + float inside_size = 0.f; + ColorFloat color_main = ToColorF(sf::Color::Black); + ColorFloat color_second = ToColorF(sf::Color::Transparent); + ColorFloat color_border = ColorFloat(200,0,0); +}; + +//interpolate between states for animation effects +State interpolate(State a, State b, float t); + +void UpdateAllObjects(sf::RenderWindow * window, InputState& state); + + +//the object base class +class Object +{ +public: + enum States + { + DEFAULT, ONHOVER, ACTIVE + }; + + enum Allign + { + LEFT, CENTER, RIGHT + }; + + void ApplyScroll(float x); + void SetPosition(float x, float y); + void SetSize(float x, float y); + void SetHeigth(float x); + void SetWidth(float x); + void SetBackgroundColor(sf::Color color); + void SetBorderColor(sf::Color color); + void SetBorderWidth(float S); + void SetMargin(float x); + void SetInsideSize(float x); + void SetScroll(float x); + void Move(sf::Vector2f dx); + + void SetDefaultFunction(std::function fun); + void SetCallbackFunction(std::function fun, bool limit_repeat = false); + void SetHoverFunction(std::function fun); + + + bool RunCallback(sf::RenderWindow * window, InputState& state); + void clone_states(); + + virtual void Draw(sf::RenderWindow * window, InputState& state); + virtual void AddObject(Object* a, Allign b); + void Update(sf::RenderWindow * window, InputState& state); + void UpdateAction(sf::RenderWindow * window, InputState& state); + + Object(); + + Object(Object& A); + Object(Object&& A); + Object(Object* A); + + virtual void operator=(Object& A); + virtual void operator=(Object&& A); + + void copy(Object& A); + void copy(Object&& A); + + virtual Object* GetCopy(); + + State curstate; + State activestate; + State hoverstate; + State defaultstate; + States curmode; + + Allign obj_allign; + + sf::View used_view; + + std::function callback, hoverfn, defaultfn; + + //objects inside this object + std::vector> objects; + std::stack z_buffer; + + //operation time limiter + float action_time; + bool static_object; + bool limiter; + + sf::Vector2f worldPos; + sf::FloatRect obj; + + int id; +}; + +void UpdateAspectRatio(float width, float heigth); +int AddGlobalObject(Object & a); +Object& get_glob_obj(int id); +void RemoveGlobalObject(int id); +void RemoveAllObjects(); +void Add2DeleteQueue(int id); + +//a box to add stuff in +class Box: public Object +{ +public: + void SetBackground(const sf::Texture & texture); + void Draw(sf::RenderWindow *window, InputState& state); + + Box(float x, float y, float dx, float dy, sf::Color color_main = default_main_color); + Box(float dx, float dy); + Box(); + + Box(Box& A); + Box(Box&& A); + + void operator=(Box& A); + void operator=(Box&& A); + + virtual Object* GetCopy(); + + ~Box(); +private: + sf::Texture image; + sf::RectangleShape rect; + sf::View boxView; +}; + + +class Text: public Object +{ +public: + sf::Text text; + + void Draw(sf::RenderWindow *window, InputState& state); + + template + Text(T str, sf::Font &f, float size, sf::Color col); + Text(sf::Text t); + + Text(Text& A); + Text(Text&& A); + + void operator=(Text& A); + void operator=(Text&& A); + + virtual Object* GetCopy(); +}; + + +class MenuBox : public Box +{ +public: + int cursor_id; + + sf::Vector2f dmouse; + + virtual void AddObject(Object * something, Allign a); + + void Cursor(int d); + + void ScrollBy(float dx); + void ScrollTo(float scroll); + + MenuBox(float dx, float dy, float x = 0, float y = 0, sf::Color color_main = sf::Color(0, 0, 0, 0)); + + MenuBox(MenuBox& A); + MenuBox(MenuBox&& A); + + void CreateCallbacks(); + + void operator=(MenuBox& A); + void operator=(MenuBox&& A); + + virtual Object* GetCopy(); +}; + + +class Window: public Box +{ +public: + sf::Vector2f dmouse; + + void Add(Object * something, Allign a = LEFT); + + template + Window(float x, float y, float dx, float dy, sf::Color color_main, T title, sf::Font & font); + + Window(Window& A); + Window(Window&& A); + + void CreateCallbacks(); + + void operator=(Window& A); + void operator=(Window&& A); + + virtual Object* GetCopy(); +}; + + +template +inline Window::Window(float x, float y, float dx, float dy, sf::Color color_main = default_main_color, T title = LOCAL["Window"], sf::Font & font = LOCAL("default")) +{ + defaultstate.position.x = x; + defaultstate.position.y = y; + defaultstate.size.x = dx; + defaultstate.size.y = dy; + defaultstate.color_main = ToColorF(color_main); + clone_states(); + + Box Bar(0, 0, dx, 30, sf::Color(0, 100, 200, 128)), + CloseBx(0, 0, 30, 30, sf::Color(255, 255, 255, 255)); + Text Title(title, font, 25, sf::Color::White); + + CloseBx.hoverstate.color_main = sf::Color(255, 0, 0, 255); + + sf::Image close; close.loadFromFile(close_png); + sf::Texture closetxt; closetxt.loadFromImage(close); + closetxt.setSmooth(true); + CloseBx.SetBackground(closetxt); + + Bar.AddObject(&Title, Box::LEFT); + Bar.AddObject(&CloseBx, Box::RIGHT); + + MenuBox Inside(dx, dy - 30); + + this->AddObject(&Bar, Box::CENTER); + this->AddObject(&Inside, Box::LEFT); + + CreateCallbacks(); +} + +template +inline Text::Text(T str, sf::Font & f, float size, sf::Color col) +{ + text.setString(str); + text.setFont(f); + defaultstate.font_size = size; + defaultstate.color_main = col; + + clone_states(); +} diff --git a/src/Level.cpp b/src/Level.cpp index 6dd5526..7cc23b2 100644 --- a/src/Level.cpp +++ b/src/Level.cpp @@ -16,7 +16,41 @@ */ #include "Level.h" -const Level all_levels[num_levels] = { +using namespace std; + +string ConvertSpaces2_(string text) +{ + while (true) + { + std::size_t pos = text.find(" "); + + if (pos == std::string::npos) + break; + + text = text.replace(pos, 1, "_"); + } + return text; +} + +//get path vector of all files of given type +vector GetFilesInFolder(string folder, string filetype) +{ + vector paths; + + for (const auto & entry : fs::directory_iterator(folder)) + { + //check if the file has the correct filetype + if (entry.path().extension().string() == filetype) + { + paths.push_back(entry.path()); + } + } + + return paths; +} + + +Level all_levels[num_levels] = { //Level 1 Level( 1.8f, -0.12f, 0.5f, //Scale, Angle1, Angle2 @@ -352,6 +386,18 @@ const Level all_levels[num_levels] = { "Fatal Fissures"), //Description }; +Level default_level(1.5f, 0.0f, 0.0f, //Scale, Angle1, Angle2 + Eigen::Vector3f(-1.f, -2.f, 0.f), //Offset + Eigen::Vector3f(1.f, 1.f, 1.f), //Color + 0.035f, //Marble Radius + -2.0f, //Start Look Direction + 3.3f, //Orbit Distance + Eigen::Vector3f(-2.95862f, 2.68825f, -1.11868f), //Marble Position + Eigen::Vector3f(2.95227f, 2.65057f, 1.11848f), //Flag Position + -4.0f, //Death Barrier + false, //Is Planet + "Untitled level"); + Level::Level(float s, float a1, float a2, const Eigen::Vector3f& v, const Eigen::Vector3f& c, @@ -362,7 +408,7 @@ Level::Level(float s, float a1, float a2, const Eigen::Vector3f& end, float kill, bool pg, - const char* desc, + const char* name, float an1, float an2, float an3) { params[0] = s; @@ -377,8 +423,336 @@ Level::Level(float s, float a1, float a2, end_pos = end; kill_y = kill; planet = pg; - txt = desc; + txt = name; anim_1 = an1; anim_2 = an2; anim_3 = an3; + + FractalIter = 16; + + PBR_roughness = 0.5; + PBR_metal = 0.4; + + desc = " "; + + light_dir = Eigen::Vector3f(-0.36, 0.8, 0.48); + light_col = Eigen::Vector3f(1.0, 0.95, 0.8); + + level_id = 0; + link_level = -1; + use_music = "level1.ogg"; + + ground_force = 0.008f; + air_force = 0.004f; + ground_friction = 0.99f; + air_friction = 0.995f; + gravity = 0.005f; + ground_ratio = 1.15f; + FractalParamsAmp = FractalParams(); + FractalParamsFrq = FractalParams(); + + background_col = Eigen::Vector3f(0.6, 0.8, 1.0); + gravity_dir = Eigen::Vector3f(0, 0, -1.0); +} + +void Level::LoadFromFile(fs::path path) +{ + int lvl_size = fs::file_size(path); + int LevelF_size = sizeof(LevelF); + + if (lvl_size != LevelF_size) + { + ERROR_MSG("Invalid .LVL file"); + } + + LevelF loaded_lvl; + + std::ifstream level_file(path, ios_base::in | ios_base::binary); + + level_file.seekg(0); + level_file.read(reinterpret_cast(&loaded_lvl), sizeof(LevelF)); + + level_file.close(); + + LoadLevelF(loaded_lvl); + +} + +void Level::SaveToFile(std::string file, int ID, int Link) +{ + std::ofstream level_file(file, ios_base::out | ios_base::trunc | ios_base::binary); + + LevelF lvl_to_save = GetLevelF(); + + lvl_to_save.level_id = ID; + lvl_to_save.link_level = Link; + + level_file.write(reinterpret_cast(&lvl_to_save), sizeof(lvl_to_save)); + + level_file.close(); +} + +void Level::LoadLevelF(LevelF lvl) +{ + FractalIter = lvl.FractalIterations; //Fractal iterations + params = FractalParams(lvl.FractalParams); //Fractal parameters + + PBR_roughness = lvl.PBR_roughness; //Fractal roughness + PBR_metal = lvl.PBR_metal; //Fractal metalicity + + marble_rad = lvl.marble_rad; //Radius of the marble + start_look_x = lvl.start_look_x; //Camera direction on start + orbit_dist = lvl.orbit_dist; //Distance to orbit + start_pos = Eigen::Vector3f(lvl.start_pos); //Starting position of the marble + end_pos = Eigen::Vector3f(lvl.end_pos); //Ending goal flag position + kill_y = lvl.kill_y; //Below this height player is killed + planet = lvl.planet; //True if gravity should be like a planet + txt = std::string(lvl.txt); //Title + desc = std::string(lvl.desc); //Description + anim_1 = lvl.anim_1; //Animation amount for angle1 parameter + anim_2 = lvl.anim_2; //Animation amount for angle2 parameter + anim_3 = lvl.anim_3; //Animation amount for offset_y parameter + light_dir = Eigen::Vector3f(lvl.light_dir); //Sun light direction + light_col = Eigen::Vector3f(lvl.light_col); //Sun light color + + level_id = lvl.level_id; //level identifier + link_level = lvl.link_level; //Play what level after finish + use_music = std::string(lvl.use_music); //what track to use + + ground_force = lvl.ground_force; + air_force = lvl.air_force; + ground_friction = lvl.ground_friction; + air_friction = lvl.air_friction; + gravity = lvl.gravity; + ground_ratio = lvl.ground_ratio; + background_col = Eigen::Vector3f(lvl.background_col); + gravity_dir = Eigen::Vector3f(lvl.gravity_dir); + + FractalParamsAmp = FractalParams(lvl.FractalParamsAmp); + FractalParamsFrq = FractalParams(lvl.FractalParamsFrq); +} + +LevelF Level::GetLevelF() +{ + LevelF lvlF; + + strcpy(lvlF.txt, txt.c_str()); + strcpy(lvlF.desc, desc.c_str()); + strcpy(lvlF.use_music, use_music.c_str()); + + lvlF.FractalIterations = FractalIter; + std::copy(params.data(), params.data() + 9, lvlF.FractalParams); + + lvlF.PBR_roughness = PBR_roughness; + lvlF.PBR_metal = PBR_metal; + + lvlF.marble_rad = marble_rad; + lvlF.start_look_x = start_look_x; + lvlF.orbit_dist = orbit_dist; + std::copy(start_pos.data(), start_pos.data() + 3, lvlF.start_pos); + std::copy(end_pos.data(), end_pos.data() + 3, lvlF.end_pos); + + lvlF.kill_y = kill_y; + lvlF.planet = planet; + lvlF.anim_1 = anim_1; + lvlF.anim_2 = anim_2; + lvlF.anim_3 = anim_3; + std::copy(light_dir.data(), light_dir.data() + 3, lvlF.light_dir); + std::copy(light_col.data(), light_col.data() + 3, lvlF.light_col); + + lvlF.level_id = level_id; + lvlF.link_level = link_level; + + lvlF.ground_force = ground_force; + lvlF.air_force = air_force; + lvlF.ground_friction = ground_friction; + lvlF.air_friction = air_friction; + lvlF.gravity = gravity; + lvlF.ground_ratio = ground_ratio; + + std::copy(background_col.data(), background_col.data() + 3, lvlF.background_col); + std::copy(gravity_dir.data(), gravity_dir.data() + 3, lvlF.gravity_dir); + + std::copy(FractalParamsAmp.data(), FractalParamsAmp.data() + 9, lvlF.FractalParamsAmp); + std::copy(FractalParamsFrq.data(), FractalParamsFrq.data() + 9, lvlF.FractalParamsFrq); + + return lvlF; +} + +void All_Levels::LoadLevelsFromFolder(std::string folder) +{ + lvl_folder = folder; + level_num = 0; + vector files = GetFilesInFolder(folder, ".lvl"); + for (int i = 0; i < files.size(); i++) + { + LoadLevelFromFile(files[i]); + } + + LoadScoresFromFile(folder + "/scores.bin"); +} + +void All_Levels::LoadMusicFromFolder(std::string folder) +{ + vector files = GetFilesInFolder(folder, ".ogg"); + for (int i = 0; i < files.size(); i++) + { + music_map.insert(std::make_pair(files[i].filename().string(), new sf::Music())); + music_map[files[i].filename().string()]->openFromFile(files[i].string()); + music_map[files[i].filename().string()]->setLoop(true); + music_names.push_back(files[i].filename().string()); + } +} + +Level All_Levels::GetLevel(int ID) +{ + return level_map[ID]; +} + +int All_Levels::GetLevelNum() +{ + return level_num; +} + +std::map All_Levels::getLevelNames() +{ + return level_names; +} + +std::map All_Levels::getLevelDesc() +{ + return level_descriptions; +} + +std::vector All_Levels::getLevelIds() +{ + return level_ids; +} + +std::map All_Levels::getLevelScores() +{ + return score_map; +} + +sf::Music* All_Levels::GetLevelMusic(int ID) +{ + return music_map[level_map[ID].use_music]; +} + +void All_Levels::ReloadLevels() +{ + level_map.clear(); + level_id_map.clear(); + level_names.clear(); + level_descriptions.clear(); + level_ids.clear(); + LoadLevelsFromFolder(lvl_folder); +} + +void All_Levels::LoadLevelFromFile(fs::path file) +{ + Level cur_lvl; + cur_lvl.LoadFromFile(file); + level_map.insert(std::make_pair(cur_lvl.level_id, cur_lvl)); + level_id_map.insert(std::make_pair(level_num, cur_lvl.level_id)); + level_names[cur_lvl.level_id] = cur_lvl.txt; + level_descriptions[cur_lvl.level_id] = cur_lvl.desc; + level_ids.push_back(cur_lvl.level_id); + level_num++; +} + +void All_Levels::LoadScoresFromFile(std::string file) +{ + if (!fs::exists(file)) + { + return; + } + + int scores_size = fs::file_size(file); + int score_size = sizeof(Score); + + if (scores_size % score_size != 0) + { + return; + } + + int lvl_num = scores_size / score_size; + + std::ifstream score_file(file, ios_base::in | ios_base::binary); + //score_file.seekg(0); + for (int i = 0; i < lvl_num; i++) + { + Score sc; + score_file.read(reinterpret_cast(&sc), sizeof(Score)); + if(sc.best_time != 0) + score_map[sc.level_id] = sc; + } + score_file.close(); +} + +float All_Levels::GetBest(int lvl) +{ + return score_map[lvl].best_time; +} + + +void All_Levels::SaveScoresToFile() +{ + std::ofstream score_file(lvl_folder + "/scores.bin", ios_base::trunc | ios_base::binary); + + for (auto &score : score_map) + { + score_file.write(reinterpret_cast(&score.second), sizeof(Score)); + } + + score_file.close(); +} + +bool All_Levels::UpdateScore(int lvl, float time) +{ + float last = 1e10; + if (score_map.count(lvl) > 0 && score_map[lvl].best_time!=0) + { + last = score_map[lvl].best_time; + } + else + { + score_map[lvl] = Score(); + } + bool is_best = timestop(); + } +} + +std::vector All_Levels::GetMusicNames() +{ + return music_names; +} + diff --git a/src/Level.h b/src/Level.h index e7bba0e..a9ca6c0 100644 --- a/src/Level.h +++ b/src/Level.h @@ -15,12 +15,69 @@ * along with this program.If not, see . */ #pragma once +#include +#include +#include #include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#define ERROR_MSG(x) MessageBox(nullptr, TEXT(x), TEXT("ERROR"), MB_OK); +#else +#define ERROR_MSG(x) std::cerr << x << std::endl; +#endif + +namespace fs = std::filesystem; static const int num_levels = 24; static const int num_fractal_params = 9; typedef Eigen::Matrix FractalParams; +struct LevelF +{ + int FractalIterations; + float FractalParams[9]; + float PBR_roughness; //Fractal roughness + float PBR_metal; //Fractal metalicity + + float marble_rad; //Radius of the marble + float start_look_x; //Camera direction on start + float orbit_dist; //Distance to orbit + float start_pos[3]; //Starting position of the marble + float end_pos[3]; //Ending goal flag position + float kill_y; //Below this height player is killed + bool planet; //True if gravity should be like a planet + char txt[255]; //Title + char desc[255]; //Description + float anim_1; //Animation amount for angle1 parameter + float anim_2; //Animation amount for angle2 parameter + float anim_3; //Animation amount for offset_y parameter + float light_dir[3]; //Sun light direction + float light_col[3]; //Sun light color + float background_col[3]; + + int level_id; //level identifier + int link_level; //Play what level after finish + char use_music[255]; //what track to use + + float ground_force; + float air_force; + float ground_friction; + float air_friction; + float gravity; + float ground_ratio; + + float gravity_dir[3]; + + //Advanced Animation + float FractalParamsAmp[9]; + float FractalParamsFrq[9]; +}; + class Level { public: Level() {} @@ -37,7 +94,18 @@ class Level { const char* desc, float an1=0.0f, float an2=0.0f, float an3=0.0f); + void LoadFromFile(fs::path path); + void SaveToFile(std::string file, int ID, int Link); + + void LoadLevelF(LevelF lvl); + LevelF GetLevelF(); + + int FractalIter; //Fractal iterations FractalParams params; //Fractal parameters + + float PBR_roughness; //Fractal roughness + float PBR_metal; //Fractal metalicity + float marble_rad; //Radius of the marble float start_look_x; //Camera direction on start float orbit_dist; //Distance to orbit @@ -45,10 +113,93 @@ class Level { Eigen::Vector3f end_pos; //Ending goal flag position float kill_y; //Below this height player is killed bool planet; //True if gravity should be like a planet - const char* txt; //Description displayed before level + std::string txt; //Title + std::string desc; //Description float anim_1; //Animation amount for angle1 parameter float anim_2; //Animation amount for angle2 parameter float anim_3; //Animation amount for offset_y parameter + Eigen::Vector3f light_dir; //Sun light direction + Eigen::Vector3f light_col; //Sun light color + + int level_id; //level identifier + int link_level; //Play what level after finish + std::string use_music; //what track to use + + Eigen::Vector3f background_col; + + float ground_force; + float air_force; + float ground_friction; + float air_friction; + float gravity; + float ground_ratio; + + Eigen::Vector3f gravity_dir; + + //Advanced Animation + FractalParams FractalParamsAmp; + FractalParams FractalParamsFrq; +}; + +extern Level all_levels[num_levels]; +extern Level default_level; + +std::vector GetFilesInFolder(std::string folder, std::string filetype); + +struct Score +{ + int level_id = 0; + int played_num = 0; + float best_time = 0.f; + float all_time = 0.f; + float last_time = 0.f; +}; + +class All_Levels +{ +public: + All_Levels() {} + + void LoadLevelsFromFolder(std::string folder); + void LoadMusicFromFolder(std::string folder); + + Level GetLevel(int ID); + int GetLevelNum(); + std::map getLevelNames(); + std::map getLevelDesc(); + std::vector getLevelIds(); + std::map getLevelScores(); + sf::Music* GetLevelMusic(int ID); + + void ReloadLevels(); + + void LoadLevelFromFile(fs::path file); + void LoadScoresFromFile(std::string file); + float GetBest(int lvl); + void SaveScoresToFile(); + + bool UpdateScore(int lvl, float time); + void DeleteLevel(int lvl); + sf::Music* GetMusicByID(int ID); + + void StopAllMusic(); + + std::vector GetMusicNames(); + +private: + std::map level_map; + std::map score_map; + std::map level_id_map; + std::map level_names; + std::map level_descriptions; + std::vector level_ids; + + std::map music_map; + std::vector music_names; + + std::string lvl_folder; + + int level_num; }; -extern const Level all_levels[num_levels]; +std::string ConvertSpaces2_(std::string text); \ No newline at end of file diff --git a/src/Localization.cpp b/src/Localization.cpp new file mode 100644 index 0000000..b7cbadd --- /dev/null +++ b/src/Localization.cpp @@ -0,0 +1,115 @@ +#include "Localization.h" + +Localization LOCAL; + +Localization::Localization() +{ +} + +void Localization::LoadLocalsFromFolder(std::string folder) +{ + std::vector files = GetFilesInFolder(folder, ".loc"); + for (int i = 0; i < files.size(); i++) + { + LoadLocalFromFile(files[i]); + } +} + +std::string tostring(std::wstring string_to_convert) +{ + //setup converter + using convert_type = std::codecvt_utf8; + std::wstring_convert converter; + + //use converter (.to_bytes: wstr->str, .from_bytes: str->wstr) + return converter.to_bytes(string_to_convert); +} + +std::wstring utf8_to_wstring(const std::string& str) +{ + std::wstring_convert> myconv; + return myconv.from_bytes(str); +} + +void Localization::LoadLocalFromFile(fs::path path) +{ + std::ifstream local_file(path); + + int element = 0; + + std::string line; + std::string element_name; + std::wstring element_text; + std::string lang; + + std::map local; + std::map fontmap; + + std::wstring_convert> converter; + + int line_num = 0; + while (std::getline(local_file, line)) + { + if (line.substr(0, 1) != "#") + { + element_text.append(((line_num != 0)?L"\n":L"") + converter.from_bytes(line)); + line_num++; + } + else + { + if (element != 0) + { + local[element_name] = element_text; + } + line.erase(std::remove(line.begin(), line.end(), '#'), line.end()); + element_name = line; + if (element == 0) + { + lang = element_name; + } + element_text.clear(); + element++; + line_num = 0; + } + } + //last element + local[element_name] = element_text; + + //Load the font + sf::Font font; + std::wstring assets = L"assets/"; + if (!font.loadFromFile(tostring(assets + local["font_1"]))) { + ERROR_MSG("Unable to load font"); + } + //Load the mono font + sf::Font font_mono; + if (!font_mono.loadFromFile(tostring(assets + local["font_2"]))) { + ERROR_MSG("Unable to load mono font"); + } + + fontmap["default"] = font; + fontmap["mono"] = font_mono; + + local_file.close(); + + locales[lang] = local; + fonts[lang] = fontmap; +} + +void Localization::SetLanguage(std::string lang) +{ + cur_language = lang; +} + +std::wstring Localization::operator[](std::string str) +{ + return locales[cur_language][str]; +} + +sf::Font & Localization::operator()(std::string str) +{ + if (fonts[cur_language].count(str) != 0) + return fonts[cur_language][str]; + else + return sf::Font(); +} diff --git a/src/Localization.h b/src/Localization.h new file mode 100644 index 0000000..1647e65 --- /dev/null +++ b/src/Localization.h @@ -0,0 +1,36 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fs = std::filesystem; + +class Localization +{ +public: + Localization(); + + void LoadLocalsFromFolder(std::string folder); + void LoadLocalFromFile(fs::path path); + void SetLanguage(std::string lang); + + std::wstring operator[](std::string str); + sf::Font& operator()(std::string str); + +private: + std::string cur_language; + std::map> locales; + std::map> fonts; +}; + +extern Localization LOCAL; + +std::wstring utf8_to_wstring(const std::string& str); \ No newline at end of file diff --git a/src/Main.cpp b/src/Main.cpp index 6f3221a..e1b5984 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -14,15 +14,15 @@ * You should have received a copy of the GNU General Public License * along with this program.If not, see . */ -#include "Scene.h" + +#include #include "Level.h" -#include "Overlays.h" #include "Res.h" #include "SelectRes.h" -#include "Scores.h" #include #include #include +#include #include #include #include @@ -31,6 +31,10 @@ #include #include + + + + #ifdef _WIN32 #include #define ERROR_MSG(x) MessageBox(nullptr, TEXT(x), TEXT("ERROR"), MB_OK); @@ -38,67 +42,9 @@ #define ERROR_MSG(x) std::cerr << x << std::endl; #endif -//Constants -static const float mouse_sensitivity = 0.005f; -static const float wheel_sensitivity = 0.2f; -static const float music_vol = 75.0f; -static const float target_fps = 60.0f; - -//Game modes -enum GameMode { - MAIN_MENU, - PLAYING, - PAUSED, - SCREEN_SAVER, - CONTROLS, - LEVELS, - CREDITS, - MIDPOINT -}; - -//Global variables -static sf::Vector2i mouse_pos; -static bool all_keys[sf::Keyboard::KeyCount] = { 0 }; -static bool mouse_clicked = false; -static bool show_cheats = false; -static GameMode game_mode = MAIN_MENU; - -float GetVol() { - if (game_settings.mute) { - return 0.0f; - } else if (game_mode == PAUSED) { - return music_vol / 4; - } else { - return music_vol; - } -} - -void LockMouse(sf::RenderWindow& window) { - window.setMouseCursorVisible(false); - const sf::Vector2u size = window.getSize(); - mouse_pos = sf::Vector2i(size.x / 2, size.y / 2); - sf::Mouse::setPosition(mouse_pos); -} -void UnlockMouse(sf::RenderWindow& window) { - window.setMouseCursorVisible(true); -} - -void PauseGame(sf::RenderWindow& window, Scene& scene) { - game_mode = PAUSED; - scene.GetCurMusic().setVolume(GetVol()); - UnlockMouse(window); - scene.SetExposure(0.5f); -} +//Graphics settings +static bool VSYNC = true; -int DirExists(const char *path) { - struct stat info; - if (stat(path, &info) != 0) { - return 0; - } else if (info.st_mode & S_IFDIR) { - return 1; - } - return 0; -} #if defined(_WIN32) int WinMain(HINSTANCE hInstance, HINSTANCE, LPTSTR lpCmdLine, int nCmdShow) { @@ -106,19 +52,21 @@ int WinMain(HINSTANCE hInstance, HINSTANCE, LPTSTR lpCmdLine, int nCmdShow) { int main(int argc, char *argv[]) { #endif //Make sure shader is supported + std::stringstream error_log; + sf::err().rdbuf(error_log.rdbuf()); if (!sf::Shader::isAvailable()) { - ERROR_MSG("Graphics card does not support shaders"); + ERROR_MSG((error_log.str()).c_str()); return 1; } //Load the vertex shader sf::Shader shader; if (!shader.loadFromFile(vert_glsl, sf::Shader::Vertex)) { - ERROR_MSG("Failed to compile vertex shader"); + ERROR_MSG((error_log.str()).c_str()); return 1; } //Load the fragment shader if (!shader.loadFromFile(frag_glsl, sf::Shader::Fragment)) { - ERROR_MSG("Failed to compile fragment shader"); + ERROR_MSG((error_log.str()).c_str()); return 1; } @@ -173,8 +121,6 @@ int main(int argc, char *argv[]) { const std::string save_file = save_dir + "/scores.bin"; const std::string settings_file = save_dir + "/settings.bin"; - //Load scores if available - high_scores.Load(save_file); game_settings.Load(settings_file); //Have user select the resolution @@ -187,8 +133,8 @@ int main(int argc, char *argv[]) { //GL settings sf::ContextSettings settings; - settings.majorVersion = 2; - settings.minorVersion = 0; + settings.majorVersion = 4; + settings.minorVersion = 3; //Create the window sf::VideoMode screen_size; @@ -199,235 +145,391 @@ int main(int argc, char *argv[]) { window_style = sf::Style::Fullscreen; } else { screen_size = sf::VideoMode(resolution->width, resolution->height, 24); - window_style = sf::Style::Close; + window_style = sf::Style::Default; } - sf::RenderWindow window(screen_size, "Marble Marcher", window_style, settings); - window.setVerticalSyncEnabled(true); + + AdditionalSettings addsett; + addsett.Load("assets/config.txt"); + LOCAL.LoadLocalsFromFolder(local_folder); + LOCAL.SetLanguage(addsett.lang); + + sf::Vector2f screenshot_size = sf::Vector2f(addsett.screenshot_width, addsett.screenshot_height); + + sf::RenderWindow window(screen_size, "Marble Marcher Community Edition", window_style, settings); + window.setVerticalSyncEnabled(VSYNC); window.setKeyRepeatEnabled(false); + + INIT(); + + sf::VideoMode fs_size = sf::VideoMode::getDesktopMode(); + window.setSize(sf::Vector2u(fs_size.width, fs_size.width*9.f/16.f)); + window.setPosition(sf::Vector2i(0, 0)); window.requestFocus(); + UpdateAspectRatio(window.getSize().x, window.getSize().y); + //set window icon + sf::Image icon; + icon.loadFromFile(icon_png); + window.setIcon(icon.getSize().x, icon.getSize().y, icon.getPixelsPtr()); //If native resolution is the same, then we don't need a render texture if (resolution->width == screen_size.width && resolution->height == screen_size.height) { fullscreen = false; } + //force fullscreen mode + fullscreen = true; + + Renderer rend(resolution->width, resolution->height, "shaders/compute/MAIN.cfg"); + + //fullscreen = false; //Create the render texture if needed sf::RenderTexture renderTexture; + //screenshot number + int screenshot_i = 0; + sf::RenderTexture screenshotTexture; + + //new rendering textures + sf::Image mimg, simg; + mimg.create(resolution->width, resolution->height, sf::Color::Blue); + sf::Texture main_txt, screenshot_txt; + main_txt.loadFromImage(mimg); + screenshot_txt.create(screenshot_size.x, screenshot_size.y); + + screenshotTexture.create(screenshot_size.x, screenshot_size.y, settings); + screenshotTexture.setSmooth(false); + if (fullscreen) { renderTexture.create(resolution->width, resolution->height, settings); - renderTexture.setSmooth(true); - renderTexture.setActive(true); - window.setActive(false); + renderTexture.setSmooth(false); } + sf::View default_window_view = window.getDefaultView(); + //Create the fractal scene Scene scene(level_music); const sf::Glsl::Vec2 window_res((float)resolution->width, (float)resolution->height); - shader.setUniform("iResolution", window_res); + const sf::Glsl::Vec2 sres_res((float)screenshot_size.x, (float)screenshot_size.y); scene.Write(shader); - + scene.SetResolution(shader, window_res.x, window_res.y); + scene.SetWindowResolution(window.getSize().x, window.getSize().y); //Create screen rectangle sf::RectangleShape rect; rect.setSize(window_res); rect.setPosition(0, 0); - //Create the menus - Overlays overlays(&font, &font_mono); - overlays.SetScale(float(screen_size.width) / 1280.0f); + sf::RectangleShape rect_scrshot; + rect_scrshot.setSize(sres_res); + rect_scrshot.setPosition(0, 0); + menu_music.setVolume(GetVol()); - menu_music.play(); + //menu_music.play(); //Main loop sf::Clock clock; float smooth_fps = target_fps; float lag_ms = 0.0f; + mouse_pos = sf::Vector2i(0, 0); + mouse_prev_pos = sf::Vector2i(0, 0); + + //temporary level generation code + /*for (int i = 0; i < 24; i++) + { + all_levels[i].desc = "Official Level by Codeparade"; + all_levels[i].SaveToFile(std::string(level_folder) + "/" + ConvertSpaces2_(all_levels[i].txt)+".lvl", i, (i<24)?(i+1):-1); + }*/ + + scene.levels.LoadLevelsFromFolder(level_folder); + scene.levels.LoadMusicFromFolder(music_folder); + + //Create the menus + Overlays overlays(&font, &font_mono, &scene); + overlays.SetScale(float(screen_size.width) / 1280.0f); + + + scene.StartDefault(); + overlays.SetAntTweakBar(window.getSize().x, window.getSize().y, smooth_fps, &scene, &rend, &VSYNC, &mouse_sensitivity, &wheel_sensitivity, &music_vol, &target_fps); + + io_state.window_size = sf::Vector2f(window.getSize().x, window.getSize().y); + float prev_s = 0; + + OpenMainMenu(&scene, &overlays); + while (window.isOpen()) { sf::Event event; - float mouse_wheel = 0.0f; - while (window.pollEvent(event)) { - if (event.type == sf::Event::Closed) { - window.close(); - break; - } else if (event.type == sf::Event::LostFocus) { - if (game_mode == PLAYING) { - PauseGame(window, scene); - } - } else if (event.type == sf::Event::KeyPressed) { - const sf::Keyboard::Key keycode = event.key.code; - if (event.key.code < 0 || event.key.code >= sf::Keyboard::KeyCount) { continue; } - if (game_mode == CREDITS) { - game_mode = MAIN_MENU; - UnlockMouse(window); - scene.SetMode(Scene::INTRO); - scene.SetExposure(1.0f); - credits_music.stop(); - menu_music.setVolume(GetVol()); - menu_music.play(); - } else if (game_mode == MIDPOINT) { - game_mode = PLAYING; - scene.SetExposure(1.0f); - credits_music.stop(); - scene.StartNextLevel(); - } else if (keycode == sf::Keyboard::Escape) { - if (game_mode == MAIN_MENU) { - window.close(); - break; - } else if (game_mode == CONTROLS || game_mode == LEVELS) { - game_mode = MAIN_MENU; - scene.SetExposure(1.0f); - } else if (game_mode == SCREEN_SAVER) { - game_mode = MAIN_MENU; - scene.SetMode(Scene::INTRO); - } else if (game_mode == PAUSED) { - game_mode = PLAYING; - scene.GetCurMusic().setVolume(GetVol()); - scene.SetExposure(1.0f); - LockMouse(window); - } else if (game_mode == PLAYING) { - PauseGame(window, scene); - } - } else if (keycode == sf::Keyboard::R) { - if (game_mode == PLAYING) { - scene.ResetLevel(); - } - } else if (keycode == sf::Keyboard::F1) { - if (game_mode == PLAYING && high_scores.HasCompleted(num_levels - 1)) { - show_cheats = !show_cheats; - scene.EnbaleCheats(); - } - } else if (keycode == sf::Keyboard::C) { - scene.Cheat_ColorChange(); - } else if (keycode == sf::Keyboard::F) { - scene.Cheat_FreeCamera(); - } else if (keycode == sf::Keyboard::G) { - scene.Cheat_Gravity(); - } else if (keycode == sf::Keyboard::H) { - scene.Cheat_HyperSpeed(); - } else if (keycode == sf::Keyboard::I) { - scene.Cheat_IgnoreGoal(); - } else if (keycode == sf::Keyboard::M) { - scene.Cheat_Motion(); - } else if (keycode == sf::Keyboard::P) { - scene.Cheat_Planet(); - } else if (keycode == sf::Keyboard::Z) { - if (scene.GetParamMod() == -1) { - scene.Cheat_Zoom(); - } else { - scene.Cheat_Param(-1); - } - } if (keycode >= sf::Keyboard::Num0 && keycode <= sf::Keyboard::Num9) { - scene.Cheat_Param(int(keycode) - int(sf::Keyboard::Num1)); - } - all_keys[keycode] = true; - } else if (event.type == sf::Event::KeyReleased) { - const sf::Keyboard::Key keycode = event.key.code; - if (event.key.code < 0 || event.key.code >= sf::Keyboard::KeyCount) { continue; } - all_keys[keycode] = false; - } else if (event.type == sf::Event::MouseButtonPressed) { - if (event.mouseButton.button == sf::Mouse::Left) { - mouse_pos = sf::Vector2i(event.mouseButton.x, event.mouseButton.y); - mouse_clicked = true; - if (game_mode == MAIN_MENU) { - const Overlays::Texts selected = overlays.GetOption(Overlays::PLAY, Overlays::EXIT); - if (selected == Overlays::PLAY) { - game_mode = PLAYING; - menu_music.stop(); - scene.StartNewGame(); - scene.GetCurMusic().setVolume(GetVol()); - scene.GetCurMusic().play(); - LockMouse(window); - } else if (selected == Overlays::CONTROLS) { - game_mode = CONTROLS; - } else if (selected == Overlays::LEVELS) { - game_mode = LEVELS; - overlays.GetLevelPage() = 0; - scene.SetExposure(0.5f); - } else if (selected == Overlays::SCREEN_SAVER) { - game_mode = SCREEN_SAVER; - scene.SetMode(Scene::SCREEN_SAVER); - } else if (selected == Overlays::EXIT) { - window.close(); - break; - } - } else if (game_mode == CONTROLS) { - const Overlays::Texts selected = overlays.GetOption(Overlays::BACK, Overlays::BACK); - if (selected == Overlays::BACK) { - game_mode = MAIN_MENU; - } - } else if (game_mode == LEVELS) { - const Overlays::Texts selected = overlays.GetOption(Overlays::L0, Overlays::BACK2); - if (selected == Overlays::BACK2) { - game_mode = MAIN_MENU; - scene.SetExposure(1.0f); - } else if (selected == Overlays::PREV) { - overlays.GetLevelPage() -= 1; - } else if (selected == Overlays::NEXT) { - overlays.GetLevelPage() += 1; - } else if (selected >= Overlays::L0 && selected <= Overlays::L14) { - const int level = selected - Overlays::L0 + overlays.GetLevelPage() * Overlays::LEVELS_PER_PAGE; - if (high_scores.HasUnlocked(level)) { - game_mode = PLAYING; - menu_music.stop(); - scene.SetExposure(1.0f); - scene.StartSingle(level); - scene.GetCurMusic().setVolume(GetVol()); - scene.GetCurMusic().play(); - LockMouse(window); - } - } - } else if (game_mode == SCREEN_SAVER) { - scene.SetMode(Scene::INTRO); - game_mode = MAIN_MENU; - } else if (game_mode == PAUSED) { - const Overlays::Texts selected = overlays.GetOption(Overlays::CONTINUE, Overlays::MOUSE); - if (selected == Overlays::CONTINUE) { - game_mode = PLAYING; - scene.GetCurMusic().setVolume(GetVol()); - scene.SetExposure(1.0f); - LockMouse(window); - } else if (selected == Overlays::RESTART) { - game_mode = PLAYING; - scene.ResetLevel(); - scene.GetCurMusic().setVolume(GetVol()); - scene.SetExposure(1.0f); - LockMouse(window); - } else if (selected == Overlays::QUIT) { - if (scene.IsSinglePlay()) { - game_mode = LEVELS; - } else { - game_mode = MAIN_MENU; - scene.SetExposure(1.0f); - } - scene.SetMode(Scene::INTRO); - scene.StopAllMusic(); - menu_music.setVolume(GetVol()); - menu_music.play(); - } else if (selected == Overlays::MUSIC) { - game_settings.mute = !game_settings.mute; - for (int i = 0; i < num_level_music; ++i) { - level_music[i].setVolume(GetVol()); - } - } else if (selected == Overlays::MOUSE) { - game_settings.mouse_sensitivity = (game_settings.mouse_sensitivity + 1) % 3; - } - } - } else if (event.mouseButton.button == sf::Mouse::Right) { - if (game_mode == PLAYING) { - scene.ResetLevel(); - } - } - } else if (event.type == sf::Event::MouseButtonReleased) { - if (event.mouseButton.button == sf::Mouse::Left) { - mouse_pos = sf::Vector2i(event.mouseButton.x, event.mouseButton.y); - mouse_clicked = false; - } - } else if (event.type == sf::Event::MouseMoved) { - mouse_pos = sf::Vector2i(event.mouseMove.x, event.mouseMove.y); - } else if (event.type == sf::Event::MouseWheelScrolled) { - mouse_wheel += event.mouseWheelScroll.delta; - } - } - + + float mouse_wheel = 0.0f; + mouse_prev_pos = mouse_pos; + io_state.mouse_prev = sf::Vector2f(mouse_prev_pos.x, mouse_prev_pos.y); + io_state.wheel = mouse_wheel; + io_state.mouse_press[2] = false; + io_state.mouse_press[0] = false; + while (window.pollEvent(event)) + { + bool handled = overlays.TwManageEvent(&event); + if (event.type == sf::Event::Closed) { + window.close(); + break; + } + else if (event.type == sf::Event::LostFocus) { + if (game_mode == PLAYING) { + PauseGame(window, scene); + } + } + else if (event.type == sf::Event::Resized) { + screen_size.width = event.size.width; + screen_size.height = event.size.height; + overlays.SetScale( std::max(float(screen_size.width), float(screen_size.height))/ 1280.0f); + sf::FloatRect visibleArea(0, 0, event.size.width, event.size.height); + default_window_view = sf::View(visibleArea); + window.setView(default_window_view); + io_state.window_size = sf::Vector2f(window.getSize().x, window.getSize().y); + UpdateAspectRatio(window.getSize().x, window.getSize().y); + scene.SetWindowResolution(window.getSize().x, window.getSize().y); + rend.camera.SetAspectRatio((float)window.getSize().x / (float)window.getSize().y); + } + + // If event has not been handled by AntTweakBar, process it + if (!handled) + { + if (event.type == sf::Event::KeyPressed) { + const sf::Keyboard::Key keycode = event.key.code; + all_keys[keycode] = true; + io_state.keys[keycode] = true; + if (event.key.code < 0 || event.key.code >= sf::Keyboard::KeyCount) { continue; } + if (game_mode == CREDITS) { + OpenMainMenu(&scene, &overlays); + UnlockMouse(window); + scene.SetMode(Scene::INTRO); + scene.SetExposure(1.0f); + credits_music.stop(); + scene.levels.StopAllMusic(); + menu_music.setVolume(GetVol()); + //menu_music.play(); + } + else if (game_mode == MIDPOINT) { + game_mode = PLAYING; + scene.SetExposure(1.0f); + credits_music.stop(); + scene.StartNextLevel(); + } + else if (keycode == sf::Keyboard::Escape) { + if (game_mode == MAIN_MENU) { + window.close(); + break; + } + else if (game_mode == CONTROLS || game_mode == LEVELS) { + OpenMainMenu(&scene, &overlays); + } + else if (game_mode == SCREEN_SAVER) { + OpenMainMenu(&scene, &overlays); + } + else if (game_mode == PAUSED) { + game_mode = PLAYING; + scene.GetCurMusic().setVolume(GetVol()); + scene.SetExposure(1.0f); + LockMouse(window); + } + else if (game_mode == PLAYING) { + PauseGame(window, scene); + } + else if (game_mode == LEVEL_EDITOR) + { + RemoveGlobalObject(focused); + ConfirmEditorExit(&scene, &overlays); + } + } + else if (keycode == sf::Keyboard::R) { + if (game_mode == PLAYING) { + scene.ResetLevel(); + } + } + else if (keycode == sf::Keyboard::F1) { + if (game_mode == PLAYING) { + show_cheats = !show_cheats; + scene.EnbaleCheats(); + } + } else if (keycode == sf::Keyboard::F5) { + ///Screenshot + screenshot_i++; + //Update the shader values + scene.SetResolution(shader, sres_res.x, sres_res.y); + rend.ReInitialize(sres_res.x, sres_res.y); + scene.WriteRenderer(rend); + shader.setUniform("render_texture", screenshot_txt); + rend.SetOutputTexture(screenshot_txt); + scene.Write(shader); + + //Setup full-screen shader + sf::RenderStates states = sf::RenderStates::Default; + states.shader = &shader; + //shader.setUniform("render_texture", screenshot_txt); + + window.setActive(false); + //Draw the fractal + //Draw to the render texture + screenshotTexture.setActive(true); + + rend.camera.SetMotionBlur(0); + rend.Render(); + + screenshotTexture.draw(rect_scrshot, states); + screenshotTexture.display(); + screenshotTexture.getTexture().copyToImage().saveToFile((std::string)"screenshots/screenshot"+(std::string)num2str(time(NULL))+".jpg"); + + screenshotTexture.setActive(false); + window.setActive(true); + + scene.SetResolution(shader, window_res.x, window_res.y); + rend.ReInitialize(window_res.x, window_res.y); + rend.camera.SetAspectRatio((float)window.getSize().x / (float)window.getSize().y); + scene.Write(shader); + } else if (keycode == sf::Keyboard::F4) { + overlays.TWBAR_ENABLED = !overlays.TWBAR_ENABLED; + } else if (keycode == sf::Keyboard::C) { + scene.Cheat_ColorChange(); + } else if (keycode == sf::Keyboard::F) { + scene.Cheat_FreeCamera(); + } + else if (keycode == sf::Keyboard::G) { + scene.Cheat_Gravity(); + } + else if (keycode == sf::Keyboard::H) { + scene.Cheat_HyperSpeed(); + } + else if (keycode == sf::Keyboard::I) { + scene.Cheat_IgnoreGoal(); + } + else if (keycode == sf::Keyboard::M) { + scene.Cheat_Motion(); + } + else if (keycode == sf::Keyboard::P) { + scene.Cheat_Planet(); + } + else if (keycode == sf::Keyboard::Z) { + if (scene.GetParamMod() == -1) { + scene.Cheat_Zoom(); + } + else { + scene.Cheat_Param(-1); + } + } if (keycode >= sf::Keyboard::Num0 && keycode <= sf::Keyboard::Num9) { + scene.Cheat_Param(int(keycode) - int(sf::Keyboard::Num1)); + } + + } + else if (event.type == sf::Event::KeyReleased) { + const sf::Keyboard::Key keycode = event.key.code; + if (event.key.code < 0 || event.key.code >= sf::Keyboard::KeyCount) { continue; } + all_keys[keycode] = false; + io_state.keys[keycode] = false; + } + else if (event.type == sf::Event::MouseButtonPressed) { + if (event.mouseButton.button == sf::Mouse::Left) { + mouse_pos = sf::Vector2i(event.mouseButton.x, event.mouseButton.y); + mouse_clicked = true; + io_state.mouse[0] = true; + io_state.mouse_press[0] = true; + if (game_mode == MAIN_MENU) { + const Overlays::Texts selected = overlays.GetOption(Overlays::PLAY, Overlays::EXIT); + } + else if (game_mode == CONTROLS) { + const Overlays::Texts selected = overlays.GetOption(Overlays::BACK, Overlays::BACK); + if (selected == Overlays::BACK) { + OpenMainMenu(&scene, &overlays); + } + } + else if (game_mode == LEVELS) { + + } + else if (game_mode == SCREEN_SAVER) { + OpenMainMenu(&scene, &overlays); + } + else if (game_mode == PAUSED) { + const Overlays::Texts selected = overlays.GetOption(Overlays::CONTINUE, Overlays::MOUSE); + if (selected == Overlays::CONTINUE) { + game_mode = PLAYING; + scene.GetCurMusic().setVolume(GetVol()); + scene.SetExposure(1.0f); + LockMouse(window); + } + else if (selected == Overlays::RESTART) { + game_mode = PLAYING; + scene.ResetLevel(); + scene.GetCurMusic().setVolume(GetVol()); + scene.SetExposure(1.0f); + LockMouse(window); + } + else if (selected == Overlays::QUIT) { + if (scene.IsSinglePlay()) + { + OpenLevelMenu(&scene, &overlays); + } + else + { + OpenMainMenu(&scene, &overlays); + } + scene.SetMode(Scene::INTRO); + scene.StopAllMusic(); + menu_music.setVolume(GetVol()); + //menu_music.play(); + } + else if (selected == Overlays::MUSIC) { + game_settings.mute = !game_settings.mute; + for (int i = 0; i < num_level_music; ++i) { + level_music[i].setVolume(GetVol()); + } + } + else if (selected == Overlays::MOUSE) { + game_settings.mouse_sensitivity = (game_settings.mouse_sensitivity + 1) % 3; + } + } + if (game_mode == LEVEL_EDITOR) + { + if (scene.cur_ed_mode == Scene::EditorMode::PLACE_MARBLE + || scene.cur_ed_mode == Scene::EditorMode::PLACE_FLAG) + { + scene.cur_ed_mode = Scene::EditorMode::DEFAULT; + } + } + } + else if (event.mouseButton.button == sf::Mouse::Right) { + io_state.mouse[2] = true; + io_state.mouse_press[2] = true; + if (game_mode == PLAYING) { + scene.ResetLevel(); + } + } + } + else if (event.type == sf::Event::MouseButtonReleased) { + if (event.mouseButton.button == sf::Mouse::Left) { + io_state.mouse[0] = false; + mouse_pos = sf::Vector2i(event.mouseButton.x, event.mouseButton.y); + mouse_clicked = false; + } + else if (event.mouseButton.button == sf::Mouse::Right) { + io_state.mouse[2] = false; + } + } + else if (event.type == sf::Event::MouseMoved) { + mouse_pos = sf::Vector2i(event.mouseMove.x, event.mouseMove.y); + io_state.mouse_pos = sf::Vector2f(mouse_pos.x, mouse_pos.y); + if (scene.cur_ed_mode == Scene::EditorMode::PLACE_MARBLE) + { + Eigen::Vector3f marble_pos = scene.MouseRayCast(mouse_pos.x, mouse_pos.y, scene.level_copy.marble_rad); + scene.level_copy.start_pos = marble_pos; + } + else if (scene.cur_ed_mode == Scene::EditorMode::PLACE_FLAG) + { + Eigen::Vector3f flag_pos = scene.MouseRayCast(mouse_pos.x, mouse_pos.y); + scene.level_copy.end_pos = flag_pos; + } + } + else if (event.type == sf::Event::MouseWheelScrolled) { + mouse_wheel += event.mouseWheelScroll.delta; + io_state.wheel = mouse_wheel; + } + } + } //Check if the game was beat if (scene.GetMode() == Scene::FINAL && game_mode != CREDITS) { game_mode = CREDITS; @@ -450,10 +552,9 @@ int main(int argc, char *argv[]) { overlays.UpdateControls((float)mouse_pos.x, (float)mouse_pos.y); } else if (game_mode == LEVELS) { scene.UpdateCamera(); - overlays.UpdateLevels((float)mouse_pos.x, (float)mouse_pos.y); } else if (game_mode == SCREEN_SAVER) { scene.UpdateCamera(); - } else if (game_mode == PLAYING || game_mode == CREDITS || game_mode == MIDPOINT) { + } else if (game_mode == PLAYING || game_mode == CREDITS || game_mode == MIDPOINT || game_mode == LEVEL_EDITOR) { //Collect keyboard input const float force_lr = (all_keys[sf::Keyboard::Left] || all_keys[sf::Keyboard::A] ? -1.0f : 0.0f) + @@ -462,22 +563,55 @@ int main(int argc, char *argv[]) { (all_keys[sf::Keyboard::Down] || all_keys[sf::Keyboard::S] ? -1.0f : 0.0f) + (all_keys[sf::Keyboard::Up] || all_keys[sf::Keyboard::W] ? 1.0f : 0.0f); - //Collect mouse input - const sf::Vector2i mouse_delta = mouse_pos - screen_center; - sf::Mouse::setPosition(screen_center, window); - float ms = mouse_sensitivity; - if (game_settings.mouse_sensitivity == 1) { - ms *= 0.5f; - } else if (game_settings.mouse_sensitivity == 2) { - ms *= 0.25f; - } - const float cam_lr = float(-mouse_delta.x) * ms; - const float cam_ud = float(-mouse_delta.y) * ms; - const float cam_z = mouse_wheel * wheel_sensitivity; + //Apply forces to marble and camera + scene.UpdateMarble(force_lr, force_ud); + - //Apply forces to marble and camera - scene.UpdateMarble(force_lr, force_ud); - scene.UpdateCamera(cam_lr, cam_ud, cam_z, mouse_clicked); + scene.free_camera_speed *= 1 + mouse_wheel * 0.05; + //Collect mouse input + if (overlays.TWBAR_ENABLED) + { + + sf::Vector2i mouse_delta = sf::Vector2i(0, 0); + window.setMouseCursorVisible(true); + if (mouse_clicked) + { + mouse_delta = mouse_pos - mouse_prev_pos; + } + + float ms = mouse_sensitivity; + if (game_settings.mouse_sensitivity == 1) { + ms *= 0.5f; + } + else if (game_settings.mouse_sensitivity == 2) { + ms *= 0.25f; + } + const float cam_lr = float(-mouse_delta.x) * ms; + const float cam_ud = float(-mouse_delta.y) * ms; + const float cam_z = mouse_wheel * wheel_sensitivity; + + scene.UpdateCamera(cam_lr, cam_ud, cam_z, mouse_clicked); + } + else + { + window.setMouseCursorVisible(false); + const sf::Vector2i mouse_delta = mouse_pos - screen_center; + sf::Mouse::setPosition(screen_center, window); + float ms = mouse_sensitivity; + if (game_settings.mouse_sensitivity == 1) { + ms *= 0.5f; + } + else if (game_settings.mouse_sensitivity == 2) { + ms *= 0.25f; + } + const float cam_lr = float(-mouse_delta.x) * ms; + const float cam_ud = float(-mouse_delta.y) * ms; + const float cam_z = mouse_wheel * wheel_sensitivity; + + scene.UpdateCamera(cam_lr, cam_ud, cam_z, mouse_clicked); + } + + } else if (game_mode == PAUSED) { overlays.UpdatePaused((float)mouse_pos.x, (float)mouse_pos.y); } @@ -488,8 +622,12 @@ int main(int argc, char *argv[]) { lag_ms -= 1000.0f / target_fps; skip_frame = true; } else { + window.setVerticalSyncEnabled(VSYNC); //Update the shader values scene.Write(shader); + scene.WriteRenderer(rend); + shader.setUniform("render_texture", main_txt); + rend.SetOutputTexture(main_txt); //Setup full-screen shader sf::RenderStates states = sf::RenderStates::Default; @@ -497,7 +635,10 @@ int main(int argc, char *argv[]) { //Draw the fractal if (fullscreen) { + window.setActive(false); + renderTexture.setActive(true); //Draw to the render texture + rend.Render(); renderTexture.draw(rect, states); renderTexture.display(); @@ -506,7 +647,10 @@ int main(int argc, char *argv[]) { sprite.setScale(float(screen_size.width) / float(resolution->width), float(screen_size.height) / float(resolution->height)); window.draw(sprite); + renderTexture.setActive(false); + window.setActive(true); } else { + window.setActive(true); //Draw directly to the main window window.draw(rect, states); } @@ -521,7 +665,7 @@ int main(int argc, char *argv[]) { overlays.DrawLevels(window); } else if (game_mode == PLAYING) { if (scene.GetMode() == Scene::ORBIT && scene.GetMarble().x() < 998.0f) { - overlays.DrawLevelDesc(window, scene.GetLevel()); + overlays.DrawLevelDesc(window, scene.level_copy.txt); } else if (scene.GetMode() == Scene::MARBLE && !scene.IsFreeCamera()) { overlays.DrawArrow(window, scene.GetGoalDirection()); } @@ -547,16 +691,29 @@ int main(int argc, char *argv[]) { } else if (game_mode == MIDPOINT) { overlays.DrawMidPoint(window, scene.IsFullRun(), scene.GetSumTime()); } - if (!scene.IsFreeCamera()) { + if (!scene.IsFreeCamera() || game_mode == LEVEL_EDITOR) { overlays.DrawFPS(window, int(smooth_fps + 0.5f)); } + + //new interface render stuff + io_state.dt = prev_s; + io_state.time += io_state.dt; + UpdateAllObjects(&window, io_state); + window.setView(default_window_view); + if (!skip_frame) { //Finally display to the screen + + if(overlays.TWBAR_ENABLED) + scene.Synchronize(); + overlays.DrawAntTweakBar(); + window.display(); //If V-Sync is running higher than desired fps, slow down! const float s = clock.restart().asSeconds(); + prev_s = s; if (s > 0.0f) { smooth_fps = smooth_fps*0.9f + std::min(1.0f / s, target_fps)*0.1f; } @@ -570,12 +727,15 @@ int main(int argc, char *argv[]) { } } + // RemoveAllObjects(); + //UpdateAllObjects(&window, io_state); //Stop all music menu_music.stop(); scene.StopAllMusic(); credits_music.stop(); - high_scores.Save(save_file); + scene.levels.SaveScoresToFile(); game_settings.Save(settings_file); + TwTerminate(); #ifdef _DEBUG system("pause"); diff --git a/src/Overlays.cpp b/src/Overlays.cpp index e8298bd..722740f 100644 --- a/src/Overlays.cpp +++ b/src/Overlays.cpp @@ -15,20 +15,21 @@ * along with this program.If not, see . */ #include "Overlays.h" -#include "Level.h" #include "Res.h" -#include "Scores.h" +#include -static const float pi = 3.14159265359f; +static const float PI = 3.14159265359f; static const int num_level_pages = 1 + (num_levels - 1) / Overlays::LEVELS_PER_PAGE; Settings game_settings; -Overlays::Overlays(const sf::Font* _font, const sf::Font* _font_mono) : +Overlays::Overlays(sf::Font* _font, sf::Font* _font_mono, Scene* scene) : font(_font), font_mono(_font_mono), draw_scale(1.0f), level_page(0), - top_level(true) { + top_level(true), + TWBAR_ENABLED(false) +{ memset(all_hover, 0, sizeof(all_hover)); buff_hover.loadFromFile(menu_hover_wav); sound_hover.setBuffer(buff_hover); @@ -42,6 +43,13 @@ Overlays::Overlays(const sf::Font* _font, const sf::Font* _font_mono) : arrow_tex.setSmooth(true); arrow_spr.setTexture(arrow_tex); arrow_spr.setOrigin(arrow_spr.getLocalBounds().width / 2, arrow_spr.getLocalBounds().height / 2); + + ReloadLevelMenu(scene); +} + +void Overlays::ReloadLevelMenu(Scene* scene) +{ + } Overlays::Texts Overlays::GetOption(Texts from, Texts to) { @@ -56,25 +64,25 @@ Overlays::Texts Overlays::GetOption(Texts from, Texts to) { void Overlays::UpdateMenu(float mouse_x, float mouse_y) { //Update text boxes - MakeText("Marble\nMarcher", 60, 20, 72, sf::Color::White, all_text[TITLE]); - MakeText("Play", 80, 230, 60, sf::Color::White, all_text[PLAY]); - MakeText("Levels", 80, 300, 60, sf::Color::White, all_text[LEVELS]); - MakeText("Controls", 80, 370, 60, sf::Color::White, all_text[CONTROLS]); - MakeText("Screen Saver", 80, 440, 60, sf::Color::White, all_text[SCREEN_SAVER]); - MakeText("Exit", 80, 510, 60, sf::Color::White, all_text[EXIT]); - MakeText("\xA9""2019 CodeParade 1.1.1\nMusic by PettyTheft", 16, 652, 32, sf::Color::White, all_text[CREDITS], true); - all_text[TITLE].setLineSpacing(0.76f); - all_text[CREDITS].setLineSpacing(0.9f); + //MakeText(LOCAL["Marble_Marcher"], 60, 20, 72, sf::Color::White, all_text[TITLE]); + /* MakeText(LOCAL["Play"], 80, 230, 60, sf::Color::White, all_text[PLAY]); + MakeText(LOCAL["Levels"], 80, 300, 60, sf::Color::White, all_text[LEVELS]); + MakeText(LOCAL["Controls"], 80, 370, 60, sf::Color::White, all_text[CONTROLS]); + MakeText(LOCAL["Screen_Saver"], 80, 440, 60, sf::Color::White, all_text[SCREEN_SAVER]); + MakeText(LOCAL["Exit"], 80, 510, 60, sf::Color::White, all_text[EXIT]); + */ + //MakeText(LOCAL["About"], 16, 652, 32, sf::Color::White, all_text[CREDITS], true); + // all_text[TITLE].setLineSpacing(0.76f); + //all_text[CREDITS].setLineSpacing(0.9f); //Check if mouse intersects anything - UpdateHover(PLAY, EXIT, mouse_x, mouse_y); + // UpdateHover(PLAY, EXIT, mouse_x, mouse_y); } void Overlays::UpdateControls(float mouse_x, float mouse_y) { //Update text boxes - MakeText("Roll\nCamera\nZoom\nRestart\nPause", 40, 200, 46, sf::Color::White, all_text[CONTROLS_L]); - MakeText("WASD or Arrows\nMouse\nScroll Wheel\nR or Right-Click\nEsc", 280, 200, 46, sf::Color::White, all_text[CONTROLS_R]); - MakeText("Back", 60, 550, 40, sf::Color::White, all_text[BACK]); + MakeText(LOCAL["DetailControls"], 40, 200, 46, sf::Color::White, all_text[CONTROLS_L]); + MakeText(LOCAL["Back"], 60, 550, 40, sf::Color::White, all_text[BACK]); //A little extra vertical spacing all_text[CONTROLS_L].setLineSpacing(1.1f); @@ -85,55 +93,38 @@ void Overlays::UpdateControls(float mouse_x, float mouse_y) { } void Overlays::UpdateLevels(float mouse_x, float mouse_y) { - //Update text boxes - const int page_start = level_page * LEVELS_PER_PAGE; - const int page_end = page_start + LEVELS_PER_PAGE; - for (int i = page_start; i < page_end; ++i) { - const int j = i % LEVELS_PER_PAGE; - if (i < num_levels) { - const float y = 80.0f + float(j / 3) * 120.0f; - const float x = 240.0f + float(j % 3) * 400.0f; - const char* txt = high_scores.HasUnlocked(i) ? all_levels[i].txt : "???"; - MakeText(txt, x, y, 32, sf::Color::White, all_text[j + L0]); - const sf::FloatRect text_bounds = all_text[j + L0].getLocalBounds(); - all_text[j + L0].setOrigin(text_bounds.width / 2, text_bounds.height / 2); - } else { - all_text[j + L0] = sf::Text(); - } - } - if (level_page > 0) { - MakeText("<", 540, 652, 48, sf::Color::White, all_text[PREV]); - } else { - all_text[PREV] = sf::Text(); - } - MakeText("Back", 590, 660, 40, sf::Color::White, all_text[BACK2]); - if (level_page < num_level_pages - 1) { - MakeText(">", 732, 652, 48, sf::Color::White, all_text[NEXT]); - } else { - all_text[NEXT] = sf::Text(); - } + +} + +void Overlays::UpdateLevelMenu(float mouse_x, float mouse_y, float scroll) +{ - //Check if mouse intersects anything - UpdateHover(L0, BACK2, mouse_x, mouse_y); } void Overlays::UpdatePaused(float mouse_x, float mouse_y) { //Update text boxes - MakeText("Paused", 540, 288, 54, sf::Color::White, all_text[PAUSED]); - MakeText("Continue", 370, 356, 40, sf::Color::White, all_text[CONTINUE]); - MakeText("Restart", 620, 356, 40, sf::Color::White, all_text[RESTART]); - MakeText("Quit", 845, 356, 40, sf::Color::White, all_text[QUIT]); + MakeText(LOCAL["Paused"], 540, 288, 54, sf::Color::White, all_text[PAUSED]); + MakeText(LOCAL["Continue"], 370, 356, 40, sf::Color::White, all_text[CONTINUE]); + MakeText(LOCAL["Restart"], 620, 356, 40, sf::Color::White, all_text[RESTART]); + MakeText(LOCAL["Quit"], 845, 356, 40, sf::Color::White, all_text[QUIT]); //Update music setting - const char* music_txt = (game_settings.mute ? "Music: Off" : "Music: On"); + std::wstring music_txt = LOCAL["Music"] + L": " + std::wstring(game_settings.mute ? LOCAL["On"] : LOCAL["Off"]); MakeText(music_txt, 410, 500, 40, sf::Color::White, all_text[MUSIC]); //Update mouse sensitivity setting - const char* mouse_txt = "Mouse Sensitivity: High"; - if (game_settings.mouse_sensitivity == 1) { - mouse_txt = "Mouse Sensitivity: Medium"; - } else if (game_settings.mouse_sensitivity == 2) { - mouse_txt = "Mouse Sensitivity: Low"; + std::wstring mouse_txt = LOCAL["Mouse_sensitivity"] + L": "; + if (game_settings.mouse_sensitivity == 1) + { + mouse_txt += LOCAL["Medium"]; + } + else if (game_settings.mouse_sensitivity == 2) + { + mouse_txt += LOCAL["Low"]; + } + else + { + mouse_txt += LOCAL["High"]; } MakeText(mouse_txt, 410, 550, 40, sf::Color::White, all_text[MOUSE]); @@ -199,9 +190,9 @@ void Overlays::DrawTimer(sf::RenderWindow& window, int t, bool is_high_score) { window.draw(text); } -void Overlays::DrawLevelDesc(sf::RenderWindow& window, int level) { +void Overlays::DrawLevelDesc(sf::RenderWindow& window, std::string desc) { sf::Text text; - MakeText(all_levels[level].txt, 640, 60, 48, sf::Color::White, text); + MakeText(desc.c_str(), 640, 60, 48, sf::Color::White, text); const sf::FloatRect text_bounds = text.getLocalBounds(); text.setOrigin(text_bounds.width / 2, text_bounds.height / 2); window.draw(text); @@ -230,7 +221,7 @@ void Overlays::DrawArrow(sf::RenderWindow& window, const sf::Vector3f& v3) { const sf::Uint8 alpha = sf::Uint8(102.0f * std::max(0.0f, std::min(1.0f, (v3.z - 5.0f) / 30.0f))); if (alpha > 0) { arrow_spr.setScale(draw_scale * 0.1f, draw_scale * 0.1f); - arrow_spr.setRotation(90.0f + v3.x * 180.0f / pi); + arrow_spr.setRotation(90.0f + v3.x * 180.0f / PI); arrow_spr.setPosition(draw_scale * x, draw_scale * y); arrow_spr.setColor(sf::Color(255, 255, 255, alpha)); window.draw(arrow_spr); @@ -238,11 +229,7 @@ void Overlays::DrawArrow(sf::RenderWindow& window, const sf::Vector3f& v3) { } void Overlays::DrawCredits(sf::RenderWindow& window, bool fullrun, int t) { - const char* txt = - " Congratulations, you beat all the levels!\n\n\n\n" - "As a reward, cheats have been unlocked!\n" - "Activate them with the F1 key during gameplay.\n\n" - "Thanks for playing!"; + std::wstring txt = LOCAL["CongratsEnd"]; sf::Text text; MakeText(txt, 100, 100, 44, sf::Color::White, text); text.setLineSpacing(1.3f); @@ -258,11 +245,7 @@ void Overlays::DrawCredits(sf::RenderWindow& window, bool fullrun, int t) { } void Overlays::DrawMidPoint(sf::RenderWindow& window, bool fullrun, int t) { - const char* txt = - " You've done well so far.\n\n\n\n" - " But this is only the beginning.\n" - "If you need a quick break, take it now.\n" - "The challenge levels are coming up..."; + std::wstring txt = LOCAL["CongratsMid"]; sf::Text text; MakeText(txt, 205, 100, 44, sf::Color::White, text); text.setLineSpacing(1.3f); @@ -278,23 +261,7 @@ void Overlays::DrawMidPoint(sf::RenderWindow& window, bool fullrun, int t) { } void Overlays::DrawLevels(sf::RenderWindow& window) { - //Draw the level names - for (int i = L0; i <= BACK2; ++i) { - window.draw(all_text[i]); - } - //Draw the times - const int page_start = level_page * LEVELS_PER_PAGE; - const int page_end = page_start + LEVELS_PER_PAGE; - for (int i = page_start; i < page_end; ++i) { - if (i < num_levels && high_scores.HasCompleted(i)) { - sf::Text text; - const int j = i % LEVELS_PER_PAGE; - const float y = 98.0f + float(j / 3) * 120.0f; - const float x = 148.0f + float(j % 3) * 400.0f; - MakeTime(high_scores.Get(i), x, y, 48, sf::Color(64, 255, 64), text); - window.draw(text); - } - } + } void Overlays::DrawSumTime(sf::RenderWindow& window, int t) { @@ -305,29 +272,21 @@ void Overlays::DrawSumTime(sf::RenderWindow& window, int t) { void Overlays::DrawCheatsEnabled(sf::RenderWindow& window) { sf::Text text; - MakeText("Cheats Enabled", 10, 680, 32, sf::Color::White, text); + MakeText(LOCAL["CheatsON"], 10, 680, 32, sf::Color::White, text); window.draw(text); } void Overlays::DrawCheats(sf::RenderWindow& window) { sf::Text text; - const char* txt = - "[ C ] Color change\n" - "[ F ] Free camera\n" - "[ G ] Gravity strength\n" - "[ H ] Hyperspeed toggle\n" - "[ I ] Ignore goal\n" - "[ M ] Motion disable\n" - "[ P ] Planet toggle\n" - "[ Z ] Zoom to scale\n" - "[1-9] Scroll fractal parameter\n"; + std::wstring txt = LOCAL["CheatsInfo"]; MakeText(txt, 460, 160, 32, sf::Color::White, text, true); window.draw(text); } -void Overlays::MakeText(const char* str, float x, float y, float size, const sf::Color& color, sf::Text& text, bool mono) { + +template void Overlays::MakeText(T str, float x, float y, float size, const sf::Color& color, sf::Text& text, bool mono) { text.setString(str); - text.setFont(mono ? *font_mono : *font); + text.setFont(mono ? LOCAL("mono"): LOCAL("default")); text.setCharacterSize(int(size * draw_scale)); text.setLetterSpacing(0.8f); text.setPosition((x - 2.0f) * draw_scale, (y - 2.0f) * draw_scale); @@ -351,11 +310,11 @@ void Overlays::MakeTime(int t, float x, float y, float size, const sf::Color& co void Overlays::UpdateHover(Texts from, Texts to, float mouse_x, float mouse_y) { for (int i = from; i <= to; ++i) { - const sf::FloatRect bounds = all_text[i].getGlobalBounds(); + sf::FloatRect bounds = all_text[i].getGlobalBounds(); if (bounds.contains(mouse_x, mouse_y)) { all_text[i].setFillColor(sf::Color(255, 64, 64)); if (!all_hover[i]) { - sound_hover.play(); + //sound_hover.play(); all_hover[i] = true; } } else { @@ -363,3 +322,353 @@ void Overlays::UpdateHover(Texts from, Texts to, float mouse_x, float mouse_y) { } } } + + +Scene *scene_ptr; + +extern bool confirmed = false; +extern bool canceled = false; +int music_id = 0; +bool music_play = false; + + +void TW_CALL Confirm(void *data) +{ + confirmed = true; +} + +void TW_CALL Cancel(void *data) +{ + canceled = true; +} + +void TW_CALL MarbleSet(void *data) +{ + scene_ptr->cur_ed_mode = Scene::EditorMode::PLACE_MARBLE; +} + +void TW_CALL FlagSet(void *data) +{ + scene_ptr->cur_ed_mode = Scene::EditorMode::PLACE_FLAG; +} + +void TW_CALL PlayMusic(void *data) +{ + scene_ptr->levels.StopAllMusic(); + music_play = !music_play; + if (music_play) + { + scene_ptr->levels.GetMusicByID(music_id)->play(); + } +} + +void TW_CALL SaveLevel(void *data) +{ + Level* copy = &scene_ptr->level_copy; + int lvlid = scene_ptr->GetLevel(); + + std::vector music_list = scene_ptr->levels.GetMusicNames(); + std::vector lvlnum = scene_ptr->levels.getLevelIds(); + copy->use_music = music_list[music_id]; + bool same_level = scene_ptr->original_level_name == copy->txt; + if (lvlid < 0 || !same_level) + lvlid = time(NULL); + copy->level_id = lvlid; + copy->SaveToFile(std::string(level_folder) + "/" + ConvertSpaces2_(copy->txt) + ".lvl", lvlid, copy->link_level); + scene_ptr->levels.ReloadLevels(); + if (!(scene_ptr->GetLevel() >= 0 && same_level)) + { + scene_ptr->WriteLVL(lvlid); + scene_ptr->original_level_name = copy->txt; + } +} + + +void TW_CALL CopyStdStringToClient(std::string& destinationClientString, const std::string& sourceLibraryString) +{ + // Copy the content of souceString handled by the AntTweakBar library to destinationClientString handled by your application + destinationClientString = sourceLibraryString; +} + + +void Overlays::SetAntTweakBar(int Width, int Height, float &fps, Scene *scene, Renderer* rd, bool *vsync, float *mouse_sensitivity, float *wheel_sensitivity, float *music_vol, float *target_fps) +{ + //TW interface + TwInit(TW_OPENGL, NULL); + TwWindowSize(Width, Height); + scene_ptr = scene; + + stats = TwNewBar("Statistics" ); + TwDefine(" GLOBAL help='Marble Marcher Community Edition. Work in progress.' "); + + // Change bar position + int barPos[2] = { 16, 60 }; + TwSetParam(stats, NULL, "position", TW_PARAM_INT32, 2, &barPos); + TwAddVarRO(stats, "FPS", TW_TYPE_FLOAT, &fps, " label='FPS' "); + TwAddVarRO(stats, "Marble velocity", TW_TYPE_DIR3F, scene->marble_vel.data(), " "); + TwAddVarRO(stats, "Marble position", TW_TYPE_DIR3F, scene->marble_pos.data(), " "); + + settings = TwNewBar("Settings"); + + TwAddVarRW(settings, "VSYNC", TW_TYPE_BOOLCPP, vsync, "group='Graphics settings'"); + TwAddVarRW(settings, "Shadows", TW_TYPE_BOOLCPP, &scene->Shadows_Enabled, "group='Graphics settings'"); + //TwAddVarRW(settings, "Reflection and Refraction", TW_TYPE_BOOLCPP, &scene->Refl_Refr_Enabled, "group='Graphics settings'"); + TwAddVarRW(settings, "Blur", TW_TYPE_FLOAT, &rd->camera.mblur, "min=0 step=0.001 max=0.75 group='Graphics settings'"); + TwAddVarRW(settings, "Exposure", TW_TYPE_FLOAT, &rd->camera.exposure, "min=0 max=5 step=0.001 group='Graphics settings'"); + + TwEnumVal marble_type[] = { { 0, "Glass" }, + { 1, "Metal" } }; + + TwType Marble_type = TwDefineEnum("Marble type", marble_type, 2); + TwAddVarRW(settings, "Marble type", Marble_type, &scene->MarbleType, "group='Gameplay settings'"); + TwAddVarRW(settings, "Mouse sensitivity", TW_TYPE_FLOAT, mouse_sensitivity, "min=0.001 max=0.02 step=0.001 group='Gameplay settings'"); + TwAddVarRW(settings, "Wheel sensitivity", TW_TYPE_FLOAT, wheel_sensitivity, "min=0.01 max=0.5 step=0.01 group='Gameplay settings'"); + TwAddVarRW(settings, "Music volume", TW_TYPE_FLOAT, music_vol, "min=0 max=100 step=1 group='Gameplay settings'"); + TwAddVarRW(settings, "Target FPS", TW_TYPE_FLOAT, target_fps, "min=24 max=144 step=1 group='Gameplay settings'"); + TwAddVarRW(settings, "Camera size", TW_TYPE_FLOAT, &scene->camera_size, "min=0 max=10 step=0.001 group='Gameplay settings'"); + TwAddVarRW(settings, "Camera speed(Free mode)", TW_TYPE_FLOAT, &scene->free_camera_speed, "min=0 max=10 step=0.001 group='Gameplay settings'"); + + int barPos1[2] = { 16, 250 }; + + TwSetParam(settings, NULL, "position", TW_PARAM_INT32, 2, &barPos1); + + TwCopyStdStringToClientFunc(CopyStdStringToClient); + + level_editor = TwNewBar("LevelEditor"); + Level *copy = &scene->level_copy; + + TwAddVarRW(level_editor, "Level Name", TW_TYPE_STDSTRING, ©->txt, ""); + TwAddVarRW(level_editor, "Level Description", TW_TYPE_STDSTRING, ©->desc, ""); + + TwAddButton(level_editor, "Save", SaveLevel, NULL, + " label='Save Level' "); + + TwAddButton(level_editor, "Set Marble", MarbleSet, NULL, + " label='Set Marble Position' help='Click on the fractal to place' "); + + TwAddButton(level_editor, "Set Flag", FlagSet, NULL, + " label='Set Flag Position' help='Click on the fractal to place' "); + + TwAddVarRW(level_editor, "Flag Position", TW_TYPE_DIR3F, copy->end_pos.data(), ""); + TwAddVarRW(level_editor, "Marble Position", TW_TYPE_DIR3F, copy->start_pos.data(), ""); + TwAddVarRW(level_editor, "Marble Radius(Scale)", TW_TYPE_FLOAT, ©->marble_rad, "min=0 max=10 step=0.001 "); + + std::vector music_list = scene->levels.GetMusicNames(); + TwEnumVal *music_enums = new TwEnumVal[music_list.size()]; + for (int i = 0; i < music_list.size(); i++) + { + TwEnumVal enumval; + enumval.Label = music_list[i].c_str(); + enumval.Value = i; + music_enums[i] = enumval; + } + + TwType Level_music = TwDefineEnum("Level music", music_enums, music_list.size()); + TwAddVarRW(level_editor, "Level music", Level_music, &music_id, ""); + + TwAddButton(level_editor, "Play", PlayMusic, NULL, " label='Play/Stop current music' "); + + std::map level_list = scene->levels.getLevelNames(); + TwEnumVal *level_enums = new TwEnumVal[level_list.size()+1]; + TwEnumVal enumval; + enumval.Label = "None"; + enumval.Value = -1; + level_enums[0] = enumval; + int i = 0; + for (auto &name:level_list) + { + enumval.Label = name.second.c_str(); + enumval.Value = i; + level_enums[i+1] = enumval; + i++; + } + + TwType Levels = TwDefineEnum("levels", level_enums, level_list.size()+1); + TwAddVarRW(level_editor, "Play level after finish(TODO)", Levels, ©->link_level, ""); + + TwAddVarRW(level_editor, "Sun direction", TW_TYPE_DIR3F, copy->light_dir.data(), "group='Level parameters'"); + TwAddVarRW(level_editor, "Sun color", TW_TYPE_DIR3F, copy->light_col.data(), "group='Level parameters'"); + TwAddVarRW(level_editor, "Background color", TW_TYPE_DIR3F, copy->background_col.data(), "group='Level parameters'"); + TwAddVarRW(level_editor, "Gravity strenght", TW_TYPE_FLOAT, ©->gravity, "min=0 max=0.5 step=0.0001 group='Level parameters'"); + fractal_editor = TwNewBar("FractalEditor"); + + TwAddVarRW(fractal_editor, "PBR roughness", TW_TYPE_FLOAT, ©->PBR_roughness, "min=0 max=1 step=0.001 "); + TwAddVarRW(fractal_editor, "PBR metallic", TW_TYPE_FLOAT, ©->PBR_metal, "min=0 max=1 step=0.001"); + float *p = copy->params.data(); + TwAddVarRW(fractal_editor, "Fractal Iterations", TW_TYPE_INT32, ©->FractalIter, "min=1 max=20 step=1"); + TwAddVarRW(fractal_editor, "Fractal Scale", TW_TYPE_FLOAT, p, "min=0 max=5 step=0.0001"); + TwAddVarRW(fractal_editor, "Fractal Angle1", TW_TYPE_FLOAT, p + 1, "min=-10 max=10 step=0.0001 "); + TwAddVarRW(fractal_editor, "Fractal Angle2", TW_TYPE_FLOAT, p + 2, "min=-10 max=10 step=0.0001 "); + TwAddVarRW(fractal_editor, "Fractal Shift", TW_TYPE_DIR3F, p + 3, ""); + TwAddVarRW(fractal_editor, "Fractal Color", TW_TYPE_DIR3F, p + 6, ""); + + //TwAddButton(stats, "Info1.1", NULL, NULL, string); + + confirmation_box = TwNewBar("confirm"); + + TwAddVarRW(confirmation_box, "OK", TW_TYPE_BOOLCPP, ©->txt, ""); + TwAddVarRW(confirmation_box, "Cancel", TW_TYPE_BOOLCPP, ©->desc, ""); + + TwDefine("confirm visible=false size='300 100' color='255 50 0' alpha=255 label='Are you sure?'"); + + TwDefine(" GLOBAL fontsize=3 "); + TwDefine("LevelEditor visible=false size='420 350' color='0 80 230' alpha=210 label='Level editor' valueswidth=200"); + TwDefine("FractalEditor visible=false size='420 350' color='0 120 200' alpha=210 label='Fractal editor' valueswidth=200"); + TwDefine("Settings color='255 128 0' alpha=210 size='420 350' valueswidth=200"); + TwDefine("Statistics color='0 128 255' alpha=210 size='420 160' valueswidth=200"); +} + +void Overlays::DrawAntTweakBar() +{ + //Refresh tweak bar + if (TWBAR_ENABLED) + { + TwRefreshBar(stats); + TwRefreshBar(settings); + TwDraw(); + } +} + +bool Overlays::TwManageEvent(sf::Event *event) +{ + bool handled = 0; + + TwMouseAction mouseAction; + int key = 0; + static int s_KMod = 0; + static bool s_PreventTextHandling = false; + static int s_WheelPos = 0; + if(TWBAR_ENABLED) + { + switch (event->type) + { + case sf::Event::KeyPressed: + s_PreventTextHandling = false; + s_KMod = 0; + if (event->key.shift) s_KMod |= TW_KMOD_SHIFT; + if (event->key.alt) s_KMod |= TW_KMOD_ALT; + if (event->key.control) s_KMod |= TW_KMOD_CTRL; + key = 0; + switch (event->key.code) + { + case sf::Keyboard::Escape: + key = TW_KEY_ESCAPE; + break; + case sf::Keyboard::Return: + key = TW_KEY_RETURN; + break; + case sf::Keyboard::Tab: + key = TW_KEY_TAB; + break; + case sf::Keyboard::BackSpace: + key = TW_KEY_BACKSPACE; + break; + case sf::Keyboard::PageUp: + key = TW_KEY_PAGE_UP; + break; + case sf::Keyboard::PageDown: + key = TW_KEY_PAGE_DOWN; + break; + case sf::Keyboard::Up: + key = TW_KEY_UP; + break; + case sf::Keyboard::Down: + key = TW_KEY_DOWN; + break; + case sf::Keyboard::Left: + key = TW_KEY_LEFT; + break; + case sf::Keyboard::Right: + key = TW_KEY_RIGHT; + break; + case sf::Keyboard::End: + key = TW_KEY_END; + break; + case sf::Keyboard::Home: + key = TW_KEY_HOME; + break; + case sf::Keyboard::Insert: + key = TW_KEY_INSERT; + break; + case sf::Keyboard::Delete: + key = TW_KEY_DELETE; + break; + case sf::Keyboard::Space: + key = TW_KEY_SPACE; + break; + default: + if (event->key.code >= sf::Keyboard::F1 && event->key.code <= sf::Keyboard::F15) + key = TW_KEY_F1 + event->key.code - sf::Keyboard::F1; + else if (s_KMod & TW_KMOD_ALT) + { + if (event->key.code >= sf::Keyboard::A && event->key.code <= sf::Keyboard::Z) + { + if (s_KMod & TW_KMOD_SHIFT) + key = 'A' + event->key.code - sf::Keyboard::A; + else + key = 'a' + event->key.code - sf::Keyboard::A; + } + } + } + if (key != 0) + { + handled = TwKeyPressed(key, s_KMod); + s_PreventTextHandling = true; + } + break; + case sf::Event::KeyReleased: + s_PreventTextHandling = false; + s_KMod = 0; + break; + case sf::Event::TextEntered: + if (!s_PreventTextHandling && event->text.unicode != 0 && (event->text.unicode & 0xFF00) == 0) + { + if ((event->text.unicode & 0xFF) < 32) // CTRL+letter + handled = TwKeyPressed((event->text.unicode & 0xFF) + 'a' - 1, TW_KMOD_CTRL | s_KMod); + else + handled = TwKeyPressed(event->text.unicode & 0xFF, 0); + } + s_PreventTextHandling = false; + break; + case sf::Event::MouseMoved: + handled = TwMouseMotion(event->mouseMove.x, event->mouseMove.y); + break; + case sf::Event::MouseButtonPressed: + case sf::Event::MouseButtonReleased: + mouseAction = (event->type == sf::Event::MouseButtonPressed) ? TW_MOUSE_PRESSED : TW_MOUSE_RELEASED; + switch (event->mouseButton.button) + { + case sf::Mouse::Left: + handled = TwMouseButton(mouseAction, TW_MOUSE_LEFT); + break; + case sf::Mouse::Middle: + handled = TwMouseButton(mouseAction, TW_MOUSE_MIDDLE); + break; + case sf::Mouse::Right: + handled = TwMouseButton(mouseAction, TW_MOUSE_RIGHT); + break; + default: + break; + } + break; + case sf::Event::MouseWheelMoved: + s_WheelPos += event->mouseWheel.delta; + handled = TwMouseWheel(s_WheelPos); + break; + default: + break; + } + } + + if (sf::Event::Resized == event->type) + { + TwWindowSize(event->size.width, event->size.height); + } + + return handled; +} + +void Overlays::SetTWBARResolution(int Width, int Height) +{ + TwWindowSize(Width, Height); +} diff --git a/src/Overlays.h b/src/Overlays.h index eaf25fc..6a2480f 100644 --- a/src/Overlays.h +++ b/src/Overlays.h @@ -15,9 +15,12 @@ * along with this program.If not, see . */ #pragma once +#include "Level.h" #include "Settings.h" +#include "Scene.h" #include -#include + +#include extern Settings game_settings; @@ -49,11 +52,21 @@ class Overlays { NUM_TEXTS }; static const int LEVELS_PER_PAGE = 15; + bool TWBAR_ENABLED; + TwBar *stats, *settings, *fractal_editor, *level_editor, *confirmation_box; + + sf::Sound sound_hover; + sf::Sound sound_click; + sf::Sound sound_count; + sf::Sound sound_go; - Overlays(const sf::Font* _font, const sf::Font* _font_mono); + Overlays(sf::Font* _font, sf::Font* _font_mono, Scene* scene); + + void ReloadLevelMenu(Scene * scene); //Relative to 1280x720 void SetScale(float scale) { draw_scale = scale; } + void SetTWBARResolution(int Width, int Height); Texts GetOption(Texts from, Texts to); int& GetLevelPage() { return level_page; } @@ -61,12 +74,13 @@ class Overlays { void UpdateMenu(float mouse_x, float mouse_y); void UpdateControls(float mouse_x, float mouse_y); void UpdateLevels(float mouse_x, float mouse_y); + void UpdateLevelMenu(float mouse_x, float mouse_y, float scroll); void UpdatePaused(float mouse_x, float mouse_y); void DrawMenu(sf::RenderWindow& window); void DrawControls(sf::RenderWindow& window); void DrawTimer(sf::RenderWindow& window, int t, bool is_high_score); - void DrawLevelDesc(sf::RenderWindow& window, int level); + void DrawLevelDesc(sf::RenderWindow& window, std::string desc); void DrawFPS(sf::RenderWindow& window, int fps); void DrawPaused(sf::RenderWindow& window); void DrawArrow(sf::RenderWindow& window, const sf::Vector3f& v3); @@ -76,9 +90,13 @@ class Overlays { void DrawSumTime(sf::RenderWindow& window, int t); void DrawCheatsEnabled(sf::RenderWindow& window); void DrawCheats(sf::RenderWindow& window); + void SetAntTweakBar(int Width, int Height, float & fps, Scene * scene, Renderer * rd, bool * vsync, float * mouse_sensitivity, float * wheel_sensitivity, float * music_vol, float * target_fps); + void DrawAntTweakBar(); + bool TwManageEvent(sf::Event * event); protected: - void MakeText(const char* str, float x, float y, float size, const sf::Color& color, sf::Text& text, bool mono=false); + template + void MakeText(T str, float x, float y, float size, const sf::Color & color, sf::Text & text, bool mono = 0); void MakeTime(int t, float x, float y, float size, const sf::Color& color, sf::Text& text); void UpdateHover(Texts from, Texts to, float mouse_x, float mouse_y); @@ -86,13 +104,9 @@ class Overlays { sf::Text all_text[NUM_TEXTS]; bool all_hover[NUM_TEXTS]; - sf::Sound sound_hover; sf::SoundBuffer buff_hover; - sf::Sound sound_click; sf::SoundBuffer buff_click; - sf::Sound sound_count; sf::SoundBuffer buff_count; - sf::Sound sound_go; sf::SoundBuffer buff_go; sf::Texture arrow_tex; @@ -104,4 +118,5 @@ class Overlays { const sf::Font* font; const sf::Font* font_mono; + }; diff --git a/src/Renderer.cpp b/src/Renderer.cpp new file mode 100644 index 0000000..94b0e9f --- /dev/null +++ b/src/Renderer.cpp @@ -0,0 +1,234 @@ +#include "Renderer.h" + +Renderer::Renderer(int w, int h,std::string config_file) +{ + Initialize(w, h, config_file); +} + +Renderer::Renderer() +{ + main_textures.clear(); + shader_textures.clear(); + global_size.clear(); +} + +void Renderer::Initialize(int w, int h, std::string config_f) +{ + //glDeleteTextures(main_textures.size(), main_textures.data()); + main_textures.clear(); + + for (int i = 0; i < shader_textures.size(); i++) + { + // glDeleteTextures(shader_textures[i].size(), shader_textures[i].data()); + } + shader_textures.clear(); + + global_size.clear(); + + width = w; + height = h; + camera.SetResolution(vec2(w, h)); + camera.SetAspectRatio((float)w / (float)h); + + ExprParser parser; + config_file = config_f; + std::string compute_folder = fs::path(config_file).parent_path().generic_string(); + + std::ifstream config(config_file); + if (config.fail()) + { + ERROR_MSG("Error opening pipeline configuration"); + return; + } + std::string line; + + int element = -1; + int cur_shader = 0; + + std::map variables; + variables["width"] = width; + variables["height"] = height; + + std::vector stage_textures; + std::string shader_file; + vec2 global, tex_resolution; + while (std::getline(config, line)) + { + if (line.substr(0, 1) != "#") + { + parser.Parse(line); + switch (element++) + { + case -1: + for (int i = 0; i < parser.Evaluate(variables); i++) + { + main_textures.push_back(GenerateTexture(width, height)); + } + break; + case 0: + shader_file = compute_folder + "/" + line; + LoadShader(shader_file); + break; + case 1: + global.x = ceil(parser.Evaluate(variables)); + break; + case 2: + global.y = ceil(parser.Evaluate(variables)); + break; + case 3: + tex_resolution.x = ceil(parser.Evaluate(variables)); + break; + case 4: + tex_resolution.y = ceil(parser.Evaluate(variables)); + break; + case 5: + for (int i = 0; i < parser.Evaluate(variables); i++) + { + stage_textures.push_back(GenerateTexture(tex_resolution.x, tex_resolution.y)); + } + shader_textures.push_back(stage_textures); + stage_textures.clear(); + global_size.push_back(global); + element = 0; + break; + } + } + } + + config.close(); +} + +void Renderer::ReInitialize(int w, int h) +{ + main_textures.clear(); + shader_textures.clear(); + global_size.clear(); + + width = w; + height = h; + camera.SetResolution(vec2(w, h)); + camera.SetAspectRatio((float)w / (float)h); + + ExprParser parser; + + std::string compute_folder = fs::path(config_file).parent_path().generic_string(); + + std::ifstream config(config_file); + if (config.fail()) + { + ERROR_MSG("Error opening pipeline configuration"); + return; + } + std::string line; + + int element = -1; + int cur_shader = 0; + + std::map variables; + variables["width"] = width; + variables["height"] = height; + + std::vector stage_textures; + std::string shader_file; + vec2 global, tex_resolution; + while (std::getline(config, line)) + { + if (line.substr(0, 1) != "#") + { + parser.Parse(line); + switch (element++) + { + case -1: + for (int i = 0; i < parser.Evaluate(variables); i++) + { + main_textures.push_back(GenerateTexture(width, height)); + } + break; + case 0: + //shader_file = compute_folder + "/" + line; + // LoadShader(shader_file); + break; + case 1: + global.x = ceil(parser.Evaluate(variables)); + break; + case 2: + global.y = ceil(parser.Evaluate(variables)); + break; + case 3: + tex_resolution.x = ceil(parser.Evaluate(variables)); + break; + case 4: + tex_resolution.y = ceil(parser.Evaluate(variables)); + break; + case 5: + for (int i = 0; i < parser.Evaluate(variables); i++) + { + stage_textures.push_back(GenerateTexture(tex_resolution.x, tex_resolution.y)); + } + shader_textures.push_back(stage_textures); + stage_textures.clear(); + global_size.push_back(global); + element = 0; + break; + } + } + } + + config.close(); +} + + +void Renderer::SetOutputTexture(sf::Texture & tex) +{ + shader_textures[shader_textures.size()-1][0] = tex.getNativeHandle(); +} + +void Renderer::LoadShader(std::string shader_file) +{ + shader_pipeline.push_back(ComputeShader(shader_file)); +} + +void Renderer::Render() +{ + int stages = global_size.size(); + for (int i = 0; i < stages; i++) + { + int tex_id = 0; + + //bind textures from the previous step + if (i != 0) + { + for (int j = 0; j < shader_textures[i - 1].size(); j++) + { + glBindImageTexture(tex_id++, shader_textures[i - 1][j], 0, GL_FALSE, 0, GL_READ_ONLY, GL_RGBA32F); + } + } + + //bind textures from the current step + for (int j = 0; j < shader_textures[i].size(); j++) + { + glBindImageTexture(tex_id++, shader_textures[i][j], 0, GL_FALSE, 0, GL_READ_WRITE, (i == stages-1)?GL_RGBA8:GL_RGBA32F); + } + + + for (int j = 0; j < main_textures.size(); j++) + { + glBindImageTexture(tex_id++, main_textures[j], 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA32F); + } + + shader_pipeline[i].setCamera(camera.GetGLdata()); + shader_pipeline[i].Run(global_size[i]); + } +} + +GLuint Renderer::GenerateTexture(float w, float h) +{ + GLuint texture; + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + //HDR texture + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, w, h, 0, GL_RGBA, GL_FLOAT, NULL); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + return texture; +} diff --git a/src/Renderer.h b/src/Renderer.h new file mode 100644 index 0000000..c6dd9fd --- /dev/null +++ b/src/Renderer.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include + +//TODO +class Wobject +{ + vec3 position; + vec3 radius; //bounding volume radius + +}; + +class WorldModel +{ + +}; + +class Renderer +{ +public: + Renderer(int w, int h, std::string config); + Renderer(); + + void Initialize(int w, int h, std::string config); + void ReInitialize(int w, int h); + + void SetOutputTexture(sf::Texture& tex); + void LoadShader(std::string shader_file); + + void Render(); + std::vector shader_pipeline; + Camera camera; + +private: + std::string config_file; + GLuint GenerateTexture(float w, float h); + + int width, height; + std::vector global_size; + std::vector main_textures; + std::vector> shader_textures; +}; \ No newline at end of file diff --git a/src/Res.h b/src/Res.h index 272b505..34adafd 100644 --- a/src/Res.h +++ b/src/Res.h @@ -18,23 +18,28 @@ #include static const int num_level_music = 4; -static const char vert_glsl[] = "assets/vert.glsl"; -static const char frag_glsl[] = "assets/frag.glsl"; +static const char vert_glsl[] = "shaders/vert.glsl"; +static const char frag_glsl[] = "shaders/frag.glsl"; static const char Orbitron_Bold_ttf[] = "assets/Orbitron-Bold.ttf"; static const char Inconsolata_Bold_ttf[] = "assets/Inconsolata-Bold.ttf"; -static const char menu_ogg[] = "assets/menu.ogg"; -static const char level1_ogg[] = "assets/level1.ogg"; -static const char level2_ogg[] = "assets/level2.ogg"; -static const char level3_ogg[] = "assets/level3.ogg"; -static const char level4_ogg[] = "assets/level4.ogg"; -static const char credits_ogg[] = "assets/credits.ogg"; -static const char menu_hover_wav[] = "assets/menu_hover.wav"; -static const char menu_click_wav[] = "assets/menu_click.wav"; -static const char count_down_wav[] = "assets/count_down.wav"; -static const char count_go_wav[] = "assets/count_go.wav"; -static const char arrow_png[] = "assets/arrow.png"; -static const char goal_wav[] = "assets/goal.wav"; -static const char bounce1_wav[] = "assets/bounce1.wav"; -static const char bounce2_wav[] = "assets/bounce2.wav"; -static const char bounce3_wav[] = "assets/bounce3.wav"; -static const char shatter_wav[] = "assets/shatter.wav"; +static const char menu_ogg[] = "sound/music/menu.ogg"; +static const char level1_ogg[] = "sound/music/level1.ogg"; +static const char level2_ogg[] = "sound/music/level2.ogg"; +static const char level3_ogg[] = "sound/music/level3.ogg"; +static const char level4_ogg[] = "sound/music/level4.ogg"; +static const char credits_ogg[] = "sound/music/credits.ogg"; +static const char menu_hover_wav[] = "sound/fx/menu_hover.wav"; +static const char menu_click_wav[] = "sound/fx/menu_click.wav"; +static const char count_down_wav[] = "sound/fx/count_down.wav"; +static const char count_go_wav[] = "sound/fx/count_go.wav"; +static const char arrow_png[] = "images/arrow.png"; +static const char icon_png[] = "images/MarbleMarcher.png"; +static const char goal_wav[] = "sound/fx/goal.wav"; +static const char bounce1_wav[] = "sound/fx/bounce1.wav"; +static const char bounce2_wav[] = "sound/fx/bounce2.wav"; +static const char bounce3_wav[] = "sound/fx/bounce3.wav"; +static const char shatter_wav[] = "sound/fx/shatter.wav"; + +static const char level_folder[] = "levels"; +static const char music_folder[] = "sound/music"; +static std::string local_folder = "locals"; \ No newline at end of file diff --git a/src/Resource.aps b/src/Resource.aps new file mode 100644 index 0000000..6b96510 Binary files /dev/null and b/src/Resource.aps differ diff --git a/src/Scene.cpp b/src/Scene.cpp index 83838ef..69fe09b 100644 --- a/src/Scene.cpp +++ b/src/Scene.cpp @@ -1,25 +1,24 @@ /* This file is part of the Marble Marcher (https://github.com/HackerPoet/MarbleMarcher). * Copyright(C) 2018 CodeParade -* +* * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. -* +* * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. -* +* * You should have received a copy of the GNU General Public License * along with this program.If not, see . */ #include "Scene.h" -#include "Scores.h" #include "Res.h" #include -static const float pi = 3.14159265359f; +static const float PI = 3.14159265359f; static const float ground_force = 0.008f; static const float air_force = 0.004f; static const float ground_friction = 0.99f; @@ -38,39 +37,51 @@ static const int frame_deorbit = 800; static const int frame_countdown = frame_deorbit + 3*60; static const float default_zoom = 15.0f; static const int fractal_iters = 16; -static const float gravity = 0.005f; static const float ground_ratio = 1.15f; static const int mus_switches[num_level_music] = {9, 15, 21, 24}; static const int num_levels_midpoint = 15; static void ModPi(float& a, float b) { - if (a - b > pi) { - a -= 2 * pi; - } else if (a - b < -pi) { - a += 2 * pi; + if (a - b > PI) { + a -= 2 * PI; + } else if (a - b < -PI) { + a += 2 * PI; } } Scene::Scene(sf::Music* level_music) : - intro_needs_snap(true), - play_single(false), - is_fullrun(false), - exposure(1.0f), - cam_mat(Eigen::Matrix4f::Identity()), - cam_look_x(0.0f), - cam_look_y(0.0f), - cam_dist(default_zoom), - cam_pos(0.0f, 0.0f, 0.0f), - cam_mode(CamMode::INTRO), - marble_rad(1.0f), - marble_pos(0.0f, 0.0f, 0.0f), - marble_vel(0.0f, 0.0f, 0.0f), - marble_mat(Eigen::Matrix3f::Identity()), - flag_pos(0.0f, 0.0f, 0.0f), - timer(0), - sum_time(0), - music(level_music), - cur_level(0) + intro_needs_snap(true), + play_single(false), + is_fullrun(false), + exposure(1.0f), + cam_mat(Eigen::Matrix4f::Identity()), + cam_look_x(0.0f), + cam_look_y(0.0f), + cam_dist(default_zoom), + cam_pos(0.0f, 0.0f, 0.0f), + cam_mode(CamMode::INTRO), + marble_rad(1.0f), + marble_pos(0.0f, 0.0f, 0.0f), + marble_vel(0.0f, 0.0f, 0.0f), + marble_mat(Eigen::Matrix3f::Identity()), + flag_pos(0.0f, 0.0f, 0.0f), + timer(0), + sum_time(0), + music(level_music), + cur_level(0), + PBR_Enabled(1), + Refl_Refr_Enabled(1), + Shadows_Enabled(1), + Fractal_Iterations(16), + MarbleType(0), + free_camera_speed(1.f), + LIGHT_DIRECTION(Eigen::Vector3f(-0.36, 0.8, 0.48)), + PBR_METALLIC(0.5), + PBR_ROUGHNESS(0.4), + camera_size(0.035), + cur_ed_mode(DEFAULT), + level_editor(false), + gravity(0.005f) { ResetCheats(); frac_params.setOnes(); @@ -87,6 +98,9 @@ Scene::Scene(sf::Music* level_music) : buff_shatter.loadFromFile(shatter_wav); sound_shatter.setBuffer(buff_shatter); } +Eigen::Vector3f Scene::GetVelocity() { + return marble_vel; +} void Scene::LoadLevel(int level) { SetLevel(level); @@ -101,14 +115,17 @@ void Scene::SetMarble(float x, float y, float z, float r) { marble_pos = Eigen::Vector3f(x, y, z); marble_vel.setZero(); } +void Scene::SetMarbleScale(float r) { + marble_rad = r; +} void Scene::SetFlag(float x, float y, float z) { flag_pos = Eigen::Vector3f(x, y, z); } void Scene::SetLevel(int level) { - cur_level = level; - level_copy = all_levels[level]; + cur_level = level; + level_copy = levels.GetLevel(level); } void Scene::SetMode(CamMode mode) { @@ -122,6 +139,20 @@ void Scene::SetMode(CamMode mode) { cam_mode = mode; } +void Scene::SetResolution(sf::Shader& shader, int x, int y) +{ + ResX = x; + ResY = y; + const sf::Glsl::Vec2 window_res((float)ResX, (float)ResY); + shader.setUniform("iResolution", window_res); +} + +void Scene::SetWindowResolution(int x, int y) +{ + WinX = x; + WinY = y; +} + int Scene::GetCountdownTime() const { if (cam_mode == DEORBIT && timer >= frame_deorbit) { return timer - frame_deorbit; @@ -139,7 +170,7 @@ sf::Vector3f Scene::GetGoalDirection() const { goal_delta.y() = 0.0f; const float goal_dir = std::atan2(-goal_delta.z(), goal_delta.x()); const float a = cam_look_x - goal_dir; - const float b = std::abs(cam_look_y * 2.0f / pi); + const float b = std::abs(cam_look_y * 2.0f / PI); const float d = goal_delta.norm() / marble_rad; return sf::Vector3f(a, b, d); } @@ -157,13 +188,15 @@ void Scene::StopAllMusic() { for (int i = 0; i < num_level_music; ++i) { music[i].stop(); } + levels.StopAllMusic(); } -bool Scene::IsHighScore() const { +bool Scene::IsHighScore(){ if (cam_mode != GOAL) { return false; } else { - return final_time == high_scores.Get(cur_level); + float best = levels.GetBest(cur_level); + return best == final_time / 60.f; } } @@ -171,8 +204,8 @@ void Scene::StartNewGame() { sum_time = 0; play_single = false; ResetCheats(); - SetLevel(high_scores.GetStartLevel()); - is_fullrun = high_scores.HasCompleted(num_levels - 1); + SetLevel(0); + // is_fullrun = high_scores.HasCompleted(num_levels - 1); HideObjects(); SetMode(ORBIT); } @@ -207,6 +240,36 @@ void Scene::StartSingle(int level) { SetMode(ORBIT); } + + +void Scene::StartLevelEditor(int level) +{ + if (level < 0) + { + cur_level = -1; + level_copy = default_level; + } + else + { + cur_level = level; + level_copy = levels.GetLevel(level); + } + original_level_name = level_copy.txt; + play_single = true; + is_fullrun = false; + ResetCheats(); + level_editor = true; + SetMode(ORBIT); + enable_cheats = false; + free_camera = true; +} + +void Scene::StartDefault() +{ + cur_level = -1; + level_copy = default_level; +} + void Scene::ResetLevel() { if (cam_mode == MARBLE || play_single) { SetMode(DEORBIT); @@ -239,8 +302,17 @@ void Scene::ResetCheats() { zoom_to_scale = false; } +void Scene::Synchronize() +{ + for (int i = 0; i < 9; i++) + { + frac_params_smooth[i] = frac_params[i] = level_copy.params[i]; + } +} + void Scene::UpdateCamera(float dx, float dy, float dz, bool speedup) { //Camera update depends on current mode + gravity = level_copy.gravity; const int iters = speedup ? 5 : 1; if (cam_mode == INTRO) { UpdateIntro(false); @@ -287,9 +359,9 @@ void Scene::UpdateMarble(float dx, float dy) { } if (free_camera) { - cam_pos += cam_mat.block<3,1>(0,2) * (-marble_rad * dy * 0.5f); - cam_pos += cam_mat.block<3, 1>(0,0) * (marble_rad * dx * 0.5f); - cam_pos_smooth = cam_pos_smooth*0.8f + cam_pos*0.2f; + cam_pos += cam_mat.block<3,1>(0,2) * (-marble_rad * dy * 0.5f * free_camera_speed); + cam_pos += cam_mat.block<3, 1>(0,0) * (marble_rad * dx * 0.5f * free_camera_speed); + cam_pos_smooth = cam_pos_smooth*0.9f + cam_pos*0.1f; } else { //Apply all physics (gravity and collision) bool onGround = false; @@ -348,7 +420,7 @@ void Scene::UpdateMarble(float dx, float dy) { if (fx*fx + fz*fz < 6 * marble_rad*marble_rad) { final_time = timer; if (!enable_cheats) { - high_scores.Update(cur_level, final_time); + levels.UpdateScore(cur_level, final_time/60.f); } SetMode(GOAL); sound_goal.play(); @@ -392,7 +464,7 @@ void Scene::UpdateIntro(bool ssaver) { //Update demo fractal frac_params[0] = 1.6f; frac_params[1] = 2.0f + 0.5f*std::cos(timer * 0.0021f); - frac_params[2] = pi + 0.5f*std::cos(timer * 0.000287f); + frac_params[2] = PI + 0.5f*std::cos(timer * 0.000287f); frac_params[3] = -4.0f + 0.5f*std::sin(timer * 0.00161f); frac_params[4] = -1.0f + 0.1f*std::sin(timer * 0.00123f); frac_params[5] = -1.0f + 0.1f*std::cos(timer * 0.00137f); @@ -424,16 +496,16 @@ void Scene::UpdateOrbit() { const Eigen::Vector3f perp_vec(std::sin(t), 0.0f, std::cos(t)); cam_pos = orbit_pt + perp_vec * (orbit_dist * 2.5f); cam_pos_smooth = cam_pos_smooth*orbit_smooth + cam_pos*(1 - orbit_smooth); - + //Solve for the look direction cam_look_x = std::atan2(cam_pos_smooth.x(), cam_pos_smooth.z()); ModPi(cam_look_x_smooth, cam_look_x); cam_look_x_smooth = cam_look_x_smooth*(1 - a) + cam_look_x*a; - + //Update look smoothing cam_look_y = -0.3f; cam_look_y_smooth = cam_look_y_smooth*orbit_smooth + cam_look_y*(1 - orbit_smooth); - + //Update the camera matrix marble_mat.setIdentity(); MakeCameraRotation(); @@ -532,9 +604,9 @@ void Scene::UpdateCameraOnly(float dx, float dy, float dz) { //Update look direction cam_look_x += dx; cam_look_y += dy; - cam_look_y = std::min(std::max(cam_look_y, -pi / 2), pi / 2); - while (cam_look_x > pi) { cam_look_x -= 2 * pi; } - while (cam_look_x < -pi) { cam_look_x += 2 * pi; } + cam_look_y = std::min(std::max(cam_look_y, -PI / 2), PI / 2); + while (cam_look_x > PI) { cam_look_x -= 2 * PI; } + while (cam_look_x < -PI) { cam_look_x += 2 * PI; } //Update look smoothing const float a = (free_camera ? look_smooth_free_camera : look_smooth); @@ -634,17 +706,44 @@ void Scene::HideObjects() { void Scene::Write(sf::Shader& shader) const { shader.setUniform("iMat", sf::Glsl::Mat4(cam_mat.data())); - shader.setUniform("iMarblePos", free_camera ? - sf::Glsl::Vec3(999.0f, 999.0f, 999.0f) : - sf::Glsl::Vec3(marble_pos.x(), marble_pos.y(), marble_pos.z()) - ); - shader.setUniform("iMarbleRad", marble_rad); + if (level_editor) + { + shader.setUniform("iMarblePos", sf::Glsl::Vec3(level_copy.start_pos.x(), level_copy.start_pos.y(), level_copy.start_pos.z())); + shader.setUniform("iFlagPos", sf::Glsl::Vec3(level_copy.end_pos.x(), level_copy.end_pos.y(), level_copy.end_pos.z())); + } + else + { + shader.setUniform("iMarblePos", free_camera ? + sf::Glsl::Vec3(999.0f, 999.0f, 999.0f) : + sf::Glsl::Vec3(marble_pos.x(), marble_pos.y(), marble_pos.z()) + ); + shader.setUniform("iFlagPos", free_camera ? + sf::Glsl::Vec3(-999.0f, -999.0f, -999.0f) : + sf::Glsl::Vec3(flag_pos.x(), flag_pos.y(), flag_pos.z()) + ); + } + + if (cam_mode != INTRO) + { + shader.setUniform("LIGHT_DIRECTION", sf::Glsl::Vec3(level_copy.light_dir[0], level_copy.light_dir[1], level_copy.light_dir[2])); + shader.setUniform("PBR_ENABLED", PBR_Enabled); + shader.setUniform("PBR_METALLIC", level_copy.PBR_metal); + shader.setUniform("PBR_ROUGHNESS", level_copy.PBR_roughness); + } + else + { + shader.setUniform("LIGHT_DIRECTION", sf::Glsl::Vec3(LIGHT_DIRECTION[0], LIGHT_DIRECTION[1], LIGHT_DIRECTION[2])); + shader.setUniform("PBR_ENABLED", PBR_Enabled); + shader.setUniform("PBR_METALLIC", PBR_METALLIC); + shader.setUniform("PBR_ROUGHNESS", PBR_ROUGHNESS); + } - shader.setUniform("iFlagScale", level_copy.planet ? -marble_rad : marble_rad); - shader.setUniform("iFlagPos", free_camera ? - sf::Glsl::Vec3(-999.0f, -999.0f, -999.0f) : - sf::Glsl::Vec3(flag_pos.x(), flag_pos.y(), flag_pos.z()) - ); + shader.setUniform("BACKGROUND_COLOR", sf::Glsl::Vec3(level_copy.background_col[0], level_copy.background_col[1], level_copy.background_col[2])); + shader.setUniform("LIGHT_COLOR", sf::Glsl::Vec3(level_copy.light_col[0], level_copy.light_col[1], level_copy.light_col[2])); + + shader.setUniform("iMarbleRad", level_copy.marble_rad); + + shader.setUniform("iFlagScale", level_copy.planet ? -level_copy.marble_rad : level_copy.marble_rad); shader.setUniform("iFracScale", frac_params_smooth[0]); shader.setUniform("iFracAng1", frac_params_smooth[1]); @@ -653,6 +752,93 @@ void Scene::Write(sf::Shader& shader) const { shader.setUniform("iFracCol", sf::Glsl::Vec3(frac_params_smooth[6], frac_params_smooth[7], frac_params_smooth[8])); shader.setUniform("iExposure", exposure); + + + shader.setUniform("SHADOWS_ENABLED", Shadows_Enabled); + shader.setUniform("CAMERA_SIZE", camera_size*level_copy.marble_rad/0.035f); + shader.setUniform("FRACTAL_ITER", Fractal_Iterations); + shader.setUniform("REFL_REFR_ENABLED", Refl_Refr_Enabled); + shader.setUniform("MARBLE_MODE", MarbleType); +} + + + +void Scene::WriteRenderer(Renderer & rd) +{ + //Update the camera + vec3 cam_pos = vec3(cam_mat(0, 3), cam_mat(1, 3), cam_mat(2, 3)); + vec3 dirx = vec3(cam_mat(0, 0), cam_mat(1, 0), cam_mat(2, 0)); + vec3 diry = vec3(cam_mat(0, 1), cam_mat(1, 1), cam_mat(2, 1)); + vec3 dirz = -vec3(cam_mat(0, 2), cam_mat(1, 2), cam_mat(2, 2)); + rd.camera.SetPosition(cam_pos); + rd.camera.SetDirX(dirx); + rd.camera.SetDirY(diry); + rd.camera.SetDirZ(dirz); + rd.camera.SetCameraSize(camera_size*level_copy.marble_rad / 0.035f); + + //write all the uniform values to the rendering pipeline + for (auto &shader : rd.shader_pipeline) + { + WriteShader(shader); + } +} + +void Scene::WriteShader(ComputeShader& shader) +{ + + if (level_editor) + { + shader.setUniform("iMarblePos", vec3(level_copy.start_pos.x(), level_copy.start_pos.y(), level_copy.start_pos.z())); + shader.setUniform("iFlagPos", vec3(level_copy.end_pos.x(), level_copy.end_pos.y(), level_copy.end_pos.z())); + } + else + { + shader.setUniform("iMarblePos", free_camera ? + vec3(999.0f, 999.0f, 999.0f) : + vec3(marble_pos.x(), marble_pos.y(), marble_pos.z()) + ); + shader.setUniform("iFlagPos", free_camera ? + vec3(-999.0f, -999.0f, -999.0f) : + vec3(flag_pos.x(), flag_pos.y(), flag_pos.z()) + ); + } + + if (cam_mode != INTRO) + { + shader.setUniform("LIGHT_DIRECTION", vec3(level_copy.light_dir[0], level_copy.light_dir[1], level_copy.light_dir[2])); + shader.setUniform("PBR_ENABLED", PBR_Enabled); + shader.setUniform("PBR_METALLIC", level_copy.PBR_metal); + shader.setUniform("PBR_ROUGHNESS", level_copy.PBR_roughness); + } + else + { + shader.setUniform("LIGHT_DIRECTION", vec3(LIGHT_DIRECTION[0], LIGHT_DIRECTION[1], LIGHT_DIRECTION[2])); + shader.setUniform("PBR_ENABLED", PBR_Enabled); + shader.setUniform("PBR_METALLIC", PBR_METALLIC); + shader.setUniform("PBR_ROUGHNESS", PBR_ROUGHNESS); + } + + shader.setUniform("BACKGROUND_COLOR", vec3(level_copy.background_col[0], level_copy.background_col[1], level_copy.background_col[2])); + shader.setUniform("LIGHT_COLOR", vec3(level_copy.light_col[0], level_copy.light_col[1], level_copy.light_col[2])); + + shader.setUniform("iMarbleRad", level_copy.marble_rad); + + shader.setUniform("iFlagScale", level_copy.planet ? -level_copy.marble_rad : level_copy.marble_rad); + + shader.setUniform("iFracScale", frac_params_smooth[0]); + shader.setUniform("iFracAng1", frac_params_smooth[1]); + shader.setUniform("iFracAng2", frac_params_smooth[2]); + shader.setUniform("iFracShift", vec3(frac_params_smooth[3], frac_params_smooth[4], frac_params_smooth[5])); + shader.setUniform("iFracCol", vec3(frac_params_smooth[6], frac_params_smooth[7], frac_params_smooth[8])); + + shader.setUniform("iExposure", exposure); + + + shader.setUniform("SHADOWS_ENABLED", Shadows_Enabled); + shader.setUniform("CAMERA_SIZE", camera_size*level_copy.marble_rad / 0.035f); + shader.setUniform("FRACTAL_ITER", Fractal_Iterations); + shader.setUniform("REFL_REFR_ENABLED", Refl_Refr_Enabled); + shader.setUniform("MARBLE_MODE", MarbleType); } //Hard-coded to match the fractal @@ -790,7 +976,7 @@ bool Scene::MarbleCollision(float& delta_v) { if (de >= marble_rad) { return de < marble_rad * ground_ratio; } - + //Check if the marble has been crushed by the fractal if (de < marble_rad * 0.001f) { sound_shatter.play(); @@ -849,3 +1035,41 @@ void Scene::Cheat_Param(int param) { if (!enable_cheats) { return; } param_mod = param; } + +void Scene::ExitEditor() +{ + level_editor = false; +} + + +Eigen::Vector3f Scene::MouseRayCast(int mousex, int mousey, float min_dist) +{ + Eigen::Vector2f screen_pos = Eigen::Vector2f((float)mousex / (float)WinX,1.f - (float)mousey/ (float)WinY); + + Eigen::Vector2f uv = 2 * screen_pos - Eigen::Vector2f(1.f, 1.f); + uv.x() *= (float)ResX / (float)ResY; + + //Convert screen coordinate to 3d ray + Eigen::Vector4f v1 = Eigen::Vector4f(uv.x(), uv.y(), -FOCAL_DIST, 0.0); + v1.normalize(); + Eigen::Vector4f v2 = Eigen::Vector4f(camera_size*uv.x(), camera_size*uv.y(), 0, 1); + Eigen::Vector4f ray = cam_mat * v1; + Eigen::Vector4f p = cam_mat * v2; + + return RayMarch(Eigen::Vector3f(p[0], p[1], p[2]), Eigen::Vector3f(ray[0], ray[1], ray[2]), min_dist); +} + +Eigen::Vector3f Scene::RayMarch(const Eigen::Vector3f & pt, const Eigen::Vector3f & ray, float min_dist) +{ + float td = 0; + for (int i = 0; i < MAX_MARCHES && td < MAX_DIST; i++) + { + float de = DE(pt + td * ray); + if (de < min_dist) + { + break; + } + td += de; + } + return pt + td * ray; +} diff --git a/src/Scene.h b/src/Scene.h index 2430251..bf2b369 100644 --- a/src/Scene.h +++ b/src/Scene.h @@ -1,25 +1,32 @@ /* This file is part of the Marble Marcher (https://github.com/HackerPoet/MarbleMarcher). * Copyright(C) 2018 CodeParade -* +* * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. -* +* * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. -* +* * You should have received a copy of the GNU General Public License * along with this program.If not, see . */ #pragma once + #include "Level.h" #include #include #include + +#define MAX_DIST 20.f +#define MAX_MARCHES 1000 +#define MIN_DIST 1e-4f +#define FOCAL_DIST 1.73205080757 + class Scene { public: enum CamMode { @@ -33,24 +40,62 @@ class Scene { MIDPOINT }; + enum EditorMode + { + DEFAULT, + PLACE_MARBLE, + PLACE_FLAG + }; + + EditorMode cur_ed_mode; + FractalParams frac_params; + FractalParams frac_params_smooth; + + Level level_copy; + All_Levels levels; + + bool PBR_Enabled; + bool Refl_Refr_Enabled; + bool Shadows_Enabled; + int Fractal_Iterations; + float camera_size; + float free_camera_speed; + int MarbleType; + Eigen::Vector3f LIGHT_DIRECTION; + float PBR_METALLIC; + float PBR_ROUGHNESS; + std::string original_level_name; + + float marble_rad; + Eigen::Vector3f marble_pos; + Eigen::Vector3f marble_vel; + Eigen::Matrix3f marble_mat; + Scene(sf::Music* level_music); void LoadLevel(int level); void SetMarble(float x, float y, float z, float r); + void SetMarbleScale(float r); void SetFlag(float x, float y, float z); void SetMode(CamMode mode); void SetExposure(float e) { exposure = e; } + void SetResolution(sf::Shader& shader, int x, int y); + void SetWindowResolution(int x, int y); void EnbaleCheats() { enable_cheats = true; } + Eigen::Vector3f GetVelocity(); const Eigen::Vector3f& GetMarble() const { return marble_pos; }; float GetCamLook() const { return cam_look_x_smooth; } + float GetCamLookX() const { return cam_look_x; } + float GetMarbleScale() const { return marble_rad; } + const Eigen::Vector3f& GetFlagPos() const { return flag_pos; } CamMode GetMode() const { return cam_mode; } int GetLevel() const { return cur_level; } int GetCountdownTime() const; int GetSumTime() const { return sum_time; } sf::Vector3f GetGoalDirection() const; bool IsSinglePlay() const { return play_single; } - bool IsHighScore() const; + bool IsHighScore(); bool IsFullRun() const { return is_fullrun && !enable_cheats; } bool IsFreeCamera() const { return free_camera; } bool HasCheats() const { return enable_cheats; } @@ -62,8 +107,11 @@ class Scene { void StartNewGame(); void StartNextLevel(); void StartSingle(int level); + void StartLevelEditor(int level); + void StartDefault(); void ResetLevel(); void ResetCheats(); + void Synchronize(); void UpdateMarble(float dx=0.0f, float dy=0.0f); void UpdateCamera(float dx=0.0f, float dy=0.0f, float dz=0.0f, bool speedup=false); @@ -72,6 +120,14 @@ class Scene { void HideObjects(); void Write(sf::Shader& shader) const; + void WriteLVL(int lvl) + { + cur_level = lvl; + } + + void WriteRenderer(Renderer & rd); + + void WriteShader(ComputeShader & rd); float DE(const Eigen::Vector3f& pt) const; Eigen::Vector3f NP(const Eigen::Vector3f& pt) const; @@ -87,6 +143,11 @@ class Scene { void Cheat_Zoom(); void Cheat_Param(int param); + void ExitEditor(); + + Eigen::Vector3f MouseRayCast(int mousex, int mousey, float min_dist = MIN_DIST); + Eigen::Vector3f RayMarch(const Eigen::Vector3f& pt, const Eigen::Vector3f& ray, float min_dist = MIN_DIST); + protected: void SetLevel(int level); @@ -100,10 +161,10 @@ class Scene { private: int cur_level; - Level level_copy; bool is_fullrun; bool intro_needs_snap; bool play_single; + bool level_editor; Eigen::Matrix4f cam_mat; float cam_look_x; @@ -117,16 +178,11 @@ class Scene { float cam_dist_smooth; Eigen::Vector3f cam_pos_smooth; - float marble_rad; - Eigen::Vector3f marble_pos; - Eigen::Vector3f marble_vel; - Eigen::Matrix3f marble_mat; - Eigen::Vector3f flag_pos; - FractalParams frac_params; - FractalParams frac_params_smooth; + int ResX, ResY; + int WinX, WinY; int timer; int final_time; int sum_time; @@ -145,6 +201,7 @@ class Scene { sf::Music* music; + bool enable_cheats; bool free_camera; int gravity_type; @@ -153,4 +210,5 @@ class Scene { bool hyper_speed; bool disable_motion; bool zoom_to_scale; + float gravity; }; diff --git a/src/Scores.cpp b/src/Scores.cpp index cfe7e47..c76e4c4 100644 --- a/src/Scores.cpp +++ b/src/Scores.cpp @@ -15,5 +15,3 @@ * along with this program.If not, see . */ #include "Scores.h" - -Scores high_scores; \ No newline at end of file diff --git a/src/Scores.h b/src/Scores.h index 7de5585..c1dee05 100644 --- a/src/Scores.h +++ b/src/Scores.h @@ -15,53 +15,5 @@ * along with this program.If not, see . */ #pragma once -#include "Level.h" #include #include - -class Scores { -public: - Scores() { memset(this, -1, sizeof(Scores)); } - - bool HasUnlocked(int level) const { - return level <= 0 || scores[level - 1] >= 0; - } - bool HasCompleted(int level) const { - return scores[level] >= 0; - } - int Get(int level) const { - return scores[level]; - } - - int GetStartLevel() const { - for (int i = 0; i < num_levels; ++i) { - if (!HasCompleted(i)) { - return i; - } - } - return 0; - } - - bool Update(int level, int score) { - if (score < scores[level] || scores[level] < 0) { - scores[level] = score; - return true; - } - return false; - } - - void Load(const std::string& fname) { - std::ifstream fin(fname, std::ios::binary); - if (!fin) { return; } - fin.read((char*)this, sizeof(Scores)); - } - - void Save(const std::string& fname) { - std::ofstream fout(fname, std::ios::binary); - if (!fout) { return; } - fout.write((const char*)this, sizeof(Scores)); - } - - int scores[num_levels]; -}; -extern Scores high_scores; diff --git a/src/Settings.h b/src/Settings.h index 1fded1d..2fa8c95 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -16,6 +16,7 @@ */ #pragma once #include +#include class Settings { public: @@ -37,4 +38,59 @@ class Settings { bool mute; int mouse_sensitivity; +}; + +class AdditionalSettings +{ +public: + int screenshot_width; + int screenshot_height; + std::string lang; + + AdditionalSettings() : + screenshot_width(1920), + screenshot_height(1080), + lang("English") + {} + + void Load(const std::string& fname) { + int increment = 0; + std::ifstream config; + config.open(fname); + if (config.fail()) + { + return; + } + std::string line; + while (getline(config, line)) + { + if (line.substr(0, 1) != "#") + { + increment++; + std::istringstream iss(line); + float num; + while ((iss >> num)) + { + switch (increment) + { + case 1: + screenshot_width = num; + break; + case 2: + screenshot_height = num; + break; + case 4: + + break; + default: + break; + } + } + if (increment == 3) + { + lang = line; + } + } + } + } }; \ No newline at end of file diff --git a/src/Shaders.cpp b/src/Shaders.cpp new file mode 100644 index 0000000..d6fb9a4 --- /dev/null +++ b/src/Shaders.cpp @@ -0,0 +1,204 @@ +#include "Shaders.h" + +ComputeShader::ComputeShader() +{ + int work_grp_cnt[3]; + + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 0, &work_grp_cnt[0]); + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 1, &work_grp_cnt[1]); + glGetIntegeri_v(GL_MAX_COMPUTE_WORK_GROUP_COUNT, 2, &work_grp_cnt[2]); +} + +ComputeShader::ComputeShader(const std::string file_path) +{ + LoadShader(file_path); +} + +std::string ComputeShader::LoadFileText(fs::path path) +{ + std::string text; + std::ifstream TextStream(path, std::ios::in); + if (TextStream.is_open()) + { + std::string Line = ""; + while (getline(TextStream, Line)) + text += Line + "\n"; + TextStream.close(); + } + else + { + ERROR_MSG("Impossible to open text file"); + } + return text; +} + +void ComputeShader::LoadShader(const std::string file_path) +{ + // Create the shaders + GLuint ComputeShaderID = glCreateShader(GL_COMPUTE_SHADER); + + // Read the Vertex Shader code from the file + std::string ComputeShaderCode = PreprocessIncludes(fs::path(file_path)); + + GLint Result = GL_FALSE; + int InfoLogLength; + + // Compile Compute Shader + char const * ComputeSourcePointer = ComputeShaderCode.c_str(); + glShaderSource(ComputeShaderID, 1, &ComputeSourcePointer, NULL); + glCompileShader(ComputeShaderID); + + // Check Compute Shader + glGetShaderiv(ComputeShaderID, GL_COMPILE_STATUS, &Result); + glGetShaderiv(ComputeShaderID, GL_INFO_LOG_LENGTH, &InfoLogLength); + if (Result == GL_FALSE) + { + std::vector ComputeShaderErrorMessage(InfoLogLength + 1); + glGetShaderInfoLog(ComputeShaderID, InfoLogLength, NULL, &ComputeShaderErrorMessage[0]); + ERROR_MSG(("Compute shader compilation error. \n" + std::string(&ComputeShaderErrorMessage[0])).c_str()); + SaveErrors(file_path, ComputeShaderCode, std::string(&ComputeShaderErrorMessage[0])); + } + + // Link the program + ProgramID = glCreateProgram(); + glAttachShader(ProgramID, ComputeShaderID); + glLinkProgram(ProgramID); + + // Check the program + glGetProgramiv(ProgramID, GL_LINK_STATUS, &Result); + glGetProgramiv(ProgramID, GL_INFO_LOG_LENGTH, &InfoLogLength); + if (Result == GL_FALSE) + { + std::vector ProgramErrorMessage(InfoLogLength + 1); + glGetProgramInfoLog(ProgramID, InfoLogLength, NULL, &ProgramErrorMessage[0]); + ERROR_MSG(("Compute program error. \n" + std::string(&ProgramErrorMessage[0])).c_str()); + } +} + +void ComputeShader::Run(vec2 global) +{ + glUseProgram(ProgramID); + glDispatchCompute(ceil(global.x), ceil(global.y), 1); + glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); +} + +void ComputeShader::setUniform(std::string name, float X, float Y) +{ + glUseProgram(ProgramID); + GLuint A = glGetUniformLocation(ProgramID, name.c_str()); + glUniform2f(A, X, Y); +} + +void ComputeShader::setUniform(std::string name, float X, float Y, float Z) +{ + glUseProgram(ProgramID); + GLuint A = glGetUniformLocation(ProgramID, name.c_str()); + glUniform3f(A, X, Y, Z); +} + +void ComputeShader::setUniform(std::string name, float X) +{ + glUseProgram(ProgramID); + GLuint A = glGetUniformLocation(ProgramID, name.c_str()); + glUniform1f(A, X); +} + +void ComputeShader::setUniform(std::string name, int X) +{ + glUseProgram(ProgramID); + GLuint A = glGetUniformLocation(ProgramID, name.c_str()); + glUniform1i(A, X); +} + +void ComputeShader::setUniform(std::string name, glm::mat3 X, bool transpose) +{ + glUseProgram(ProgramID); + GLuint A = glGetUniformLocation(ProgramID, name.c_str()); + glUniformMatrix3fv(A, 1, transpose, glm::value_ptr(X)); +} + +void ComputeShader::setUniform(std::string name, glm::vec3 X) +{ + glUseProgram(ProgramID); + GLuint A = glGetUniformLocation(ProgramID, name.c_str()); + glUniform3fv(A, 1, glm::value_ptr(X)); +} + +void ComputeShader::setUniform(std::string name, glm::vec2 X) +{ + glUseProgram(ProgramID); + GLuint A = glGetUniformLocation(ProgramID, name.c_str()); + glUniform2fv(A, 1, glm::value_ptr(X)); +} + +void ComputeShader::setCamera(gl_camera cam) +{ + setUniform("Camera.position", cam.position); + setUniform("Camera.bokeh", cam.bokeh); + setUniform("Camera.dirx", cam.dirx); + setUniform("Camera.diry", cam.diry); + setUniform("Camera.dirz", cam.dirz); + setUniform("Camera.aspect_ratio", cam.aspect_ratio); + setUniform("Camera.exposure", cam.exposure); + setUniform("Camera.focus", cam.focus); + setUniform("Camera.FOV", cam.FOV); + setUniform("Camera.mblur", cam.mblur); + setUniform("Camera.position", cam.position); + setUniform("Camera.resolution", cam.resolution); + setUniform("Camera.size", cam.size); + setUniform("Camera.speckle", cam.speckle); + setUniform("Camera.stepN", cam.stepN); +} + +GLuint ComputeShader::getNativeHandle() +{ + return ProgramID; +} + +bool INIT() +{ + if (glewInit() != GLEW_OK) { + ERROR_MSG("Failed to initialize GLEW\n"); + return false; + } + return true; +} + + +std::string ComputeShader::PreprocessIncludes(const fs::path& filename, int level /*= 0 */) +{ + if (level > 32) + ERROR_MSG("Header inclusion depth limit reached, might be caused by cyclic header inclusion"); + using namespace std; + + static const regex re("^[ ]*#include\s*[\"<](.*)[\">].*"); + stringstream input; + stringstream output; + input << LoadFileText(filename); + + smatch matches; + string line; + while (std::getline(input, line)) + { + if (regex_search(line, matches, re)) + { + //add the code from the included file + std::string include_file = filename.parent_path().string() + "/" + matches[1].str(); + output << PreprocessIncludes(include_file, level + 1) << endl; + } + else + { + output << line << "\n"; + } + } + return output.str(); +} + +void ComputeShader::SaveErrors(const fs::path& filename, std::string code, std::string errors) +{ + fs::path outf = (filename.parent_path() / filename.filename()).concat("_error.txt"); + std::ofstream error_out(outf); + + error_out << code << std::endl << errors; + error_out.close(); +} \ No newline at end of file diff --git a/src/Shaders.h b/src/Shaders.h new file mode 100644 index 0000000..77e36ab --- /dev/null +++ b/src/Shaders.h @@ -0,0 +1,59 @@ +#pragma once +#include + +#if !defined(__gl_h_) && !defined(__GL_H__) && !defined(_GL_H) && !defined(__X_GL_H) +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fs = std::filesystem; + +#ifdef _WIN32 +#include +#define ERROR_MSG(x) MessageBox(nullptr, TEXT(x), TEXT("ERROR"), MB_OK); +#else +#define ERROR_MSG(x) std::cerr << x << std::endl; +#endif + +bool INIT(); + +class ComputeShader +{ +public: + GLuint ProgramID; + + ComputeShader(); + ComputeShader(const std::string file_path); + + void LoadShader(const std::string file_path); + + void Run(vec2 global); + + void setUniform(std::string name, float X, float Y); + void setUniform(std::string name, float X, float Y, float Z); + void setUniform(std::string name, float X); + void setUniform(std::string name, int X); + void setUniform(std::string name, glm::mat3 X, bool transpose); + void setUniform(std::string name, glm::vec3 X); + void setUniform(std::string name, glm::vec2 X); + void setCamera(gl_camera cam); + + GLuint getNativeHandle(); + + std::string PreprocessIncludes(const fs::path & filename, int level = 0); + + void SaveErrors(const fs::path & filename, std::string code, std::string errors); + + std::string LoadFileText(fs::path path); +}; diff --git a/src/fixthings.c b/src/fixthings.c new file mode 100644 index 0000000..5718993 --- /dev/null +++ b/src/fixthings.c @@ -0,0 +1,10 @@ +#define _CRTBLD +#include + +FILE *__cdecl __acrt_iob_func(unsigned index) +{ + return &(__iob_func()[index]); +} + +typedef FILE *__cdecl (*_f__acrt_iob_func)(unsigned index); +_f__acrt_iob_func __MINGW_IMP_SYMBOL(__acrt_iob_func) = __acrt_iob_func; diff --git a/winMacOSBuild.sh b/winMacOSBuild.sh new file mode 100755 index 0000000..2f1ad50 --- /dev/null +++ b/winMacOSBuild.sh @@ -0,0 +1,48 @@ +#! /bin/bash + +mkdir -p winBuild +cp src/* winBuild/ +cd winBuild +wget -nc https://bitbucket.org/eigen/eigen/get/3.3.7.zip +wget -nc https://www.sfml-dev.org/files/SFML-2.5.1-windows-gcc-7.3.0-mingw-64-bit.zip +wget -nc https://astuteinternet.dl.sourceforge.net/project/anttweakbar/AntTweakBar_116.zip +unzip -u -o 3.3.7.zip +unzip -u -o SFML-2.5.1-windows-gcc-7.3.0-mingw-64-bit.zip +unzip -u -o AntTweakBar_116.zip +git clone https://github.com/SFML/SFML.git +export CPATH=`pwd`/eigen-eigen-323c052e1731/:`pwd`/SFML-2.5.1/include:`pwd`/SFML/exttlibs/headers:`pwd`/AntTweakBar/lib:`pwd`/AntTweakBar/include + +x86_64-w64-mingw32-g++ -Ofast -c Level.cpp -D SFML_STATIC +x86_64-w64-mingw32-g++ -Ofast -c Level.h -D SFML_STATIC +x86_64-w64-mingw32-g++ -Ofast -c Overlays.cpp -D SFML_STATIC +x86_64-w64-mingw32-g++ -Ofast -c Overlays.h -D SFML_STATIC +x86_64-w64-mingw32-g++ -Ofast -c Res.h -D SFML_STATIC +x86_64-w64-mingw32-g++ -Ofast -c Scene.cpp -D SFML_STATIC +x86_64-w64-mingw32-g++ -Ofast -c Scene.h -D SFML_STATIC +x86_64-w64-mingw32-g++ -Ofast -c Scores.cpp -D SFML_STATIC +x86_64-w64-mingw32-g++ -Ofast -c Scores.h -D SFML_STATIC +x86_64-w64-mingw32-g++ -Ofast -c SelectRes.cpp -D SFML_STATIC +x86_64-w64-mingw32-g++ -Ofast -c SelectRes.h -D SFML_STATIC +x86_64-w64-mingw32-g++ -Ofast -c Settings.h -D SFML_STATIC + +rm MarbleMarcherSources.a +x86_64-w64-mingw32-ar rvs MarbleMarcherSources.a Level.o +x86_64-w64-mingw32-ar rvs MarbleMarcherSources.a Level.h.gch +x86_64-w64-mingw32-ar rvs MarbleMarcherSources.a Overlays.o +x86_64-w64-mingw32-ar rvs MarbleMarcherSources.a Overlays.h.gch +x86_64-w64-mingw32-ar rvs MarbleMarcherSources.a Res.h.gch +x86_64-w64-mingw32-ar rvs MarbleMarcherSources.a Scene.o +x86_64-w64-mingw32-ar rvs MarbleMarcherSources.a Scene.h.gch +x86_64-w64-mingw32-ar rvs MarbleMarcherSources.a Scores.o +x86_64-w64-mingw32-ar rvs MarbleMarcherSources.a Scores.h.gch +x86_64-w64-mingw32-ar rvs MarbleMarcherSources.a SelectRes.o +x86_64-w64-mingw32-ar rvs MarbleMarcherSources.a SelectRes.h.gch +x86_64-w64-mingw32-ar rvs MarbleMarcherSources.a Settings.h.gch + +x86_64-w64-mingw32-g++ -Ofast -pipe -o MarbleMarcher Main.cpp -D SFML_STATIC -D SFML_USE_STATIC_LIBS=true \ +-static -static-libgcc -static-libstdc++ MarbleMarcherSources.a -L `pwd`/SFML/extlibs/libs-mingw/x64 -L \ +`pwd`/SFML-2.5.1/lib -L `pwd`/AntTweakBar/lib/AntTweakBar.dll -lsfml-window-s -lsfml-graphics-s -lsfml-audio-s -lsfml-system-s -lopengl32 -lfreetype \ +-lwinmm -lgdi32 -lopenal32 -lvorbisfile -lvorbisenc -lvorbis -lflac -logg + +cd .. +cp winBuild/MarbleMarcher.exe ./